Files
MeBox/internal/service/scraper_test.go
T
2026-06-24 15:18:57 +08:00

268 lines
8.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
import (
"path/filepath"
"strings"
"testing"
"github.com/ShukeBta/MediaStationGo/internal/model"
)
func TestEnrichOneUsesExistingTMDbIDForCloudMedia(t *testing.T) {
scraper, repos, closeServer := newTestScraper(t)
defer closeServer()
lib := model.Library{Name: "OpenList · 国漫", Path: "cloud://openlist/国漫", Type: "anime", Enabled: true}
if err := repos.DB.Create(&lib).Error; err != nil {
t.Fatal(err)
}
media := model.Media{
LibraryID: lib.ID,
Title: "dirty release title",
Path: "cloud://openlist/国漫/间谍过家家 (2022) {tmdb-12345}/Season 1/间谍过家家.S01E01.2160p.mkv",
SeasonNum: 1,
EpisodeNum: 1,
TMDbID: 12345,
ScrapeStatus: "pending",
}
if err := repos.DB.Create(&media).Error; err != nil {
t.Fatal(err)
}
if err := scraper.EnrichOne(t.Context(), &media); err != nil {
t.Fatal(err)
}
var got model.Media
if err := repos.DB.First(&got, "id = ?", media.ID).Error; err != nil {
t.Fatal(err)
}
if got.ScrapeStatus != "matched" || got.Title != "间谍过家家" || got.TMDbID != 12345 || got.PosterURL == "" {
t.Fatalf("tmdb id scrape did not apply match: title=%q status=%q tmdb=%d poster=%q", got.Title, got.ScrapeStatus, got.TMDbID, got.PosterURL)
}
}
func TestEnrichOneWritesTMDbIDColumn(t *testing.T) {
scraper, repos, closeServer := newTestScraper(t)
defer closeServer()
lib := model.Library{Name: "番剧", Path: t.TempDir(), Type: "tv", Enabled: true}
if err := repos.DB.Create(&lib).Error; err != nil {
t.Fatal(err)
}
mediaPath := filepath.Join(lib.Path, "间谍过家家 - S02E01.mkv")
if err := repos.DB.Create(&model.Media{
LibraryID: lib.ID,
Title: "间谍过家家",
Path: mediaPath,
SeasonNum: 2,
EpisodeNum: 1,
ScrapeStatus: "pending",
}).Error; err != nil {
t.Fatal(err)
}
var media model.Media
if err := repos.DB.First(&media, "path = ?", mediaPath).Error; err != nil {
t.Fatal(err)
}
if err := scraper.EnrichOne(t.Context(), &media); err != nil {
t.Fatal(err)
}
var got model.Media
if err := repos.DB.First(&got, "id = ?", media.ID).Error; err != nil {
t.Fatal(err)
}
if got.ScrapeStatus != "matched" || got.TMDbID != 12345 {
t.Fatalf("unexpected scraped media: status=%q tmdb=%d", got.ScrapeStatus, got.TMDbID)
}
}
func TestEnrichOneTreatsEpisodicMediaInMovieLibraryAsTV(t *testing.T) {
scraper, repos, closeServer := newTestScraper(t)
defer closeServer()
lib := model.Library{Name: "混合库", Path: t.TempDir(), Type: "movie", Enabled: true}
if err := repos.DB.Create(&lib).Error; err != nil {
t.Fatal(err)
}
media := model.Media{
LibraryID: lib.ID,
Title: "间谍过家家 S02E01",
Path: filepath.Join(lib.Path, "间谍过家家", "Season 02", "间谍过家家 - S02E01.mkv"),
SeasonNum: 2,
EpisodeNum: 1,
ScrapeStatus: "pending",
}
if err := repos.DB.Create(&media).Error; err != nil {
t.Fatal(err)
}
if err := scraper.EnrichOne(t.Context(), &media); err != nil {
t.Fatal(err)
}
var got model.Media
if err := repos.DB.First(&got, "id = ?", media.ID).Error; err != nil {
t.Fatal(err)
}
if got.ScrapeStatus != "matched" || got.TMDbID != 12345 {
t.Fatalf("episodic media in movie library should use tv scrape: status=%q tmdb=%d", got.ScrapeStatus, got.TMDbID)
}
}
func TestEnrichOneWritesTMDbEpisodeMetadata(t *testing.T) {
scraper, repos, closeServer := newTestScraper(t)
defer closeServer()
lib := model.Library{Name: "番剧", Path: t.TempDir(), Type: "tv", Enabled: true}
if err := repos.DB.Create(&lib).Error; err != nil {
t.Fatal(err)
}
mediaPath := filepath.Join(lib.Path, "间谍过家家 - S02E01.mkv")
existingPoster := "https://image.tmdb.org/t/p/w500/existing-poster.jpg"
existingBackdrop := "https://image.tmdb.org/t/p/w1280/existing-backdrop.jpg"
media := model.Media{
LibraryID: lib.ID,
Title: "间谍过家家",
Path: mediaPath,
SeasonNum: 2,
EpisodeNum: 1,
PosterURL: existingPoster,
BackdropURL: existingBackdrop,
ScrapeStatus: "pending",
}
if err := repos.DB.Create(&media).Error; err != nil {
t.Fatal(err)
}
if err := scraper.EnrichOne(t.Context(), &media); err != nil {
t.Fatal(err)
}
var got model.Media
if err := repos.DB.First(&got, "id = ?", media.ID).Error; err != nil {
t.Fatal(err)
}
// 单集专属信息(简介/剧照/评分/时长)应回填到该集行。
if got.Overview != "单集剧情" {
t.Fatalf("episode overview not saved: overview=%q", got.Overview)
}
if !strings.HasSuffix(got.BackdropURL, "/images/w500/still.jpg") || got.DurationSec != 24*60 {
t.Fatalf("episode still/runtime not saved: backdrop=%q duration=%d", got.BackdropURL, got.DurationSec)
}
if got.Rating < 9.09 || got.Rating > 9.11 {
t.Fatalf("episode rating = %v, want 9.1", got.Rating)
}
if got.EpisodeTitle != "任务代号: 猫" {
t.Fatalf("episode_title should store per-episode name, got %q", got.EpisodeTitle)
}
// original_name 必须保持「整剧原名」,绝不能被单集名(任务代号: 猫)覆盖,
// 否则同剧每集 original_name 不同会导致合集被拆成多集无法合并。
if got.OriginalName != "SPY×FAMILY" {
t.Fatalf("original_name should stay series-level, got %q (episode name must not overwrite it)", got.OriginalName)
}
}
func TestEnrichOneSkipsTMDbEpisodeStillWhenDisabled(t *testing.T) {
scraper, repos, closeServer := newTestScraper(t)
defer closeServer()
lib := model.Library{Name: "番剧", Path: t.TempDir(), Type: "tv", Enabled: true}
if err := repos.DB.Create(&lib).Error; err != nil {
t.Fatal(err)
}
mediaPath := filepath.Join(lib.Path, "间谍过家家 - S02E01.mkv")
existingPoster := "https://image.tmdb.org/t/p/w500/existing-poster.jpg"
existingBackdrop := "https://image.tmdb.org/t/p/w1280/existing-backdrop.jpg"
media := model.Media{
LibraryID: lib.ID,
Title: "间谍过家家",
Path: mediaPath,
SeasonNum: 2,
EpisodeNum: 1,
PosterURL: existingPoster,
BackdropURL: existingBackdrop,
ScrapeStatus: "pending",
}
if err := repos.DB.Create(&media).Error; err != nil {
t.Fatal(err)
}
episodeArtwork := false
if err := scraper.EnrichOneWithOptions(t.Context(), &media, ScrapeOptions{EpisodeArtwork: &episodeArtwork}); err != nil {
t.Fatal(err)
}
var got model.Media
if err := repos.DB.First(&got, "id = ?", media.ID).Error; err != nil {
t.Fatal(err)
}
if got.Overview != "单集剧情" || got.DurationSec != 24*60 {
t.Fatalf("episode metadata should still be saved: overview=%q duration=%d", got.Overview, got.DurationSec)
}
if got.Rating < 9.09 || got.Rating > 9.11 {
t.Fatalf("episode rating = %v, want 9.1", got.Rating)
}
if strings.HasSuffix(got.BackdropURL, "/images/w500/still.jpg") {
t.Fatalf("episode still should not be saved when disabled: backdrop=%q", got.BackdropURL)
}
if !strings.HasSuffix(got.PosterURL, "/images/w500/poster.jpg") {
t.Fatalf("series poster should still be saved when episode artwork is disabled: got %q", got.PosterURL)
}
if !strings.HasSuffix(got.BackdropURL, "/images/w1280/backdrop.jpg") {
t.Fatalf("series backdrop should still be saved when episode artwork is disabled: got %q", got.BackdropURL)
}
if got.PosterURL == existingPoster || got.BackdropURL == existingBackdrop {
t.Fatalf("main artwork should be refreshed while episode still is skipped: poster=%q backdrop=%q", got.PosterURL, got.BackdropURL)
}
}
func TestApplyManualMatchSkipsTMDbEpisodeStillWhenDisabled(t *testing.T) {
scraper, repos, closeServer := newTestScraper(t)
defer closeServer()
lib := model.Library{Name: "番剧", Path: t.TempDir(), Type: "tv", Enabled: true}
if err := repos.DB.Create(&lib).Error; err != nil {
t.Fatal(err)
}
media := model.Media{
LibraryID: lib.ID,
Title: "待匹配",
Path: filepath.Join(lib.Path, "间谍过家家 - S02E01.mkv"),
SeasonNum: 2,
EpisodeNum: 1,
ScrapeStatus: "pending",
}
if err := repos.DB.Create(&media).Error; err != nil {
t.Fatal(err)
}
episodeArtwork := false
got, err := scraper.ApplyManualMatch(t.Context(), media.ID, ManualScrapeRequest{
Source: "tmdb",
MediaType: "tv",
Title: "间谍过家家",
TMDbID: 12345,
EpisodeArtwork: &episodeArtwork,
})
if err != nil {
t.Fatal(err)
}
if got == nil {
t.Fatal("manual match returned nil media")
}
if got.Overview != "单集剧情" || got.DurationSec != 24*60 {
t.Fatalf("episode metadata should still be saved: overview=%q duration=%d", got.Overview, got.DurationSec)
}
if strings.HasSuffix(got.BackdropURL, "/images/w500/still.jpg") {
t.Fatalf("manual episode still should not be saved when disabled: backdrop=%q", got.BackdropURL)
}
if !strings.HasSuffix(got.PosterURL, "/images/w500/poster.jpg") {
t.Fatalf("series poster should still be saved when manual episode artwork is disabled: got %q", got.PosterURL)
}
if !strings.HasSuffix(got.BackdropURL, "/images/w1280/backdrop.jpg") {
t.Fatalf("series backdrop should still be saved when manual episode artwork is disabled: got %q", got.BackdropURL)
}
}