- Fix TestCollectionsBulkOperations/BulkAddBooks_SingleOperation failure - Each subtest was creating "Test Collection" with same name - Collections table has UNIQUE(user_id, name) constraint causing 500 errors - Made collection names unique by adding test name suffix: - Test Collection - InvalidBookID - Test Collection - SingleOperation - Test Collection - MultipleBooksSingleCollection - Test Collection 1 - MultipleCollections - Test Collection 2 - MultipleCollections - Test Collection - DuplicateBooks This preserves test data for manual API testing with Bruno while ensuring test isolation and preventing unique constraint violations.
398 lines
13 KiB
Go
398 lines
13 KiB
Go
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) {
|
|
setup := setupTestServer(t)
|
|
token := loginTestUser(t, setup.Server, setup.DB)
|
|
client := &http.Client{}
|
|
|
|
t.Run("BulkAddBooks_WithoutAuth", func(t *testing.T) {
|
|
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", setup.Server.URL+"/api/collections/bulk-add-books", bytes.NewBuffer(body))
|
|
httpReq.Header.Set("Content-Type", "application/json")
|
|
|
|
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) {
|
|
req := map[string]interface{}{
|
|
"operations": []map[string]interface{}{},
|
|
}
|
|
body, _ := json.Marshal(req)
|
|
|
|
httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/collections/bulk-add-books", bytes.NewBuffer(body))
|
|
httpReq.Header.Set("Content-Type", "application/json")
|
|
httpReq.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
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) {
|
|
bookID := createTestMediaItemID(t, setup.Server, 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", setup.Server.URL+"/api/collections/bulk-add-books", bytes.NewBuffer(body))
|
|
httpReq.Header.Set("Content-Type", "application/json")
|
|
httpReq.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
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, "added")
|
|
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) {
|
|
// Create a collection first
|
|
collectionReq := map[string]interface{}{
|
|
"name": "Test Collection - InvalidBookID",
|
|
"description": "A test collection",
|
|
}
|
|
collectionBody, _ := json.Marshal(collectionReq)
|
|
|
|
collectionHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/collections", bytes.NewBuffer(collectionBody))
|
|
collectionHTTP.Header.Set("Content-Type", "application/json")
|
|
collectionHTTP.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
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", setup.Server.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) {
|
|
// Create a collection
|
|
collectionReq := map[string]interface{}{
|
|
"name": "Test Collection - SingleOperation",
|
|
"description": "A test collection",
|
|
}
|
|
collectionBody, _ := json.Marshal(collectionReq)
|
|
|
|
collectionHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/collections", bytes.NewBuffer(collectionBody))
|
|
collectionHTTP.Header.Set("Content-Type", "application/json")
|
|
collectionHTTP.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
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 := createTestMediaItemID(t, setup.Server, 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", setup.Server.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, "added")
|
|
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) {
|
|
// Create a collection
|
|
collectionReq := map[string]interface{}{
|
|
"name": "Test Collection - MultipleBooksSingleCollection",
|
|
"description": "A test collection",
|
|
}
|
|
collectionBody, _ := json.Marshal(collectionReq)
|
|
|
|
collectionHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/collections", bytes.NewBuffer(collectionBody))
|
|
collectionHTTP.Header.Set("Content-Type", "application/json")
|
|
collectionHTTP.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
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 := createTestMediaItemID(t, setup.Server, token)
|
|
bookID2 := createTestMediaItemID(t, setup.Server, token)
|
|
bookID3 := createTestMediaItemID(t, setup.Server, 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", setup.Server.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["added"].(float64) > 0)
|
|
})
|
|
|
|
t.Run("BulkAddBooks_MultipleCollections", func(t *testing.T) {
|
|
// Create multiple collections
|
|
collectionReq := map[string]interface{}{
|
|
"name": "Test Collection 1 - MultipleCollections",
|
|
"description": "First test collection",
|
|
}
|
|
collectionBody, _ := json.Marshal(collectionReq)
|
|
|
|
collectionHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/collections", bytes.NewBuffer(collectionBody))
|
|
collectionHTTP.Header.Set("Content-Type", "application/json")
|
|
collectionHTTP.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
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 - MultipleCollections",
|
|
"description": "Second test collection",
|
|
}
|
|
collectionBody2, _ := json.Marshal(collectionReq2)
|
|
|
|
collectionHTTP2, _ := http.NewRequest("POST", setup.Server.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 := createTestMediaItemID(t, setup.Server, token)
|
|
bookID2 := createTestMediaItemID(t, setup.Server, 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", setup.Server.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) {
|
|
// Create a collection
|
|
collectionReq := map[string]interface{}{
|
|
"name": "Test Collection - DuplicateBooks",
|
|
"description": "A test collection",
|
|
}
|
|
collectionBody, _ := json.Marshal(collectionReq)
|
|
|
|
collectionHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/collections", bytes.NewBuffer(collectionBody))
|
|
collectionHTTP.Header.Set("Content-Type", "application/json")
|
|
collectionHTTP.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
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 := createTestMediaItemID(t, setup.Server, 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", setup.Server.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 same book again - create new request with fresh body
|
|
addBody2, _ := json.Marshal(addReq)
|
|
addHTTP2, _ := http.NewRequest("POST", setup.Server.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) {
|
|
// Send invalid JSON
|
|
httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/collections/bulk-add-books", bytes.NewBuffer([]byte("invalid json")))
|
|
httpReq.Header.Set("Content-Type", "application/json")
|
|
httpReq.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
resp, err := client.Do(httpReq)
|
|
require.NoError(t, err)
|
|
defer resp.Body.Close()
|
|
|
|
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
|
})
|
|
}
|