package main import ( "bytes" "encoding/json" "net/http" "testing" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestCollectionsBulkOperations tests bulk collection operations func TestCollectionsBulkOperations(t *testing.T) { t.Run("BulkAddBooks_WithoutAuth", func(t *testing.T) { ts, _, _ := setupTestServer(t) defer ts.Close() req := map[string]interface{}{ "operations": []map[string]interface{}{ { "collection_id": uuid.New().String(), "book_ids": []string{uuid.New().String()}, }, }, } body, _ := json.Marshal(req) httpReq, _ := http.NewRequest("POST", ts.URL+"/api/collections/bulk-add-books", bytes.NewBuffer(body)) httpReq.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) }) t.Run("BulkAddBooks_EmptyOperations", func(t *testing.T) { ts, db, _ := setupTestServer(t) defer ts.Close() token := loginTestUser(t, ts, db) req := map[string]interface{}{ "operations": []map[string]interface{}{}, } body, _ := json.Marshal(req) httpReq, _ := http.NewRequest("POST", ts.URL+"/api/collections/bulk-add-books", bytes.NewBuffer(body)) httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) t.Run("BulkAddBooks_InvalidCollectionID", func(t *testing.T) { ts, db, _ := setupTestServer(t) defer ts.Close() token := loginTestUser(t, ts, db) bookID := createTestEbookID(t, ts, token) req := map[string]interface{}{ "operations": []map[string]interface{}{ { "collection_id": "invalid-uuid", "book_ids": []string{bookID}, }, }, } body, _ := json.Marshal(req) httpReq, _ := http.NewRequest("POST", ts.URL+"/api/collections/bulk-add-books", bytes.NewBuffer(body)) httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) assert.Contains(t, result, "results") assert.Contains(t, result, "total") assert.Contains(t, result, "success") assert.Contains(t, result, "failed") results := result["results"].([]interface{}) assert.True(t, len(results) > 0) firstResult := results[0].(map[string]interface{}) assert.Equal(t, "error", firstResult["status"]) }) t.Run("BulkAddBooks_InvalidBookID", func(t *testing.T) { ts, db, _ := setupTestServer(t) defer ts.Close() token := loginTestUser(t, ts, db) // Create a collection first collectionReq := map[string]interface{}{ "name": "Test Collection", "description": "A test collection", } collectionBody, _ := json.Marshal(collectionReq) collectionHTTP, _ := http.NewRequest("POST", ts.URL+"/api/collections", bytes.NewBuffer(collectionBody)) collectionHTTP.Header.Set("Content-Type", "application/json") collectionHTTP.Header.Set("Authorization", "Bearer "+token) client := &http.Client{} resp, err := client.Do(collectionHTTP) require.NoError(t, err) defer resp.Body.Close() var collectionResult map[string]interface{} json.NewDecoder(resp.Body).Decode(&collectionResult) collectionID := collectionResult["id"].(string) // Now try to add invalid book IDs addReq := map[string]interface{}{ "operations": []map[string]interface{}{ { "collection_id": collectionID, "book_ids": []string{"invalid-uuid"}, }, }, } addBody, _ := json.Marshal(addReq) addHTTP, _ := http.NewRequest("POST", ts.URL+"/api/collections/bulk-add-books", bytes.NewBuffer(addBody)) addHTTP.Header.Set("Content-Type", "application/json") addHTTP.Header.Set("Authorization", "Bearer "+token) resp, err = client.Do(addHTTP) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) results := result["results"].([]interface{}) firstResult := results[0].(map[string]interface{}) assert.Equal(t, "error", firstResult["status"]) }) t.Run("BulkAddBooks_SingleOperation", func(t *testing.T) { ts, db, _ := setupTestServer(t) defer ts.Close() token := loginTestUser(t, ts, db) // Create a collection collectionReq := map[string]interface{}{ "name": "Test Collection", "description": "A test collection", } collectionBody, _ := json.Marshal(collectionReq) collectionHTTP, _ := http.NewRequest("POST", ts.URL+"/api/collections", bytes.NewBuffer(collectionBody)) collectionHTTP.Header.Set("Content-Type", "application/json") collectionHTTP.Header.Set("Authorization", "Bearer "+token) client := &http.Client{} resp, err := client.Do(collectionHTTP) require.NoError(t, err) defer resp.Body.Close() var collectionResult map[string]interface{} json.NewDecoder(resp.Body).Decode(&collectionResult) collectionID := collectionResult["id"].(string) // Create a book bookID := createTestEbookID(t, ts, token) // Add book to collection addReq := map[string]interface{}{ "operations": []map[string]interface{}{ { "collection_id": collectionID, "book_ids": []string{bookID}, }, }, } addBody, _ := json.Marshal(addReq) addHTTP, _ := http.NewRequest("POST", ts.URL+"/api/collections/bulk-add-books", bytes.NewBuffer(addBody)) addHTTP.Header.Set("Content-Type", "application/json") addHTTP.Header.Set("Authorization", "Bearer "+token) resp, err = client.Do(addHTTP) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) assert.Contains(t, result, "results") assert.Contains(t, result, "total") assert.Contains(t, result, "success") assert.Contains(t, result, "failed") results := result["results"].([]interface{}) firstResult := results[0].(map[string]interface{}) assert.Equal(t, "success", firstResult["status"]) }) t.Run("BulkAddBooks_MultipleBooksSingleCollection", func(t *testing.T) { ts, db, _ := setupTestServer(t) defer ts.Close() token := loginTestUser(t, ts, db) // Create a collection collectionReq := map[string]interface{}{ "name": "Test Collection", "description": "A test collection", } collectionBody, _ := json.Marshal(collectionReq) collectionHTTP, _ := http.NewRequest("POST", ts.URL+"/api/collections", bytes.NewBuffer(collectionBody)) collectionHTTP.Header.Set("Content-Type", "application/json") collectionHTTP.Header.Set("Authorization", "Bearer "+token) client := &http.Client{} resp, err := client.Do(collectionHTTP) require.NoError(t, err) defer resp.Body.Close() var collectionResult map[string]interface{} json.NewDecoder(resp.Body).Decode(&collectionResult) collectionID := collectionResult["id"].(string) // Create multiple books bookID1 := createTestEbookID(t, ts, token) bookID2 := createTestEbookID(t, ts, token) bookID3 := createTestEbookID(t, ts, token) // Add all books to collection addReq := map[string]interface{}{ "operations": []map[string]interface{}{ { "collection_id": collectionID, "book_ids": []string{bookID1, bookID2, bookID3}, }, }, } addBody, _ := json.Marshal(addReq) addHTTP, _ := http.NewRequest("POST", ts.URL+"/api/collections/bulk-add-books", bytes.NewBuffer(addBody)) addHTTP.Header.Set("Content-Type", "application/json") addHTTP.Header.Set("Authorization", "Bearer "+token) resp, err = client.Do(addHTTP) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) assert.Equal(t, 3.0, result["total"]) assert.True(t, result["success"].(float64) > 0) }) t.Run("BulkAddBooks_MultipleCollections", func(t *testing.T) { ts, db, _ := setupTestServer(t) defer ts.Close() token := loginTestUser(t, ts, db) // Create multiple collections collectionReq := map[string]interface{}{ "name": "Test Collection 1", "description": "First test collection", } collectionBody, _ := json.Marshal(collectionReq) collectionHTTP, _ := http.NewRequest("POST", ts.URL+"/api/collections", bytes.NewBuffer(collectionBody)) collectionHTTP.Header.Set("Content-Type", "application/json") collectionHTTP.Header.Set("Authorization", "Bearer "+token) client := &http.Client{} resp, err := client.Do(collectionHTTP) require.NoError(t, err) defer resp.Body.Close() var collectionResult1 map[string]interface{} json.NewDecoder(resp.Body).Decode(&collectionResult1) collectionID1 := collectionResult1["id"].(string) collectionReq2 := map[string]interface{}{ "name": "Test Collection 2", "description": "Second test collection", } collectionBody2, _ := json.Marshal(collectionReq2) collectionHTTP2, _ := http.NewRequest("POST", ts.URL+"/api/collections", bytes.NewBuffer(collectionBody2)) collectionHTTP2.Header.Set("Content-Type", "application/json") collectionHTTP2.Header.Set("Authorization", "Bearer "+token) resp, err = client.Do(collectionHTTP2) require.NoError(t, err) defer resp.Body.Close() var collectionResult2 map[string]interface{} json.NewDecoder(resp.Body).Decode(&collectionResult2) collectionID2 := collectionResult2["id"].(string) // Create books bookID1 := createTestEbookID(t, ts, token) bookID2 := createTestEbookID(t, ts, token) // Add books to multiple collections addReq := map[string]interface{}{ "operations": []map[string]interface{}{ { "collection_id": collectionID1, "book_ids": []string{bookID1}, }, { "collection_id": collectionID2, "book_ids": []string{bookID1, bookID2}, }, }, } addBody, _ := json.Marshal(addReq) addHTTP, _ := http.NewRequest("POST", ts.URL+"/api/collections/bulk-add-books", bytes.NewBuffer(addBody)) addHTTP.Header.Set("Content-Type", "application/json") addHTTP.Header.Set("Authorization", "Bearer "+token) resp, err = client.Do(addHTTP) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) assert.Contains(t, result, "results") assert.Equal(t, 3.0, result["total"]) }) t.Run("BulkAddBooks_DuplicateBooks", func(t *testing.T) { ts, db, _ := setupTestServer(t) defer ts.Close() token := loginTestUser(t, ts, db) // Create a collection collectionReq := map[string]interface{}{ "name": "Test Collection", "description": "A test collection", } collectionBody, _ := json.Marshal(collectionReq) collectionHTTP, _ := http.NewRequest("POST", ts.URL+"/api/collections", bytes.NewBuffer(collectionBody)) collectionHTTP.Header.Set("Content-Type", "application/json") collectionHTTP.Header.Set("Authorization", "Bearer "+token) client := &http.Client{} resp, err := client.Do(collectionHTTP) require.NoError(t, err) defer resp.Body.Close() var collectionResult map[string]interface{} json.NewDecoder(resp.Body).Decode(&collectionResult) collectionID := collectionResult["id"].(string) // Create a book bookID := createTestEbookID(t, ts, token) // Add book to collection addReq := map[string]interface{}{ "operations": []map[string]interface{}{ { "collection_id": collectionID, "book_ids": []string{bookID}, }, }, } addBody, _ := json.Marshal(addReq) addHTTP, _ := http.NewRequest("POST", ts.URL+"/api/collections/bulk-add-books", bytes.NewBuffer(addBody)) addHTTP.Header.Set("Content-Type", "application/json") addHTTP.Header.Set("Authorization", "Bearer "+token) resp, err = client.Do(addHTTP) require.NoError(t, err) defer resp.Body.Close() // Try to add the same book again - create new request with fresh body addBody2, _ := json.Marshal(addReq) addHTTP2, _ := http.NewRequest("POST", ts.URL+"/api/collections/bulk-add-books", bytes.NewBuffer(addBody2)) addHTTP2.Header.Set("Content-Type", "application/json") addHTTP2.Header.Set("Authorization", "Bearer "+token) resp2, err := client.Do(addHTTP2) require.NoError(t, err) defer resp2.Body.Close() // Should handle duplicate gracefully (either succeed or return error) assert.Equal(t, http.StatusOK, resp2.StatusCode) }) t.Run("BulkAddBooks_InvalidRequestBody", func(t *testing.T) { ts, db, _ := setupTestServer(t) defer ts.Close() token := loginTestUser(t, ts, db) // Send invalid JSON httpReq, _ := http.NewRequest("POST", ts.URL+"/api/collections/bulk-add-books", bytes.NewBuffer([]byte("invalid json"))) httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+token) client := &http.Client{} resp, err := client.Do(httpReq) require.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusBadRequest, resp.StatusCode) }) }