Files

105 lines
3.1 KiB
Go
Raw Permalink Normal View History

package handlers
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"
"time"
"github.com/jackc/pgx/v5/pgxpool"
)
// TestHandleListUnmatchedItemsExcludesMangaChapters verifies that manga
// chapter rows (type='ebook' items linked into a series via manga_chapters)
// do not appear in the admin Unmatched queue, while genuinely pending items
// still do. Regression test for issue #204.
func TestHandleListUnmatchedItemsExcludesMangaChapters(t *testing.T) {
dsn := os.Getenv("SILO_TEST_DATABASE_URL")
if dsn == "" {
t.Skip("set SILO_TEST_DATABASE_URL to run DB-backed unmatched items handler test")
}
ctx := context.Background()
pool, err := pgxpool.New(ctx, dsn)
if err != nil {
t.Fatalf("connect db: %v", err)
}
t.Cleanup(pool.Close)
suffix := time.Now().UnixNano()
// Embed a unique per-run tag in every seeded title and scope the search
// query to it, so concurrent or leftover rows in a shared test database
// cannot skew the exact-total assertion below.
tag := fmt.Sprintf("issue204-%d", suffix)
seriesID := fmt.Sprintf("manga-series-%d", suffix)
chapterID := fmt.Sprintf("manga-chapter-%d", suffix)
pendingID := fmt.Sprintf("pending-ebook-%d", suffix)
t.Cleanup(func() {
_, _ = pool.Exec(ctx, `DELETE FROM media_items WHERE content_id = ANY($1)`,
[]string{chapterID, seriesID, pendingID})
})
for _, row := range []struct {
id, typ, title, status string
}{
{seriesID, "manga", tag + " Series", "matched"},
{chapterID, "ebook", tag + " Series c001", "pending"},
{pendingID, "ebook", tag + " Plain Ebook", "pending"},
} {
if _, err := pool.Exec(ctx, `
INSERT INTO media_items (content_id, type, title, status, genres)
VALUES ($1, $2, $3, $4, '{}'::text[])
`, row.id, row.typ, row.title, row.status); err != nil {
t.Fatalf("seed media item %s: %v", row.id, err)
}
}
if _, err := pool.Exec(ctx, `
INSERT INTO manga_chapters (chapter_content_id, series_content_id, chapter_index)
VALUES ($1, $2, 1)
`, chapterID, seriesID); err != nil {
t.Fatalf("seed manga chapter link: %v", err)
}
h := NewLibraryHandler(nil, nil, nil, pool, nil)
req := httptest.NewRequest(http.MethodGet, "/libraries/unmatched-items?q="+url.QueryEscape(tag), nil)
rec := httptest.NewRecorder()
h.HandleListUnmatchedItems(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status = %d, want 200 (body: %s)", rec.Code, rec.Body.String())
}
var resp struct {
Items []struct {
ContentID string `json:"content_id"`
} `json:"items"`
Total int `json:"total"`
}
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
t.Fatalf("decode response: %v", err)
}
var sawChapter, sawPending bool
for _, item := range resp.Items {
switch item.ContentID {
case chapterID:
sawChapter = true
case pendingID:
sawPending = true
}
}
if !sawPending {
t.Errorf("pending ebook %s missing from unmatched list", pendingID)
}
if sawChapter {
t.Errorf("manga chapter %s should be excluded from unmatched list", chapterID)
}
if resp.Total != 1 {
t.Errorf("total = %d, want 1 (chapter must be excluded from the count too)", resp.Total)
}
}