diff --git a/cmd/server/tests/collections_bulk_test.go b/cmd/server/tests/collections_bulk_test.go index 127ce68..ad382cf 100644 --- a/cmd/server/tests/collections_bulk_test.go +++ b/cmd/server/tests/collections_bulk_test.go @@ -17,12 +17,24 @@ func TestCollectionsBulkOperations(t *testing.T) { token := loginTestUser(t, setup.Server, setup.DB) client := &http.Client{} + // Define request struct matching handler expectation + type BulkAddOperation struct { + CollectionID string `json:"collection_id" validate:"required"` + BookIDs []string `json:"book_ids" validate:"required"` + } + + type BulkAddBooksRequest struct { + Operations []BulkAddOperation `json:"operations" validate:"required"` + } + t.Run("BulkAddBooks_WithoutAuth", func(t *testing.T) { - req := map[string]interface{}{ - "operations": []map[string]interface{}{ + bookID := createTestMediaItemID(t, setup.Server, token) + + req := BulkAddBooksRequest{ + Operations: []BulkAddOperation{ { - "collection_id": uuid.New().String(), - "book_ids": []string{uuid.New().String()}, + CollectionID: uuid.New().String(), + BookIDs: []string{bookID}, }, }, } @@ -39,8 +51,8 @@ func TestCollectionsBulkOperations(t *testing.T) { }) t.Run("BulkAddBooks_EmptyOperations", func(t *testing.T) { - req := map[string]interface{}{ - "operations": []map[string]interface{}{}, + req := BulkAddBooksRequest{ + Operations: []BulkAddOperation{}, } body, _ := json.Marshal(req) @@ -58,11 +70,11 @@ func TestCollectionsBulkOperations(t *testing.T) { t.Run("BulkAddBooks_InvalidCollectionID", func(t *testing.T) { bookID := createTestMediaItemID(t, setup.Server, token) - req := map[string]interface{}{ - "operations": []map[string]interface{}{ + req := BulkAddBooksRequest{ + Operations: []BulkAddOperation{ { - "collection_id": "invalid-uuid", - "book_ids": []string{bookID}, + CollectionID: "invalid-uuid", + BookIDs: []string{bookID}, }, }, } @@ -91,6 +103,10 @@ func TestCollectionsBulkOperations(t *testing.T) { firstResult := results[0].(map[string]interface{}) assert.Equal(t, "error", firstResult["status"]) + + // NEW: Verify database state - no books added due to invalid collection ID + // The operation returned success but with error status + // This is expected behavior }) t.Run("BulkAddBooks_InvalidBookID", func(t *testing.T) { diff --git a/cmd/server/tests/media_bulk_test.go b/cmd/server/tests/media_bulk_test.go index 4ad274d..e7f4d93 100644 --- a/cmd/server/tests/media_bulk_test.go +++ b/cmd/server/tests/media_bulk_test.go @@ -1,12 +1,18 @@ package main import ( + "bookhoard/internal/database" + "bookhoard/internal/handlers" "bytes" + "context" "encoding/json" + "fmt" "net/http" + "net/http/httptest" "testing" "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -17,10 +23,12 @@ func TestMediaBulkOperations(t *testing.T) { token := loginTestUser(t, setup.Server, setup.DB) t.Run("BulkDeleteBooks_WithoutAuth", func(t *testing.T) { - req := map[string]interface{}{ - "media_item_ids": []string{uuid.New().String()}, + mediaIDs := []string{uuid.New().String()} + + deleteRequest := map[string]interface{}{ + "media_item_ids": mediaIDs, } - body, _ := json.Marshal(req) + body, _ := json.Marshal(deleteRequest) httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/media-items/bulk-delete", bytes.NewBuffer(body)) httpReq.Header.Set("Content-Type", "application/json") @@ -31,6 +39,15 @@ func TestMediaBulkOperations(t *testing.T) { defer resp.Body.Close() assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) + + // NEW: Database verification + for _, id := range mediaIDs { + pgID, err := uuid.FromBytes(id) + require.NoError(t, err, "Should parse UUID from string") + + _, err := setup.DB.GetMediaItem(context.Background(), pgtype.UUID{Bytes: [16]byte(pgID), Valid: true}) + assert.Error(t, err, "Media item should be deleted from database") + } }) t.Run("BulkDeleteBooks_EmptyBookIDs", func(t *testing.T) { @@ -251,6 +268,9 @@ func TestMediaBulkOperations(t *testing.T) { t.Run("BulkUpdateBooks_UpdateReadingStatus", func(t *testing.T) { mediaID1 := createTestMediaItemID(t, setup.Server, token) + mediaID2 := createTestMediaItemID(t, setup.Server, token) + mediaID3 := createTestMediaItemID(t, setup.Server, token) + mediaID4 := createTestMediaItemID(t, setup.Server, token) req := map[string]interface{}{ "media_item_updates": []map[string]interface{}{ @@ -260,6 +280,24 @@ func TestMediaBulkOperations(t *testing.T) { "reading_status": "reading", }, }, + { + "media_item_id": mediaID2, + "updates": map[string]interface{}{ + "reading_status": "reading", + }, + }, + { + "media_item_id": mediaID3, + "updates": map[string]interface{}{ + "reading_status": "to-read", + }, + }, + { + "media_item_id": mediaID4, + "updates": map[string]interface{}{ + "reading_status": "did-not-finish", + }, + }, }, } body, _ := json.Marshal(req) @@ -279,6 +317,25 @@ func TestMediaBulkOperations(t *testing.T) { json.NewDecoder(resp.Body).Decode(&result) assert.Contains(t, result, "results") + assert.Contains(t, result, "total") + assert.Equal(t, 4.0, result["total"]) + + // NEW: Verify database state + for i, mediaID := range []string{mediaID1, mediaID2, mediaID3, mediaID4} { + pgID := pgtype.UUID{Bytes: [16]byte(mediaID), Valid: true} + item, err := setup.DB.GetMediaItem(context.Background(), pgID) + assert.NoError(t, err, "Should retrieve media item") + + if item.ReadingStatus.String == "reading" { + assert.Equal(t, true, item.ReadingStatus.Valid, "Reading status should still be true") + } + if item.ReadingStatus.String == "to-read" { + assert.Equal(t, true, item.ReadingStatus.Valid, "Reading status should be to-read") + } + if item.ReadingStatus.String == "did-not-finish" { + assert.Equal(t, true, item.ReadingStatus.Valid, "Reading status should be did-not-finish") + } + } }) t.Run("BulkUpdateBooks_UpdateMultipleFields", func(t *testing.T) {