Files
silo-server/internal/metadata/apply_item_image_test.go
1664c60425 fix(metadata): publish artwork revisions atomically (#399)
* fix(metadata): publish artwork revisions atomically

* fix(metadata): harden artwork revision cleanup

* fix(metadata): address artwork revision review findings

- restore image applies for all media_items types and reject unsupported
  target/image combinations with 400 before uploading; episodes coerce to
  stills and the web dialog no longer offers image tabs episodes can't use
- add WHEN clauses to displacement triggers and hoist to_jsonb so bulk
  catalog upserts that assign unchanged artwork columns skip the trigger
- make artworkkey the single variant-ladder owner: imagecache derives its
  widths from it and triggers store image_type instead of hardcoded
  variant arrays, expanded by the collector at deletion time
- sweep dormant registry rows periodically so references lost through
  untriggered surfaces degrade to slow cleanup instead of leaking
- park just-published revisions dormant, keep dormant rows dormant on
  re-cache, and batch the GC reference pre-check per run
- heal rows re-referencing a just-deleted revision via reconciler-style
  resets after the deletion commits
- share a per-URL image-loaded hook across DetailHero, ItemCard,
  SectionItemCard, GlobalSearch, and CollectionPosterCard
- deduplicate Cache/CacheBytes finalization and drop unused VariantPaths
  plumbing

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(catalog): cast reused timestamp parameter in revision upsert

Postgres cannot deduce one type for $3 used both as a plain value and
inside a CASE arm; the dev deploy surfaced it as SQLSTATE 42P08 on every
publication. Cast both uses and cover the arm/park/track upserts with
database-backed tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(metadata): address artwork revision review comments

- keep a durable heal path: deletion marks deleted_at instead of removing
  the registry row, so a failed post-delete heal retries with backoff and
  broken references never park; trackers clear the marker on re-upload
- never treat bare existence as an immutable-content match; backends
  without content verification rewrite the object
- exercise revisioned cover keys in scanner/enrichment fakes, compare the
  tracked manifest exactly, and honor cancellation in the blocking test
  deleter

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Quick104 <31828688+Quick104@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 17:33:21 -04:00

50 lines
1.4 KiB
Go

package metadata
import (
"context"
"testing"
)
const (
applyImageTestExt = ".webp"
applyImageTestProviderID = "tmdb"
applyImageTestContentType = "movie"
)
type recordingImageCacher struct {
request CacheImageRequest
}
func (c *recordingImageCacher) CacheImage(_ context.Context, req CacheImageRequest) (*CacheImageResult, error) {
c.request = req
return &CacheImageResult{
BasePath: "tmdb/movies/335984/poster",
OriginalPath: "tmdb/movies/335984/poster/original.new-revision.webp",
Revision: "new-revision",
Thumbhash: "new-thumbhash",
Ext: applyImageTestExt,
}, nil
}
func TestApplyItemImageReturnsExactImmutableRevision(t *testing.T) {
cacher := &recordingImageCacher{}
service := &MetadataService{imageCacher: cacher}
result, err := service.ApplyItemImage(context.Background(), ApplyItemImageRequest{
OriginalURL: "https://image.tmdb.org/t/p/original/new-poster.jpg",
ProviderID: applyImageTestProviderID,
ContentType: applyImageTestContentType,
ContentID: "335984",
ImageType: ImagePoster,
})
if err != nil {
t.Fatalf("ApplyItemImage: %v", err)
}
if result.StoredPath != "tmdb/movies/335984/poster/original.new-revision.webp" {
t.Fatalf("StoredPath = %q, want exact immutable revision path", result.StoredPath)
}
if result.Revision != "new-revision" {
t.Fatalf("Revision = %q, want new-revision", result.Revision)
}
}