mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 03:26:37 +08:00
44ca451cd4
优化
214 lines
7.1 KiB
Go
214 lines
7.1 KiB
Go
package service
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/ShukeBta/MMTL/internal/config"
|
|
"github.com/ShukeBta/MMTL/internal/model"
|
|
)
|
|
|
|
func TestScrapeDelayUsesSettings(t *testing.T) {
|
|
scraper, repos, closeServer := newTestScraper(t)
|
|
defer closeServer()
|
|
if err := repos.DB.AutoMigrate(&model.Setting{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if got := scraper.scrapeDelay(t.Context()); got < 250*time.Millisecond || got > 500*time.Millisecond {
|
|
t.Fatalf("default scrapeDelay = %s, want 250-500ms", got)
|
|
}
|
|
|
|
if err := repos.Setting.Set(t.Context(), "scrape.delay_min_ms", "0"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := repos.Setting.Set(t.Context(), "scrape.delay_max_ms", "0"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := scraper.scrapeDelay(t.Context()); got != 0 {
|
|
t.Fatalf("disabled scrapeDelay = %s, want 0", got)
|
|
}
|
|
|
|
if err := repos.Setting.Set(t.Context(), "scrape.delay_min_ms", "800"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := repos.Setting.Set(t.Context(), "scrape.delay_max_ms", "200"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := scraper.scrapeDelay(t.Context()); got != 800*time.Millisecond {
|
|
t.Fatalf("normalized scrapeDelay = %s, want 800ms", got)
|
|
}
|
|
}
|
|
|
|
func TestApplyProviderMatchInvalidatesMediaCache(t *testing.T) {
|
|
scraper, repos, closeServer := newTestScraper(t)
|
|
defer closeServer()
|
|
|
|
cache := NewRuntimeCacheService(&config.Config{}, zap.NewNop())
|
|
cache.SetJSON(t.Context(), "media:list:stale", map[string]string{"poster": ""}, time.Minute)
|
|
cache.SetJSON(t.Context(), "stats:snapshot:base", map[string]int{"media": 1}, time.Minute)
|
|
scraper.SetRuntimeCache(cache)
|
|
|
|
libPath := t.TempDir()
|
|
lib := model.Library{Name: "Movies", Path: libPath, Type: "movie", Enabled: true}
|
|
if err := repos.Library.Create(t.Context(), &lib); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
media := model.Media{LibraryID: lib.ID, Title: "Raw", Path: "/media/movies/raw.mkv", ScrapeStatus: "pending"}
|
|
if err := repos.DB.Create(&media).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
match := &Match{Title: "Matched", PosterURL: "https://image.tmdb.org/t/p/w500/poster.jpg", BackdropURL: "https://image.tmdb.org/t/p/w1280/backdrop.jpg"}
|
|
if err := scraper.applyProviderMatch(t.Context(), &media, &lib, match); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var stale map[string]string
|
|
if cache.GetJSON(t.Context(), "media:list:stale", &stale) {
|
|
t.Fatal("scraper should invalidate media list cache after applying artwork")
|
|
}
|
|
var stats map[string]int
|
|
if cache.GetJSON(t.Context(), "stats:snapshot:base", &stats) {
|
|
t.Fatal("scraper should invalidate stats cache after applying artwork")
|
|
}
|
|
var stored model.Media
|
|
if err := repos.DB.First(&stored, "id = ?", media.ID).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if stored.PosterURL == "" || stored.BackdropURL == "" || stored.ScrapeStatus != "matched" {
|
|
t.Fatalf("match not saved: poster=%q backdrop=%q status=%q", stored.PosterURL, stored.BackdropURL, stored.ScrapeStatus)
|
|
}
|
|
}
|
|
|
|
func TestApplyProviderMatchKeepsExistingArtworkWhenNewPrefetchFails(t *testing.T) {
|
|
scraper, repos, closeServer := newTestScraper(t)
|
|
defer closeServer()
|
|
|
|
images := NewImageProxy(&config.Config{Cache: config.CacheConfig{CacheDir: filepath.Join(t.TempDir(), "cache")}}, zap.NewNop())
|
|
images.client = &http.Client{Transport: imageRoundTripFunc(func(req *http.Request) (*http.Response, error) {
|
|
return &http.Response{
|
|
StatusCode: http.StatusBadGateway,
|
|
Status: "502 Bad Gateway",
|
|
Header: make(http.Header),
|
|
Body: io.NopCloser(strings.NewReader("bad gateway")),
|
|
Request: req,
|
|
}, nil
|
|
})}
|
|
scraper.SetImageProxy(images)
|
|
|
|
libPath := t.TempDir()
|
|
lib := model.Library{Name: "Movies", Path: libPath, Type: "movie", Enabled: true}
|
|
if err := repos.Library.Create(t.Context(), &lib); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
oldPoster := "https://image.tmdb.org/t/p/w500/old-poster.jpg"
|
|
oldBackdrop := "https://image.tmdb.org/t/p/w1280/old-backdrop.jpg"
|
|
media := model.Media{
|
|
LibraryID: lib.ID,
|
|
Title: "Raw",
|
|
Path: filepath.Join(libPath, "raw.mkv"),
|
|
PosterURL: oldPoster,
|
|
BackdropURL: oldBackdrop,
|
|
ScrapeStatus: "matched",
|
|
OriginalName: "Raw",
|
|
}
|
|
if err := repos.DB.Create(&media).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
match := &Match{
|
|
Title: "Matched",
|
|
PosterURL: "https://image.tmdb.org/t/p/w500/new-broken-poster.jpg",
|
|
BackdropURL: "",
|
|
}
|
|
if err := scraper.applyProviderMatch(t.Context(), &media, &lib, match); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var stored model.Media
|
|
if err := repos.DB.First(&stored, "id = ?", media.ID).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if stored.PosterURL != oldPoster {
|
|
t.Fatalf("poster should keep existing URL when new prefetch fails: got %q want %q", stored.PosterURL, oldPoster)
|
|
}
|
|
if stored.BackdropURL != oldBackdrop {
|
|
t.Fatalf("blank match backdrop should not clear existing backdrop: got %q want %q", stored.BackdropURL, oldBackdrop)
|
|
}
|
|
}
|
|
|
|
func TestApplyProviderMatchReplacesArtworkAndRemovesOldCache(t *testing.T) {
|
|
scraper, repos, closeServer := newTestScraper(t)
|
|
defer closeServer()
|
|
|
|
images := NewImageProxy(&config.Config{Cache: config.CacheConfig{CacheDir: filepath.Join(t.TempDir(), "cache")}}, zap.NewNop())
|
|
images.client = &http.Client{Transport: imageRoundTripFunc(func(req *http.Request) (*http.Response, error) {
|
|
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
|
|
})}
|
|
scraper.SetImageProxy(images)
|
|
|
|
libPath := t.TempDir()
|
|
lib := model.Library{Name: "Movies", Path: libPath, Type: "movie", Enabled: true}
|
|
if err := repos.Library.Create(t.Context(), &lib); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
oldPoster := "https://image.tmdb.org/t/p/w500/old-cache-poster.jpg"
|
|
newPoster := "https://image.tmdb.org/t/p/w500/new-cache-poster.jpg"
|
|
if err := images.PrefetchRemote(t.Context(), oldPoster); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, oldCachePath, _, err := images.remoteImageCachePaths(oldPoster)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := os.Stat(oldCachePath); err != nil {
|
|
t.Fatalf("old poster cache should exist before replace: %v", err)
|
|
}
|
|
media := model.Media{
|
|
LibraryID: lib.ID,
|
|
Title: "Raw",
|
|
Path: filepath.Join(libPath, "raw.mkv"),
|
|
PosterURL: oldPoster,
|
|
ScrapeStatus: "matched",
|
|
}
|
|
if err := repos.DB.Create(&media).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
match := &Match{Title: "Matched", PosterURL: newPoster}
|
|
if err := scraper.applyProviderMatch(t.Context(), &media, &lib, match); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var stored model.Media
|
|
if err := repos.DB.First(&stored, "id = ?", media.ID).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if stored.PosterURL != newPoster {
|
|
t.Fatalf("poster = %q, want %q", stored.PosterURL, newPoster)
|
|
}
|
|
if _, err := os.Stat(oldCachePath); !errors.Is(err, os.ErrNotExist) {
|
|
t.Fatalf("old poster cache should be removed, stat err=%v", err)
|
|
}
|
|
_, newCachePath, _, err := images.remoteImageCachePaths(newPoster)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := os.Stat(newCachePath); err != nil {
|
|
t.Fatalf("new poster cache should exist: %v", err)
|
|
}
|
|
}
|