Files
silo-server/internal/metadata/image_source_path_test.go
T
QuickandClaude Fable 5 799754dde7 feat(notifications): rich Discord embeds with posters, provider links, and ratings
Upgrade all outbound Discord surfaces (personal webhooks, bot DMs, server
channels, request events) from bare title/description embeds to rich ones:
poster thumbnail, overview teaser, TMDB/IMDb/TVDB links, rating and genre
fields, content-rating footer, and a clickable title URL.

Artwork respects the v1 privacy contract via a new admin poster mode
(notifications.discord.poster_mode): "provider" (default) only emits public
provider-CDN URLs, "server" additionally presigns locally cached posters
from this server's image storage, "off" drops images entirely. Builders
never derive artwork URLs themselves; the sender layer resolves PosterURL
through System.discordPosterURL.

To keep provider-CDN URLs derivable after image caching rewrites
poster_path to a local storage key, media_items gains poster_source_path,
captured during cacheItemImages, preserved across refreshes that keep the
cached poster, and cleared on explicit poster overrides.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 09:32:53 -04:00

58 lines
2.0 KiB
Go

package metadata
import (
"context"
"testing"
"github.com/Silo-Server/silo-server/internal/models"
)
type stubImageCacher struct{}
func (stubImageCacher) CacheImage(_ context.Context, req CacheImageRequest) (*CacheImageResult, error) {
return &CacheImageResult{
BasePath: req.ProviderID + "/" + req.ContentType + "/" + req.ContentID + "/poster",
Thumbhash: "hash",
Ext: ".jpg",
}, nil
}
// Caching rewrites poster_path to a local storage key; the provider-origin
// path must survive in PosterSourcePath so outbound notification embeds can
// keep building public provider-CDN URLs.
func TestCacheItemImagesKeepsPosterSourcePath(t *testing.T) {
s := &MetadataService{imageCacher: stubImageCacher{}}
item := &models.MediaItem{
Type: "series",
TmdbID: "95396",
PosterPath: "https://image.tmdb.org/t/p/original/severance.jpg",
}
s.cacheItemImages(context.Background(), item, []RemoteImage{
{URL: item.PosterPath, ProviderID: "tmdb", Type: ImagePoster},
})
if item.PosterSourcePath != "https://image.tmdb.org/t/p/original/severance.jpg" {
t.Fatalf("provider poster path not preserved, got %q", item.PosterSourcePath)
}
if item.PosterPath != "tmdb/series/95396/poster/original.jpg" {
t.Fatalf("poster path not rewritten to cached key, got %q", item.PosterPath)
}
}
// Already-cached posters (bare storage keys) produce no cache job, so the
// source path must not be overwritten and the caller's carry-forward applies.
func TestCacheItemImagesSkipsBareKeys(t *testing.T) {
s := &MetadataService{imageCacher: stubImageCacher{}}
item := &models.MediaItem{
Type: "series",
TmdbID: "95396",
PosterPath: "tmdb/series/95396/poster/original.jpg",
}
s.cacheItemImages(context.Background(), item, nil)
if item.PosterSourcePath != "" {
t.Fatalf("bare key must not produce a source path, got %q", item.PosterSourcePath)
}
if item.PosterPath != "tmdb/series/95396/poster/original.jpg" {
t.Fatalf("bare key must pass through unchanged, got %q", item.PosterPath)
}
}