test(server): fixture collisions fixed, stale contract asserts re-anchored, gofmt — suite now fully green

Closes the remaining integration-suite failures (handoff open bug 3),
line by line:

- Fixture family (the upsert equivalence): tags_filter_test
  ("Science Fiction" found 1 of 3; "Fantasy" 404'd on zero results),
  media_item_isbn_test (normalization read back the first case's ISBN
  everywhere; edge cases saw a nil ISBN; pagination collapsed 5
  creates onto 1 row), search_unified_test (its 3 books were actually
  1 row + two no-op creates) — all POSTed a constant file_path into
  one library while CreateMediaItem upserts ON CONFLICT
  (library_id, file_path). Every fixture now uses unique per-item
  paths, the suite's universal convention elsewhere. TestTagsFilter
  confirmed same-family: counts came right with no second bug.
- New TestMediaItemCreateDuplicatePath pins the honest-create
  contract from da13898: 201 for the first create; 200 + duplicate
  flag + the EXISTING row (never a 201 lie), second payload not
  overwritten.
- library_browse_test: the admin subtest browsed the container's
  /tmp and required it non-empty — env-coupled (empty /tmp → "was
  []"). Now browses a self-created t.TempDir with two subdirs and a
  decoy file, asserting ONLY directories are listed.
- series_integration_test: three stale assertions re-anchored to the
  shipped contracts, each with a ground-truth comment: /dashboard/
  sections key identity on the collection NAME (BuildSections ID =
  CollectionName; the web/app reorder + hidden prefs persist those
  strings) — the kebab query_type is the storage key and is not
  exposed; /api/series/books is name-ONLY (the app client and the
  web SSR page both send name only; library_id is ignored, never
  validated); /api/series is optional-scope BY DESIGN (GetDistinct-
  Series: narg IS NULL OR library_id = narg aggregates across
  libraries when omitted). The former 400-on-missing-library_id
  expectations asserted validation that never shipped.
- gofmt rider on three untouched-in-this-arc test files
  (dashboard_integration, search, test_helpers).

Full suite with -parallel=1: 217 groups pass, zero failures.
This commit is contained in:
John O'Keefe committed 2026-10-09 17:11:12 -04:00
1 parent da13898046
commit f24a2d6c22
8 files changed
+185 -74

No files matched your search

+69 -6
View File
@@ -7,6 +7,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
@@ -151,11 +152,15 @@ func TestMediaItemISBNNormalization(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Unique file_path per case: CreateMediaItem upserts ON
// CONFLICT (library_id, file_path), so a constant path would
// return the FIRST row's ISBN for every later case.
slug := strings.ReplaceAll(strings.ToLower(tc.name), " ", "-")
payload := map[string]interface{}{
"title": fmt.Sprintf("Test Book %s", tc.name),
"isbn": tc.input,
"library_id": libID,
"file_path": "/test/path.epub",
"file_path": "/test/" + slug + ".epub",
"file_size": 1024,
"mime_type": "application/epub+zip",
}
@@ -203,7 +208,7 @@ func TestMediaItemISBNEdgeCases(t *testing.T) {
payload := map[string]interface{}{
"title": "Book without ISBN",
"library_id": libID,
"file_path": "/test/path.epub",
"file_path": "/test/book-without-isbn.epub",
"file_size": 1024,
"mime_type": "application/epub+zip",
}
@@ -228,7 +233,7 @@ func TestMediaItemISBNEdgeCases(t *testing.T) {
"title": "Multi-Hyphen ISBN",
"isbn": "978-0-306-40615-7",
"library_id": libID,
"file_path": "/test/path.epub",
"file_path": "/test/multi-hyphen-isbn.epub",
"file_size": 1024,
"mime_type": "application/epub+zip",
}
@@ -258,7 +263,7 @@ func TestMediaItemISBNEdgeCases(t *testing.T) {
"title": "Trailing Hyphen ISBN",
"isbn": "978-0-596-00965-2",
"library_id": libID,
"file_path": "/test/path.epub",
"file_path": "/test/trailing-hyphen-isbn.epub",
"file_size": 1024,
"mime_type": "application/epub+zip",
}
@@ -292,13 +297,15 @@ func TestMediaItemsPagination(t *testing.T) {
libID := createTestLibrary(t, setup.Server, token, "test-pagination-lib")
addFolderToLibrary(t, setup, libID, "/app/uploads")
// Create some test media items
// Create some test media items. Unique file_path per item: the create
// endpoint upserts ON CONFLICT (library_id, file_path), so a constant
// path would collapse all five creates onto one row.
for i := 1; i <= 5; i++ {
payload := map[string]interface{}{
"title": fmt.Sprintf("Book %d", i),
"isbn": fmt.Sprintf("978030640615%d", i),
"library_id": libID,
"file_path": "/test/path.epub",
"file_path": fmt.Sprintf("/test/book-%d.epub", i),
"file_size": 1024,
"mime_type": "application/epub+zip",
}
@@ -408,6 +415,62 @@ func TestMediaItemsPagination(t *testing.T) {
})
}
// TestMediaItemCreateDuplicatePath pins the create endpoint's duplicate
// behavior: a POST with a (library_id, file_path) that already exists
// answers 200 OK with the EXISTING item and duplicate=true — never a
// 201 with the old row pretending to be new, and nothing overwritten.
func TestMediaItemCreateDuplicatePath(t *testing.T) {
setup := setupTestServer(t)
token := setup.Token
libID := createTestLibrary(t, setup.Server, token, "test-dup-create-lib")
addFolderToLibrary(t, setup, libID, "/app/uploads")
createPayload := func(title string) []byte {
body, _ := json.Marshal(map[string]interface{}{
"title": title,
"author": "First Author",
"isbn": "9780306406157",
"library_id": libID,
"file_path": "/test/dup-target.epub",
"file_size": 1024,
"mime_type": "application/epub+zip",
})
return body
}
post := func(body []byte) (*http.Response, []byte) {
req, _ := http.NewRequest("POST", setup.Server.URL+"/api/media-items", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
resp, err := (&http.Client{}).Do(req)
require.NoError(t, err)
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(resp.Body)
raw, err := io.ReadAll(resp.Body)
require.NoError(t, err)
return resp, raw
}
resp, raw := post(createPayload("Original Title"))
require.Equal(t, http.StatusCreated, resp.StatusCode, "first create should be 201")
var created map[string]interface{}
require.NoError(t, json.Unmarshal(raw, &created))
originalID := created["id"].(string)
resp, raw = post(createPayload("Different Title"))
require.Equal(t, http.StatusOK, resp.StatusCode, "duplicate create should be 200, not a 201 lie")
var dup map[string]interface{}
require.NoError(t, json.Unmarshal(raw, &dup))
assert.Equal(t, true, dup["duplicate"], "response must flag the duplicate")
assert.Equal(t, originalID, dup["item"].(map[string]interface{})["id"].(string),
"duplicate response carries the existing item")
assert.Equal(t, "Original Title", dup["item"].(map[string]interface{})["title"],
"the second create's payload must NOT overwrite the existing row")
}
// TestMediaItemLibraryRequirement tests that media items require a library
func TestMediaItemLibraryRequirement(t *testing.T) {
setup := setupTestServer(t)