package contracts import ( "strings" "testing" ) func TestProducerDiagnosticValidationAcceptsClassificationMatrix(t *testing.T) { for _, test := range []struct { name string disposition DiagnosticDisposition category DiagnosticCategory }{ {name: "configuration warning", disposition: DiagnosticDispositionWarning, category: DiagnosticCategoryConfiguration}, {name: "degradation warning", disposition: DiagnosticDispositionWarning, category: DiagnosticCategoryDegradation}, {name: "incomplete validation warning", disposition: DiagnosticDispositionWarning, category: DiagnosticCategoryValidationIncomplete}, {name: "fallback warning", disposition: DiagnosticDispositionWarning, category: DiagnosticCategoryFallback}, {name: "quality advisory", disposition: DiagnosticDispositionAdvisory, category: DiagnosticCategoryDataQuality}, {name: "normalization observation", disposition: DiagnosticDispositionObservation, category: DiagnosticCategoryNormalization}, } { t.Run(test.name, func(t *testing.T) { diagnostic := validProducerDiagnostic() diagnostic.Disposition = test.disposition diagnostic.Category = test.category if err := diagnostic.Validate(); err != nil { t.Fatalf("Validate() error = %v", err) } }) } } func TestProducerDiagnosticValidationRejectsInvalidFieldsAndCounts(t *testing.T) { tooLongReason := strings.Repeat("r", MaxDiagnosticReasonCodeBytes+1) tooLongScope := strings.Repeat("s", MaxDiagnosticScopeBytes+1) tooLongMessage := strings.Repeat("m", MaxDiagnosticMessageBytes+1) for _, test := range []struct { name string mutate func(*ProducerDiagnostic) }{ {name: "invalid classification", mutate: func(diagnostic *ProducerDiagnostic) { diagnostic.Category = DiagnosticCategoryDataQuality }}, {name: "blank reason", mutate: func(diagnostic *ProducerDiagnostic) { diagnostic.ReasonCode = " \t" }}, {name: "invalid reason UTF-8", mutate: func(diagnostic *ProducerDiagnostic) { diagnostic.ReasonCode = string([]byte{0xff}) }}, {name: "oversized reason", mutate: func(diagnostic *ProducerDiagnostic) { diagnostic.ReasonCode = tooLongReason }}, {name: "blank scope", mutate: func(diagnostic *ProducerDiagnostic) { diagnostic.Samples[0].Scope = "\n" }}, {name: "invalid scope UTF-8", mutate: func(diagnostic *ProducerDiagnostic) { diagnostic.Samples[0].Scope = string([]byte{0xff}) }}, {name: "oversized scope", mutate: func(diagnostic *ProducerDiagnostic) { diagnostic.Samples[0].Scope = tooLongScope }}, {name: "blank message", mutate: func(diagnostic *ProducerDiagnostic) { diagnostic.Samples[0].Message = " " }}, {name: "invalid message UTF-8", mutate: func(diagnostic *ProducerDiagnostic) { diagnostic.Samples[0].Message = string([]byte{0xff}) }}, {name: "oversized message", mutate: func(diagnostic *ProducerDiagnostic) { diagnostic.Samples[0].Message = tooLongMessage }}, {name: "zero occurrences", mutate: func(diagnostic *ProducerDiagnostic) { diagnostic.OccurrenceCount = 0 }}, {name: "missing samples", mutate: func(diagnostic *ProducerDiagnostic) { diagnostic.Samples = nil diagnostic.OmittedSampleCount = diagnostic.OccurrenceCount }}, {name: "too many samples", mutate: func(diagnostic *ProducerDiagnostic) { diagnostic.OccurrenceCount = 4 diagnostic.Samples = []DiagnosticSample{{Scope: "one", Message: "one"}, {Scope: "two", Message: "two"}, {Scope: "three", Message: "three"}, {Scope: "four", Message: "four"}} diagnostic.OmittedSampleCount = 0 }}, {name: "duplicate samples", mutate: func(diagnostic *ProducerDiagnostic) { diagnostic.OccurrenceCount = 2 diagnostic.Samples = []DiagnosticSample{{Scope: "scope", Message: "message"}, {Scope: "scope", Message: "message"}} diagnostic.OmittedSampleCount = 0 }}, {name: "inconsistent omission", mutate: func(diagnostic *ProducerDiagnostic) { diagnostic.OmittedSampleCount = 1 }}, {name: "producer chunk context", mutate: func(diagnostic *ProducerDiagnostic) { chunkIndex := 0; diagnostic.Samples[0].ChunkIndex = &chunkIndex }}, } { t.Run(test.name, func(t *testing.T) { diagnostic := validProducerDiagnostic() test.mutate(&diagnostic) if err := diagnostic.Validate(); err == nil { t.Fatal("Validate() error = nil, want invalid diagnostic error") } }) } } func TestValidateProducerDiagnosticsEnforcesLocalGroupBound(t *testing.T) { diagnostics := make([]ProducerDiagnostic, MaxProducerDiagnosticGroups) for index := range diagnostics { diagnostics[index] = validProducerDiagnostic() diagnostics[index].ReasonCode = "reason-" + string(rune('a'+index)) } if err := ValidateProducerDiagnostics(diagnostics); err != nil { t.Fatalf("ValidateProducerDiagnostics() error = %v", err) } diagnostics = append(diagnostics, validProducerDiagnostic()) if err := ValidateProducerDiagnostics(diagnostics); err == nil { t.Fatal("ValidateProducerDiagnostics() error = nil, want excessive-group error") } } func TestDiagnosticGroupValidationPreservesChunkIndexZero(t *testing.T) { chunkIndex := 0 group := DiagnosticGroup{ Disposition: DiagnosticDispositionAdvisory, Category: DiagnosticCategoryDataQuality, ReasonCode: "unresolved", Origin: DiagnosticOrigin{Stage: DiagnosticOriginStageExtract, StepID: "extract", LaneID: "spells", ModuleKey: "dnd/spells", ValidatorKey: "dnd/spells/source-relatedness"}, OccurrenceCount: 1, Samples: []DiagnosticSample{{Scope: "spells[0]", Message: "Spell was not found", ChunkID: "chunk-1", ChunkIndex: &chunkIndex}}, } if err := group.Validate(); err != nil { t.Fatalf("Validate() error = %v", err) } collection := DiagnosticCollection{Groups: []DiagnosticGroup{group}} if err := collection.Validate(); err != nil { t.Fatalf("DiagnosticCollection.Validate() error = %v", err) } } func TestDiagnosticGroupRejectsRepeatedSampleWithEqualChunkIndex(t *testing.T) { firstIndex := 0 secondIndex := 0 group := DiagnosticGroup{ Disposition: DiagnosticDispositionAdvisory, Category: DiagnosticCategoryDataQuality, ReasonCode: "unresolved", Origin: DiagnosticOrigin{Stage: DiagnosticOriginStageExtract, StepID: "extract", LaneID: "spells", ModuleKey: "dnd/spells"}, OccurrenceCount: 2, Samples: []DiagnosticSample{ {Scope: "spells[0]", Message: "Spell was not found", ChunkID: "chunk-1", ChunkIndex: &firstIndex}, {Scope: "spells[0]", Message: "Spell was not found", ChunkID: "chunk-1", ChunkIndex: &secondIndex}, }, } if err := group.Validate(); err == nil { t.Fatal("Validate() error = nil, want duplicate sample error") } } func TestCloneDiagnosticCollectionOwnsGroupsAndChunkIndex(t *testing.T) { chunkIndex := 0 collection := DiagnosticCollection{Groups: []DiagnosticGroup{{ Disposition: DiagnosticDispositionAdvisory, Category: DiagnosticCategoryDataQuality, ReasonCode: "unresolved", Origin: DiagnosticOrigin{Stage: DiagnosticOriginStageExtract, StepID: "extract", LaneID: "spells", ModuleKey: "dnd/spells"}, OccurrenceCount: 1, Samples: []DiagnosticSample{{Scope: "spells[0]", Message: "Spell was not found", ChunkIndex: &chunkIndex}}, }}} cloned := CloneDiagnosticCollection(collection) collection.Groups[0].Samples[0].Message = "changed" *collection.Groups[0].Samples[0].ChunkIndex = 1 if got := cloned.Groups[0].Samples[0]; got.Message != "Spell was not found" || got.ChunkIndex == nil || *got.ChunkIndex != 0 { t.Fatalf("cloned sample = %#v, want independently owned original", got) } } func TestProjectDiagnosticCollectionPartitionsAndChecksTotals(t *testing.T) { warning := validDiagnosticGroup(DiagnosticDispositionWarning, DiagnosticCategoryFallback, "fallback", 2) diagnostic := validDiagnosticGroup(DiagnosticDispositionAdvisory, DiagnosticCategoryDataQuality, "quality", 3) collection := DiagnosticCollection{ Groups: []DiagnosticGroup{warning, diagnostic}, Truncated: true, UnrepresentedOccurrenceCount: 4, } projection, err := ProjectDiagnosticCollection(collection) if err != nil { t.Fatal(err) } if len(projection.Warnings) != 1 || len(projection.Diagnostics) != 1 || projection.WarningOccurrenceCount != 2 || projection.DiagnosticOccurrenceCount != 7 { t.Fatalf("projection = %#v, want partitioned exact totals", projection) } collection.Groups[0].Samples[0].Message = "mutated" if projection.Warnings[0].Samples[0].Message != "message" { t.Fatal("projection retained caller-owned sample storage") } overflow := DiagnosticCollection{Groups: []DiagnosticGroup{ validDiagnosticGroup(DiagnosticDispositionWarning, DiagnosticCategoryFallback, "first", int(^uint(0)>>1)), validDiagnosticGroup(DiagnosticDispositionWarning, DiagnosticCategoryFallback, "second", 1), }} if _, err := ProjectDiagnosticCollection(overflow); err == nil { t.Fatal("ProjectDiagnosticCollection() overflow error = nil") } } func validDiagnosticGroup(disposition DiagnosticDisposition, category DiagnosticCategory, reason string, occurrences int) DiagnosticGroup { return DiagnosticGroup{ Disposition: disposition, Category: category, ReasonCode: reason, Origin: DiagnosticOrigin{Stage: DiagnosticOriginStageNormalize, StepID: "step", LaneID: "lane", ModuleKey: "module"}, OccurrenceCount: occurrences, Samples: []DiagnosticSample{{Scope: "scope", Message: "message"}}, OmittedSampleCount: occurrences - 1, } } func validProducerDiagnostic() ProducerDiagnostic { return ProducerDiagnostic{ Disposition: DiagnosticDispositionWarning, Category: DiagnosticCategoryConfiguration, ReasonCode: "empty_reference", OccurrenceCount: 1, Samples: []DiagnosticSample{{Scope: "references.glossary", Message: "Reference is empty"}}, } }