package streamgate import ( "testing" "time" ) // TestNormalizedConstructorsRejectEmptyPayload verifies that text, reasoning, // and tool call fragment constructors reject empty payloads by running the // assembled value through Validate. func TestNormalizedConstructorsRejectEmptyPayload(t *testing.T) { ts := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) // Empty text delta if _, err := NewTextDeltaEvent("ch", "", ts); err == nil { t.Fatal("NewTextDeltaEvent with empty text should return error") } // Empty reasoning delta if _, err := NewReasoningDeltaEvent("ch", "", ts); err == nil { t.Fatal("NewReasoningDeltaEvent with empty reasoning should return error") } // Empty tool call args if _, err := NewToolCallFragmentEvent("ch", "id", "name", "", ts); err == nil { t.Fatal("NewToolCallFragmentEvent with empty args should return error") } // Non-empty variants should succeed and pass Validate ev, err := NewTextDeltaEvent("ch", "hello", ts) if err != nil { t.Fatalf("NewTextDeltaEvent with non-empty text: %v", err) } if err := ev.Validate(); err != nil { t.Fatalf("valid text delta failed Validate: %v", err) } ev, err = NewReasoningDeltaEvent("ch", "thinking...", ts) if err != nil { t.Fatalf("NewReasoningDeltaEvent with non-empty reasoning: %v", err) } if err := ev.Validate(); err != nil { t.Fatalf("valid reasoning delta failed Validate: %v", err) } ev, err = NewToolCallFragmentEvent("ch", "id", "name", `{"key":"val"}`, ts) if err != nil { t.Fatalf("NewToolCallFragmentEvent with non-empty args: %v", err) } if err := ev.Validate(); err != nil { t.Fatalf("valid tool call fragment failed Validate: %v", err) } } // TestFilterValueValidationRejectsMalformedPrivateState verifies that directly // assembled FilterOutcome, RecoveryDirective, FilterDecision, SanitizedEvidence, // and RecoveryIntent values with opposite payloads, invalid tokens, or unknown // enums are rejected by Validate. func TestFilterValueValidationRejectsMalformedPrivateState(t *testing.T) { ts := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) // --- FilterOutcome: evaluated with errorCode --- badEvaluated := FilterOutcome{ kind: FilterOutcomeKindEvaluated, decision: buildValidDecision(t), errorCode: mustToken("err.code"), } if err := badEvaluated.Validate(); err == nil { t.Fatal("evaluated outcome with errorCode should be rejected") } // --- FilterOutcome: evaluation_error with decision --- badError := FilterOutcome{ kind: FilterOutcomeKindEvaluationError, decision: buildValidDecision(t), errorCode: mustToken("err.code"), } if err := badError.Validate(); err == nil { t.Fatal("evaluation_error outcome with decision should be rejected") } // --- FilterOutcome: not_applicable with decision --- badNA := FilterOutcome{ kind: FilterOutcomeKindNotApplicableForEpoch, decision: buildValidDecision(t), } if err := badNA.Validate(); err == nil { t.Fatal("not_applicable outcome with decision should be rejected") } // --- FilterOutcome: deferred with errorCode --- badDeferred := FilterOutcome{ kind: FilterOutcomeKindDeferredByRequirement, errorCode: mustToken("err.code"), } if err := badDeferred.Validate(); err == nil { t.Fatal("deferred outcome with errorCode should be rejected") } // --- FilterOutcome: unknown kind --- badKind := FilterOutcome{ kind: FilterOutcomeKind("unknown"), } if err := badKind.Validate(); err == nil { t.Fatal("unknown outcome kind should be rejected") } // --- RecoveryDirective: exact with continuation fields --- badExact := RecoveryDirective{ kind: RecoveryDirectiveKindExact, requestRef: mustToken("req.ref"), cursor: 5, snapshotRef: mustToken("snap.ref"), } if err := badExact.Validate(); err == nil { t.Fatal("exact directive with cursor/snapshotRef should be rejected") } // --- RecoveryDirective: continuation with exact fields --- badCont := RecoveryDirective{ kind: RecoveryDirectiveKindContinuation, requestRef: mustToken("req.ref"), snapshotRef: mustToken("snap.ref"), cursor: 0, } if err := badCont.Validate(); err == nil { t.Fatal("continuation directive with requestRef should be rejected") } // --- RecoveryDirective: schema with exact fields --- badSchema := RecoveryDirective{ kind: RecoveryDirectiveKindSchema, requestRef: mustToken("req.ref"), schemaRef: mustToken("schema.ref"), patchCode: mustToken("patch.code"), } if err := badSchema.Validate(); err == nil { t.Fatal("schema directive with requestRef should be rejected") } // --- RecoveryDirective: unknown kind --- badDirKind := RecoveryDirective{ kind: RecoveryDirectiveKind("unknown"), } if err := badDirKind.Validate(); err == nil { t.Fatal("unknown directive kind should be rejected") } // --- FilterDecision: invalid consumerID token --- badDecision := FilterDecision{ kind: FilterDecisionKindPass, consumerID: StableToken{value: "INVALID"}, filterID: mustToken("filter.id"), ruleID: mustToken("rule.id"), evidence: buildValidEvidence(t, ts), } if err := badDecision.Validate(); err == nil { t.Fatal("decision with invalid consumerID should be rejected") } // --- SanitizedEvidence: unknown event kind --- badEvidence := SanitizedEvidence{ eventKind: EventKind("unknown_kind"), channel: "ch", filterRule: mustToken("filter.rule"), outcome: FilterOutcomeKindEvaluated, descriptorCode: mustToken("desc.code"), fingerprint: [32]byte{1}, count: 0, offset: 0, timestamp: ts, } if err := badEvidence.Validate(); err == nil { t.Fatal("evidence with unknown event kind should be rejected") } // --- SanitizedEvidence: unknown outcome kind --- badEvidence2 := SanitizedEvidence{ eventKind: EventKindTextDelta, channel: "ch", filterRule: mustToken("filter.rule"), outcome: FilterOutcomeKind("unknown_outcome"), descriptorCode: mustToken("desc.code"), fingerprint: [32]byte{1}, count: 0, offset: 0, timestamp: ts, } if err := badEvidence2.Validate(); err == nil { t.Fatal("evidence with unknown outcome kind should be rejected") } // --- SanitizedEvidence: invalid filterRule token --- badEvidence3 := SanitizedEvidence{ eventKind: EventKindTextDelta, channel: "ch", filterRule: StableToken{value: "INVALID"}, outcome: FilterOutcomeKindEvaluated, descriptorCode: mustToken("desc.code"), fingerprint: [32]byte{1}, count: 0, offset: 0, timestamp: ts, } if err := badEvidence3.Validate(); err == nil { t.Fatal("evidence with invalid filterRule token should be rejected") } // --- RecoveryIntent: invalid reason token --- badIntent := RecoveryIntent{ strategy: RecoveryStrategyExactReplay, directive: mustBuildExactDirective(t), reason: StableToken{value: "INVALID"}, priority: 0, } if err := badIntent.Validate(); err == nil { t.Fatal("intent with invalid reason token should be rejected") } } // TestTerminalValueValidationRejectsMalformedPrivateState verifies that directly // assembled FailureCause, ExternalDescriptor, and TerminalResult values with // invalid required tokens, malformed nested values, or over-long cause chains // are rejected by Validate. func TestTerminalValueValidationRejectsMalformedPrivateState(t *testing.T) { ts := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) // --- FailureCause: invalid stage token --- badCause := FailureCause{ stage: StableToken{value: "INVALID"}, code: mustToken("code"), consumer: mustTokenOptional("consumer"), filter: mustTokenOptional("filter"), ruleID: mustTokenOptional("rule"), } if err := badCause.Validate(); err == nil { t.Fatal("cause with invalid stage should be rejected") } // --- FailureCause: invalid code token --- badCause2 := FailureCause{ stage: mustToken("stage"), code: StableToken{value: "INVALID"}, consumer: mustTokenOptional("consumer"), filter: mustTokenOptional("filter"), ruleID: mustTokenOptional("rule"), } if err := badCause2.Validate(); err == nil { t.Fatal("cause with invalid code should be rejected") } // --- ExternalDescriptor: invalid errorType token --- badDesc := ExternalDescriptor{ errorType: StableToken{value: "INVALID"}, code: mustTokenOptional("code"), message: mustToken("message"), param: mustTokenOptional("param"), } if err := badDesc.Validate(); err == nil { t.Fatal("descriptor with invalid errorType should be rejected") } // --- ExternalDescriptor: invalid message token --- badDesc2 := ExternalDescriptor{ errorType: mustToken("errortype"), code: mustTokenOptional("code"), message: StableToken{value: "INVALID"}, param: mustTokenOptional("param"), } if err := badDesc2.Validate(); err == nil { t.Fatal("descriptor with invalid message should be rejected") } // --- TerminalResult: error with invalid descriptor --- badTr := TerminalResult{ channel: "ch", err: true, externalDesc: badDescPtr(), occurredAt: ts, } if err := badTr.Validate(); err == nil { t.Fatal("error terminal with invalid descriptor should be rejected") } // --- TerminalResult: error with over-long cause chain --- overLongCauses := make([]FailureCause, MaxFailureCauses+1) for i := range overLongCauses { overLongCauses[i] = FailureCause{ stage: mustToken("stage"), code: mustToken("code"), } } badTr2 := TerminalResult{ channel: "ch", err: true, externalDesc: validDescPtr(), failureCauses: FailureCauseChain{causes: overLongCauses}, occurredAt: ts, } if err := badTr2.Validate(); err == nil { t.Fatal("error terminal with over-long cause chain should be rejected") } // --- TerminalResult: error with malformed cause --- badCauseChains := []FailureCause{ { stage: mustToken("stage"), code: mustToken("code"), }, { stage: StableToken{value: "INVALID"}, code: mustToken("code"), }, } badTr3 := TerminalResult{ channel: "ch", err: true, externalDesc: validDescPtr(), failureCauses: FailureCauseChain{causes: badCauseChains}, occurredAt: ts, } if err := badTr3.Validate(); err == nil { t.Fatal("error terminal with malformed nested cause should be rejected") } // --- Valid 4-entry error chain should pass --- validCauses := make([]FailureCause, MaxFailureCauses) for i := range validCauses { validCauses[i] = FailureCause{ stage: mustToken("stage"), code: mustToken("code"), } } validTr := TerminalResult{ channel: "ch", err: true, externalDesc: validDescPtr(), failureCauses: FailureCauseChain{causes: validCauses}, occurredAt: ts, } if err := validTr.Validate(); err != nil { t.Fatalf("valid 4-entry error chain failed Validate: %v", err) } // --- Valid success terminal without descriptor should pass --- validSuccess := TerminalResult{ channel: "ch", success: true, occurredAt: ts, } if err := validSuccess.Validate(); err != nil { t.Fatalf("valid success terminal failed Validate: %v", err) } } // TestEvidenceBatchValidationRejectsMalformedStagedStart verifies that EvidenceBatch // rejects invalid HTTP status codes in staged response-start regardless of // terminal flag, and enforces transport_uncommitted for staged starts. func TestEvidenceBatchValidationRejectsMalformedStagedStart(t *testing.T) { ts := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) // --- Private status 99 (below min) --- badStart99 := ResponseStart{ channel: "ch", status: 99, timestamp: ts, } if _, err := NewEvidenceBatch( []NormalizedEvent{}, nil, nil, &badStart99, false, CommitStateTransportUncommitted, ts, ); err == nil { t.Fatal("evidence batch with status 99 should be rejected") } // --- Private status 600 (above max) --- badStart600 := ResponseStart{ channel: "ch", status: 600, timestamp: ts, } if _, err := NewEvidenceBatch( []NormalizedEvent{}, nil, nil, &badStart600, false, CommitStateTransportUncommitted, ts, ); err == nil { t.Fatal("evidence batch with status 600 should be rejected") } // --- Direct Validate on batch with bad staged start --- badBatch := EvidenceBatch{ capturedAt: ts, commitState: CommitStateTransportUncommitted, stagedStart: &badStart99, terminalFlag: false, } if err := badBatch.Validate(); err == nil { t.Fatal("batch with status 99 staged start should be rejected by Validate") } // --- Valid status 100 with transport_uncommitted should pass --- validStart := ResponseStart{ channel: "ch", status: 100, timestamp: ts, } if _, err := NewEvidenceBatch( []NormalizedEvent{}, nil, nil, &validStart, false, CommitStateTransportUncommitted, ts, ); err != nil { t.Fatalf("valid staged start with transport_uncommitted failed: %v", err) } // --- Valid status 599 with transport_uncommitted should pass --- validStart2 := ResponseStart{ channel: "ch", status: 599, timestamp: ts, } if _, err := NewEvidenceBatch( []NormalizedEvent{}, nil, nil, &validStart2, false, CommitStateTransportUncommitted, ts, ); err != nil { t.Fatalf("valid staged start with status 599 failed: %v", err) } // --- Staged start with stream_open should be rejected --- if _, err := NewEvidenceBatch( []NormalizedEvent{}, nil, nil, &validStart, false, CommitStateStreamOpen, ts, ); err == nil { t.Fatal("staged start with stream_open should be rejected") } } // --- Helper functions --- func buildValidDecision(t *testing.T) *FilterDecision { t.Helper() d, err := NewFilterDecision( FilterDecisionKindPass, "consumer.id", "filter.id", "rule.id", buildValidEvidence(t, time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)), nil, ) if err != nil { t.Fatalf("buildValidDecision: %v", err) } return &d } func buildValidEvidence(t *testing.T, ts time.Time) SanitizedEvidence { t.Helper() e, err := NewSanitizedEvidence( EventKindTextDelta, "ch", "filter.rule", "desc.code", [32]byte{1}, 0, 0, FilterOutcomeKindEvaluated, ts, ) if err != nil { t.Fatalf("buildValidEvidence: %v", err) } return e } func mustToken(v string) StableToken { tk, err := NewStableTokenRequired("test", v) if err != nil { panic(err) } return tk } func mustTokenOptional(v string) StableToken { tk, err := NewStableTokenOptional("test", v) if err != nil { panic(err) } return tk } func mustBuildExactDirective(t *testing.T) RecoveryDirective { t.Helper() d, err := NewRecoveryDirectiveExact("req.ref") if err != nil { t.Fatalf("mustBuildExactDirective: %v", err) } return d } func badDescPtr() *ExternalDescriptor { d := ExternalDescriptor{ errorType: mustToken("errortype"), code: mustTokenOptional("code"), message: StableToken{value: "INVALID"}, param: mustTokenOptional("param"), } return &d } func validDescPtr() *ExternalDescriptor { d, err := NewExternalDescriptor("error.type", "code", "message", "") if err != nil { panic(err) } return &d } // TestNormalizedEventValidationEnforcesCauseBound verifies that the Validate // method on NormalizedEvent rejects provider_error events whose failure cause // chain exceeds MaxFailureCauses (4), while accepting chains of exactly 4. // Invalid nested causes remain rejected as before. func TestNormalizedEventValidationEnforcesCauseBound(t *testing.T) { ts := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) desc, err := NewExternalDescriptor("error.type", "code", "message", "") if err != nil { t.Fatalf("NewExternalDescriptor: %v", err) } // Build 4 valid causes (at the boundary). causes4 := make([]FailureCause, MaxFailureCauses) for i := range causes4 { causes4[i] = FailureCause{ stage: mustToken("stage"), code: mustToken("code"), } } chain4 := FailureCauseChain{causes: causes4} // Build 5 causes (exceeds boundary). causes5 := make([]FailureCause, MaxFailureCauses+1) for i := range causes5 { causes5[i] = FailureCause{ stage: mustToken("stage"), code: mustToken("code"), } } chain5 := FailureCauseChain{causes: causes5} // 4 causes must pass Validate. ev4 := NormalizedEvent{ kind: EventKindProviderError, channel: "ch", timestamp: ts, disposition: BaseDispositionTerminalErrorCandidate, externalDesc: &desc, failureCauses: chain4, } if err := ev4.Validate(); err != nil { t.Fatalf("4-cause provider error event should pass Validate: %v", err) } // 5 causes must fail Validate. ev5 := NormalizedEvent{ kind: EventKindProviderError, channel: "ch", timestamp: ts, disposition: BaseDispositionTerminalErrorCandidate, externalDesc: &desc, failureCauses: chain5, } if err := ev5.Validate(); err == nil { t.Fatal("5-cause provider error event should fail Validate") } // Invalid nested cause must still fail. badCauses := []FailureCause{ { stage: mustToken("stage"), code: mustToken("code"), }, { stage: StableToken{value: "INVALID"}, code: mustToken("code"), }, } badChain := FailureCauseChain{causes: badCauses} evBad := NormalizedEvent{ kind: EventKindProviderError, channel: "ch", timestamp: ts, disposition: BaseDispositionTerminalErrorCandidate, externalDesc: &desc, failureCauses: badChain, } if err := evBad.Validate(); err == nil { t.Fatal("provider error with invalid nested cause should fail Validate") } } // TestFilterDecisionConstructorCopiesRecoveryIntent verifies that NewFilterDecision // defensively copies the caller-owned *RecoveryIntent so that subsequent // variable reassignment of the original intent does not affect the returned // FilterDecision or any previously created evaluated FilterOutcome. func TestFilterDecisionConstructorCopiesRecoveryIntent(t *testing.T) { ts := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) evidence := buildValidEvidence(t, ts) directive, err := NewRecoveryDirectiveExact("req.ref") if err != nil { t.Fatalf("NewRecoveryDirectiveExact: %v", err) } intent, err := NewRecoveryIntent(RecoveryStrategyExactReplay, directive, "reason", 0) if err != nil { t.Fatalf("NewRecoveryIntent: %v", err) } decision, err := NewFilterDecision( FilterDecisionKindViolation, "consumer.id", "filter.id", "rule.id", evidence, &intent, ) if err != nil { t.Fatalf("NewFilterDecision: %v", err) } // Create an evaluated outcome from the decision before any reassignment. outcome, err := NewFilterOutcomeEvaluated(decision) if err != nil { t.Fatalf("NewFilterOutcomeEvaluated: %v", err) } // Reassign the original intent variable to a zero value. intent = RecoveryIntent{} // The decision must still validate and retain its original intent values. if err := decision.Validate(); err != nil { t.Fatalf("decision should survive caller intent reassignment: %v", err) } d := decision.Intent() if d == nil { t.Fatal("decision Intent() returned nil") } if d.Strategy() != RecoveryStrategyExactReplay { t.Fatalf("decision Intent strategy should be exact_replay, got %s", d.Strategy()) } if d.Reason() != "reason" { t.Fatalf("decision Intent reason should be 'reason', got %s", d.Reason()) } if d.Priority() != 0 { t.Fatalf("decision Intent priority should be 0, got %d", d.Priority()) } // The evaluated outcome must also survive and retain nested intent values. if err := outcome.Validate(); err != nil { t.Fatalf("outcome should survive caller intent reassignment: %v", err) } oDecision := outcome.Decision() if oDecision == nil { t.Fatal("outcome Decision() returned nil") } oIntent := oDecision.Intent() if oIntent == nil { t.Fatal("outcome decision Intent() returned nil") } if oIntent.Strategy() != RecoveryStrategyExactReplay { t.Fatalf("outcome decision Intent strategy should be exact_replay, got %s", oIntent.Strategy()) } if oIntent.Reason() != "reason" { t.Fatalf("outcome decision Intent reason should be 'reason', got %s", oIntent.Reason()) } if oIntent.Priority() != 0 { t.Fatalf("outcome decision Intent priority should be 0, got %d", oIntent.Priority()) } } // TestFilterOutcomeValidationRejectsEachForbiddenField verifies that each // FilterOutcome variant rejects exactly the forbidden field set for its kind // when injected independently. func TestFilterOutcomeValidationRejectsEachForbiddenField(t *testing.T) { ts := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) evidence := buildValidEvidence(t, ts) decision, err := NewFilterDecision( FilterDecisionKindPass, "consumer.id", "filter.id", "rule.id", evidence, nil, ) if err != nil { t.Fatalf("NewFilterDecision: %v", err) } errCode := mustToken("err.code") tests := []struct { name string make func() FilterOutcome }{ { name: "evaluated_with_errorCode", make: func() FilterOutcome { return FilterOutcome{ kind: FilterOutcomeKindEvaluated, decision: &decision, errorCode: errCode, } }, }, { name: "evaluation_error_with_decision", make: func() FilterOutcome { return FilterOutcome{ kind: FilterOutcomeKindEvaluationError, decision: &decision, errorCode: errCode, } }, }, { name: "not_applicable_with_decision", make: func() FilterOutcome { return FilterOutcome{ kind: FilterOutcomeKindNotApplicableForEpoch, decision: &decision, } }, }, { name: "not_applicable_with_errorCode", make: func() FilterOutcome { return FilterOutcome{ kind: FilterOutcomeKindNotApplicableForEpoch, errorCode: errCode, } }, }, { name: "deferred_with_decision", make: func() FilterOutcome { return FilterOutcome{ kind: FilterOutcomeKindDeferredByRequirement, decision: &decision, } }, }, { name: "deferred_with_errorCode", make: func() FilterOutcome { return FilterOutcome{ kind: FilterOutcomeKindDeferredByRequirement, errorCode: errCode, } }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { if err := tc.make().Validate(); err == nil { t.Fatalf("%s should be rejected", tc.name) } }) } } // TestRecoveryDirectiveValidationRejectsEachForbiddenField verifies that each // RecoveryDirective variant rejects exactly the forbidden fields for its kind // when injected independently. func TestRecoveryDirectiveValidationRejectsEachForbiddenField(t *testing.T) { exactRef := mustToken("req.ref") snapRef := mustToken("snap.ref") schemaRef := mustToken("schema.ref") patchCode := mustToken("patch.code") tests := []struct { name string make func() RecoveryDirective }{ { name: "exact_with_cursor", make: func() RecoveryDirective { return RecoveryDirective{ kind: RecoveryDirectiveKindExact, requestRef: exactRef, cursor: 5, } }, }, { name: "exact_with_snapshotRef", make: func() RecoveryDirective { return RecoveryDirective{ kind: RecoveryDirectiveKindExact, requestRef: exactRef, snapshotRef: snapRef, } }, }, { name: "exact_with_schemaRef", make: func() RecoveryDirective { return RecoveryDirective{ kind: RecoveryDirectiveKindExact, requestRef: exactRef, schemaRef: schemaRef, } }, }, { name: "exact_with_patchCode", make: func() RecoveryDirective { return RecoveryDirective{ kind: RecoveryDirectiveKindExact, requestRef: exactRef, patchCode: patchCode, } }, }, { name: "continuation_with_requestRef", make: func() RecoveryDirective { return RecoveryDirective{ kind: RecoveryDirectiveKindContinuation, requestRef: exactRef, snapshotRef: snapRef, cursor: 0, } }, }, { name: "continuation_with_schemaRef", make: func() RecoveryDirective { return RecoveryDirective{ kind: RecoveryDirectiveKindContinuation, snapshotRef: snapRef, schemaRef: schemaRef, cursor: 0, } }, }, { name: "continuation_with_patchCode", make: func() RecoveryDirective { return RecoveryDirective{ kind: RecoveryDirectiveKindContinuation, snapshotRef: snapRef, patchCode: patchCode, cursor: 0, } }, }, { name: "schema_with_requestRef", make: func() RecoveryDirective { return RecoveryDirective{ kind: RecoveryDirectiveKindSchema, requestRef: exactRef, schemaRef: schemaRef, patchCode: patchCode, } }, }, { name: "schema_with_cursor", make: func() RecoveryDirective { return RecoveryDirective{ kind: RecoveryDirectiveKindSchema, schemaRef: schemaRef, patchCode: patchCode, cursor: 5, } }, }, { name: "schema_with_snapshotRef", make: func() RecoveryDirective { return RecoveryDirective{ kind: RecoveryDirectiveKindSchema, snapshotRef: snapRef, schemaRef: schemaRef, patchCode: patchCode, } }, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { if err := tc.make().Validate(); err == nil { t.Fatalf("%s should be rejected", tc.name) } }) } } // TestFilterAndTerminalConstructorsProduceValidValues verifies that all public // constructors in the filter/terminal domain produce values that pass their // own Validate, covering every normal variant matrix. func TestFilterAndTerminalConstructorsProduceValidValues(t *testing.T) { ts := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) // --- FilterDecision: all kinds with valid evidence --- evidence := buildValidEvidence(t, ts) kinds := []FilterDecisionKind{ FilterDecisionKindPass, FilterDecisionKindObserve, FilterDecisionKindViolation, FilterDecisionKindFatal, FilterDecisionKindReplacement, } for _, k := range kinds { t.Run("FilterDecision_"+string(k), func(t *testing.T) { d, err := NewFilterDecision(k, "consumer.id", "filter.id", "rule.id", evidence, nil) if err != nil { t.Fatalf("NewFilterDecision %s: %v", k, err) } if err := d.Validate(); err != nil { t.Fatalf("FilterDecision %s failed Validate: %v", k, err) } }) } // --- FilterDecision with violation + recovery intent --- directive, err := NewRecoveryDirectiveExact("req.ref") if err != nil { t.Fatalf("NewRecoveryDirectiveExact: %v", err) } intent, err := NewRecoveryIntent(RecoveryStrategyExactReplay, directive, "reason", 0) if err != nil { t.Fatalf("NewRecoveryIntent: %v", err) } d, err := NewFilterDecision(FilterDecisionKindViolation, "consumer.id", "filter.id", "rule.id", evidence, &intent) if err != nil { t.Fatalf("NewFilterDecision violation+intent: %v", err) } if err := d.Validate(); err != nil { t.Fatalf("FilterDecision violation+intent failed Validate: %v", err) } // --- FilterOutcome: all kinds --- t.Run("FilterOutcome_evaluated", func(t *testing.T) { o, err := NewFilterOutcomeEvaluated(d) if err != nil { t.Fatalf("NewFilterOutcomeEvaluated: %v", err) } if err := o.Validate(); err != nil { t.Fatalf("FilterOutcome evaluated failed Validate: %v", err) } }) t.Run("FilterOutcome_evaluation_error", func(t *testing.T) { o, err := NewFilterOutcomeEvaluationError("err.code") if err != nil { t.Fatalf("NewFilterOutcomeEvaluationError: %v", err) } if err := o.Validate(); err != nil { t.Fatalf("FilterOutcome evaluation_error failed Validate: %v", err) } }) t.Run("FilterOutcome_not_applicable", func(t *testing.T) { o := NewFilterOutcomeNotApplicableForEpoch() if err := o.Validate(); err != nil { t.Fatalf("FilterOutcome not_applicable failed Validate: %v", err) } }) t.Run("FilterOutcome_deferred", func(t *testing.T) { o := NewFilterOutcomeDeferredByRequirement() if err := o.Validate(); err != nil { t.Fatalf("FilterOutcome deferred failed Validate: %v", err) } }) // --- RecoveryDirective: all kinds --- t.Run("RecoveryDirective_exact", func(t *testing.T) { dir, err := NewRecoveryDirectiveExact("req.ref") if err != nil { t.Fatalf("NewRecoveryDirectiveExact: %v", err) } if err := dir.Validate(); err != nil { t.Fatalf("RecoveryDirective exact failed Validate: %v", err) } }) t.Run("RecoveryDirective_continuation", func(t *testing.T) { dir, err := NewRecoveryDirectiveContinuation(10, "snap.ref") if err != nil { t.Fatalf("NewRecoveryDirectiveContinuation: %v", err) } if err := dir.Validate(); err != nil { t.Fatalf("RecoveryDirective continuation failed Validate: %v", err) } }) t.Run("RecoveryDirective_schema", func(t *testing.T) { dir, err := NewRecoveryDirectiveSchema("schema.ref", "patch.code") if err != nil { t.Fatalf("NewRecoveryDirectiveSchema: %v", err) } if err := dir.Validate(); err != nil { t.Fatalf("RecoveryDirective schema failed Validate: %v", err) } }) // --- RecoveryIntent: all strategies --- strategies := []struct { strategy RecoveryStrategy directive RecoveryDirective }{ {RecoveryStrategyExactReplay, mustBuildExactDirective(t)}, {RecoveryStrategyContinuationRepair, mustBuildContinuationDirective(t)}, {RecoveryStrategySchemaRepair, mustBuildSchemaDirective(t)}, } for _, s := range strategies { t.Run("RecoveryIntent_"+string(s.strategy), func(t *testing.T) { intent, err := NewRecoveryIntent(s.strategy, s.directive, "reason", 0) if err != nil { t.Fatalf("NewRecoveryIntent %s: %v", s.strategy, err) } if err := intent.Validate(); err != nil { t.Fatalf("RecoveryIntent %s failed Validate: %v", s.strategy, err) } }) } // --- SanitizedEvidence: valid variant --- t.Run("SanitizedEvidence", func(t *testing.T) { e, err := NewSanitizedEvidence( EventKindTextDelta, "ch", "filter.rule", "desc.code", [32]byte{1}, 0, 0, FilterOutcomeKindEvaluated, ts, ) if err != nil { t.Fatalf("NewSanitizedEvidence: %v", err) } if err := e.Validate(); err != nil { t.Fatalf("SanitizedEvidence failed Validate: %v", err) } }) // --- FailureCause: required-only (stage + code, optional fields empty) --- t.Run("FailureCause_required_only", func(t *testing.T) { fc, err := NewFailureCause("stage", "code", "", "", "") if err != nil { t.Fatalf("NewFailureCause required-only: %v", err) } if err := fc.Validate(); err != nil { t.Fatalf("FailureCause required-only failed Validate: %v", err) } }) // --- FailureCause: optional-full (all fields provided) --- t.Run("FailureCause_optional_full", func(t *testing.T) { fc, err := NewFailureCause("stage", "code", "consumer", "filter", "rule") if err != nil { t.Fatalf("NewFailureCause optional-full: %v", err) } if err := fc.Validate(); err != nil { t.Fatalf("FailureCause optional-full failed Validate: %v", err) } }) // --- ExternalDescriptor: required-only (errorType + message, optional fields empty) --- t.Run("ExternalDescriptor_required_only", func(t *testing.T) { ed, err := NewExternalDescriptor("error.type", "", "message", "") if err != nil { t.Fatalf("NewExternalDescriptor required-only: %v", err) } if err := ed.Validate(); err != nil { t.Fatalf("ExternalDescriptor required-only failed Validate: %v", err) } }) // --- ExternalDescriptor: optional-full (all fields provided) --- t.Run("ExternalDescriptor_optional_full", func(t *testing.T) { ed, err := NewExternalDescriptor("error.type", "code", "message", "param") if err != nil { t.Fatalf("NewExternalDescriptor optional-full: %v", err) } if err := ed.Validate(); err != nil { t.Fatalf("ExternalDescriptor optional-full failed Validate: %v", err) } }) // --- Invalid inputs must be rejected by constructors --- t.Run("NewFilterDecision_unknownKind", func(t *testing.T) { _, err := NewFilterDecision(FilterDecisionKind("bad"), "consumer.id", "filter.id", "rule.id", evidence, nil) if err == nil { t.Fatal("NewFilterDecision unknown kind should return error") } }) t.Run("NewFilterDecision_nonViolationWithIntent", func(t *testing.T) { _, err := NewFilterDecision(FilterDecisionKindPass, "consumer.id", "filter.id", "rule.id", evidence, &intent) if err == nil { t.Fatal("NewFilterDecision non-violation with intent should return error") } }) t.Run("NewFilterOutcomeEvaluated_invalidDecision", func(t *testing.T) { badDecision := FilterDecision{ kind: FilterDecisionKindPass, consumerID: StableToken{value: "INVALID"}, filterID: mustToken("filter.id"), ruleID: mustToken("rule.id"), evidence: evidence, } _, err := NewFilterOutcomeEvaluated(badDecision) if err == nil { t.Fatal("NewFilterOutcomeEvaluated with invalid decision should return error") } }) t.Run("NewFilterOutcomeEvaluationError_emptyCode", func(t *testing.T) { _, err := NewFilterOutcomeEvaluationError("") if err == nil { t.Fatal("NewFilterOutcomeEvaluationError with empty code should return error") } }) t.Run("NewRecoveryDirectiveExact_emptyRequestID", func(t *testing.T) { _, err := NewRecoveryDirectiveExact("") if err == nil { t.Fatal("NewRecoveryDirectiveExact with empty requestID should return error") } }) t.Run("NewRecoveryDirectiveContinuation_negativeCursor", func(t *testing.T) { _, err := NewRecoveryDirectiveContinuation(-1, "snap.ref") if err == nil { t.Fatal("NewRecoveryDirectiveContinuation with negative cursor should return error") } }) t.Run("NewRecoveryDirectiveSchema_emptySchemaRef", func(t *testing.T) { _, err := NewRecoveryDirectiveSchema("", "patch.code") if err == nil { t.Fatal("NewRecoveryDirectiveSchema with empty schemaRef should return error") } }) t.Run("NewRecoveryIntent_emptyReason", func(t *testing.T) { exactDir, err := NewRecoveryDirectiveExact("req.ref") if err != nil { t.Fatalf("NewRecoveryDirectiveExact: %v", err) } _, err = NewRecoveryIntent(RecoveryStrategyExactReplay, exactDir, "", 0) if err == nil { t.Fatal("NewRecoveryIntent with empty reason should return error") } }) t.Run("NewSanitizedEvidence_zeroFingerprint", func(t *testing.T) { _, err := NewSanitizedEvidence( EventKindTextDelta, "ch", "filter.rule", "desc.code", [32]byte{}, 0, 0, FilterOutcomeKindEvaluated, ts, ) if err == nil { t.Fatal("NewSanitizedEvidence with zero fingerprint should return error") } }) t.Run("NewFailureCause_invalidStage", func(t *testing.T) { _, err := NewFailureCause("INVALID", "code", "", "", "") if err == nil { t.Fatal("NewFailureCause invalid stage should return error") } }) t.Run("NewExternalDescriptor_invalidErrorType", func(t *testing.T) { _, err := NewExternalDescriptor("INVALID", "code", "message", "") if err == nil { t.Fatal("NewExternalDescriptor invalid errorType should return error") } }) } // --- Additional helpers for TestFilterAndTerminalConstructorsProduceValidValues --- func mustBuildContinuationDirective(t *testing.T) RecoveryDirective { t.Helper() d, err := NewRecoveryDirectiveContinuation(10, "snap.ref") if err != nil { t.Fatalf("mustBuildContinuationDirective: %v", err) } return d } func mustBuildSchemaDirective(t *testing.T) RecoveryDirective { t.Helper() d, err := NewRecoveryDirectiveSchema("schema.ref", "patch.code") if err != nil { t.Fatalf("mustBuildSchemaDirective: %v", err) } return d }