* 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>
50 lines
1.4 KiB
Go
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)
|
|
}
|
|
}
|