package artifactvault import ( "context" "os" "path/filepath" "testing" "time" ) func testStore(t *testing.T) *FilesystemStore { t.Helper() dir := t.TempDir() st, err := NewFilesystemStore(dir, "vault-id-test", []byte("0123456789abcdef0123456789abcdef")) if err != nil { t.Fatal(err) } return st } func TestPublisherIdempotentAndCrashSafe(t *testing.T) { ctx := context.Background() reg := NewMemoryRegistry() store := testStore(t) pub := &Publisher{Reg: reg, Store: store} facts := Facts{ ArtifactType: TypeAgentCommit, CandidateStatus: "committed", OutputSnapshotID: 4, ChangeKind: "agent_commit", ChangeToSnapshotID: 4, Classification: ClassificationOK, Payload: []byte("# final\n"), } req := PublishRequest{ ArtifactType: TypeAgentCommit, Environment: "dev", ProjectExternalKey: "proj-a", LogicalResourceID: "doc_a", SourceEventKind: "agent_commit", SourceEventID: "ev-1", Payload: []byte("# final\n"), TerminalState: "committed", } art1, _, err := reg.Request(ctx, facts, req) if err != nil { t.Fatal(err) } art2, _, err := reg.Request(ctx, facts, req) if err != nil || art1.UUID != art2.UUID { t.Fatalf("idempotent request: %v %s %s", err, art1.UUID, art2.UUID) } if err := pub.Drain(ctx); err != nil { t.Fatal(err) } if err := pub.Drain(ctx); err != nil { t.Fatal(err) } cov := reg.Coverage(ctx) if cov.Verified != 1 { t.Fatalf("verified=%d", cov.Verified) } b, sha, err := pub.DegradedPayload(ctx, "dev", "doc_a", "proj-a") if err != nil || string(b) != "# final\n" || sha == "" { t.Fatalf("degraded: %v %q", err, b) } } func TestPublisherRejectsBadSHAAndCrossProjectKeys(t *testing.T) { ctx := context.Background() reg := NewMemoryRegistry() store := testStore(t) pub := &Publisher{Reg: reg, Store: store} facts := Facts{ ArtifactType: TypeSealedVersion, VersionStatus: "sealed", SealedAt: ptrTime(), HeadSnapshotID: 2, Classification: ClassificationOK, Payload: []byte("same-bytes"), } aReq := PublishRequest{ArtifactType: TypeSealedVersion, Environment: "dev", ProjectExternalKey: "p1", LogicalResourceID: "d1", SourceEventKind: "seal", SourceEventID: "s1", Payload: []byte("same-bytes"), TerminalState: "sealed"} bReq := aReq bReq.ProjectExternalKey = "p2" bReq.LogicalResourceID = "d2" bReq.SourceEventID = "s2" if _, _, err := reg.Request(ctx, facts, aReq); err != nil { t.Fatal(err) } if _, _, err := reg.Request(ctx, facts, bReq); err != nil { t.Fatal(err) } if err := pub.Drain(ctx); err != nil { t.Fatal(err) } if reg.ActiveRefs(ctx, "p1", SHA256Hex([]byte("same-bytes"))) != 1 { t.Fatal("p1 refs") } if reg.ActiveRefs(ctx, "p2", SHA256Hex([]byte("same-bytes"))) != 1 { t.Fatal("p2 refs") } _ = reg.Tombstone(ctx, "unused", "test", "test") } func TestFilesystemRefusesRuntimeDataDev(t *testing.T) { root := filepath.Join(t.TempDir(), "data", "dev") _ = os.MkdirAll(root, 0o755) if _, err := NewFilesystemStore(root, "id", []byte("0123456789abcdef0123456789abcdef")); err == nil { t.Fatal("must refuse data/dev") } } func TestTombstoneUnionDeny(t *testing.T) { ctx := context.Background() reg := NewMemoryRegistry() store := testStore(t) pub := &Publisher{Reg: reg, Store: store} now := time.Now().UTC() facts := Facts{ArtifactType: TypeApprovedDocument, DocState: "approved", ApprovedAt: &now, ReviewSnapshotID: 8, Classification: ClassificationOK, Payload: []byte("ok body")} req := PublishRequest{ArtifactType: TypeApprovedDocument, Environment: "dev", ProjectExternalKey: "p", LogicalResourceID: "doc", SourceEventKind: "approved", SourceEventID: "ap1", Payload: []byte("ok body"), TerminalState: "approved"} art, _, err := reg.Request(ctx, facts, req) if err != nil { t.Fatal(err) } if err := pub.Drain(ctx); err != nil { t.Fatal(err) } _ = reg.Tombstone(ctx, art.UUID, "legal", "admin") if _, _, err := pub.DegradedPayload(ctx, "dev", "doc", "p"); err == nil { t.Fatal("tombstone must deny read") } } func TestLastReferencePurgeAndLegalHold(t *testing.T) { ctx := context.Background() reg := NewMemoryRegistry() store := testStore(t) pub := &Publisher{Reg: reg, Store: store} now := time.Now().UTC() payload := []byte("shared-bytes") facts := Facts{ArtifactType: TypeSealedVersion, VersionStatus: "sealed", SealedAt: &now, HeadSnapshotID: 2, Classification: ClassificationOK, Payload: payload} req := PublishRequest{ArtifactType: TypeSealedVersion, Environment: "dev", ProjectExternalKey: "p1", LogicalResourceID: "d1", SourceEventKind: "seal", SourceEventID: "s1", Payload: payload, TerminalState: "sealed"} if _, _, err := reg.Request(ctx, facts, req); err != nil { t.Fatal(err) } if err := pub.Drain(ctx); err != nil { t.Fatal(err) } if reg.ActiveRefs(ctx, "p1", SHA256Hex(payload)) != 1 { t.Fatal("expected one active ref") } if err := CanPurge(false, 1); err == nil { t.Fatal("must not purge while refs remain") } if err := CanPurge(true, 0); err == nil { t.Fatal("legal hold blocks purge") } if err := CanPurge(false, 0); err != nil { t.Fatal(err) } } func TestFilesystemIdentityMismatch(t *testing.T) { dir := t.TempDir() key := []byte("0123456789abcdef0123456789abcdef") if _, err := NewFilesystemStore(dir, "id-a", key); err != nil { t.Fatal(err) } if _, err := NewFilesystemStore(dir, "id-b", key); err == nil { t.Fatal("identity mismatch must fail closed") } } func TestUnpublishedWithoutCommitMarkerInvisible(t *testing.T) { ctx := context.Background() reg := NewMemoryRegistry() store := testStore(t) pub := &Publisher{Reg: reg, Store: store} facts := Facts{ ArtifactType: TypeAgentCommit, CandidateStatus: "committed", OutputSnapshotID: 4, ChangeKind: "agent_commit", ChangeToSnapshotID: 4, Classification: ClassificationOK, Payload: []byte("# pending\n"), } req := PublishRequest{ ArtifactType: TypeAgentCommit, Environment: "dev", ProjectExternalKey: "proj-a", LogicalResourceID: "doc_pending", SourceEventKind: "agent_commit", SourceEventID: "ev-pending", Payload: []byte("# pending\n"), TerminalState: "committed", } if _, _, err := reg.Request(ctx, facts, req); err != nil { t.Fatal(err) } if _, _, err := pub.DegradedPayload(ctx, "dev", "doc_pending", "proj-a"); err == nil { t.Fatal("requested but unpublished must be invisible") } } func TestPublisherRetriesAfterMissingCommit(t *testing.T) { ctx := context.Background() reg := NewMemoryRegistry() store := testStore(t) pub := &Publisher{Reg: reg, Store: store} payload := []byte("# retry-me\n") facts := Facts{ ArtifactType: TypeAgentCommit, CandidateStatus: "committed", OutputSnapshotID: 5, ChangeKind: "agent_commit", ChangeToSnapshotID: 5, Classification: ClassificationOK, Payload: payload, } req := PublishRequest{ ArtifactType: TypeAgentCommit, Environment: "dev", ProjectExternalKey: "proj-a", LogicalResourceID: "doc_retry", SourceEventKind: "agent_commit", SourceEventID: "ev-retry", Payload: payload, TerminalState: "committed", } art, _, err := reg.Request(ctx, facts, req) if err != nil { t.Fatal(err) } if err := pub.Drain(ctx); err != nil { t.Fatal(err) } if err := os.RemoveAll(filepath.Join(store.Root, "commits", art.UUID)); err != nil { t.Fatal(err) } if _, _, err := pub.DegradedPayload(ctx, "dev", "doc_retry", "proj-a"); err == nil { t.Fatal("missing commit marker must hide payload") } if err := reg.MarkFailed(ctx, art.UUID, "commit_missing"); err != nil { t.Fatal(err) } if err := pub.Drain(ctx); err != nil { t.Fatal(err) } b, _, err := pub.DegradedPayload(ctx, "dev", "doc_retry", "proj-a") if err != nil || string(b) != string(payload) { t.Fatalf("retry after exclusive blob/manifest: %v %q", err, b) } } type requestCountingReg struct { Registry requests int } func (r *requestCountingReg) Request(ctx context.Context, facts Facts, req PublishRequest) (*Artifact, *Staging, error) { r.requests++ return r.Registry.Request(ctx, facts, req) } func TestDegradedPayloadDoesNotWrite(t *testing.T) { ctx := context.Background() mem := NewMemoryRegistry() count := &requestCountingReg{Registry: mem} store := testStore(t) pub := &Publisher{Reg: count, Store: store} facts := Facts{ ArtifactType: TypeAgentCommit, CandidateStatus: "committed", OutputSnapshotID: 6, ChangeKind: "agent_commit", ChangeToSnapshotID: 6, Classification: ClassificationOK, Payload: []byte("# get\n"), } req := PublishRequest{ ArtifactType: TypeAgentCommit, Environment: "dev", ProjectExternalKey: "proj-a", LogicalResourceID: "doc_get", SourceEventKind: "agent_commit", SourceEventID: "ev-get", Payload: []byte("# get\n"), TerminalState: "committed", } if _, _, err := mem.Request(ctx, facts, req); err != nil { t.Fatal(err) } if err := pub.Drain(ctx); err != nil { t.Fatal(err) } count.requests = 0 if _, _, err := pub.DegradedPayload(ctx, "dev", "doc_get", "proj-a"); err != nil { t.Fatal(err) } if count.requests != 0 { t.Fatalf("GET/degraded read wrote Request %d times", count.requests) } } func ptrTime() *time.Time { t := time.Now().UTC(); return &t }