Files
jellybit/internal/recognize/integration_test.go
T

92 lines
3.1 KiB
Go

package recognize_test
import (
"context"
"io"
"log/slog"
"os"
"strconv"
"testing"
"time"
"git.vakhrushev.me/av/jellybit/internal/llm"
"git.vakhrushev.me/av/jellybit/internal/recognize"
)
func derefInt(p *int) string {
if p == nil {
return "nil"
}
return strconv.Itoa(*p)
}
// TestIntegration_RecognizeSeries гоняет полный конвейер против реального
// LLM на настоящих (русских) именах файлов раздачи. По умолчанию
// пропускается; включается так же, как llm-интеграция:
//
// JELLYBIT_LLM_BASE_URL=https://bothub.chat/api/v2/openai/v1 \
// JELLYBIT_LLM_API_KEY=... JELLYBIT_LLM_MODEL=deepseek-v4-flash \
// go test ./internal/recognize/ -run Integration -v
func TestIntegration_RecognizeSeries(t *testing.T) {
base := os.Getenv("JELLYBIT_LLM_BASE_URL")
key := os.Getenv("JELLYBIT_LLM_API_KEY")
model := os.Getenv("JELLYBIT_LLM_MODEL")
if base == "" || model == "" {
t.Skip("set JELLYBIT_LLM_BASE_URL and JELLYBIT_LLM_MODEL to run")
}
provider, err := llm.New(llm.Config{
Type: "openai-compat", BaseURL: base, APIKey: key, Model: model,
Timeout: 90 * time.Second,
})
if err != nil {
t.Fatalf("llm.New: %v", err)
}
log := slog.New(slog.NewTextHandler(io.Discard, nil))
r := recognize.New(provider, recognize.Config{MaxRetries: 2}, log)
const dir = "Аватар Легенда об Аанге.Книга 2.Земля(Avatar The Last Airbender The book 2.Earth)/"
in := recognize.Input{
Name: "Аватар Легенда об Аанге.Книга 2.Земля",
Context: "Аватар: Легенда об Аанге / Книга 2: Земля [2006, США, DVDRip-AVC]",
Files: []recognize.File{
{Path: dir + "1.Состояние Аватара (The Avatar State).mkv", Size: 215_000_000},
{Path: dir + "6.Слепой бандит (Blind bandit).mkv", Size: 215_910_977},
{Path: dir + "8.Погоня (The Chase).mkv", Size: 216_587_695},
{Path: dir + "12.Змеиный перевал (The Serpent's Pass).mkv", Size: 216_330_940},
{Path: dir + "20.Перекрестки судьбы (The Crossroads of Destiny).mkv", Size: 215_934_285},
},
}
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
defer cancel()
res, err := r.Recognize(ctx, in)
if err != nil {
t.Fatalf("Recognize: %v", err)
}
t.Logf("type=%s title=%q year=%d files=%d attempts=%d\nreasons=%v\nnotes=%s",
res.Plan.Type, res.Plan.Title, res.Plan.Year, len(res.Plan.Files),
res.Attempts, res.Decision.Reasons, res.Plan.Notes)
for _, f := range res.Plan.Files {
t.Logf(" %s -> role=%s season=%s episode=%s", f.Src, f.Role, derefInt(f.Season), derefInt(f.Episode))
}
if res.Plan.Type != recognize.MediaSeries {
t.Errorf("type = %q, want series", res.Plan.Type)
}
if res.Decision.Auto {
t.Error("Ф2 must not auto-resolve")
}
episodes := 0
for _, f := range res.Plan.Files {
if f.Role == recognize.RoleEpisode {
episodes++
}
}
if episodes != len(in.Files) {
t.Errorf("recognized %d episodes, want %d", episodes, len(in.Files))
}
}