package pipeline import ( "context" "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 TestPrepareEvidencePlanSelectsActiveLanesAndOwnsPolicy(t *testing.T) { registries, _ := constructionRegistries(t, nil, nil) registries.ArtifactEvidence = NewArtifactEvidenceRegistry() registerTestEvidenceOutput(t, ®istries, EvidenceContextPolicy{Enabled: true, WindowUnits: 2, LaneIDs: []string{"artifact", "inactive"}}) if err := RegisterArtifactEvidence(registries.ArtifactEvidence, "test/notes", func(codecNotes) []source.SourceRef { return nil }); err != nil { t.Fatal(err) } profile := constructionProfile() profile.Output.Options = map[string]any{"known": true} resolved, err := ResolvePipeline(profile, ResolveOptions{}, registries.catalog()) if err != nil { t.Fatal(err) } 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() registry := NewOutputEncoderRegistry() if err := registry.RegisterBuilderWithSpec(defaultModuleSpec("output", StageOutput), func(options map[string]any) error { return RejectUnknownOptions(options, "known") }, func(BuildRequest) (contracts.OutputEncoder, error) { return testEvidenceOutput{policy: cloneEvidenceContextPolicy(policy)}, nil }); err != nil { t.Fatal(err) } registries.Outputs = registry }