test: add episode title enrichment tests
This commit is contained in:
145
internal/episodes/service/titles_test.go
Normal file
145
internal/episodes/service/titles_test.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"mal/integrations/jikan"
|
||||
"mal/internal/db"
|
||||
"mal/internal/domain"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
type titleProviderStub struct {
|
||||
loads int
|
||||
resolves int
|
||||
}
|
||||
|
||||
func (p *titleProviderStub) Name() string { return "TVmaze" }
|
||||
|
||||
func (p *titleProviderStub) ResolveEpisodeProviderID(context.Context, int, []string) (string, error) {
|
||||
p.resolves++
|
||||
return "80712", nil
|
||||
}
|
||||
|
||||
func (p *titleProviderStub) GetEpisodeTitlesByProviderID(context.Context, string) (map[int]string, error) {
|
||||
p.loads++
|
||||
return map[int]string{1: "TVmaze title one", 2: "TVmaze title two"}, nil
|
||||
}
|
||||
|
||||
func TestMergeMissingTitlesPreservesAllAnimeTitles(t *testing.T) {
|
||||
episodes := []domain.CanonicalEpisode{
|
||||
{Number: 1, Title: "AllAnime title"},
|
||||
{Number: 2, Title: "Episode 2"},
|
||||
{Number: 3, Title: "Episode 3"},
|
||||
}
|
||||
|
||||
changed := mergeMissingTitles(episodes, map[int]string{
|
||||
1: "TVmaze title one",
|
||||
2: "TVmaze title two",
|
||||
})
|
||||
|
||||
if !changed {
|
||||
t.Fatal("expected missing title to be enriched")
|
||||
}
|
||||
if episodes[0].Title != "AllAnime title" {
|
||||
t.Fatalf("AllAnime title was overwritten: %q", episodes[0].Title)
|
||||
}
|
||||
if episodes[1].Title != "TVmaze title two" {
|
||||
t.Fatalf("episode 2 title = %q", episodes[1].Title)
|
||||
}
|
||||
if episodes[2].Title != "Episode 3" {
|
||||
t.Fatalf("episode 3 title = %q", episodes[2].Title)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnrichEpisodeTitlesCachesTVmazeTitles(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
sqlDB := newTitleTestDB(ctx, t)
|
||||
provider := &titleProviderStub{}
|
||||
svc := &EpisodeService{queries: db.New(sqlDB), titles: provider, clock: realClock{}}
|
||||
anime := domain.Anime{Anime: jikan.Anime{
|
||||
MalID: 59846,
|
||||
Title: "Saigo ni Hitotsu dake Onegai shitemo Yoroshii deshou ka",
|
||||
TitleEnglish: "May I Ask for One Final Thing?",
|
||||
}}
|
||||
|
||||
for range 2 {
|
||||
assertEnrichedTitleList(t, svc, anime)
|
||||
}
|
||||
if provider.resolves != 1 || provider.loads != 1 {
|
||||
t.Fatalf("provider calls = resolves:%d loads:%d, want 1 each", provider.resolves, provider.loads)
|
||||
}
|
||||
assertCachedTitles(ctx, t, sqlDB)
|
||||
}
|
||||
|
||||
func newTitleTestDB(ctx context.Context, t *testing.T) *sql.DB {
|
||||
t.Helper()
|
||||
sqlDB, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if err := sqlDB.Close(); err != nil {
|
||||
t.Errorf("close sqlite: %v", err)
|
||||
}
|
||||
})
|
||||
sqlDB.SetMaxOpenConns(1)
|
||||
|
||||
for _, statement := range []string{
|
||||
`CREATE TABLE episode_availability_cache (
|
||||
anime_id INTEGER PRIMARY KEY, data TEXT NOT NULL, next_refresh_at DATETIME,
|
||||
retry_until_at DATETIME, last_attempt_at DATETIME, last_success_at DATETIME,
|
||||
failure_count INTEGER NOT NULL DEFAULT 0, last_error TEXT NOT NULL DEFAULT '',
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)`,
|
||||
`CREATE TABLE episode_provider_mapping (
|
||||
anime_id INTEGER NOT NULL, provider TEXT NOT NULL, provider_show_id TEXT NOT NULL,
|
||||
failed_until DATETIME, last_error TEXT NOT NULL DEFAULT '',
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (anime_id, provider)
|
||||
)`,
|
||||
`INSERT INTO episode_availability_cache (
|
||||
anime_id, data, next_refresh_at, failure_count, last_error
|
||||
) VALUES (
|
||||
59846,
|
||||
'{"anime_id":59846,"source":"AllAnime","availability_version":4,"episodes":[{"number":1,"title":"AllAnime title","has_sub":true},{"number":2,"title":"Episode 2","has_sub":true}]}',
|
||||
'2999-01-01T00:00:00Z', 2, 'preserve me'
|
||||
)`,
|
||||
} {
|
||||
if _, err := sqlDB.ExecContext(ctx, statement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
return sqlDB
|
||||
}
|
||||
|
||||
func assertEnrichedTitleList(t *testing.T, svc *EpisodeService, anime domain.Anime) {
|
||||
t.Helper()
|
||||
list, err := svc.EnrichEpisodeTitles(context.Background(), anime)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if list.Episodes[0].Title != "AllAnime title" || list.Episodes[1].Title != "TVmaze title two" {
|
||||
t.Fatalf("episodes = %#v", list.Episodes)
|
||||
}
|
||||
}
|
||||
|
||||
func assertCachedTitles(ctx context.Context, t *testing.T, sqlDB *sql.DB) {
|
||||
t.Helper()
|
||||
var raw, lastError string
|
||||
var failureCount int
|
||||
if err := sqlDB.QueryRowContext(ctx, `SELECT data, failure_count, last_error FROM episode_availability_cache WHERE anime_id = 59846`).Scan(&raw, &failureCount, &lastError); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var cached domain.CanonicalEpisodeList
|
||||
if err := json.Unmarshal([]byte(raw), &cached); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cached.Episodes[1].Title != "TVmaze title two" || failureCount != 2 || lastError != "preserve me" {
|
||||
t.Fatalf("cached = %#v failure_count=%d last_error=%q", cached.Episodes, failureCount, lastError)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user