Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
5141cba
Characterize Wolverine validation, authorization and discovery before…
woksin Nov 11, 2026
733bc91
Split Wolverine validation, authorization and handler discovery into …
woksin Nov 11, 2026
c8ef54d
Satisfy analyzers in Wolverine discovery helpers
woksin Nov 11, 2026
c6c47bc
Adopt C# 15 idioms in Wolverine discovery
woksin Nov 11, 2026
8c27066
Characterize Wolverine FluentValidation option resolution with public…
woksin Nov 11, 2026
f3b64d5
Characterize Wolverine saga, return, production-link and slice-patter…
woksin Nov 11, 2026
48fadca
Split Wolverine saga discovery into role and correlation helpers
woksin Nov 11, 2026
e158ae7
Restore saga correlation precedence
woksin Nov 11, 2026
61f2ab2
Split Wolverine production links, return consequences and slice patte…
woksin Nov 11, 2026
58c0615
Decompose slice patterns, return consequences and production links fu…
woksin Nov 11, 2026
c301995
Run Wolverine variant analysis in Because instead of Establish
woksin Nov 11, 2026
d269cff
Fix format findings in Wolverine batch 6 files and specs
woksin Nov 11, 2026
043f0bf
Characterize legacy declaration path mapping before refactoring
woksin Nov 11, 2026
c07fa6f
Split CritterStack source mapping, placement and enum concept helpers
woksin Nov 11, 2026
96696c0
Pin missing source structure diagnostic order
woksin Nov 11, 2026
35e542a
Pin canonical expectation matching and parsing
woksin Nov 11, 2026
df6850a
Table-drive canonical expectation checks and parsing
woksin Nov 11, 2026
d6cdc27
Format new specs
woksin Nov 11, 2026
83fe574
Capture expectation outcomes in Because and pin the fan-out collectio…
woksin Nov 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 54 additions & 43 deletions Source/DotNET/Canonical/Expectations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,28 @@ enum ExpectationKind

sealed record Expectation(ExpectationKind Kind, string Value)
{
public bool IsMetBy(GeneratedScreenplayDefinition result) => Kind switch
static readonly Dictionary<ExpectationKind, Func<GeneratedScreenplayDefinition, string, bool>> _checks = new()
{
ExpectationKind.Source => result.Source.Contains(Value, StringComparison.Ordinal),
ExpectationKind.NotSource => !result.Source.Contains(Value, StringComparison.Ordinal),
ExpectationKind.SourceHash => IsSourceHash(result, Value),
ExpectationKind.Diagnostic => result.Diagnostics.Any(_ => _.Code == Value),
ExpectationKind.DiagnosticMessage => IsDiagnosticMessageIn(result, Value),
ExpectationKind.Artifact => result.Graph.Artifacts.Any(_ => $"{_.Key.Kind}:{_.Variants[0].Definition.Name}" == Value),
ExpectationKind.ArtifactCount => IsArtifactCountIn(result, Value),
ExpectationKind.Evidence => IsEvidenceIn(result, Value),
ExpectationKind.EvidenceCount => IsEvidenceCountIn(result, Value),
ExpectationKind.Relationship => IsRelationshipIn(result, Value),
ExpectationKind.RelationshipCount => IsRelationshipCountIn(result, Value),
ExpectationKind.Identifier => IsIdentifierIn(result, Value),
ExpectationKind.NotIdentifier => !IsIdentifierIn(result, Value),
ExpectationKind.Grouping => IsGroupingIn(result, Value),
ExpectationKind.FanOut => IsFanOutIn(result, Value),
_ => false
[ExpectationKind.Source] = (result, value) => result.Source.Contains(value, StringComparison.Ordinal),
[ExpectationKind.NotSource] = (result, value) => !result.Source.Contains(value, StringComparison.Ordinal),
[ExpectationKind.SourceHash] = IsSourceHash,
[ExpectationKind.Diagnostic] = (result, value) => result.Diagnostics.Any(_ => _.Code == value),
[ExpectationKind.DiagnosticMessage] = IsDiagnosticMessageIn,
[ExpectationKind.Artifact] = (result, value) => result.Graph.Artifacts.Any(_ => $"{_.Key.Kind}:{_.Variants[0].Definition.Name}" == value),
[ExpectationKind.ArtifactCount] = IsArtifactCountIn,
[ExpectationKind.Evidence] = IsEvidenceIn,
[ExpectationKind.EvidenceCount] = IsEvidenceCountIn,
[ExpectationKind.Relationship] = IsRelationshipIn,
[ExpectationKind.RelationshipCount] = IsRelationshipCountIn,
[ExpectationKind.Identifier] = IsIdentifierIn,
[ExpectationKind.NotIdentifier] = (result, value) => !IsIdentifierIn(result, value),
[ExpectationKind.Grouping] = IsGroupingIn,
[ExpectationKind.FanOut] = IsFanOutIn
};

public bool IsMetBy(GeneratedScreenplayDefinition result) =>
_checks.TryGetValue(Kind, out var check) && check(result, Value);

public override string ToString() => $"{Kind}: {Value}";

static bool IsDiagnosticMessageIn(GeneratedScreenplayDefinition result, string value)
Expand Down Expand Up @@ -180,20 +182,24 @@ static bool IsFanOutIn(GeneratedScreenplayDefinition result, string value)
return false;
}

var parent = result.Graph.Artifacts
.FirstOrDefault(_ =>
_.Key.Kind == ArtifactKind.Event &&
_.Variants.Any(variant => variant.Definition.Name == parts[0]))?.Key.Subject.Value;
var parent = EventSubject(result, parts[0]);
return parent is not null && result.Graph.Relationships.Any(relationship =>
relationship.Key.Kind == RelationshipKind.Consumes &&
ArtifactName(result, relationship.Key.Source, ArtifactKind.Reducer) is not null &&
ArtifactName(result, relationship.Key.Target, ArtifactKind.Event) == parts[1] &&
relationship.Definitions.Any(definition =>
definition.Key.Discriminator?.StartsWith($"marten:fan-out-child:{parent}:{parts[3]}:", StringComparison.Ordinal) == true &&
definition.SourceMember == parts[2] &&
definition.IsCollection));
relationship.Definitions.Any(definition => IsFanOutChild(definition, parent, parts[2], parts[3])));
}

static string? EventSubject(GeneratedScreenplayDefinition result, string name) => result.Graph.Artifacts
.FirstOrDefault(_ =>
_.Key.Kind == ArtifactKind.Event &&
_.Variants.Any(variant => variant.Definition.Name == name))?.Key.Subject.Value;

static bool IsFanOutChild(RelationshipDefinition definition, string parent, string member, string mode) =>
definition.Key.Discriminator?.StartsWith($"marten:fan-out-child:{parent}:{mode}:", StringComparison.Ordinal) == true &&
definition.SourceMember == member &&
definition.IsCollection;

static string? ArtifactName(
GeneratedScreenplayDefinition result,
SubjectId subject,
Expand All @@ -206,6 +212,25 @@ static bool IsFanOutIn(GeneratedScreenplayDefinition result, string value)

static class Expectations
{
static readonly Dictionary<string, ExpectationKind> _kinds = new()
{
["source"] = ExpectationKind.Source,
["not-source"] = ExpectationKind.NotSource,
["source-sha256"] = ExpectationKind.SourceHash,
["diagnostic"] = ExpectationKind.Diagnostic,
["diagnostic-message"] = ExpectationKind.DiagnosticMessage,
["artifact"] = ExpectationKind.Artifact,
["artifact-count"] = ExpectationKind.ArtifactCount,
["evidence"] = ExpectationKind.Evidence,
["evidence-count"] = ExpectationKind.EvidenceCount,
["relationship"] = ExpectationKind.Relationship,
["relationship-count"] = ExpectationKind.RelationshipCount,
["identifier"] = ExpectationKind.Identifier,
["not-identifier"] = ExpectationKind.NotIdentifier,
["grouping"] = ExpectationKind.Grouping,
["fan-out"] = ExpectationKind.FanOut
};

public static IReadOnlyList<Expectation> Read(string path) =>
[
.. File.ReadAllLines(path)
Expand All @@ -222,25 +247,11 @@ static Expectation Parse(string value)
throw new InvalidExpectation(value);
}

var kind = value[..separator] switch
if (!_kinds.TryGetValue(value[..separator], out var kind))
{
"source" => ExpectationKind.Source,
"not-source" => ExpectationKind.NotSource,
"source-sha256" => ExpectationKind.SourceHash,
"diagnostic" => ExpectationKind.Diagnostic,
"diagnostic-message" => ExpectationKind.DiagnosticMessage,
"artifact" => ExpectationKind.Artifact,
"artifact-count" => ExpectationKind.ArtifactCount,
"evidence" => ExpectationKind.Evidence,
"evidence-count" => ExpectationKind.EvidenceCount,
"relationship" => ExpectationKind.Relationship,
"relationship-count" => ExpectationKind.RelationshipCount,
"identifier" => ExpectationKind.Identifier,
"not-identifier" => ExpectationKind.NotIdentifier,
"grouping" => ExpectationKind.Grouping,
"fan-out" => ExpectationKind.FanOut,
_ => throw new InvalidExpectation(value)
};
throw new InvalidExpectation(value);
}

return new(kind, value[(separator + 1)..].Trim());
}
}
Expand Down
Loading