diff --git a/cmd/server/tests/isbn_and_library_test.go b/cmd/server/tests/isbn_and_library_test.go deleted file mode 100644 index 16298ba..0000000 --- a/cmd/server/tests/isbn_and_library_test.go +++ /dev/null @@ -1,506 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" - - "github.com/google/uuid" - "github.com/stretchr/testify/assert" -) - -// TestISBNNormalization tests ISBN normalization with various formats -func TestISBNNormalization(t *testing.T) { - t.Run("ISBN-13 with hyphens should be normalized", func(t *testing.T) { - // Test cases for ISBN-13 with various hyphen placements - testCases := []struct { - name string - input string - expected string - }{ - { - name: "ISBN-13 with hyphens (978-0-12345-678-9)", - input: "978-0-12345-678-9", - expected: "9780123456789", - }, - { - name: "ISBN-13 with single hyphen (978-0123456789)", - input: "978-0123456789", - expected: "9780123456789", - }, - { - name: "ISBN-13 without hyphens", - input: "9780123456789", - expected: "9780123456789", - }, - { - name: "ISBN-13 with spaces", - input: "978 0123456789", - expected: "9780123456789", - }, - { - name: "ISBN-13 with mixed hyphens and spaces", - input: "978-0 1234-56789", - expected: "9780123456789", - }, - { - name: "ISBN-10 with hyphens", - input: "0-12345-678-9", - expected: "0123456789", - }, - { - name: "ISBN-10 without hyphens", - input: "0123456789", - expected: "0123456789", - }, - { - name: "ISBN-10 with X", - input: "0-12345-678-X", - expected: "012345678X", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - payload := map[string]interface{}{ - "title": "Test Ebook", - "isbn": tc.input, - "file_path": "/path/to/file.epub", - "file_size": 1024, - "mime_type": "application/epub+zip", - } - jsonData, _ := json.Marshal(payload) - - req := httptest.NewRequest("POST", "/api/ebooks", bytes.NewBuffer(jsonData)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer admin-token") - req.Header.Set("X-User-Role", "admin") - req.Header.Set("X-User-Id", uuid.New().String()) - rr := httptest.NewRecorder() - - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Simulate successful creation with normalized ISBN - w.WriteHeader(http.StatusCreated) - response := map[string]interface{}{ - "id": uuid.New().String(), - "title": "Test Ebook", - "isbn": tc.expected, - } - json.NewEncoder(w).Encode(response) - }) - - handler.ServeHTTP(rr, req) - - assert.Equal(t, http.StatusCreated, rr.Code) - - var response map[string]interface{} - json.Unmarshal(rr.Body.Bytes(), &response) - assert.Equal(t, tc.expected, response["isbn"]) - }) - } - }) - - t.Run("Empty ISBN should be handled", func(t *testing.T) { - payload := map[string]interface{}{ - "title": "Test Ebook", - "file_path": "/path/to/file.epub", - "file_size": 1024, - "mime_type": "application/epub+zip", - } - jsonData, _ := json.Marshal(payload) - - req := httptest.NewRequest("POST", "/api/ebooks", bytes.NewBuffer(jsonData)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer admin-token") - req.Header.Set("X-User-Role", "admin") - rr := httptest.NewRecorder() - - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusCreated) - }) - - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusCreated, rr.Code) - }) - - t.Run("Invalid ISBN format should still be stored as-is", func(t *testing.T) { - // Test that very short or obviously invalid ISBNs are still accepted - // The database function will normalize what it can - testCases := []struct { - name string - isbn string - valid bool - }{ - {"Too short ISBN", "123", true}, - {"Valid ISBN-13", "9780123456789", true}, - {"Valid ISBN-10", "0123456789", true}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - payload := map[string]interface{}{ - "title": "Test Ebook", - "isbn": tc.isbn, - "file_path": "/path/to/file.epub", - "file_size": 1024, - "mime_type": "application/epub+zip", - } - jsonData, _ := json.Marshal(payload) - - req := httptest.NewRequest("POST", "/api/ebooks", bytes.NewBuffer(jsonData)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer admin-token") - req.Header.Set("X-User-Role", "admin") - rr := httptest.NewRecorder() - - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if tc.valid { - w.WriteHeader(http.StatusCreated) - } else { - w.WriteHeader(http.StatusBadRequest) - } - }) - - handler.ServeHTTP(rr, req) - - if tc.valid { - assert.Equal(t, http.StatusCreated, rr.Code) - } else { - assert.Equal(t, http.StatusBadRequest, rr.Code) - } - }) - } - }) -} - -// TestEbookLibraryRequirement tests that ebooks require an existing library -func TestEbookLibraryRequirement(t *testing.T) { - t.Run("Create ebook without any libraries should fail gracefully", func(t *testing.T) { - payload := map[string]interface{}{ - "title": "Test Ebook", - "isbn": "978-0123456789", - "file_path": "/path/to/file.epub", - "file_size": 1024, - "mime_type": "application/epub+zip", - } - jsonData, _ := json.Marshal(payload) - - req := httptest.NewRequest("POST", "/api/ebooks", bytes.NewBuffer(jsonData)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer admin-token") - req.Header.Set("X-User-Role", "admin") - req.Header.Set("X-User-Id", uuid.New().String()) - rr := httptest.NewRecorder() - - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Simulate no library found scenario - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(`{"error":"no ebook library found. Please create an ebook library first"}`)) - }) - - handler.ServeHTTP(rr, req) - - assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.Contains(t, rr.Body.String(), "no ebook library found") - }) - - t.Run("Create ebook with existing library should succeed", func(t *testing.T) { - libraryID := uuid.New() - payload := map[string]interface{}{ - "title": "Test Ebook", - "isbn": "978-0123456789", - "file_path": "/path/to/file.epub", - "file_size": 1024, - "mime_type": "application/epub+zip", - } - jsonData, _ := json.Marshal(payload) - - req := httptest.NewRequest("POST", "/api/ebooks", bytes.NewBuffer(jsonData)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer admin-token") - req.Header.Set("X-User-Role", "admin") - req.Header.Set("X-User-Id", uuid.New().String()) - req.Header.Set("X-Library-Id", libraryID.String()) - rr := httptest.NewRecorder() - - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Simulate successful creation - w.WriteHeader(http.StatusCreated) - response := map[string]interface{}{ - "id": uuid.New().String(), - "title": "Test Ebook", - "isbn": "9780123456789", - "library_id": libraryID.String(), - } - json.NewEncoder(w).Encode(response) - }) - - handler.ServeHTTP(rr, req) - - assert.Equal(t, http.StatusCreated, rr.Code) - - var response map[string]interface{} - json.Unmarshal(rr.Body.Bytes(), &response) - assert.Equal(t, "9780123456789", response["isbn"]) - assert.Equal(t, libraryID.String(), response["library_id"]) - }) - - t.Run("Update ebook with normalized ISBN", func(t *testing.T) { - ebookID := uuid.New() - payload := map[string]interface{}{ - "title": "Updated Ebook", - "isbn": "978-987654321-0", - } - jsonData, _ := json.Marshal(payload) - - req := httptest.NewRequest("PUT", "/api/ebooks/"+ebookID.String(), bytes.NewBuffer(jsonData)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer admin-token") - req.Header.Set("X-User-Role", "admin") - rr := httptest.NewRecorder() - - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - response := map[string]interface{}{ - "id": ebookID.String(), - "title": "Updated Ebook", - "isbn": "9789876543210", - } - json.NewEncoder(w).Encode(response) - }) - - handler.ServeHTTP(rr, req) - - assert.Equal(t, http.StatusOK, rr.Code) - - var response map[string]interface{} - json.Unmarshal(rr.Body.Bytes(), &response) - assert.Equal(t, "9789876543210", response["isbn"]) - }) -} - -// TestISBNEdgeCases tests edge cases for ISBN handling -func TestISBNEdgeCases(t *testing.T) { - t.Run("ISBN with special characters should be normalized", func(t *testing.T) { - testCases := []struct { - name string - input string - expected string - }{ - { - name: "ISBN with multiple hyphens", - input: "978-0-123-45678-9", - expected: "9780123456789", - }, - { - name: "ISBN with trailing hyphen", - input: "9780123456789-", - expected: "9780123456789", - }, - { - name: "ISBN with leading hyphen", - input: "-9780123456789", - expected: "9780123456789", - }, - { - name: "ISBN with multiple spaces", - input: "978 0123456789", - expected: "9780123456789", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - payload := map[string]interface{}{ - "title": "Test Ebook", - "isbn": tc.input, - "file_path": "/path/to/file.epub", - "file_size": 1024, - "mime_type": "application/epub+zip", - } - jsonData, _ := json.Marshal(payload) - - req := httptest.NewRequest("POST", "/api/ebooks", bytes.NewBuffer(jsonData)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer admin-token") - req.Header.Set("X-User-Role", "admin") - rr := httptest.NewRecorder() - - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusCreated) - response := map[string]interface{}{ - "id": uuid.New().String(), - "title": "Test Ebook", - "isbn": tc.expected, - } - json.NewEncoder(w).Encode(response) - }) - - handler.ServeHTTP(rr, req) - - assert.Equal(t, http.StatusCreated, rr.Code) - - var response map[string]interface{} - json.Unmarshal(rr.Body.Bytes(), &response) - assert.Equal(t, tc.expected, response["isbn"]) - }) - } - }) - - t.Run("ISBN length validation", func(t *testing.T) { - testCases := []struct { - name string - isbn string - shouldAccept bool - }{ - { - name: "Empty ISBN", - isbn: "", - shouldAccept: true, - }, - { - name: "Valid ISBN-10", - isbn: "0123456789", - shouldAccept: true, - }, - { - name: "Valid ISBN-13", - isbn: "9780123456789", - shouldAccept: true, - }, - { - name: "ISBN-13 with hyphens", - isbn: "978-0-12345-678-9", - shouldAccept: true, - }, - { - name: "Maximum length (17 chars with hyphens)", - isbn: "978-0-12345-678-9", - shouldAccept: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - payload := map[string]interface{}{ - "title": "Test Ebook", - "isbn": tc.isbn, - "file_path": "/path/to/file.epub", - "file_size": 1024, - "mime_type": "application/epub+zip", - } - jsonData, _ := json.Marshal(payload) - - req := httptest.NewRequest("POST", "/api/ebooks", bytes.NewBuffer(jsonData)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer admin-token") - req.Header.Set("X-User-Role", "admin") - rr := httptest.NewRecorder() - - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if tc.shouldAccept { - w.WriteHeader(http.StatusCreated) - } else { - w.WriteHeader(http.StatusBadRequest) - } - }) - - handler.ServeHTTP(rr, req) - - if tc.shouldAccept { - assert.Equal(t, http.StatusCreated, rr.Code) - } else { - assert.Equal(t, http.StatusBadRequest, rr.Code) - } - }) - } - }) -} - -// TestLibraryAutoSelection tests automatic library selection by resource type -func TestLibraryAutoSelection(t *testing.T) { - t.Run("Auto-select first available ebook library", func(t *testing.T) { - libraryID := uuid.New() - payload := map[string]interface{}{ - "title": "Test Ebook", - "isbn": "9780123456789", - "file_path": "/path/to/file.epub", - "file_size": 1024, - "mime_type": "application/epub+zip", - } - jsonData, _ := json.Marshal(payload) - - req := httptest.NewRequest("POST", "/api/ebooks", bytes.NewBuffer(jsonData)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer admin-token") - req.Header.Set("X-User-Role", "admin") - req.Header.Set("X-User-Id", uuid.New().String()) - req.Header.Set("X-Library-Id", libraryID.String()) - rr := httptest.NewRecorder() - - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Simulate auto-selection of first ebook library - w.WriteHeader(http.StatusCreated) - response := map[string]interface{}{ - "id": uuid.New().String(), - "title": "Test Ebook", - "isbn": "9780123456789", - "library_id": libraryID.String(), - } - json.NewEncoder(w).Encode(response) - }) - - handler.ServeHTTP(rr, req) - - assert.Equal(t, http.StatusCreated, rr.Code) - - var response map[string]interface{} - json.Unmarshal(rr.Body.Bytes(), &response) - assert.Equal(t, libraryID.String(), response["library_id"]) - }) - - t.Run("Multiple ebook libraries - should select first", func(t *testing.T) { - firstLibraryID := uuid.New() - secondLibraryID := uuid.New() - - payload := map[string]interface{}{ - "title": "Test Ebook", - "isbn": "9780123456789", - "file_path": "/path/to/file.epub", - "file_size": 1024, - "mime_type": "application/epub+zip", - } - jsonData, _ := json.Marshal(payload) - - req := httptest.NewRequest("POST", "/api/ebooks", bytes.NewBuffer(jsonData)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer admin-token") - req.Header.Set("X-User-Role", "admin") - req.Header.Set("X-User-Id", uuid.New().String()) - rr := httptest.NewRecorder() - - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Simulate auto-selection of first ebook library - w.WriteHeader(http.StatusCreated) - response := map[string]interface{}{ - "id": uuid.New().String(), - "title": "Test Ebook", - "isbn": "9780123456789", - "library_id": firstLibraryID.String(), - } - json.NewEncoder(w).Encode(response) - }) - - handler.ServeHTTP(rr, req) - - assert.Equal(t, http.StatusCreated, rr.Code) - - var response map[string]interface{} - json.Unmarshal(rr.Body.Bytes(), &response) - // Should select first library, not second - assert.Equal(t, firstLibraryID.String(), response["library_id"]) - assert.NotEqual(t, secondLibraryID.String(), response["library_id"]) - }) -}