Files
MeBox/internal/service/discover_artwork_test.go
T
2026-06-24 21:30:44 +08:00

136 lines
4.3 KiB
Go

package service
import (
"bytes"
"io"
"net/http"
"os"
"path/filepath"
"sync/atomic"
"testing"
"time"
"go.uber.org/zap"
"github.com/ShukeBta/MediaStationGo/internal/config"
)
func TestUniqueDiscoverArtworkURLsFiltersDuplicatesAndLimits(t *testing.T) {
urls := uniqueDiscoverArtworkURLs([]string{
"",
"/local/poster.jpg",
"https://image.tmdb.org/t/p/w500/a.jpg",
"https://image.tmdb.org/t/p/w500/a.jpg",
"https://image.tmdb.org/t/p/w500/b.jpg",
"https://image.tmdb.org/t/p/w500/c.jpg",
}, 2)
if len(urls) != 2 {
t.Fatalf("len = %d, want 2: %v", len(urls), urls)
}
if urls[0] != "https://image.tmdb.org/t/p/w500/a.jpg" || urls[1] != "https://image.tmdb.org/t/p/w500/b.jpg" {
t.Fatalf("urls = %v", urls)
}
}
func TestDiscoverArtworkURLsPrioritizePosters(t *testing.T) {
items := []ExternalMediaResult{
{PosterURL: "https://img.example/a-poster.jpg", BackdropURL: "https://img.example/a-backdrop.jpg"},
{PosterURL: "https://img.example/b-poster.jpg", BackdropURL: "https://img.example/b-backdrop.jpg"},
}
urls := externalArtworkURLs(items)
want := []string{
"https://img.example/a-poster.jpg",
"https://img.example/b-poster.jpg",
"https://img.example/a-backdrop.jpg",
"https://img.example/b-backdrop.jpg",
}
if len(urls) != len(want) {
t.Fatalf("len = %d, want %d: %v", len(urls), len(want), urls)
}
for i := range want {
if urls[i] != want[i] {
t.Fatalf("url[%d] = %q, want %q", i, urls[i], want[i])
}
}
}
func TestDiscoverWarmExternalArtworkPrefetchesAndCaches(t *testing.T) {
proxy := NewImageProxy(&config.Config{Cache: config.CacheConfig{CacheDir: filepath.Join(t.TempDir(), "cache")}}, zap.NewNop())
var calls int32
proxy.client = &http.Client{Transport: imageRoundTripFunc(func(req *http.Request) (*http.Response, error) {
atomic.AddInt32(&calls, 1)
return &http.Response{
StatusCode: http.StatusOK,
Status: "200 OK",
Header: http.Header{"Content-Type": []string{"image/jpeg"}},
Body: io.NopCloser(bytes.NewReader(testJPEG)),
Request: req,
}, nil
})}
discover := NewDiscoverService(zap.NewNop(), nil).SetImageProxy(proxy)
poster := "https://image.tmdb.org/t/p/w500/discover-poster.jpg"
backdrop := "https://image.tmdb.org/t/p/w1280/discover-backdrop.jpg"
queued := discover.WarmExternalArtwork([]ExternalMediaResult{
{Title: "A", PosterURL: poster, BackdropURL: backdrop},
{Title: "B", PosterURL: poster},
{Title: "Local", PosterURL: "/media/poster.jpg"},
})
if queued != 2 {
t.Fatalf("queued = %d, want 2", queued)
}
for _, raw := range []string{poster, backdrop} {
_, cachePath, _, err := proxy.remoteImageCachePaths(raw)
if err != nil {
t.Fatal(err)
}
waitForDiscoverArtworkCache(t, &calls, 2, cachePath, raw)
}
callsAfterCache := atomic.LoadInt32(&calls)
queued = discover.WarmExternalArtwork([]ExternalMediaResult{{Title: "Cached", PosterURL: poster, BackdropURL: backdrop}})
if queued != 2 {
t.Fatalf("queued cached = %d, want 2", queued)
}
time.Sleep(150 * time.Millisecond)
if got := atomic.LoadInt32(&calls); got != callsAfterCache {
t.Fatalf("cached prefetch should not call upstream again: got %d want %d", got, callsAfterCache)
}
}
func TestDiscoverWarmArtworkNoImageProxyIsNoop(t *testing.T) {
discover := NewDiscoverService(zap.NewNop(), nil)
if got := discover.WarmMatchArtwork([]Match{{PosterURL: "https://image.tmdb.org/t/p/w500/a.jpg"}}); got != 0 {
t.Fatalf("queued = %d, want 0 without image proxy", got)
}
}
func TestTMDbDiscoverPathIncludesLatestSections(t *testing.T) {
cases := map[string]string{
"tmdb_latest_movie": "/movie/now_playing",
"latest_movie": "/movie/now_playing",
"tmdb_latest_tv": "/tv/on_the_air",
"latest_tv": "/tv/on_the_air",
"tmdb_upcoming_movie": "/movie/upcoming",
}
for key, want := range cases {
if got := tmdbDiscoverPath(key); got != want {
t.Fatalf("tmdbDiscoverPath(%q) = %q, want %q", key, got, want)
}
}
}
func waitForDiscoverArtworkCache(t *testing.T, calls *int32, wantCalls int32, cachePath, raw string) {
t.Helper()
deadline := time.Now().Add(2 * time.Second)
for time.Now().Before(deadline) {
if atomic.LoadInt32(calls) >= wantCalls {
if _, err := os.Stat(cachePath); err == nil {
return
}
}
time.Sleep(10 * time.Millisecond)
}
t.Fatalf("expected cached artwork %q after %d upstream calls: %v", raw, atomic.LoadInt32(calls), os.ErrNotExist)
}