package diagnostics import ( "testing" "gitea.maximumdirect.net/eric/notarius/internal/framework/contracts" ) func TestAggregatorMergesByOriginAndPreservesFirstOccurrenceOrder(t *testing.T) { aggregator := Aggregator{} for _, group := range []contracts.DiagnosticGroup{ groupForAggregation("first", "message one", contracts.DiagnosticDispositionAdvisory, contracts.DiagnosticCategoryDataQuality, "dnd/spells"), groupForAggregation("second", "message two", contracts.DiagnosticDispositionWarning, contracts.DiagnosticCategoryFallback, "dnd/items"), groupForAggregation("third", "message three", contracts.DiagnosticDispositionAdvisory, contracts.DiagnosticCategoryDataQuality, "dnd/spells"), } { if err := aggregator.Add(group); err != nil { t.Fatalf("Add() error = %v", err) } } collection := aggregator.Collection() if len(collection.Groups) != 2 { t.Fatalf("group count = %d, want 2", len(collection.Groups)) } if got := []string{collection.Groups[0].Origin.ModuleKey, collection.Groups[1].Origin.ModuleKey}; !equalStrings(got, []string{"dnd/spells", "dnd/items"}) { t.Fatalf("group order = %#v, want first occurrence order", got) } if collection.Groups[0].OccurrenceCount != 2 || collection.Groups[0].OmittedSampleCount != 0 { t.Fatalf("merged group = %#v, want two represented occurrences", collection.Groups[0]) } if got := []string{collection.Groups[0].Samples[0].Scope, collection.Groups[0].Samples[1].Scope}; !equalStrings(got, []string{"first", "third"}) { t.Fatalf("merged samples = %#v, want first occurrence order", got) } } func TestAggregatorEnforcesWarningBoundAndTruncatesOnlyNonWarnings(t *testing.T) { warnings := Aggregator{} for index := 0; index < MaxWarningGroups; index++ { group := groupForAggregation("scope", "message", contracts.DiagnosticDispositionWarning, contracts.DiagnosticCategoryFallback, "module") group.ReasonCode = "warning-" + string(rune('a'+index)) if err := warnings.Add(group); err != nil { t.Fatalf("warning Add(%d) error = %v", index, err) } } if err := warnings.Add(groupForAggregation("overflow", "overflow", contracts.DiagnosticDispositionWarning, contracts.DiagnosticCategoryFallback, "overflow")); err == nil { t.Fatal("warning overflow error = nil, want error") } nonWarnings := Aggregator{} for index := 0; index < MaxNonWarningGroups+1; index++ { group := groupForAggregation("scope", "message", contracts.DiagnosticDispositionAdvisory, contracts.DiagnosticCategoryDataQuality, "module") group.ReasonCode = "advisory-" + string(rune('a'+index)) if err := nonWarnings.Add(group); err != nil { t.Fatalf("non-warning Add(%d) error = %v", index, err) } } collection := nonWarnings.Collection() if len(collection.Groups) != MaxNonWarningGroups || !collection.Truncated || collection.UnrepresentedOccurrenceCount != 1 { t.Fatalf("collection = %#v, want bounded non-warning groups and one unrepresented occurrence", collection) } } func groupForAggregation(scope string, message string, disposition contracts.DiagnosticDisposition, category contracts.DiagnosticCategory, module string) contracts.DiagnosticGroup { return contracts.DiagnosticGroup{ Disposition: disposition, Category: category, ReasonCode: "source_unrelated", Origin: contracts.DiagnosticOrigin{Stage: contracts.DiagnosticOriginStageExtract, StepID: "extract", LaneID: "spells", ModuleKey: module}, OccurrenceCount: 1, Samples: []contracts.DiagnosticSample{{Scope: scope, Message: message}}, } }