From c04c8ee02a68e7c97dc99f8b7f06066894aecf63 Mon Sep 17 00:00:00 2001 From: Deluan Date: Wed, 22 Jul 2026 00:58:22 -0400 Subject: [PATCH] fix(artwork): mock PutImage refreshes created_at like the SQL repository Prune specs now age fixtures directly instead of seeding stale timestamps through the upsert. --- core/artwork/prune_test.go | 22 ++++++++++++++-------- tests/mock_artwork_repo.go | 5 ++--- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/core/artwork/prune_test.go b/core/artwork/prune_test.go index 4a45ddd13..1f621363b 100644 --- a/core/artwork/prune_test.go +++ b/core/artwork/prune_test.go @@ -32,14 +32,21 @@ var _ = Describe("Prune", func() { store = NewImageStore(GinkgoT().TempDir()) }) + // PutImage refreshes created_at like the SQL repo, so fixtures are aged directly. + ageArtwork := func(h string, t time.Time) { + a := awRepo.Data[h] + a.CreatedAt = t + awRepo.Data[h] = a + } + It("deletes orphan rows and their store files, keeps referenced ones", func() { data := []byte("orphan-bytes") h, _ := HashImage(bytes.NewReader(data)) Expect(store.Write(h, "image/jpeg", bytes.NewReader(data))).To(Succeed()) old := time.Now().Add(-2 * time.Hour) Expect(os.Chtimes(store.path(h, "image/jpeg"), old, old)).To(Succeed()) - Expect(awRepo.PutImage(&model.Artwork{Hash: h, Mime: "image/jpeg", - CreatedAt: time.Now().Add(-2 * time.Hour)})).To(Succeed()) + Expect(awRepo.PutImage(&model.Artwork{Hash: h, Mime: "image/jpeg"})).To(Succeed()) + ageArtwork(h, old) awRepo.OrphanHashes = []string{h} kept := []byte("kept-bytes") @@ -62,8 +69,8 @@ var _ = Describe("Prune", func() { data := []byte("reacquired-bytes") h, _ := HashImage(bytes.NewReader(data)) Expect(store.Write(h, "image/jpeg", bytes.NewReader(data))).To(Succeed()) - Expect(awRepo.PutImage(&model.Artwork{Hash: h, Mime: "image/jpeg", - CreatedAt: time.Now().Add(-2 * time.Hour)})).To(Succeed()) + Expect(awRepo.PutImage(&model.Artwork{Hash: h, Mime: "image/jpeg"})).To(Succeed()) + ageArtwork(h, time.Now().Add(-2*time.Hour)) awRepo.OrphanHashes = []string{h} // Reacquisition: an item now references the hash the snapshot flagged as orphan. Expect(awRepo.PutItemArtwork(&model.ItemArtwork{ItemKind: "al", ItemID: "a1", @@ -83,8 +90,7 @@ var _ = Describe("Prune", func() { h, _ := HashImage(bytes.NewReader(data)) Expect(store.Write(h, "image/jpeg", bytes.NewReader(data))).To(Succeed()) // Reacquisition refreshed created_at after the snapshot; still unreferenced. - Expect(awRepo.PutImage(&model.Artwork{Hash: h, Mime: "image/jpeg", - CreatedAt: time.Now()})).To(Succeed()) + Expect(awRepo.PutImage(&model.Artwork{Hash: h, Mime: "image/jpeg"})).To(Succeed()) awRepo.OrphanHashes = []string{h} Expect(Prune(context.Background(), ds, store)).To(Succeed()) @@ -100,8 +106,8 @@ var _ = Describe("Prune", func() { data := []byte("racing-bytes") h, _ := HashImage(bytes.NewReader(data)) Expect(store.Write(h, "image/jpeg", bytes.NewReader(data))).To(Succeed()) - Expect(awRepo.PutImage(&model.Artwork{Hash: h, Mime: "image/jpeg", - CreatedAt: time.Now().Add(-2 * time.Hour)})).To(Succeed()) + Expect(awRepo.PutImage(&model.Artwork{Hash: h, Mime: "image/jpeg"})).To(Succeed()) + ageArtwork(h, time.Now().Add(-2*time.Hour)) awRepo.OrphanHashes = []string{h} // The row is legitimately orphaned, but a concurrent acquisition just touched the // file's mtime (duplicate Write) and is about to commit a row referencing it. diff --git a/tests/mock_artwork_repo.go b/tests/mock_artwork_repo.go index 9565abd7b..13ad4d5e1 100644 --- a/tests/mock_artwork_repo.go +++ b/tests/mock_artwork_repo.go @@ -34,9 +34,8 @@ func (m *MockArtworkRepo) PutImage(a *model.Artwork) error { if m.Err != nil { return m.Err } - if a.CreatedAt.IsZero() { - a.CreatedAt = time.Now() - } + // Mirrors the SQL repository: every upsert refreshes created_at. Age fixtures via Data directly. + a.CreatedAt = time.Now() m.Data[a.Hash] = *a return nil }