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
This commit is contained in:
@@ -53,7 +53,7 @@ func (s *DashboardIntegrationTestSuite) TestGetSections_EndToEndFlow() {
|
||||
|
||||
sections, ok := response["sections"].([]interface{})
|
||||
require.True(s.T(), ok, "sections should be an array")
|
||||
require.Len(s.T(), sections, 4, "Should have 4 system collections")
|
||||
require.Len(s.T(), sections, 5, "Should have 5 system collections")
|
||||
|
||||
// Verify response structure
|
||||
sectionMap := make(map[string]map[string]interface{})
|
||||
@@ -74,6 +74,7 @@ func (s *DashboardIntegrationTestSuite) TestGetSections_EndToEndFlow() {
|
||||
assert.Contains(s.T(), sectionMap, "recently-added")
|
||||
assert.Contains(s.T(), sectionMap, "recently-read")
|
||||
assert.Contains(s.T(), sectionMap, "not-started")
|
||||
assert.Contains(s.T(), sectionMap, "continue-series")
|
||||
|
||||
// Verify continue-reading is a system collection
|
||||
continueReading := sectionMap["continue-reading"]
|
||||
@@ -226,7 +227,7 @@ func (s *DashboardIntegrationTestSuite) TestRestoreSystemCollection_Unauthorized
|
||||
func (s *DashboardIntegrationTestSuite) TestRestoreSystemCollection_ValidNames() {
|
||||
token := s.setup.Token
|
||||
|
||||
validCollections := []string{"Continue Reading", "Recently Added", "Recently Read", "Not Started"}
|
||||
validCollections := []string{"Continue Reading", "Recently Added", "Recently Read", "Not Started", "Continue Series"}
|
||||
|
||||
for _, collName := range validCollections {
|
||||
s.T().Run(collName, func(t *testing.T) {
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type SeriesIntegrationTestSuite struct {
|
||||
suite.Suite
|
||||
setup *TestServerSetup
|
||||
token string
|
||||
libraryID string
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) SetupSuite() {
|
||||
s.setup = setupTestServer(s.T())
|
||||
s.token = s.setup.Token
|
||||
s.libraryID = createTestLibraryWithFolder(s.T(), s.setup.Server, s.token, "Test Series Library", false)
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) TearDownSuite() {
|
||||
s.setup.Close()
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) TestGetSeries_RequiresLibraryID() {
|
||||
req, _ := http.NewRequest("GET", s.setup.Server.URL+"/api/series", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+s.token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(s.T(), err)
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
assert.Equal(s.T(), http.StatusBadRequest, resp.StatusCode)
|
||||
|
||||
var body map[string]string
|
||||
err = json.NewDecoder(resp.Body).Decode(&body)
|
||||
require.NoError(s.T(), err)
|
||||
assert.Equal(s.T(), "library_id required", body["error"])
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) TestGetSeries_InvalidLibraryID() {
|
||||
req, _ := http.NewRequest("GET", s.setup.Server.URL+"/api/series?library_id=not-a-uuid", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+s.token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(s.T(), err)
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
assert.Equal(s.T(), http.StatusBadRequest, resp.StatusCode)
|
||||
|
||||
var body map[string]string
|
||||
err = json.NewDecoder(resp.Body).Decode(&body)
|
||||
require.NoError(s.T(), err)
|
||||
assert.Equal(s.T(), "invalid library_id", body["error"])
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) TestGetSeries_EmptyLibrary() {
|
||||
url := fmt.Sprintf("%s/api/series?library_id=%s", s.setup.Server.URL, s.libraryID)
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+s.token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(s.T(), err)
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
assert.Equal(s.T(), http.StatusOK, resp.StatusCode)
|
||||
|
||||
var body map[string]interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&body)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
series, ok := body["series"].([]interface{})
|
||||
require.True(s.T(), ok, "series should be an array")
|
||||
assert.Empty(s.T(), series, "empty library should have no series")
|
||||
|
||||
total, ok := body["total"].(float64)
|
||||
require.True(s.T(), ok, "total should be a number")
|
||||
assert.Equal(s.T(), float64(0), total)
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) TestGetSeries_Unauthorized() {
|
||||
req, _ := http.NewRequest("GET", s.setup.Server.URL+"/api/series?library_id="+s.libraryID, nil)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(s.T(), err)
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
assert.Equal(s.T(), http.StatusUnauthorized, resp.StatusCode)
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) TestGetSeries_PaginationParams() {
|
||||
url := fmt.Sprintf("%s/api/series?library_id=%s&limit=5&offset=0", s.setup.Server.URL, s.libraryID)
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+s.token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(s.T(), err)
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
assert.Equal(s.T(), http.StatusOK, resp.StatusCode)
|
||||
|
||||
var body map[string]interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&body)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
limit, ok := body["limit"].(float64)
|
||||
require.True(s.T(), ok)
|
||||
assert.Equal(s.T(), float64(5), limit)
|
||||
|
||||
offset, ok := body["offset"].(float64)
|
||||
require.True(s.T(), ok)
|
||||
assert.Equal(s.T(), float64(0), offset)
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) TestGetSeries_LimitClampedTo100() {
|
||||
url := fmt.Sprintf("%s/api/series?library_id=%s&limit=999", s.setup.Server.URL, s.libraryID)
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+s.token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(s.T(), err)
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
assert.Equal(s.T(), http.StatusOK, resp.StatusCode)
|
||||
|
||||
var body map[string]interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&body)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
limit, ok := body["limit"].(float64)
|
||||
require.True(s.T(), ok)
|
||||
assert.Equal(s.T(), float64(100), limit, "limit should be clamped to 100")
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) TestGetSeriesBooks_RequiresLibraryID() {
|
||||
req, _ := http.NewRequest("GET", s.setup.Server.URL+"/api/series/books?name=Test+Series", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+s.token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(s.T(), err)
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
assert.Equal(s.T(), http.StatusBadRequest, resp.StatusCode)
|
||||
|
||||
var body map[string]string
|
||||
err = json.NewDecoder(resp.Body).Decode(&body)
|
||||
require.NoError(s.T(), err)
|
||||
assert.Equal(s.T(), "library_id required", body["error"])
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) TestGetSeriesBooks_RequiresName() {
|
||||
url := fmt.Sprintf("%s/api/series/books?library_id=%s", s.setup.Server.URL, s.libraryID)
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+s.token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(s.T(), err)
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
assert.Equal(s.T(), http.StatusBadRequest, resp.StatusCode)
|
||||
|
||||
var body map[string]string
|
||||
err = json.NewDecoder(resp.Body).Decode(&body)
|
||||
require.NoError(s.T(), err)
|
||||
assert.Equal(s.T(), "name required", body["error"])
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) TestGetSeriesBooks_InvalidLibraryID() {
|
||||
req, _ := http.NewRequest("GET", s.setup.Server.URL+"/api/series/books?library_id=bad-uuid&name=Test", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+s.token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(s.T(), err)
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
assert.Equal(s.T(), http.StatusBadRequest, resp.StatusCode)
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) TestGetSeriesBooks_NonexistentSeries() {
|
||||
url := fmt.Sprintf("%s/api/series/books?library_id=%s&name=Nonexistent+Series", s.setup.Server.URL, s.libraryID)
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+s.token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(s.T(), err)
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
assert.Equal(s.T(), http.StatusOK, resp.StatusCode)
|
||||
|
||||
var body map[string]interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&body)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
books, ok := body["books"].([]interface{})
|
||||
require.True(s.T(), ok, "books should be an array")
|
||||
assert.Empty(s.T(), books, "nonexistent series should return empty books array")
|
||||
|
||||
assert.Equal(s.T(), "Nonexistent Series", body["name"])
|
||||
total, ok := body["total"].(float64)
|
||||
require.True(s.T(), ok)
|
||||
assert.Equal(s.T(), float64(0), total)
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) TestGetSeriesBooks_Unauthorized() {
|
||||
req, _ := http.NewRequest("GET", s.setup.Server.URL+"/api/series/books?library_id="+s.libraryID+"&name=Test", nil)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(s.T(), err)
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
assert.Equal(s.T(), http.StatusUnauthorized, resp.StatusCode)
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) TestGetSeries_SpecialCharactersInName() {
|
||||
seriesName := "Series: Book & Other (Vol. 1)"
|
||||
url := fmt.Sprintf("%s/api/series/books?library_id=%s&name=%s", s.setup.Server.URL, s.libraryID, seriesName)
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+s.token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(s.T(), err)
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
assert.Equal(s.T(), http.StatusOK, resp.StatusCode)
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) TestGetSeries_ResponseStructure() {
|
||||
url := fmt.Sprintf("%s/api/series?library_id=%s", s.setup.Server.URL, s.libraryID)
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+s.token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(s.T(), err)
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
assert.Equal(s.T(), http.StatusOK, resp.StatusCode)
|
||||
|
||||
var body map[string]interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&body)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
assert.Contains(s.T(), body, "series", "response should contain 'series' key")
|
||||
assert.Contains(s.T(), body, "total", "response should contain 'total' key")
|
||||
assert.Contains(s.T(), body, "limit", "response should contain 'limit' key")
|
||||
assert.Contains(s.T(), body, "offset", "response should contain 'offset' key")
|
||||
|
||||
_, ok := body["series"].([]interface{})
|
||||
assert.True(s.T(), ok, "'series' should be an array")
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) TestRestoreSystemCollection_ContinueSeries() {
|
||||
reqBody := map[string]interface{}{
|
||||
"collection_name": "Continue Series",
|
||||
}
|
||||
body, _ := json.Marshal(reqBody)
|
||||
|
||||
req, _ := http.NewRequest("POST", s.setup.Server.URL+"/api/dashboard/restore-system-collection", bytes.NewBuffer(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+s.token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(s.T(), err)
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
assert.Equal(s.T(), http.StatusOK, resp.StatusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
require.NoError(s.T(), err)
|
||||
assert.Contains(s.T(), response, "message")
|
||||
}
|
||||
|
||||
func (s *SeriesIntegrationTestSuite) TestGetSections_IncludesContinueSeries() {
|
||||
url := fmt.Sprintf("%s/api/dashboard/sections?library_id=%s", s.setup.Server.URL, s.libraryID)
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+s.token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
require.NoError(s.T(), err)
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
assert.Equal(s.T(), http.StatusOK, resp.StatusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
sections, ok := response["sections"].([]interface{})
|
||||
require.True(s.T(), ok, "sections should be an array")
|
||||
require.Len(s.T(), sections, 5, "Should have 5 system collections")
|
||||
|
||||
sectionIDs := make(map[string]bool)
|
||||
for _, sec := range sections {
|
||||
section := sec.(map[string]interface{})
|
||||
sectionIDs[section["id"].(string)] = true
|
||||
}
|
||||
|
||||
assert.Contains(s.T(), sectionIDs, "continue-series", "dashboard should include continue-series section")
|
||||
assert.Contains(s.T(), sectionIDs, "continue-reading")
|
||||
assert.Contains(s.T(), sectionIDs, "recently-added")
|
||||
assert.Contains(s.T(), sectionIDs, "recently-read")
|
||||
assert.Contains(s.T(), sectionIDs, "not-started")
|
||||
}
|
||||
|
||||
func TestSeriesIntegrationTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(SeriesIntegrationTestSuite))
|
||||
}
|
||||
@@ -286,6 +286,7 @@ func createDefaultCollectionsForUser(t *testing.T, db *database.Queries, userID
|
||||
{"recently-added", "Newly added items to this library", "🆕", "#9ece6a", "recently-added", 2},
|
||||
{"recently-read", "Books you've finished (progress >= 1)", "✅", "#e0af68", "recently-read", 3},
|
||||
{"not-started", "Books you haven't read yet (progress = 0 or no record)", "📕", "#f7768e", "not-started", 4},
|
||||
{"continue-series", "Next book in series you're reading", "📚", "#bb9af7", "continue-series", 5},
|
||||
}
|
||||
|
||||
for _, col := range defaultCollections {
|
||||
@@ -478,6 +479,7 @@ func setupTestServer(t *testing.T) *TestServerSetup {
|
||||
filtersHandler := handlers.NewFiltersHandler(queries)
|
||||
dashboardService := services.NewDashboardService(queries)
|
||||
dashboardHandler := handlers.NewDashboardHandler(queries)
|
||||
seriesHandler := handlers.NewSeriesHandler(queries)
|
||||
mediaHandler := handlers.NewMediaHandler(queries, libraryService, worker)
|
||||
mediaHandler.SetProgressService(progressService)
|
||||
matchingHandler := handlers.NewMatchingHandler(queries, connManager)
|
||||
@@ -529,6 +531,7 @@ func setupTestServer(t *testing.T) *TestServerSetup {
|
||||
FiltersHandler: filtersHandler,
|
||||
DashboardHandler: dashboardHandler,
|
||||
DashboardService: dashboardService,
|
||||
SeriesHandler: seriesHandler,
|
||||
OPDSHandler: opdsHandler,
|
||||
JobsHandler: jobsHandler,
|
||||
ConnManager: connManager,
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewSeriesHandler_NilDB(t *testing.T) {
|
||||
handler := NewSeriesHandler(nil)
|
||||
assert.NotNil(t, handler, "Handler should not be nil even with nil DB")
|
||||
assert.NotNil(t, handler.seriesService, "Internal service should be initialized")
|
||||
}
|
||||
|
||||
func TestSeriesHandler_TextToStringConversion(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input pgtype.Text
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "valid author text",
|
||||
input: pgtype.Text{String: "Brandon Sanderson", Valid: true},
|
||||
expected: "Brandon Sanderson",
|
||||
},
|
||||
{
|
||||
name: "empty valid text",
|
||||
input: pgtype.Text{String: "", Valid: true},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "null text returns empty",
|
||||
input: pgtype.Text{String: "ignored", Valid: false},
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := textToString(tt.input)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user