package pipeline import ( "context" "fmt" "reflect" "strings" "testing" "gitea.maximumdirect.net/eric/notarius/internal/core/source" "gitea.maximumdirect.net/eric/notarius/internal/framework/contracts" ) type testEvidenceOutput struct { policy EvidenceContextPolicy } func (output testEvidenceOutput) Key() string { return "output" } func (output testEvidenceOutput) Encode(context.Context, contracts.OutputRequest) (contracts.OutputResult, error) { return contracts.OutputResult{}, nil } func (output testEvidenceOutput) EvidenceContextPolicy() EvidenceContextPolicy { return cloneEvidenceContextPolicy(output.policy) } func TestEvidencePolicyResolutionAndPreparationSurviveLaneFiltering(t *testing.T) { registries, _ := constructionRegistries(t, nil, nil) registries.ArtifactEvidence = NewArtifactEvidenceRegistry() policy := EvidenceContextPolicy{Enabled: true, WindowUnits: 2, LaneIDs: []string{"artifact", "inactive"}} profileValidated := false registerTestEvidenceOutputWithProfileValidation(t, ®istries, policy, func(context OutputProfileOptionContext, _ map[string]any) error { profileValidated = true if want := []string{"artifact", "inactive"}; !reflect.DeepEqual(context.LaneIDs, want) { return fmt.Errorf("configured lane ids = %#v, want %#v", context.LaneIDs, want) } return nil }) if err := RegisterArtifactEvidence(registries.ArtifactEvidence, "test/notes", func(codecNotes) []source.SourceRef { return nil }); err != nil { t.Fatal(err) } profile := constructionProfile() profile.Artifacts["inactive"] = profile.Artifacts["artifact"] profile.Output.Options = map[string]any{"known": true} resolved, err := ResolvePipeline(profile, ResolveOptions{Only: []string{"artifact"}}, registries.catalog()) if err != nil { t.Fatal(err) } if !profileValidated { t.Fatal("output profile options were not validated") } if lanes := resolved.AllArtifactLanes(); len(lanes) != 1 || lanes[0].ID != "artifact" { t.Fatalf("resolved lanes = %#v, want only the invocation-selected lane", lanes) } prepared, err := Prepare(resolved, registries, ModuleDependencies{}) if err != nil { t.Fatalf("Prepare() error = %v, want nil", err) } if prepared.evidencePlan == nil || !reflect.DeepEqual(prepared.evidencePlan.policy.LaneIDs, []string{"artifact", "inactive"}) { t.Fatalf("evidence plan = %#v, want complete configured policy", prepared.evidencePlan) } if got := prepared.evidencePlan.lanes; len(got) != 1 || got[0].laneID != "artifact" { t.Fatalf("active evidence lanes = %#v, want artifact only", got) } prepared.evidencePlan.policy.LaneIDs[0] = "mutated" if policy := prepared.output.(EvidenceContextPolicyProvider).EvidenceContextPolicy(); policy.LaneIDs[0] != "artifact" { t.Fatalf("prepared plan mutated provider policy: %#v", policy) } } func TestPrepareEvidencePlanRejectsMissingAndMismatchedCapabilities(t *testing.T) { for _, test := range []struct { name string configure func(*Registries) want string }{ {name: "missing registry", configure: func(registries *Registries) { registries.ArtifactEvidence = nil }, want: "artifact evidence registry"}, {name: "unsupported kind", configure: func(registries *Registries) {}, want: "artifact evidence \"test/notes\" is not registered"}, {name: "mismatched type", configure: func(registries *Registries) { if err := RegisterArtifactEvidence(registries.ArtifactEvidence, "test/notes", func(codecScore) []source.SourceRef { return nil }); err != nil { t.Fatal(err) } }, want: "requires Go type"}, } { t.Run(test.name, func(t *testing.T) { registries, _ := constructionRegistries(t, nil, nil) registries.ArtifactEvidence = NewArtifactEvidenceRegistry() registerTestEvidenceOutput(t, ®istries, EvidenceContextPolicy{Enabled: true, LaneIDs: []string{"artifact"}}) test.configure(®istries) profile := constructionProfile() profile.Output.Options = map[string]any{"known": true} resolved, err := ResolvePipeline(profile, ResolveOptions{}, registries.catalog()) if err != nil { t.Fatal(err) } _, err = Prepare(resolved, registries, ModuleDependencies{}) if err == nil || !strings.Contains(err.Error(), test.want) { t.Fatalf("Prepare() error = %v, want %q", err, test.want) } }) } } func registerTestEvidenceOutput(t *testing.T, registries *Registries, policy EvidenceContextPolicy) { t.Helper() registerTestEvidenceOutputWithProfileValidation(t, registries, policy, nil) } func registerTestEvidenceOutputWithProfileValidation(t *testing.T, registries *Registries, policy EvidenceContextPolicy, validateProfile OutputProfileOptionValidator) { t.Helper() registry := NewOutputEncoderRegistry() if err := registry.RegisterBuilderWithProfileValidation(defaultModuleSpec("output", StageOutput), func(options map[string]any) error { return RejectUnknownOptions(options, "known") }, validateProfile, func(BuildRequest) (contracts.OutputEncoder, error) { return testEvidenceOutput{policy: cloneEvidenceContextPolicy(policy)}, nil }); err != nil { t.Fatal(err) } registries.Outputs = registry }