package cisupplychainconformance import ( "strings" "testing" "time" ) func TestRemoteManagedCICompleteEvidencePasses(t *testing.T) { t.Parallel() capture := ciFixture("REMOTE_MANAGED_CI") report := Evaluate(capture, capture.CandidateVersion, capture.BuildDigest, capture.SourceCommit, time.Now()) if report.Result != "PASS" || report.EvidenceScope != "REMOTE_CI_SUPPLY_CHAIN" || report.ExternalValidationRequired || len(report.CompletedChecks) != len(RequiredChecks) { t.Fatalf("unexpected report: %+v", report) } } func TestMissingRequiredArtifactFails(t *testing.T) { t.Parallel() capture := ciFixture("REMOTE_MANAGED_CI") capture.SBOMs = capture.SBOMs[:2] report := Evaluate(capture, capture.CandidateVersion, capture.BuildDigest, capture.SourceCommit, time.Now()) if report.Result != "FAIL" || !report.ExternalValidationRequired { t.Fatalf("expected failed report: %+v", report) } } func TestLocalToolchainCannotCompleteExternalValidation(t *testing.T) { t.Parallel() capture := ciFixture("LOCAL_TOOLCHAIN") capture.Provider = "LOCAL_GO_TEST" report := Evaluate(capture, capture.CandidateVersion, capture.BuildDigest, capture.SourceCommit, time.Now()) if report.Result != "PASS" || report.EvidenceScope != "LOCAL_CI_TOOLCHAIN" || !report.ExternalValidationRequired { t.Fatalf("unexpected local report: %+v", report) } } func ciFixture(source string) Capture { started := time.Date(2026, 8, 25, 9, 0, 0, 0, time.UTC) digest := "sha256:" + strings.Repeat("a", 64) checks := make([]CheckResult, 0, len(RequiredChecks)) for _, code := range RequiredChecks { checks = append(checks, CheckResult{Code: code, Status: "PASS", EvidenceRef: "artifact:checks/" + strings.ToLower(code)}) } images := make([]ImageArtifact, 0, len(RequiredImages)) for position, subject := range RequiredImages { images = append(images, ImageArtifact{Subject: subject, Reference: "registry.example.com/voice/" + strings.ToLower(subject), Digest: "sha256:" + strings.Repeat(string(rune('b'+position)), 64), EvidenceRef: "artifact:images/" + strings.ToLower(subject)}) } sboms := make([]SBOMArtifact, 0, len(RequiredSBOMs)) for position, subject := range RequiredSBOMs { sboms = append(sboms, SBOMArtifact{Subject: subject, Format: "CYCLONEDX_JSON", Digest: "sha256:" + strings.Repeat(string(rune('d'+position)), 64), EvidenceRef: "artifact:sbom/" + strings.ToLower(subject)}) } return Capture{ SchemaVersion: CaptureSchema, EvidenceSource: source, CandidateVersion: "0.64.0-rc.1", BuildDigest: digest, SourceCommit: strings.Repeat("1", 40), Provider: "GITHUB_ACTIONS", RepositoryRef: "github:example/voice-platform", WorkflowRef: ".github/workflows/ci.yml@refs/tags/v0.64.0-rc.1", RunID: "123456789", RunURL: "https://github.com/example/voice-platform/actions/runs/123456789", Trigger: "RELEASE", Ref: "refs/tags/v0.64.0-rc.1", Attempt: 1, Conclusion: "SUCCESS", StartedAt: started, CompletedAt: started.Add(20 * time.Minute), CollectedAt: started.Add(25 * time.Minute), ExecutedByRef: "release-engineer", ReviewedByRef: "security-reviewer", ReviewRef: "approval:SEC-2026-081", Checks: checks, Images: images, SBOMs: sboms, } }