package library import ( "context" "database/sql" "errors" "io" "log/slog" "os" "path/filepath" "testing" "github.com/mithral/vellum/internal/core" "github.com/mithral/vellum/migrations" ) func testDB(t *testing.T) *sql.DB { t.Helper() db, err := core.OpenDB(filepath.Join(t.TempDir(), "lib.db")) if err != nil { t.Fatalf("OpenDB: %v", err) } t.Cleanup(func() { _ = db.Close() }) log := slog.New(slog.NewTextHandler(io.Discard, nil)) if err := core.Migrate(context.Background(), db, migrations.FS, log); err != nil { t.Fatalf("Migrate: %v", err) } return db } func testLogger() *slog.Logger { return slog.New(slog.NewTextHandler(io.Discard, nil)) } func writeFile(t *testing.T, path, content string) { t.Helper() if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(path, []byte(content), 0o644); err != nil { t.Fatal(err) } } func TestRegistryBasics(t *testing.T) { if err := ValidateKind("ebooks"); err != nil { t.Errorf("ebooks should be valid: %v", err) } if err := ValidateKind("nonsense"); !errors.Is(err, ErrUnknownKind) { t.Errorf("nonsense kind error = %v, want ErrUnknownKind", err) } if k, ok := KindForExtension(".epub"); !ok || k != "ebooks" { t.Errorf("KindForExtension(.epub) = %q,%v", k, ok) } if k, ok := KindForExtension("CBZ"); !ok || k != "comics" { t.Errorf("KindForExtension(CBZ) = %q,%v (should be case-insensitive)", k, ok) } if len(SupportedTypes()) == 0 { t.Error("no media types registered") } } func TestCleanTitle(t *testing.T) { cases := map[string]string{ "Project Hail Mary.epub": "Project Hail Mary", "the_hobbit.mobi": "the hobbit", "Book.Title.2020.epub": "Book Title 2020", "/deep/path/Saga 001.cbz": "Saga 001", } for in, want := range cases { if got := CleanTitle(in); got != want { t.Errorf("CleanTitle(%q) = %q, want %q", in, got, want) } } } func TestCreateLibraryValidation(t *testing.T) { db := testDB(t) ctx := context.Background() root := t.TempDir() if _, err := Create(ctx, db, "Good", "ebooks", root); err != nil { t.Fatalf("valid create failed: %v", err) } if _, err := Create(ctx, db, "Good", "ebooks", root); !errors.Is(err, ErrNameExists) { t.Errorf("duplicate name error = %v, want ErrNameExists", err) } if _, err := Create(ctx, db, "Bad", "nonsense", root); !errors.Is(err, ErrUnknownKind) { t.Errorf("bad kind error = %v, want ErrUnknownKind", err) } if _, err := Create(ctx, db, "Bad", "ebooks", "/nope/not/here"); !errors.Is(err, ErrInvalidRoot) { t.Errorf("bad root error = %v, want ErrInvalidRoot", err) } } func TestListVisibleNeverMeansAll(t *testing.T) { db := testDB(t) ctx := context.Background() root := t.TempDir() if _, err := Create(ctx, db, "Ebooks", "ebooks", root); err != nil { t.Fatal(err) } libs, err := ListVisible(ctx, db, nil) if err != nil { t.Fatal(err) } if len(libs) != 0 { t.Errorf("ListVisible(nil) returned %d libraries; empty set must mean none", len(libs)) } }