package pipeline import ( "encoding/json" "strings" "testing" ) func TestResolvePipelineAppliesValidationPolicyFieldByField(t *testing.T) { profile := baselineProfile() profile.ValidationPolicy = validationPolicyOverride( ProducerStructuralFailureRejectOutput, SemanticRejectionRejectOutput, ValidatorFailureFailRun, ) profile.Chunk.ValidationPolicy = validationPolicyOverride("", "", ValidatorFailureWarnContinue) lane := profile.Artifacts["events"] lane.Extract.ValidationPolicy = validationPolicyOverride("", SemanticRejectionFailRun, "") lane.Merge.ValidationPolicy = validationPolicyOverride("", "", ValidatorFailureWarnContinue) profile.Artifacts["events"] = lane resolved, err := ResolvePipeline(profile, ResolveOptions{}, newProfileCatalog(t)) if err != nil { t.Fatalf("ResolvePipeline() error = %v", err) } if got, want := resolved.ChunkValidationPolicy, (ValidationPolicy{ProducerStructuralFailure: ProducerStructuralFailureRejectOutput, SemanticRejection: SemanticRejectionRejectOutput, ValidatorFailure: ValidatorFailureWarnContinue}); got != want { t.Fatalf("chunk validation policy = %#v, want %#v", got, want) } resolvedLane := resolved.Steps[0].ArtifactLanes[0] if got, want := resolvedLane.ExtractValidationPolicy, (ValidationPolicy{ProducerStructuralFailure: ProducerStructuralFailureRejectOutput, SemanticRejection: SemanticRejectionFailRun, ValidatorFailure: ValidatorFailureFailRun}); got != want { t.Fatalf("extract validation policy = %#v, want %#v", got, want) } if got, want := resolvedLane.MergeValidationPolicy, (ValidationPolicy{ProducerStructuralFailure: ProducerStructuralFailureRejectOutput, SemanticRejection: SemanticRejectionRejectOutput, ValidatorFailure: ValidatorFailureWarnContinue}); got != want { t.Fatalf("merge validation policy = %#v, want %#v", got, want) } if got, want := resolvedLane.NormalizeValidationPolicy, (ValidationPolicy{ProducerStructuralFailure: ProducerStructuralFailureRejectOutput, SemanticRejection: SemanticRejectionRejectOutput, ValidatorFailure: ValidatorFailureFailRun}); got != want { t.Fatalf("normalize validation policy = %#v, want %#v", got, want) } *profile.ValidationPolicy.SemanticRejection = SemanticRejectionFailRun if got := resolved.ConfiguredValidationPolicy.SemanticRejection; got == nil || *got != SemanticRejectionRejectOutput { t.Fatalf("resolved configured policy aliases profile: %#v", resolved.ConfiguredValidationPolicy) } } func TestResolvePipelineRejectsStructuralPolicyOverrideOnDeterministicProducer(t *testing.T) { profile := baselineProfile() profile.Chunk.ValidationPolicy = validationPolicyOverride(ProducerStructuralFailureRejectOutput, "", "") _, err := ResolvePipeline(profile, ResolveOptions{}, newProfileCatalog(t)) if err == nil || !strings.Contains(err.Error(), "producer_structural_failure") || !strings.Contains(err.Error(), "deterministic") { t.Fatalf("ResolvePipeline() error = %v, want deterministic structural-policy rejection", err) } } func TestValidationPolicyChangesResolvedDigestAndRoundTripsBindings(t *testing.T) { left := baselineProfile() right := baselineProfile() right.ValidationPolicy = validationPolicyOverride("", SemanticRejectionRejectOutput, "") leftResolved, err := ResolvePipeline(left, ResolveOptions{}, newProfileCatalog(t)) if err != nil { t.Fatalf("ResolvePipeline(left) error = %v", err) } rightResolved, err := ResolvePipeline(right, ResolveOptions{}, newProfileCatalog(t)) if err != nil { t.Fatalf("ResolvePipeline(right) error = %v", err) } if leftResolved.Digest == rightResolved.Digest { t.Fatal("validation policy change did not change resolved digest") } binding := ModuleBinding{Module: "producer", ValidationPolicy: validationPolicyOverride(ProducerStructuralFailureRejectOutput, SemanticRejectionRejectOutput, ValidatorFailureFailRun)} encoded, err := json.Marshal(binding) if err != nil { t.Fatalf("marshal binding: %v", err) } var decoded ModuleBinding if err := json.Unmarshal(encoded, &decoded); err != nil { t.Fatalf("unmarshal binding: %v", err) } if decoded.ValidationPolicy == nil || *decoded.ValidationPolicy.ProducerStructuralFailure != ProducerStructuralFailureRejectOutput || *decoded.ValidationPolicy.SemanticRejection != SemanticRejectionRejectOutput || *decoded.ValidationPolicy.ValidatorFailure != ValidatorFailureFailRun { t.Fatalf("round-tripped validation policy = %#v", decoded.ValidationPolicy) } cloned := cloneModuleBinding(binding) *binding.ValidationPolicy.ValidatorFailure = ValidatorFailureWarnContinue if got := *cloned.ValidationPolicy.ValidatorFailure; got != ValidatorFailureFailRun { t.Fatalf("cloned validation policy aliases binding: %q", got) } } func validationPolicyOverride(structural ProducerStructuralFailureAction, semantic SemanticRejectionAction, validator ValidatorFailureAction) *ValidationPolicyOverride { policy := &ValidationPolicyOverride{} if structural != "" { policy.ProducerStructuralFailure = &structural } if semantic != "" { policy.SemanticRejection = &semantic } if validator != "" { policy.ValidatorFailure = &validator } return policy }