Files
bookhoard/internal/services/series_service_test.go
T
john-okeefe cce7ad4907 test(series): add unit and integration tests for series feature
Unit tests:
- series_service_test.go: test continueSeriesRowToMediaItems conversion
  with valid fields, null fields, and comprehensive field mapping
- series_test.go: test handler initialization and textToString helper

Integration tests (series_integration_test.go):
- GET /api/series: requires library_id, rejects invalid UUID, returns
  empty array for empty library, pagination params, limit clamped to
  100, response structure validation, special characters in names
- GET /api/series/books: requires library_id and name, handles
  nonexistent series, unauthorized access
- Restore Continue Series system collection
- Dashboard sections include all 5 collections (including continue-series)

Update test helpers:
- Add SeriesHandler to setupTestServer router config
- Add 5th Continue Series collection to createDefaultCollectionsForUser
- Update dashboard integration test for 5 collections
2026-05-08 20:27:40 -04:00

137 lines
6.7 KiB
Go

package services
import (
"bookhoard/internal/database"
"testing"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
"github.com/stretchr/testify/assert"
)
func TestContinueSeriesRowToMediaItems_FieldsMappedCorrectly(t *testing.T) {
itemUUID := uuid.New()
libUUID := uuid.New()
adminUUID := uuid.New()
row := database.GetContinueSeriesItemsRow{
ID: pgtype.UUID{Bytes: itemUUID, Valid: true},
LibraryID: pgtype.UUID{Bytes: libUUID, Valid: true},
Title: "Test Book",
Author: pgtype.Text{String: "Test Author", Valid: true},
Isbn: pgtype.Text{String: "978-1234567890", Valid: true},
Description: pgtype.Text{String: "A test book", Valid: true},
FilePath: "/books/test.epub",
FileSize: pgtype.Int8{Int64: 1024, Valid: true},
MimeType: pgtype.Text{String: "application/epub+zip", Valid: true},
CoverImagePath: pgtype.Text{String: "/covers/test.jpg", Valid: true},
Series: pgtype.Text{String: "Test Series", Valid: true},
SeriesNumber: pgtype.Int4{Int32: 3, Valid: true},
Tags: []string{"fantasy", "adventure"},
FormatGroup: "epub",
AddedByAdminID: pgtype.UUID{Bytes: adminUUID, Valid: true},
}
result := continueSeriesRowToMediaItems(row)
assert.Equal(t, pgtype.UUID{Bytes: itemUUID, Valid: true}, result.ID, "ID should match")
assert.Equal(t, pgtype.UUID{Bytes: libUUID, Valid: true}, result.LibraryID, "LibraryID should match")
assert.Equal(t, "Test Book", result.Title, "Title should match")
assert.Equal(t, pgtype.Text{String: "Test Author", Valid: true}, result.Author, "Author should match")
assert.Equal(t, pgtype.Text{String: "978-1234567890", Valid: true}, result.Isbn, "ISBN should match")
assert.Equal(t, "/books/test.epub", result.FilePath, "FilePath should match")
assert.Equal(t, pgtype.Text{String: "application/epub+zip", Valid: true}, result.MimeType, "MimeType should match")
assert.Equal(t, pgtype.Text{String: "/covers/test.jpg", Valid: true}, result.CoverImagePath, "CoverImagePath should match")
assert.Equal(t, pgtype.Text{String: "Test Series", Valid: true}, result.Series, "Series should match")
assert.Equal(t, pgtype.Int4{Int32: 3, Valid: true}, result.SeriesNumber, "SeriesNumber should match")
assert.Equal(t, []string{"fantasy", "adventure"}, result.Tags, "Tags should match")
assert.Equal(t, "epub", result.FormatGroup, "FormatGroup should match")
}
func TestContinueSeriesRowToMediaItems_NullFieldsHandled(t *testing.T) {
itemUUID := uuid.New()
libUUID := uuid.New()
row := database.GetContinueSeriesItemsRow{
ID: pgtype.UUID{Bytes: itemUUID, Valid: true},
LibraryID: pgtype.UUID{Bytes: libUUID, Valid: true},
Title: "No Metadata Book",
Author: pgtype.Text{Valid: false},
Isbn: pgtype.Text{Valid: false},
Description: pgtype.Text{Valid: false},
FilePath: "/books/nometa.epub",
FileSize: pgtype.Int8{Valid: false},
MimeType: pgtype.Text{Valid: false},
CoverImagePath: pgtype.Text{Valid: false},
Series: pgtype.Text{Valid: false},
SeriesNumber: pgtype.Int4{Valid: false},
Tags: nil,
FormatGroup: "epub",
}
result := continueSeriesRowToMediaItems(row)
assert.Equal(t, "No Metadata Book", result.Title)
assert.False(t, result.Author.Valid, "Author should be invalid/null")
assert.False(t, result.Series.Valid, "Series should be invalid/null")
assert.False(t, result.SeriesNumber.Valid, "SeriesNumber should be invalid/null")
assert.Nil(t, result.Tags, "Tags should be nil")
}
func TestContinueSeriesRowToMediaItems_AllFieldsMapped(t *testing.T) {
itemUUID := uuid.New()
libUUID := uuid.New()
row := database.GetContinueSeriesItemsRow{
ID: pgtype.UUID{Bytes: itemUUID, Valid: true},
LibraryID: pgtype.UUID{Bytes: libUUID, Valid: true},
Title: "Full Book",
Author: pgtype.Text{String: "Author", Valid: true},
Isbn: pgtype.Text{String: "ISBN", Valid: true},
Description: pgtype.Text{String: "Desc", Valid: true},
FilePath: "/book.epub",
FileSize: pgtype.Int8{Int64: 2048, Valid: true},
MimeType: pgtype.Text{String: "epub", Valid: true},
CoverImagePath: pgtype.Text{String: "/cover.jpg", Valid: true},
Series: pgtype.Text{String: "Series", Valid: true},
SeriesNumber: pgtype.Int4{Int32: 1, Valid: true},
Tags: []string{"tag1"},
Asin: pgtype.Text{String: "ASIN", Valid: true},
Publisher: pgtype.Text{String: "Pub", Valid: true},
Language: pgtype.Text{String: "en", Valid: true},
Edition: pgtype.Text{String: "1st", Valid: true},
Genre: pgtype.Text{String: "Fiction", Valid: true},
FormatGroup: "epub",
FormatMimetype: pgtype.Text{String: "application/epub+zip", Valid: true},
IsReflowable: pgtype.Bool{Bool: true, Valid: true},
HasFixedLayout: pgtype.Bool{Bool: false, Valid: true},
TotalCharacters: pgtype.Int8{Int64: 500000, Valid: true},
ChapterCount: pgtype.Int4{Int32: 20, Valid: true},
MangaType: pgtype.Text{String: "manga", Valid: true},
ReadingDirection: pgtype.Text{String: "rtl", Valid: true},
SeriesCount: pgtype.Int4{Int32: 10, Valid: true},
Volume: pgtype.Int4{Int32: 1, Valid: true},
LibraryTypeName: pgtype.Text{String: "ebooks", Valid: true},
FileSha256: pgtype.Text{String: "abc123", Valid: true},
}
result := continueSeriesRowToMediaItems(row)
assert.Equal(t, pgtype.Text{String: "ASIN", Valid: true}, result.Asin)
assert.Equal(t, pgtype.Text{String: "Pub", Valid: true}, result.Publisher)
assert.Equal(t, pgtype.Text{String: "en", Valid: true}, result.Language)
assert.Equal(t, pgtype.Text{String: "1st", Valid: true}, result.Edition)
assert.Equal(t, pgtype.Text{String: "Fiction", Valid: true}, result.Genre)
assert.Equal(t, pgtype.Text{String: "application/epub+zip", Valid: true}, result.FormatMimetype)
assert.Equal(t, pgtype.Bool{Bool: true, Valid: true}, result.IsReflowable)
assert.Equal(t, pgtype.Bool{Bool: false, Valid: true}, result.HasFixedLayout)
assert.Equal(t, pgtype.Int8{Int64: 500000, Valid: true}, result.TotalCharacters)
assert.Equal(t, pgtype.Int4{Int32: 20, Valid: true}, result.ChapterCount)
assert.Equal(t, pgtype.Text{String: "manga", Valid: true}, result.MangaType)
assert.Equal(t, pgtype.Text{String: "rtl", Valid: true}, result.ReadingDirection)
assert.Equal(t, pgtype.Int4{Int32: 10, Valid: true}, result.SeriesCount)
assert.Equal(t, pgtype.Int4{Int32: 1, Valid: true}, result.Volume)
assert.Equal(t, pgtype.Text{String: "ebooks", Valid: true}, result.LibraryTypeName)
assert.Equal(t, pgtype.Text{String: "abc123", Valid: true}, result.FileSha256)
}