diff --git a/api/coverage.out b/api/coverage.out index 8498df0..3be7789 100644 --- a/api/coverage.out +++ b/api/coverage.out @@ -267,67 +267,74 @@ mymodule/main.go:1036.16,1040.3 3 0 mymodule/main.go:1042.2,1043.23 2 1 mymodule/main.go:1043.23,1046.3 2 0 mymodule/main.go:1048.2,1048.47 1 1 -mymodule/main.go:1052.68,1053.63 1 1 -mymodule/main.go:1053.63,1056.3 2 0 -mymodule/main.go:1059.2,1061.16 3 1 -mymodule/main.go:1061.16,1064.3 2 0 -mymodule/main.go:1067.2,1075.16 3 1 -mymodule/main.go:1075.16,1078.3 2 0 -mymodule/main.go:1079.2,1082.44 2 1 -mymodule/main.go:1082.44,1085.3 2 0 -mymodule/main.go:1088.2,1096.16 3 1 -mymodule/main.go:1096.16,1100.3 3 0 -mymodule/main.go:1103.2,1104.23 2 1 -mymodule/main.go:1104.23,1107.3 2 0 -mymodule/main.go:1109.2,1109.45 1 1 -mymodule/main.go:1113.74,1114.63 1 0 -mymodule/main.go:1114.63,1117.3 2 0 -mymodule/main.go:1120.2,1122.16 3 0 -mymodule/main.go:1122.16,1125.3 2 0 -mymodule/main.go:1128.2,1130.16 3 0 -mymodule/main.go:1130.16,1133.3 2 0 -mymodule/main.go:1134.2,1141.87 4 0 -mymodule/main.go:1141.87,1144.3 2 0 -mymodule/main.go:1147.2,1155.16 3 0 -mymodule/main.go:1155.16,1159.3 3 0 -mymodule/main.go:1162.2,1163.23 2 0 -mymodule/main.go:1163.23,1166.3 2 0 -mymodule/main.go:1168.2,1168.51 1 0 -mymodule/main.go:1172.70,1173.35 1 0 -mymodule/main.go:1173.35,1176.3 2 0 -mymodule/main.go:1179.2,1181.16 3 0 -mymodule/main.go:1181.16,1184.3 2 0 -mymodule/main.go:1187.2,1196.16 4 0 -mymodule/main.go:1196.16,1200.3 3 0 -mymodule/main.go:1203.2,1203.18 1 0 -mymodule/main.go:1203.18,1206.3 2 0 -mymodule/main.go:1209.2,1216.16 3 0 -mymodule/main.go:1216.16,1220.3 3 0 -mymodule/main.go:1223.2,1224.23 2 0 -mymodule/main.go:1224.23,1227.3 2 0 -mymodule/main.go:1229.2,1229.47 1 0 -mymodule/main.go:1233.68,1234.35 1 0 -mymodule/main.go:1234.35,1237.3 2 0 -mymodule/main.go:1240.2,1242.16 3 0 -mymodule/main.go:1242.16,1245.3 2 0 -mymodule/main.go:1248.2,1257.16 4 0 -mymodule/main.go:1257.16,1261.3 3 0 -mymodule/main.go:1264.2,1273.16 4 0 -mymodule/main.go:1273.16,1277.3 3 0 -mymodule/main.go:1280.2,1287.16 3 0 -mymodule/main.go:1287.16,1291.3 3 0 -mymodule/main.go:1294.2,1295.23 2 0 -mymodule/main.go:1295.23,1298.3 2 0 -mymodule/main.go:1301.2,1301.24 1 0 -mymodule/main.go:1301.24,1309.17 3 0 -mymodule/main.go:1309.17,1313.4 3 0 -mymodule/main.go:1316.2,1316.45 1 0 -mymodule/main.go:1320.74,1321.35 1 0 -mymodule/main.go:1321.35,1324.3 2 0 -mymodule/main.go:1327.2,1329.16 3 0 -mymodule/main.go:1329.16,1332.3 2 0 -mymodule/main.go:1335.2,1342.16 3 0 -mymodule/main.go:1342.16,1346.3 3 0 -mymodule/main.go:1349.2,1350.23 2 0 -mymodule/main.go:1350.23,1353.3 2 0 -mymodule/main.go:1355.2,1355.51 1 0 +mymodule/main.go:1052.68,1053.66 1 1 +mymodule/main.go:1053.66,1056.6 2 1 +mymodule/main.go:1058.5,1059.19 2 1 +mymodule/main.go:1059.19,1062.6 2 1 +mymodule/main.go:1064.5,1065.65 2 1 +mymodule/main.go:1065.65,1068.6 2 1 +mymodule/main.go:1069.5,1071.50 2 1 +mymodule/main.go:1071.50,1074.6 2 1 +mymodule/main.go:1076.5,1083.19 3 1 +mymodule/main.go:1083.19,1086.6 2 1 +mymodule/main.go:1088.5,1089.19 2 1 +mymodule/main.go:1089.19,1092.6 2 1 +mymodule/main.go:1093.5,1093.26 1 1 +mymodule/main.go:1093.26,1096.6 2 1 +mymodule/main.go:1098.5,1098.97 1 1 +mymodule/main.go:1103.74,1104.66 1 1 +mymodule/main.go:1104.66,1107.6 2 1 +mymodule/main.go:1109.5,1110.19 2 1 +mymodule/main.go:1110.19,1113.6 2 1 +mymodule/main.go:1115.5,1116.71 2 1 +mymodule/main.go:1116.71,1119.6 2 1 +mymodule/main.go:1120.5,1122.62 2 1 +mymodule/main.go:1122.62,1125.6 2 1 +mymodule/main.go:1127.5,1134.19 3 1 +mymodule/main.go:1134.19,1137.6 2 1 +mymodule/main.go:1139.5,1140.19 2 1 +mymodule/main.go:1140.19,1143.6 2 1 +mymodule/main.go:1144.5,1144.26 1 1 +mymodule/main.go:1144.26,1147.6 2 1 +mymodule/main.go:1149.5,1149.103 1 1 +mymodule/main.go:1153.58,1154.90 1 1 +mymodule/main.go:1154.90,1156.6 1 1 +mymodule/main.go:1157.5,1157.15 1 1 +mymodule/main.go:1162.70,1163.35 1 0 +mymodule/main.go:1163.35,1166.3 2 0 +mymodule/main.go:1169.2,1171.16 3 0 +mymodule/main.go:1171.16,1174.3 2 0 +mymodule/main.go:1177.2,1186.16 4 0 +mymodule/main.go:1186.16,1190.3 3 0 +mymodule/main.go:1193.2,1193.18 1 0 +mymodule/main.go:1193.18,1196.3 2 0 +mymodule/main.go:1199.2,1206.16 3 0 +mymodule/main.go:1206.16,1210.3 3 0 +mymodule/main.go:1213.2,1214.23 2 0 +mymodule/main.go:1214.23,1217.3 2 0 +mymodule/main.go:1219.2,1219.47 1 0 +mymodule/main.go:1223.68,1224.35 1 0 +mymodule/main.go:1224.35,1227.3 2 0 +mymodule/main.go:1230.2,1232.16 3 0 +mymodule/main.go:1232.16,1235.3 2 0 +mymodule/main.go:1238.2,1247.16 4 0 +mymodule/main.go:1247.16,1251.3 3 0 +mymodule/main.go:1254.2,1263.16 4 0 +mymodule/main.go:1263.16,1267.3 3 0 +mymodule/main.go:1270.2,1277.16 3 0 +mymodule/main.go:1277.16,1281.3 3 0 +mymodule/main.go:1284.2,1285.23 2 0 +mymodule/main.go:1285.23,1288.3 2 0 +mymodule/main.go:1291.2,1291.24 1 0 +mymodule/main.go:1291.24,1299.17 3 0 +mymodule/main.go:1299.17,1303.4 3 0 +mymodule/main.go:1306.2,1306.45 1 0 +mymodule/main.go:1310.74,1311.35 1 0 +mymodule/main.go:1311.35,1314.3 2 0 +mymodule/main.go:1317.2,1319.16 3 0 +mymodule/main.go:1319.16,1322.3 2 0 +mymodule/main.go:1325.2,1332.16 3 0 +mymodule/main.go:1332.16,1336.3 3 0 +mymodule/main.go:1339.2,1340.23 2 0 +mymodule/main.go:1340.23,1343.3 2 0 +mymodule/main.go:1345.2,1345.51 1 0 diff --git a/api/main.go b/api/main.go index dba7985..386f421 100644 --- a/api/main.go +++ b/api/main.go @@ -1046,126 +1046,116 @@ func (app *App) UpdateAuthor(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Author updated successfully") } -// UpdateBook updates an existing book in the database +// UpdateBook handles the updating of an existing book in the database func (app *App) UpdateBook(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPut && r.Method != http.MethodPost { - http.Error(w, "Only PUT or POST methods are supported", http.StatusMethodNotAllowed) - return - } - - // Extract the book ID from the URL path - vars := mux.Vars(r) - bookID, err := strconv.Atoi(vars["id"]) - if err != nil { - http.Error(w, "Invalid book ID", http.StatusBadRequest) - return - } - - // Parse the JSON data received from the request - var book struct { - Title string `json:"title"` - AuthorID int `json:"author_id"` - Photo string `json:"photo"` - Details string `json:"details"` - IsBorrowed bool `json:"is_borrowed"` - } - err = json.NewDecoder(r.Body).Decode(&book) - if err != nil { - http.Error(w, "Invalid JSON data", http.StatusBadRequest) - return - } - defer r.Body.Close() - - // Check if all required fields are filled - if book.Title == "" || book.AuthorID == 0 { - http.Error(w, "Title and AuthorID are required fields", http.StatusBadRequest) - return - } - - // Query to update the book - query := ` - UPDATE books - SET title = ?, author_id = ?, photo = ?, details = ?, is_borrowed = ? - WHERE id = ? - ` - - // Execute the query - result, err := app.DB.Exec(query, book.Title, book.AuthorID, book.Photo, book.Details, book.IsBorrowed, bookID) - if err != nil { - app.Logger.Printf("Failed to update book: %v", err) - http.Error(w, fmt.Sprintf("Failed to update book: %v", err), http.StatusInternalServerError) - return - } - - // Check if any row was actually updated - rowsAffected, _ := result.RowsAffected() - if rowsAffected == 0 { - http.Error(w, "Book not found", http.StatusNotFound) - return - } + if r.Method != http.MethodPut && r.Method != http.MethodPost { + HandleError(w, app.Logger, "Only PUT or POST methods are supported", nil, http.StatusMethodNotAllowed) + return + } + + bookID, err := GetIDFromRequest(r, "id") + if err != nil { + HandleError(w, app.Logger, "Invalid book ID", err, http.StatusBadRequest) + return + } + + var book Book + if err := json.NewDecoder(r.Body).Decode(&book); err != nil { + HandleError(w, app.Logger, "Invalid JSON data", err, http.StatusBadRequest) + return + } + defer r.Body.Close() + + if err := ValidateBookData(book); err != nil { + HandleError(w, app.Logger, err.Error(), nil, http.StatusBadRequest) + return + } + + query := ` + UPDATE books + SET title = ?, author_id = ?, photo = ?, details = ?, is_borrowed = ? + WHERE id = ? + ` - fmt.Fprintf(w, "Book updated successfully") + result, err := app.DB.Exec(query, book.Title, book.AuthorID, book.Photo, book.Details, book.IsBorrowed, bookID) + if err != nil { + HandleError(w, app.Logger, "Failed to update book", err, http.StatusInternalServerError) + return + } + + rowsAffected, err := result.RowsAffected() + if err != nil { + HandleError(w, app.Logger, "Failed to retrieve affected rows", err, http.StatusInternalServerError) + return + } + if rowsAffected == 0 { + HandleError(w, app.Logger, "Book not found", nil, http.StatusNotFound) + return + } + + RespondWithJSON(w, http.StatusOK, map[string]string{"message": "Book updated successfully"}) } + // UpdateSubscriber updates an existing subscriber in the database func (app *App) UpdateSubscriber(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPut && r.Method != http.MethodPost { - http.Error(w, "Only PUT or POST methods are supported", http.StatusMethodNotAllowed) - return - } - - // Extract the subscriber ID from the URL path - vars := mux.Vars(r) - subscriberID, err := strconv.Atoi(vars["id"]) - if err != nil { - http.Error(w, "Invalid subscriber ID", http.StatusBadRequest) - return - } - - // Parse the JSON data received from the request - var subscriber Subscriber - err = json.NewDecoder(r.Body).Decode(&subscriber) - if err != nil { - http.Error(w, "Invalid JSON data", http.StatusBadRequest) - return - } - defer r.Body.Close() - - // Log the subscriber ID and received data for update - app.Logger.Printf("Updating subscriber with ID: %d", subscriberID) - app.Logger.Printf("Received data: %+v", subscriber) - - // Check if all required fields are filled - if subscriber.Firstname == "" || subscriber.Lastname == "" || subscriber.Email == "" { - http.Error(w, "Firstname, Lastname, and Email are required fields", http.StatusBadRequest) - return - } - - // Query to update the subscriber - query := ` + if r.Method != http.MethodPut && r.Method != http.MethodPost { + HandleError(w, app.Logger, "Only PUT or POST methods are supported", nil, http.StatusMethodNotAllowed) + return + } + + subscriberID, err := GetIDFromRequest(r, "id") + if err != nil { + HandleError(w, app.Logger, "Invalid subscriber ID", err, http.StatusBadRequest) + return + } + + var subscriber Subscriber + if err := json.NewDecoder(r.Body).Decode(&subscriber); err != nil { + HandleError(w, app.Logger, "Invalid JSON data", err, http.StatusBadRequest) + return + } + defer r.Body.Close() + + if err := ValidateSubscriberData(subscriber); err != nil { + HandleError(w, app.Logger, err.Error(), nil, http.StatusBadRequest) + return + } + + query := ` UPDATE subscribers - SET lastname = ?, firstname = ?, email = ? + SET lastname = ?, firstname = ?, email = ? WHERE id = ? ` - // Execute the query - result, err := app.DB.Exec(query, subscriber.Lastname, subscriber.Firstname, subscriber.Email, subscriberID) - if err != nil { - app.Logger.Printf("Failed to update subscriber: %v", err) - http.Error(w, fmt.Sprintf("Failed to update subscriber: %v", err), http.StatusInternalServerError) - return - } - - // Check if any row was actually updated - rowsAffected, _ := result.RowsAffected() - if rowsAffected == 0 { - http.Error(w, "Subscriber not found", http.StatusNotFound) - return - } + result, err := app.DB.Exec(query, subscriber.Lastname, subscriber.Firstname, subscriber.Email, subscriberID) + if err != nil { + HandleError(w, app.Logger, "Failed to update subscriber", err, http.StatusInternalServerError) + return + } + + rowsAffected, err := result.RowsAffected() + if err != nil { + HandleError(w, app.Logger, "Failed to retrieve affected rows", err, http.StatusInternalServerError) + return + } + if rowsAffected == 0 { + HandleError(w, app.Logger, "Subscriber not found", nil, http.StatusNotFound) + return + } + + RespondWithJSON(w, http.StatusOK, map[string]string{"message": "Subscriber updated successfully"}) +} - fmt.Fprintf(w, "Subscriber updated successfully") +// ValidateSubscriberData checks if the required fields for a subscriber are present +func ValidateSubscriberData(subscriber Subscriber) error { + if subscriber.Firstname == "" || subscriber.Lastname == "" || subscriber.Email == "" { + return fmt.Errorf("firstname, lastname, and email are required fields") + } + return nil } + // DeleteAuthor deletes an existing author from the database func (app *App) DeleteAuthor(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodDelete { diff --git a/api/main_test.go b/api/main_test.go index 5c060ff..bf084f9 100644 --- a/api/main_test.go +++ b/api/main_test.go @@ -2,18 +2,18 @@ package main import ( "bytes" - "mime/multipart" + "database/sql" "encoding/json" + "fmt" "io" "log" - "os" + "mime/multipart" "net/http" "net/http/httptest" + "os" + "regexp" "testing" - "fmt" - "database/sql" - "regexp" - + "github.com/DATA-DOG/go-sqlmock" "github.com/gorilla/mux" "github.com/stretchr/testify/assert" @@ -26,7 +26,7 @@ func createTestApp(t *testing.T) (*App, sqlmock.Sqlmock) { t.Fatalf("Error creating sqlmock: %v", err) } - logger := log.New(io.Discard, "", log.LstdFlags) + logger := log.New(io.Discard, "", log.LstdFlags) return &App{ DB: db, @@ -61,7 +61,7 @@ func TestInitDB(t *testing.T) { dsn := "user:password@tcp(localhost:3306)/testdb" - originalSQLOpen := sqlOpen + originalSQLOpen := sqlOpen sqlOpen = func(driverName, dataSourceName string) (*sql.DB, error) { if dataSourceName == dsn { return db, nil @@ -114,7 +114,7 @@ func TestInitDB(t *testing.T) { // TestHome tests the Home handler func TestHome(t *testing.T) { - app, _ := createTestApp(t) + app, _ := createTestApp(t) defer app.DB.Close() req, err := http.NewRequest("GET", "/", nil) @@ -135,7 +135,7 @@ func TestHome(t *testing.T) { // TestInfo tests the Info handler func TestInfo(t *testing.T) { - app, _ := createTestApp(t) + app, _ := createTestApp(t) defer app.DB.Close() req, err := http.NewRequest("GET", "/info", nil) @@ -199,7 +199,7 @@ func TestSetupRouter(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - + tt.mockSetup() req, err := http.NewRequest(tt.method, tt.path, nil) @@ -221,407 +221,404 @@ func TestSetupRouter(t *testing.T) { // TestRespondWithJSON tests the RespondWithJSON function func TestRespondWithJSON(t *testing.T) { - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() + + payload := map[string]string{"message": "success"} - payload := map[string]string{"message": "success"} + RespondWithJSON(rr, http.StatusOK, payload) - RespondWithJSON(rr, http.StatusOK, payload) - - assert.Equal(t, "application/json", rr.Header().Get("Content-Type"), "Content-Type should be application/json") - assert.Equal(t, http.StatusOK, rr.Code, "Expected status code 200") + assert.Equal(t, "application/json", rr.Header().Get("Content-Type"), "Content-Type should be application/json") + assert.Equal(t, http.StatusOK, rr.Code, "Expected status code 200") - expectedBody, _ := json.Marshal(payload) - assert.JSONEq(t, string(expectedBody), rr.Body.String(), "Response body should match the payload") + expectedBody, _ := json.Marshal(payload) + assert.JSONEq(t, string(expectedBody), rr.Body.String(), "Response body should match the payload") } func TestRespondWithJSON_Success(t *testing.T) { - rr := httptest.NewRecorder() - payload := map[string]string{"message": "test"} + rr := httptest.NewRecorder() + payload := map[string]string{"message": "test"} - RespondWithJSON(rr, http.StatusOK, payload) + RespondWithJSON(rr, http.StatusOK, payload) - assert.Equal(t, "application/json", rr.Header().Get("Content-Type"), "Expected Content-Type application/json") - assert.Equal(t, http.StatusOK, rr.Code, "Expected status code 200") - assert.JSONEq(t, `{"message": "test"}`, rr.Body.String(), "Expected JSON response") + assert.Equal(t, "application/json", rr.Header().Get("Content-Type"), "Expected Content-Type application/json") + assert.Equal(t, http.StatusOK, rr.Code, "Expected status code 200") + assert.JSONEq(t, `{"message": "test"}`, rr.Body.String(), "Expected JSON response") } func TestRespondWithJSON_Error(t *testing.T) { - rr := httptest.NewRecorder() - payload := make(chan int) + rr := httptest.NewRecorder() + payload := make(chan int) - RespondWithJSON(rr, http.StatusOK, payload) + RespondWithJSON(rr, http.StatusOK, payload) - assert.Equal(t, "application/json", rr.Header().Get("Content-Type"), "Expected Content-Type application/json") - assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500 for encoding error") - assert.Equal(t, "Error encoding response\n", rr.Body.String(), "Expected error message in response") + assert.Equal(t, "application/json", rr.Header().Get("Content-Type"), "Expected Content-Type application/json") + assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500 for encoding error") + assert.Equal(t, "Error encoding response\n", rr.Body.String(), "Expected error message in response") } // TestHandleError tests the HandleError function func TestHandleError(t *testing.T) { - rr := httptest.NewRecorder() - logger := log.New(io.Discard, "", log.LstdFlags) // Logger care nu afiseaza nimic - message := "test error" - err := fmt.Errorf("an example error") + rr := httptest.NewRecorder() + logger := log.New(io.Discard, "", log.LstdFlags) // Logger care nu afiseaza nimic + message := "test error" + err := fmt.Errorf("an example error") - HandleError(rr, logger, message, err, http.StatusInternalServerError) + HandleError(rr, logger, message, err, http.StatusInternalServerError) - assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") - assert.Equal(t, "test error\n", rr.Body.String(), "Expected error message in response") + assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") + assert.Equal(t, "test error\n", rr.Body.String(), "Expected error message in response") } // TestGetIDFromRequest tests the GetIDFromRequest function func TestGetIDFromRequest(t *testing.T) { - req := httptest.NewRequest("GET", "/authors/1", nil) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req := httptest.NewRequest("GET", "/authors/1", nil) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - id, err := GetIDFromRequest(req, "id") - assert.NoError(t, err, "Expected no error for a valid ID") - assert.Equal(t, 1, id, "Expected ID to be 1") + id, err := GetIDFromRequest(req, "id") + assert.NoError(t, err, "Expected no error for a valid ID") + assert.Equal(t, 1, id, "Expected ID to be 1") - req = httptest.NewRequest("GET", "/authors/abc", nil) - req = mux.SetURLVars(req, map[string]string{"id": "abc"}) + req = httptest.NewRequest("GET", "/authors/abc", nil) + req = mux.SetURLVars(req, map[string]string{"id": "abc"}) - _, err = GetIDFromRequest(req, "id") - assert.Error(t, err, "Expected an error for an invalid ID") - assert.Contains(t, err.Error(), "invalid id", "Error message should mention 'invalid id'") + _, err = GetIDFromRequest(req, "id") + assert.Error(t, err, "Expected an error for an invalid ID") + assert.Contains(t, err.Error(), "invalid id", "Error message should mention 'invalid id'") } func TestValidateBookData(t *testing.T) { - book := Book{Title: "Valid Book Title", AuthorID: 1} - err := ValidateBookData(book) - assert.NoError(t, err, "Expected no error for valid book data") + book := Book{Title: "Valid Book Title", AuthorID: 1} + err := ValidateBookData(book) + assert.NoError(t, err, "Expected no error for valid book data") - book = Book{Title: "", AuthorID: 1} - err = ValidateBookData(book) - assert.Error(t, err, "Expected an error for missing title") - assert.Contains(t, err.Error(), "title and authorID are required fields", "Error message should mention missing title") + book = Book{Title: "", AuthorID: 1} + err = ValidateBookData(book) + assert.Error(t, err, "Expected an error for missing title") + assert.Contains(t, err.Error(), "title and authorID are required fields", "Error message should mention missing title") - book = Book{Title: "Valid Book Title", AuthorID: 0} - err = ValidateBookData(book) - assert.Error(t, err, "Expected an error for missing author ID") - assert.Contains(t, err.Error(), "title and authorID are required fields", "Error message should mention missing author ID") + book = Book{Title: "Valid Book Title", AuthorID: 0} + err = ValidateBookData(book) + assert.Error(t, err, "Expected an error for missing author ID") + assert.Contains(t, err.Error(), "title and authorID are required fields", "Error message should mention missing author ID") - book = Book{Title: "", AuthorID: 0} - err = ValidateBookData(book) - assert.Error(t, err, "Expected an error for missing title and author ID") - assert.Contains(t, err.Error(), "title and authorID are required fields", "Error message should mention missing fields") + book = Book{Title: "", AuthorID: 0} + err = ValidateBookData(book) + assert.Error(t, err, "Expected an error for missing title and author ID") + assert.Contains(t, err.Error(), "title and authorID are required fields", "Error message should mention missing fields") } // TestScanAuthors tests the ScanAuthors function func TestScanAuthors(t *testing.T) { - db, mock, err := sqlmock.New() - assert.NoError(t, err, "Error should be nil when creating sqlmock") - defer db.Close() + db, mock, err := sqlmock.New() + assert.NoError(t, err, "Error should be nil when creating sqlmock") + defer db.Close() - rows := sqlmock.NewRows([]string{"id", "lastname", "firstname", "photo"}). - AddRow(1, "Doe", "John", "photo.jpg"). - AddRow(2, "Smith", "Jane", "photo2.jpg") + rows := sqlmock.NewRows([]string{"id", "lastname", "firstname", "photo"}). + AddRow(1, "Doe", "John", "photo.jpg"). + AddRow(2, "Smith", "Jane", "photo2.jpg") - mock.ExpectQuery(`SELECT id, lastname, firstname, photo FROM authors`).WillReturnRows(rows) + mock.ExpectQuery(`SELECT id, lastname, firstname, photo FROM authors`).WillReturnRows(rows) - result, err := db.Query("SELECT id, lastname, firstname, photo FROM authors") - assert.NoError(t, err, "Query execution should not return an error") - authors, err := ScanAuthors(result) - assert.NoError(t, err, "Expected no error while scanning authors") - assert.Equal(t, 2, len(authors), "Expected 2 authors") - assert.Equal(t, "John", authors[0].Firstname, "Expected Firstname to be John") - assert.Equal(t, "Doe", authors[0].Lastname, "Expected Lastname to be Doe") + result, err := db.Query("SELECT id, lastname, firstname, photo FROM authors") + assert.NoError(t, err, "Query execution should not return an error") + authors, err := ScanAuthors(result) + assert.NoError(t, err, "Expected no error while scanning authors") + assert.Equal(t, 2, len(authors), "Expected 2 authors") + assert.Equal(t, "John", authors[0].Firstname, "Expected Firstname to be John") + assert.Equal(t, "Doe", authors[0].Lastname, "Expected Lastname to be Doe") } func TestScanAuthors_ErrorAfterIteration(t *testing.T) { - db, mock, err := sqlmock.New() - assert.NoError(t, err, "Eroarea ar trebui să fie nil la crearea sqlmock") - defer db.Close() + db, mock, err := sqlmock.New() + assert.NoError(t, err, "Eroarea ar trebui să fie nil la crearea sqlmock") + defer db.Close() - rows := sqlmock.NewRows([]string{"id", "lastname", "firstname", "photo"}). - AddRow(1, "Doe", "John", "photo.jpg"). - AddRow(2, "Smith", "Jane", "photo2.jpg"). - RowError(1, fmt.Errorf("iteration error")) + rows := sqlmock.NewRows([]string{"id", "lastname", "firstname", "photo"}). + AddRow(1, "Doe", "John", "photo.jpg"). + AddRow(2, "Smith", "Jane", "photo2.jpg"). + RowError(1, fmt.Errorf("iteration error")) - mock.ExpectQuery(`SELECT id, lastname, firstname, photo FROM authors`).WillReturnRows(rows) + mock.ExpectQuery(`SELECT id, lastname, firstname, photo FROM authors`).WillReturnRows(rows) - result, err := db.Query("SELECT id, lastname, firstname, photo FROM authors") - assert.NoError(t, err, "Execuția interogării nu ar trebui să returneze o eroare") + result, err := db.Query("SELECT id, lastname, firstname, photo FROM authors") + assert.NoError(t, err, "Execuția interogării nu ar trebui să returneze o eroare") - authors, err := ScanAuthors(result) + authors, err := ScanAuthors(result) - assert.Error(t, err, "Era de așteptat o eroare după iterație") - assert.Nil(t, authors, "Lista de autori ar trebui să fie nil la eroare") + assert.Error(t, err, "Era de așteptat o eroare după iterație") + assert.Nil(t, authors, "Lista de autori ar trebui să fie nil la eroare") } - func TestScanAuthors_ErrorDuringScan(t *testing.T) { - db, mock, err := sqlmock.New() - assert.NoError(t, err, "Error should be nil when creating sqlmock") - defer db.Close() + db, mock, err := sqlmock.New() + assert.NoError(t, err, "Error should be nil when creating sqlmock") + defer db.Close() - rows := sqlmock.NewRows([]string{"id", "lastname", "firstname", "photo"}). - AddRow("invalid_id", "Doe", "John", "photo.jpg") + rows := sqlmock.NewRows([]string{"id", "lastname", "firstname", "photo"}). + AddRow("invalid_id", "Doe", "John", "photo.jpg") - mock.ExpectQuery(`SELECT id, lastname, firstname, photo FROM authors`).WillReturnRows(rows) + mock.ExpectQuery(`SELECT id, lastname, firstname, photo FROM authors`).WillReturnRows(rows) - result, err := db.Query("SELECT id, lastname, firstname, photo FROM authors") - assert.NoError(t, err, "Query execution should not return an error") + result, err := db.Query("SELECT id, lastname, firstname, photo FROM authors") + assert.NoError(t, err, "Query execution should not return an error") - authors, err := ScanAuthors(result) + authors, err := ScanAuthors(result) - assert.Error(t, err, "Expected an error during scan") - assert.Nil(t, authors, "Authors should be nil on error") + assert.Error(t, err, "Expected an error during scan") + assert.Nil(t, authors, "Authors should be nil on error") } // TestValidateAuthorData tests the ValidateAuthorData function func TestValidateAuthorData(t *testing.T) { - author := Author{Firstname: "John", Lastname: "Doe"} - err := ValidateAuthorData(author) - assert.NoError(t, err, "Expected no error for valid author data") + author := Author{Firstname: "John", Lastname: "Doe"} + err := ValidateAuthorData(author) + assert.NoError(t, err, "Expected no error for valid author data") - author = Author{Firstname: "", Lastname: "Doe"} - err = ValidateAuthorData(author) - assert.Error(t, err, "Expected an error for missing Firstname") - assert.Contains(t, err.Error(), "firstname and lastname are required fields", "Error message should mention missing fields") + author = Author{Firstname: "", Lastname: "Doe"} + err = ValidateAuthorData(author) + assert.Error(t, err, "Expected an error for missing Firstname") + assert.Contains(t, err.Error(), "firstname and lastname are required fields", "Error message should mention missing fields") - author = Author{Firstname: "John", Lastname: ""} - err = ValidateAuthorData(author) - assert.Error(t, err, "Expected an error for missing Lastname") - assert.Contains(t, err.Error(), "firstname and lastname are required fields", "Error message should mention missing fields") + author = Author{Firstname: "John", Lastname: ""} + err = ValidateAuthorData(author) + assert.Error(t, err, "Expected an error for missing Lastname") + assert.Contains(t, err.Error(), "firstname and lastname are required fields", "Error message should mention missing fields") } // TestSearchAuthors_ErrorExecutingQuery tests the case where there is an error executing the SQL query func TestSearchAuthors_ErrorExecutingQuery(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/authors?query=John", nil) - assert.NoError(t, err, "Error should be nil when creating a new request") + req, err := http.NewRequest("GET", "/authors?query=John", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectQuery(`SELECT id, Firstname, Lastname, photo FROM authors WHERE Firstname LIKE \? OR Lastname LIKE \?`). - WithArgs("%John%", "%John%"). - WillReturnError(fmt.Errorf("query execution error")) + mock.ExpectQuery(`SELECT id, Firstname, Lastname, photo FROM authors WHERE Firstname LIKE \? OR Lastname LIKE \?`). + WithArgs("%John%", "%John%"). + WillReturnError(fmt.Errorf("query execution error")) - handler := http.HandlerFunc(app.SearchAuthors) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.SearchAuthors) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") - assert.Contains(t, rr.Body.String(), "Error executing query", "Expected error message for query execution error") + assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") + assert.Contains(t, rr.Body.String(), "Error executing query", "Expected error message for query execution error") - err = mock.ExpectationsWereMet() - assert.NoError(t, err, "There should be no unmet expectations") + err = mock.ExpectationsWereMet() + assert.NoError(t, err, "There should be no unmet expectations") } // TestSearchAuthors_ErrorScanningAuthors tests the case where there is an error scanning the rows func TestSearchAuthors_ErrorScanningAuthors(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/authors?query=John", nil) - assert.NoError(t, err, "Error should be nil when creating a new request") + req, err := http.NewRequest("GET", "/authors?query=John", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectQuery(`SELECT id, Firstname, Lastname, photo FROM authors WHERE Firstname LIKE \? OR Lastname LIKE \?`). - WithArgs("%John%", "%John%"). - WillReturnRows(sqlmock.NewRows([]string{"id", "Firstname", "Lastname", "photo"}). - AddRow("invalid_id", "John", "Doe", "photo.jpg")) + mock.ExpectQuery(`SELECT id, Firstname, Lastname, photo FROM authors WHERE Firstname LIKE \? OR Lastname LIKE \?`). + WithArgs("%John%", "%John%"). + WillReturnRows(sqlmock.NewRows([]string{"id", "Firstname", "Lastname", "photo"}). + AddRow("invalid_id", "John", "Doe", "photo.jpg")) - handler := http.HandlerFunc(app.SearchAuthors) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.SearchAuthors) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") - assert.Contains(t, rr.Body.String(), "Error scanning authors", "Expected error message for scan error") + assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") + assert.Contains(t, rr.Body.String(), "Error scanning authors", "Expected error message for scan error") - err = mock.ExpectationsWereMet() - assert.NoError(t, err, "There should be no unmet expectations") + err = mock.ExpectationsWereMet() + assert.NoError(t, err, "There should be no unmet expectations") } func TestSearchAuthors_MissingQueryParameter(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/search_authors", nil) - assert.NoError(t, err, "Error should be nil when creating a new request") + req, err := http.NewRequest("GET", "/search_authors", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.SearchAuthors) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.SearchAuthors) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code, "Expected status code 400 for missing query parameter") - assert.Contains(t, rr.Body.String(), "Query parameter is required", "Expected error message for missing query parameter") + assert.Equal(t, http.StatusBadRequest, rr.Code, "Expected status code 400 for missing query parameter") + assert.Contains(t, rr.Body.String(), "Query parameter is required", "Expected error message for missing query parameter") } func TestSearchAuthors_Success(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/authors?query=John", nil) - assert.NoError(t, err, "Error should be nil when creating a new request") + req, err := http.NewRequest("GET", "/authors?query=John", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{"id", "lastname", "firstname", "photo"}). - AddRow(1, "Doe", "John", "photo.jpg"). - AddRow(2, "Smith", "Jane", "photo2.jpg") + rows := sqlmock.NewRows([]string{"id", "lastname", "firstname", "photo"}). + AddRow(1, "Doe", "John", "photo.jpg"). + AddRow(2, "Smith", "Jane", "photo2.jpg") - mock.ExpectQuery(`SELECT id, Firstname, Lastname, photo FROM authors WHERE Firstname LIKE \? OR Lastname LIKE \?`). - WithArgs("%John%", "%John%"). - WillReturnRows(rows) + mock.ExpectQuery(`SELECT id, Firstname, Lastname, photo FROM authors WHERE Firstname LIKE \? OR Lastname LIKE \?`). + WithArgs("%John%", "%John%"). + WillReturnRows(rows) - handler := http.HandlerFunc(app.SearchAuthors) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.SearchAuthors) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code, "Expected status code 200") + assert.Equal(t, http.StatusOK, rr.Code, "Expected status code 200") - expected := []map[string]interface{}{ - {"id": float64(1), "firstname": "John", "lastname": "Doe", "photo": "photo.jpg"}, - {"id": float64(2), "firstname": "Jane", "lastname": "Smith", "photo": "photo2.jpg"}, - } - var actual []map[string]interface{} - err = json.Unmarshal(rr.Body.Bytes(), &actual) - assert.NoError(t, err, "Expected no error while unmarshaling JSON response") + expected := []map[string]interface{}{ + {"id": float64(1), "firstname": "John", "lastname": "Doe", "photo": "photo.jpg"}, + {"id": float64(2), "firstname": "Jane", "lastname": "Smith", "photo": "photo2.jpg"}, + } + var actual []map[string]interface{} + err = json.Unmarshal(rr.Body.Bytes(), &actual) + assert.NoError(t, err, "Expected no error while unmarshaling JSON response") - assert.Equal(t, expected, actual, "Expected JSON response") + assert.Equal(t, expected, actual, "Expected JSON response") } func TestScanBooks(t *testing.T) { - db, mock, err := sqlmock.New() - assert.NoError(t, err, "Error should be nil when creating sqlmock") - defer db.Close() + db, mock, err := sqlmock.New() + assert.NoError(t, err, "Error should be nil when creating sqlmock") + defer db.Close() - rows := sqlmock.NewRows([]string{"book_id", "book_title", "author_id", "book_photo", "is_borrowed", "book_details", "author_lastname", "author_firstname"}). - AddRow(1, "Sample Book", 1, "book.jpg", false, "A sample book", "Doe", "John"). - AddRow(2, "Another Book", 2, "another.jpg", true, "Another sample book", "Smith", "Jane") + rows := sqlmock.NewRows([]string{"book_id", "book_title", "author_id", "book_photo", "is_borrowed", "book_details", "author_lastname", "author_firstname"}). + AddRow(1, "Sample Book", 1, "book.jpg", false, "A sample book", "Doe", "John"). + AddRow(2, "Another Book", 2, "another.jpg", true, "Another sample book", "Smith", "Jane") - mock.ExpectQuery(`SELECT (.+) FROM books`).WillReturnRows(rows) + mock.ExpectQuery(`SELECT (.+) FROM books`).WillReturnRows(rows) - result, err := db.Query("SELECT book_id, book_title, author_id, book_photo, is_borrowed, book_details, author_lastname, author_firstname FROM books") - assert.NoError(t, err, "Query execution should not return an error") + result, err := db.Query("SELECT book_id, book_title, author_id, book_photo, is_borrowed, book_details, author_lastname, author_firstname FROM books") + assert.NoError(t, err, "Query execution should not return an error") - books, err := ScanBooks(result) - assert.NoError(t, err, "Expected no error while scanning books") - assert.Equal(t, 2, len(books), "Expected 2 books") - assert.Equal(t, "Sample Book", books[0].BookTitle, "Expected BookTitle to be 'Sample Book'") - assert.Equal(t, "Doe", books[0].AuthorLastname, "Expected AuthorLastname to be 'Doe'") + books, err := ScanBooks(result) + assert.NoError(t, err, "Expected no error while scanning books") + assert.Equal(t, 2, len(books), "Expected 2 books") + assert.Equal(t, "Sample Book", books[0].BookTitle, "Expected BookTitle to be 'Sample Book'") + assert.Equal(t, "Doe", books[0].AuthorLastname, "Expected AuthorLastname to be 'Doe'") } func TestSearchBooks_MissingQuery(t *testing.T) { - - app, _ := createTestApp(t) - defer app.DB.Close() - req, err := http.NewRequest("GET", "/books", nil) - assert.NoError(t, err, "Error should be nil when creating a new request") + app, _ := createTestApp(t) + defer app.DB.Close() + + req, err := http.NewRequest("GET", "/books", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.SearchBooks) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.SearchBooks) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code, "Expected status code 400") - assert.Contains(t, rr.Body.String(), "Query parameter is required", "Expected error message for missing query parameter") + assert.Equal(t, http.StatusBadRequest, rr.Code, "Expected status code 400") + assert.Contains(t, rr.Body.String(), "Query parameter is required", "Expected error message for missing query parameter") } func TestSearchBooks_ErrorExecutingQuery(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() - - req, err := http.NewRequest("GET", "/books?query=Sample", nil) - assert.NoError(t, err, "Error should be nil when creating a new request") + app, mock := createTestApp(t) + defer app.DB.Close() - rr := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/books?query=Sample", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") - mock.ExpectQuery(`SELECT (.+) FROM books`). - WithArgs("%Sample%", "%Sample%", "%Sample%"). - WillReturnError(fmt.Errorf("query execution error")) + rr := httptest.NewRecorder() + mock.ExpectQuery(`SELECT (.+) FROM books`). + WithArgs("%Sample%", "%Sample%", "%Sample%"). + WillReturnError(fmt.Errorf("query execution error")) - handler := http.HandlerFunc(app.SearchBooks) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.SearchBooks) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") - assert.Contains(t, rr.Body.String(), "Error executing query", "Expected error message for query execution error") + assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") + assert.Contains(t, rr.Body.String(), "Error executing query", "Expected error message for query execution error") } func TestSearchBooks_ErrorScanningRows(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() - req, err := http.NewRequest("GET", "/books?query=Sample", nil) - assert.NoError(t, err, "Error should be nil when creating a new request") + app, mock := createTestApp(t) + defer app.DB.Close() + req, err := http.NewRequest("GET", "/books?query=Sample", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectQuery(`SELECT (.+) FROM books`). - WithArgs("%Sample%", "%Sample%", "%Sample%"). - WillReturnRows(sqlmock.NewRows([]string{ - "book_id", "book_title", "author_id", "book_photo", "is_borrowed", "book_details", "author_lastname", "author_firstname", - }).AddRow("invalid_id", "Sample Book", 1, "book.jpg", false, "A sample book", "Doe", "John")) // Valoare invalidă pentru a provoca o eroare + mock.ExpectQuery(`SELECT (.+) FROM books`). + WithArgs("%Sample%", "%Sample%", "%Sample%"). + WillReturnRows(sqlmock.NewRows([]string{ + "book_id", "book_title", "author_id", "book_photo", "is_borrowed", "book_details", "author_lastname", "author_firstname", + }).AddRow("invalid_id", "Sample Book", 1, "book.jpg", false, "A sample book", "Doe", "John")) // Valoare invalidă pentru a provoca o eroare - handler := http.HandlerFunc(app.SearchBooks) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.SearchBooks) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") - assert.Contains(t, rr.Body.String(), "Error scanning books", "Expected error message for row scan error") + assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") + assert.Contains(t, rr.Body.String(), "Error scanning books", "Expected error message for row scan error") } func TestSearchBooks_Success(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() - req, err := http.NewRequest("GET", "/books?query=Sample", nil) - assert.NoError(t, err, "Error should be nil when creating a new request") - - rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{ - "book_id", "book_title", "author_id", "book_photo", "is_borrowed", "book_details", "author_lastname", "author_firstname", - }). - AddRow(1, "Sample Book", 1, "book.jpg", false, "A sample book", "Doe", "John"). - AddRow(2, "Another Book", 2, "another.jpg", true, "Another sample book", "Smith", "Jane") + app, mock := createTestApp(t) + defer app.DB.Close() + req, err := http.NewRequest("GET", "/books?query=Sample", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") - mock.ExpectQuery(`SELECT (.+) FROM books`). - WithArgs("%Sample%", "%Sample%", "%Sample%"). - WillReturnRows(rows) + rr := httptest.NewRecorder() + rows := sqlmock.NewRows([]string{ + "book_id", "book_title", "author_id", "book_photo", "is_borrowed", "book_details", "author_lastname", "author_firstname", + }). + AddRow(1, "Sample Book", 1, "book.jpg", false, "A sample book", "Doe", "John"). + AddRow(2, "Another Book", 2, "another.jpg", true, "Another sample book", "Smith", "Jane") + + mock.ExpectQuery(`SELECT (.+) FROM books`). + WithArgs("%Sample%", "%Sample%", "%Sample%"). + WillReturnRows(rows) - handler := http.HandlerFunc(app.SearchBooks) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.SearchBooks) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code, "Expected status code 200") + assert.Equal(t, http.StatusOK, rr.Code, "Expected status code 200") - var books []BookAuthorInfo - err = json.NewDecoder(rr.Body).Decode(&books) - assert.NoError(t, err, "Expected no error decoding JSON response") + var books []BookAuthorInfo + err = json.NewDecoder(rr.Body).Decode(&books) + assert.NoError(t, err, "Expected no error decoding JSON response") - assert.Equal(t, 2, len(books), "Expected 2 books") - assert.Equal(t, "Sample Book", books[0].BookTitle, "Expected BookTitle to be 'Sample Book'") - assert.Equal(t, "Doe", books[0].AuthorLastname, "Expected AuthorLastname to be 'Doe'") - assert.Equal(t, "John", books[0].AuthorFirstname, "Expected AuthorFirstname to be 'John'") + assert.Equal(t, 2, len(books), "Expected 2 books") + assert.Equal(t, "Sample Book", books[0].BookTitle, "Expected BookTitle to be 'Sample Book'") + assert.Equal(t, "Doe", books[0].AuthorLastname, "Expected AuthorLastname to be 'Doe'") + assert.Equal(t, "John", books[0].AuthorFirstname, "Expected AuthorFirstname to be 'John'") } func TestScanBooks_ErrorAfterIteration(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() - + app, mock := createTestApp(t) + defer app.DB.Close() - rows := sqlmock.NewRows([]string{ - "book_id", "book_title", "author_id", "book_photo", "is_borrowed", "book_details", "author_lastname", "author_firstname", - }). - AddRow(1, "Sample Book", 1, "book.jpg", false, "A sample book", "Doe", "John"). - RowError(0, fmt.Errorf("iteration error")) + rows := sqlmock.NewRows([]string{ + "book_id", "book_title", "author_id", "book_photo", "is_borrowed", "book_details", "author_lastname", "author_firstname", + }). + AddRow(1, "Sample Book", 1, "book.jpg", false, "A sample book", "Doe", "John"). + RowError(0, fmt.Errorf("iteration error")) - mock.ExpectQuery(`SELECT (.+) FROM books`).WillReturnRows(rows) + mock.ExpectQuery(`SELECT (.+) FROM books`).WillReturnRows(rows) - result, err := app.DB.Query("SELECT (.+) FROM books") - assert.NoError(t, err, "Expected no error when executing query") + result, err := app.DB.Query("SELECT (.+) FROM books") + assert.NoError(t, err, "Expected no error when executing query") - books, err := ScanBooks(result) + books, err := ScanBooks(result) - assert.Error(t, err, "Expected an error after iteration") - assert.Nil(t, books, "Books should be nil on error") + assert.Error(t, err, "Expected an error after iteration") + assert.Nil(t, books, "Books should be nil on error") - if err := mock.ExpectationsWereMet(); err != nil { - t.Errorf("Not all expectations were met: %v", err) - } + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("Not all expectations were met: %v", err) + } } // TestGetAuthors tests the GetAuthors handler with Dependency Injection @@ -696,7 +693,7 @@ func TestGetAuthors_ErrorScanningRows(t *testing.T) { rr := httptest.NewRecorder() rows := sqlmock.NewRows([]string{"id", "lastname", "firstname", "photo"}). - AddRow("invalid_id", "Doe", "John", "photo.jpg") + AddRow("invalid_id", "Doe", "John", "photo.jpg") mock.ExpectQuery(`SELECT id, Lastname, Firstname, photo FROM authors ORDER BY Lastname, Firstname`). WillReturnRows(rows) @@ -714,1720 +711,1717 @@ func TestGetAuthors_ErrorScanningRows(t *testing.T) { // TestGetAllBooks tests the GetAllBooks handler func TestGetAllBooks_Success(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/books", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/books", nil) + assert.NoError(t, err) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{ - "book_id", "book_title", "author_id", "book_photo", "is_borrowed", "book_details", "author_lastname", "author_firstname", - }). - AddRow(1, "Sample Book", 1, "book.jpg", false, "A sample book", "Doe", "John"). - AddRow(2, "Another Book", 2, "another.jpg", true, "Another sample book", "Smith", "Jane") + rows := sqlmock.NewRows([]string{ + "book_id", "book_title", "author_id", "book_photo", "is_borrowed", "book_details", "author_lastname", "author_firstname", + }). + AddRow(1, "Sample Book", 1, "book.jpg", false, "A sample book", "Doe", "John"). + AddRow(2, "Another Book", 2, "another.jpg", true, "Another sample book", "Smith", "Jane") - mock.ExpectQuery(`SELECT (.+) FROM books JOIN authors ON books.author_id = authors.id`). - WillReturnRows(rows) + mock.ExpectQuery(`SELECT (.+) FROM books JOIN authors ON books.author_id = authors.id`). + WillReturnRows(rows) - handler := http.HandlerFunc(app.GetAllBooks) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetAllBooks) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, http.StatusOK, rr.Code) - var books []BookAuthorInfo - err = json.NewDecoder(rr.Body).Decode(&books) - assert.NoError(t, err) - assert.Equal(t, 2, len(books)) - assert.Equal(t, "Sample Book", books[0].BookTitle) - assert.Equal(t, "Doe", books[0].AuthorLastname) - assert.Equal(t, "John", books[0].AuthorFirstname) + var books []BookAuthorInfo + err = json.NewDecoder(rr.Body).Decode(&books) + assert.NoError(t, err) + assert.Equal(t, 2, len(books)) + assert.Equal(t, "Sample Book", books[0].BookTitle) + assert.Equal(t, "Doe", books[0].AuthorLastname) + assert.Equal(t, "John", books[0].AuthorFirstname) } func TestGetAllBooks_ErrorQuery(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/books", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/books", nil) + assert.NoError(t, err) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectQuery(`SELECT (.+) FROM books JOIN authors ON books.author_id = authors.id`). - WillReturnError(fmt.Errorf("database query error")) + mock.ExpectQuery(`SELECT (.+) FROM books JOIN authors ON books.author_id = authors.id`). + WillReturnError(fmt.Errorf("database query error")) - handler := http.HandlerFunc(app.GetAllBooks) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetAllBooks) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Error executing query", "Expected 'Error executing query' in response") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Error executing query", "Expected 'Error executing query' in response") } func TestGetAllBooks_ErrorScan(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/books", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/books", nil) + assert.NoError(t, err) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{ - "book_id", "book_title", "author_id", "book_photo", "is_borrowed", "book_details", "author_lastname", "author_firstname", - }). - AddRow("invalid_id", "Sample Book", 1, "book.jpg", false, "A sample book", "Doe", "John") + rows := sqlmock.NewRows([]string{ + "book_id", "book_title", "author_id", "book_photo", "is_borrowed", "book_details", "author_lastname", "author_firstname", + }). + AddRow("invalid_id", "Sample Book", 1, "book.jpg", false, "A sample book", "Doe", "John") - mock.ExpectQuery(`SELECT (.+) FROM books JOIN authors ON books.author_id = authors.id`). - WillReturnRows(rows) + mock.ExpectQuery(`SELECT (.+) FROM books JOIN authors ON books.author_id = authors.id`). + WillReturnRows(rows) - handler := http.HandlerFunc(app.GetAllBooks) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetAllBooks) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Error scanning books") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Error scanning books") } // GetAuthorsAndBooks handler tests func TestGetAuthorsAndBooks_Success(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/authorsbooks", nil) - assert.NoError(t, err, "Error should be nil when creating a new request") + req, err := http.NewRequest("GET", "/authorsbooks", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{"author_firstname", "author_lastname", "book_title", "book_photo"}). - AddRow("John", "Doe", "Book 1", "book1.jpg"). - AddRow("Jane", "Smith", "Book 2", "book2.jpg") + rows := sqlmock.NewRows([]string{"author_firstname", "author_lastname", "book_title", "book_photo"}). + AddRow("John", "Doe", "Book 1", "book1.jpg"). + AddRow("Jane", "Smith", "Book 2", "book2.jpg") - mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id`). - WillReturnRows(rows) + mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id`). + WillReturnRows(rows) - handler := http.HandlerFunc(app.GetAuthorsAndBooks) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetAuthorsAndBooks) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code, "Expected status code 200") + assert.Equal(t, http.StatusOK, rr.Code, "Expected status code 200") - expected := []map[string]interface{}{ - {"author_firstname": "John", "author_lastname": "Doe", "book_title": "Book 1", "book_photo": "book1.jpg"}, - {"author_firstname": "Jane", "author_lastname": "Smith", "book_title": "Book 2", "book_photo": "book2.jpg"}, - } - var actual []map[string]interface{} - err = json.Unmarshal(rr.Body.Bytes(), &actual) - assert.NoError(t, err, "Expected no error while unmarshaling JSON response") - assert.Equal(t, expected, actual, "Expected JSON response") + expected := []map[string]interface{}{ + {"author_firstname": "John", "author_lastname": "Doe", "book_title": "Book 1", "book_photo": "book1.jpg"}, + {"author_firstname": "Jane", "author_lastname": "Smith", "book_title": "Book 2", "book_photo": "book2.jpg"}, + } + var actual []map[string]interface{} + err = json.Unmarshal(rr.Body.Bytes(), &actual) + assert.NoError(t, err, "Expected no error while unmarshaling JSON response") + assert.Equal(t, expected, actual, "Expected JSON response") } func TestGetAuthorsAndBooks_ErrorExecutingQuery(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/authorsbooks", nil) - assert.NoError(t, err, "Error should be nil when creating a new request") + req, err := http.NewRequest("GET", "/authorsbooks", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id`). - WillReturnError(fmt.Errorf("query execution error")) + mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id`). + WillReturnError(fmt.Errorf("query execution error")) - handler := http.HandlerFunc(app.GetAuthorsAndBooks) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetAuthorsAndBooks) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") - assert.Contains(t, rr.Body.String(), "Error executing query", "Expected error message for query execution error") + assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") + assert.Contains(t, rr.Body.String(), "Error executing query", "Expected error message for query execution error") } func TestGetAuthorsAndBooks_ErrorScanningRows(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/authorsbooks", nil) - assert.NoError(t, err, "Error should be nil when creating a new request") + req, err := http.NewRequest("GET", "/authorsbooks", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{"author_firstname", "author_lastname", "book_title", "book_photo"}). - AddRow("invalid_data", "Doe", "Book 1", "book1.jpg"). - RowError(0, fmt.Errorf("scan error")) + rows := sqlmock.NewRows([]string{"author_firstname", "author_lastname", "book_title", "book_photo"}). + AddRow("invalid_data", "Doe", "Book 1", "book1.jpg"). + RowError(0, fmt.Errorf("scan error")) - mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id`). - WillReturnRows(rows) + mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id`). + WillReturnRows(rows) - handler := http.HandlerFunc(app.GetAuthorsAndBooks) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetAuthorsAndBooks) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") - assert.Contains(t, rr.Body.String(), "Error scanning authors and books", "Expected error message for scan error") + assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") + assert.Contains(t, rr.Body.String(), "Error scanning authors and books", "Expected error message for scan error") } // GetAuthorsAndBooksByID tests func TestGetAuthorBooksByID_Success(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/authors/1", nil) - assert.NoError(t, err, "Error should be nil when creating a new request") - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req, err := http.NewRequest("GET", "/authors/1", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{"author_firstname", "author_lastname", "book_title", "book_photo"}). - AddRow("John", "Doe", "Book 1", "book1.jpg"). - AddRow("John", "Doe", "Book 2", "book2.jpg") + rows := sqlmock.NewRows([]string{"author_firstname", "author_lastname", "book_title", "book_photo"}). + AddRow("John", "Doe", "Book 1", "book1.jpg"). + AddRow("John", "Doe", "Book 2", "book2.jpg") - mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id WHERE a.id = ?`). - WithArgs(1).WillReturnRows(rows) + mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id WHERE a.id = ?`). + WithArgs(1).WillReturnRows(rows) - handler := http.HandlerFunc(app.GetAuthorBooksByID) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetAuthorBooksByID) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code, "Expected status code 200") + assert.Equal(t, http.StatusOK, rr.Code, "Expected status code 200") - expected := []map[string]interface{}{ - {"author_firstname": "John", "author_lastname": "Doe", "book_title": "Book 1", "book_photo": "book1.jpg"}, - {"author_firstname": "John", "author_lastname": "Doe", "book_title": "Book 2", "book_photo": "book2.jpg"}, - } - var actual []map[string]interface{} - err = json.Unmarshal(rr.Body.Bytes(), &actual) - assert.NoError(t, err, "Expected no error while unmarshaling JSON response") - assert.Equal(t, expected, actual, "Expected JSON response") + expected := []map[string]interface{}{ + {"author_firstname": "John", "author_lastname": "Doe", "book_title": "Book 1", "book_photo": "book1.jpg"}, + {"author_firstname": "John", "author_lastname": "Doe", "book_title": "Book 2", "book_photo": "book2.jpg"}, + } + var actual []map[string]interface{} + err = json.Unmarshal(rr.Body.Bytes(), &actual) + assert.NoError(t, err, "Expected no error while unmarshaling JSON response") + assert.Equal(t, expected, actual, "Expected JSON response") } func TestGetAuthorBooksByID_ErrorInvalidID(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/authors/abc", nil) - assert.NoError(t, err, "Error should be nil when creating a new request") - req = mux.SetURLVars(req, map[string]string{"id": "abc"}) + req, err := http.NewRequest("GET", "/authors/abc", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") + req = mux.SetURLVars(req, map[string]string{"id": "abc"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.GetAuthorBooksByID) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetAuthorBooksByID) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code, "Expected status code 400") - assert.Contains(t, rr.Body.String(), "Invalid author ID", "Expected 'Invalid author ID' in response") + assert.Equal(t, http.StatusBadRequest, rr.Code, "Expected status code 400") + assert.Contains(t, rr.Body.String(), "Invalid author ID", "Expected 'Invalid author ID' in response") } func TestGetAuthorBooksByID_ErrorExecutingQuery(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/authors/1", nil) - assert.NoError(t, err, "Error should be nil when creating a new request") - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req, err := http.NewRequest("GET", "/authors/1", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id WHERE a.id = ?`). - WithArgs(1).WillReturnError(fmt.Errorf("query execution error")) + mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id WHERE a.id = ?`). + WithArgs(1).WillReturnError(fmt.Errorf("query execution error")) - handler := http.HandlerFunc(app.GetAuthorBooksByID) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetAuthorBooksByID) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") - assert.Contains(t, rr.Body.String(), "Error executing query", "Expected error message for query execution error") + assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") + assert.Contains(t, rr.Body.String(), "Error executing query", "Expected error message for query execution error") } func TestGetAuthorBooksByID_ErrorScanningRows(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/authors/1", nil) - assert.NoError(t, err, "Error should be nil when creating a new request") - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req, err := http.NewRequest("GET", "/authors/1", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{"author_firstname", "author_lastname", "book_title", "book_photo"}). - AddRow("invalid_data", "Doe", "Book 1", "book1.jpg"). - RowError(0, fmt.Errorf("scan error")) + rows := sqlmock.NewRows([]string{"author_firstname", "author_lastname", "book_title", "book_photo"}). + AddRow("invalid_data", "Doe", "Book 1", "book1.jpg"). + RowError(0, fmt.Errorf("scan error")) - mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id WHERE a.id = ?`). - WithArgs(1).WillReturnRows(rows) + mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id WHERE a.id = ?`). + WithArgs(1).WillReturnRows(rows) - handler := http.HandlerFunc(app.GetAuthorBooksByID) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetAuthorBooksByID) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") - assert.Contains(t, rr.Body.String(), "Error scanning results", "Expected error message for scan error") + assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") + assert.Contains(t, rr.Body.String(), "Error scanning results", "Expected error message for scan error") } // TestGetBookByID tests the GetBookByID handler func TestGetBookByID_Success(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/books/1", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/books/1", nil) + assert.NoError(t, err) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{ - "book_title", "author_id", "book_photo", "is_borrowed", "book_id", "book_details", "author_lastname", "author_firstname", - }). - AddRow("Sample Book", 1, "book.jpg", false, 1, "A sample book", "Doe", "John") + rows := sqlmock.NewRows([]string{ + "book_title", "author_id", "book_photo", "is_borrowed", "book_id", "book_details", "author_lastname", "author_firstname", + }). + AddRow("Sample Book", 1, "book.jpg", false, 1, "A sample book", "Doe", "John") - mock.ExpectQuery(`SELECT (.+) FROM books JOIN authors ON books.author_id = authors.id WHERE books.id = ?`). - WithArgs(1). - WillReturnRows(rows) + mock.ExpectQuery(`SELECT (.+) FROM books JOIN authors ON books.author_id = authors.id WHERE books.id = ?`). + WithArgs(1). + WillReturnRows(rows) - handler := http.HandlerFunc(app.GetBookByID) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetBookByID) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, http.StatusOK, rr.Code) - var book BookAuthorInfo - err = json.NewDecoder(rr.Body).Decode(&book) - assert.NoError(t, err) - assert.Equal(t, "Sample Book", book.BookTitle) - assert.Equal(t, "Doe", book.AuthorLastname) - assert.Equal(t, "John", book.AuthorFirstname) + var book BookAuthorInfo + err = json.NewDecoder(rr.Body).Decode(&book) + assert.NoError(t, err) + assert.Equal(t, "Sample Book", book.BookTitle) + assert.Equal(t, "Doe", book.AuthorLastname) + assert.Equal(t, "John", book.AuthorFirstname) } func TestGetBookByID_InvalidID(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/books/abc", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/books/abc", nil) + assert.NoError(t, err) - req = mux.SetURLVars(req, map[string]string{"id": "abc"}) + req = mux.SetURLVars(req, map[string]string{"id": "abc"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.GetBookByID) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetBookByID) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.Contains(t, rr.Body.String(), "Invalid book ID") + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "Invalid book ID") } func TestGetBookByID_BookNotFound(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/books/1", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/books/1", nil) + assert.NoError(t, err) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{ - "book_title", "author_id", "book_photo", "is_borrowed", "book_id", "book_details", "author_lastname", "author_firstname", - }) + rows := sqlmock.NewRows([]string{ + "book_title", "author_id", "book_photo", "is_borrowed", "book_id", "book_details", "author_lastname", "author_firstname", + }) - mock.ExpectQuery(`SELECT (.+) FROM books JOIN authors ON books.author_id = authors.id WHERE books.id = ?`). - WithArgs(1). - WillReturnRows(rows) + mock.ExpectQuery(`SELECT (.+) FROM books JOIN authors ON books.author_id = authors.id WHERE books.id = ?`). + WithArgs(1). + WillReturnRows(rows) - handler := http.HandlerFunc(app.GetBookByID) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetBookByID) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusNotFound, rr.Code) - assert.Contains(t, rr.Body.String(), "Book not found") + assert.Equal(t, http.StatusNotFound, rr.Code) + assert.Contains(t, rr.Body.String(), "Book not found") } func TestGetBookByID_ErrorExecutingQuery(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/books/1", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/books/1", nil) + assert.NoError(t, err) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectQuery(`SELECT (.+) FROM books JOIN authors ON books.author_id = authors.id WHERE books.id = ?`). - WithArgs(1). - WillReturnError(fmt.Errorf("query execution error")) + mock.ExpectQuery(`SELECT (.+) FROM books JOIN authors ON books.author_id = authors.id WHERE books.id = ?`). + WithArgs(1). + WillReturnError(fmt.Errorf("query execution error")) - handler := http.HandlerFunc(app.GetBookByID) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetBookByID) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Error executing query") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Error executing query") } func TestGetBookByID_ErrorScanningRows(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/books/1", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/books/1", nil) + assert.NoError(t, err) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{ - "book_title", "author_id", "book_photo", "is_borrowed", "book_id", "book_details", "author_lastname", "author_firstname", - }). - AddRow("Sample Book", "invalid_author_id", "book.jpg", false, 1, "A sample book", "Doe", "John") + rows := sqlmock.NewRows([]string{ + "book_title", "author_id", "book_photo", "is_borrowed", "book_id", "book_details", "author_lastname", "author_firstname", + }). + AddRow("Sample Book", "invalid_author_id", "book.jpg", false, 1, "A sample book", "Doe", "John") - mock.ExpectQuery(`SELECT (.+) FROM books JOIN authors ON books.author_id = authors.id WHERE books.id = ?`). - WithArgs(1). - WillReturnRows(rows) + mock.ExpectQuery(`SELECT (.+) FROM books JOIN authors ON books.author_id = authors.id WHERE books.id = ?`). + WithArgs(1). + WillReturnRows(rows) - handler := http.HandlerFunc(app.GetBookByID) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetBookByID) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Error scanning book") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Error scanning book") } // TestGetSubscribersByBookID tests the GetSubscribersByBookID handler func TestGetSubscribersByBookID_Success(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/books/1", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/books/1", nil) + assert.NoError(t, err) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{"lastname", "firstname", "email"}). - AddRow("Doe", "John", "john.doe@example.com"). - AddRow("Smith", "Jane", "jane.smith@example.com") + rows := sqlmock.NewRows([]string{"lastname", "firstname", "email"}). + AddRow("Doe", "John", "john.doe@example.com"). + AddRow("Smith", "Jane", "jane.smith@example.com") - mock.ExpectQuery(`SELECT s.Lastname, s.Firstname, s.Email FROM subscribers s JOIN borrowed_books bb ON s.id = bb.subscriber_id WHERE bb.book_id = ?`). - WithArgs(1). - WillReturnRows(rows) + mock.ExpectQuery(`SELECT s.Lastname, s.Firstname, s.Email FROM subscribers s JOIN borrowed_books bb ON s.id = bb.subscriber_id WHERE bb.book_id = ?`). + WithArgs(1). + WillReturnRows(rows) - handler := http.HandlerFunc(app.GetSubscribersByBookID) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetSubscribersByBookID) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, http.StatusOK, rr.Code) - expected := []Subscriber{ - {Lastname: "Doe", Firstname: "John", Email: "john.doe@example.com"}, - {Lastname: "Smith", Firstname: "Jane", Email: "jane.smith@example.com"}, - } - var actual []Subscriber - err = json.NewDecoder(rr.Body).Decode(&actual) - assert.NoError(t, err) - assert.Equal(t, expected, actual) + expected := []Subscriber{ + {Lastname: "Doe", Firstname: "John", Email: "john.doe@example.com"}, + {Lastname: "Smith", Firstname: "Jane", Email: "jane.smith@example.com"}, + } + var actual []Subscriber + err = json.NewDecoder(rr.Body).Decode(&actual) + assert.NoError(t, err) + assert.Equal(t, expected, actual) } func TestGetSubscribersByBookID_InvalidID(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/books/invalid", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/books/invalid", nil) + assert.NoError(t, err) - req = mux.SetURLVars(req, map[string]string{"id": "invalid"}) + req = mux.SetURLVars(req, map[string]string{"id": "invalid"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.GetSubscribersByBookID) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetSubscribersByBookID) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.Contains(t, rr.Body.String(), "Invalid book ID") + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "Invalid book ID") } func TestGetSubscribersByBookID_NoSubscribers(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/books/1", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/books/1", nil) + assert.NoError(t, err) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{"lastname", "firstname", "email"}) + rows := sqlmock.NewRows([]string{"lastname", "firstname", "email"}) - mock.ExpectQuery(`SELECT s.Lastname, s.Firstname, s.Email FROM subscribers s JOIN borrowed_books bb ON s.id = bb.subscriber_id WHERE bb.book_id = ?`). - WithArgs(1). - WillReturnRows(rows) + mock.ExpectQuery(`SELECT s.Lastname, s.Firstname, s.Email FROM subscribers s JOIN borrowed_books bb ON s.id = bb.subscriber_id WHERE bb.book_id = ?`). + WithArgs(1). + WillReturnRows(rows) - handler := http.HandlerFunc(app.GetSubscribersByBookID) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetSubscribersByBookID) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusNotFound, rr.Code) - assert.Contains(t, rr.Body.String(), "No subscribers found") + assert.Equal(t, http.StatusNotFound, rr.Code) + assert.Contains(t, rr.Body.String(), "No subscribers found") } func TestGetSubscribersByBookID_QueryError(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/books/1", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/books/1", nil) + assert.NoError(t, err) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectQuery(`SELECT s.Lastname, s.Firstname, s.Email FROM subscribers s JOIN borrowed_books bb ON s.id = bb.subscriber_id WHERE bb.book_id = ?`). - WithArgs(1). - WillReturnError(fmt.Errorf("query error")) + mock.ExpectQuery(`SELECT s.Lastname, s.Firstname, s.Email FROM subscribers s JOIN borrowed_books bb ON s.id = bb.subscriber_id WHERE bb.book_id = ?`). + WithArgs(1). + WillReturnError(fmt.Errorf("query error")) - handler := http.HandlerFunc(app.GetSubscribersByBookID) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetSubscribersByBookID) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Error querying the database") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Error querying the database") } func TestGetSubscribersByBookID_ScanError(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/books/1", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/books/1", nil) + assert.NoError(t, err) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{"lastname", "firstname", "email"}). - AddRow("Doe", "John", nil) + rows := sqlmock.NewRows([]string{"lastname", "firstname", "email"}). + AddRow("Doe", "John", nil) - mock.ExpectQuery(`SELECT s.Lastname, s.Firstname, s.Email FROM subscribers s JOIN borrowed_books bb ON s.id = bb.subscriber_id WHERE bb.book_id = ?`). - WithArgs(1). - WillReturnRows(rows) + mock.ExpectQuery(`SELECT s.Lastname, s.Firstname, s.Email FROM subscribers s JOIN borrowed_books bb ON s.id = bb.subscriber_id WHERE bb.book_id = ?`). + WithArgs(1). + WillReturnRows(rows) - handler := http.HandlerFunc(app.GetSubscribersByBookID) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetSubscribersByBookID) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Error scanning subscribers") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Error scanning subscribers") } // TestGetAllSubscribers tests the GetAllSubscribers handler func TestGetAllSubscribers_Success(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/subscribers", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/subscribers", nil) + assert.NoError(t, err) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{"lastname", "firstname", "email"}). - AddRow("Doe", "John", "john.doe@example.com"). - AddRow("Smith", "Jane", "jane.smith@example.com") + rows := sqlmock.NewRows([]string{"lastname", "firstname", "email"}). + AddRow("Doe", "John", "john.doe@example.com"). + AddRow("Smith", "Jane", "jane.smith@example.com") - mock.ExpectQuery(`SELECT lastname, firstname, email FROM subscribers`).WillReturnRows(rows) + mock.ExpectQuery(`SELECT lastname, firstname, email FROM subscribers`).WillReturnRows(rows) - handler := http.HandlerFunc(app.GetAllSubscribers) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetAllSubscribers) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, http.StatusOK, rr.Code) - expected := []Subscriber{ - {Lastname: "Doe", Firstname: "John", Email: "john.doe@example.com"}, - {Lastname: "Smith", Firstname: "Jane", Email: "jane.smith@example.com"}, - } - var actual []Subscriber - err = json.NewDecoder(rr.Body).Decode(&actual) - assert.NoError(t, err) - assert.Equal(t, expected, actual) + expected := []Subscriber{ + {Lastname: "Doe", Firstname: "John", Email: "john.doe@example.com"}, + {Lastname: "Smith", Firstname: "Jane", Email: "jane.smith@example.com"}, + } + var actual []Subscriber + err = json.NewDecoder(rr.Body).Decode(&actual) + assert.NoError(t, err) + assert.Equal(t, expected, actual) } func TestGetAllSubscribers_QueryError(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/subscribers", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/subscribers", nil) + assert.NoError(t, err) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectQuery(`SELECT lastname, firstname, email FROM subscribers`). - WillReturnError(fmt.Errorf("query error")) + mock.ExpectQuery(`SELECT lastname, firstname, email FROM subscribers`). + WillReturnError(fmt.Errorf("query error")) - handler := http.HandlerFunc(app.GetAllSubscribers) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetAllSubscribers) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Error querying the database") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Error querying the database") } func TestGetAllSubscribers_ScanError(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/subscribers", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/subscribers", nil) + assert.NoError(t, err) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{"lastname", "firstname", "email"}). - AddRow("Doe", "John", nil) + rows := sqlmock.NewRows([]string{"lastname", "firstname", "email"}). + AddRow("Doe", "John", nil) - mock.ExpectQuery(`SELECT lastname, firstname, email FROM subscribers`).WillReturnRows(rows) + mock.ExpectQuery(`SELECT lastname, firstname, email FROM subscribers`).WillReturnRows(rows) - handler := http.HandlerFunc(app.GetAllSubscribers) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetAllSubscribers) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Error scanning subscribers") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Error scanning subscribers") } func TestGetAllSubscribers_NoSubscribers(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - req, err := http.NewRequest("GET", "/subscribers", nil) - assert.NoError(t, err) + req, err := http.NewRequest("GET", "/subscribers", nil) + assert.NoError(t, err) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - rows := sqlmock.NewRows([]string{"lastname", "firstname", "email"}) + rows := sqlmock.NewRows([]string{"lastname", "firstname", "email"}) - mock.ExpectQuery(`SELECT lastname, firstname, email FROM subscribers`).WillReturnRows(rows) + mock.ExpectQuery(`SELECT lastname, firstname, email FROM subscribers`).WillReturnRows(rows) - handler := http.HandlerFunc(app.GetAllSubscribers) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetAllSubscribers) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) - var actual []Subscriber - err = json.NewDecoder(rr.Body).Decode(&actual) - assert.NoError(t, err) - assert.Len(t, actual, 0) + assert.Equal(t, http.StatusOK, rr.Code) + var actual []Subscriber + err = json.NewDecoder(rr.Body).Decode(&actual) + assert.NoError(t, err) + assert.Len(t, actual, 0) } // Tests for AddAuthorPhoto handler func TestAddAuthorPhoto_Success(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("file", "test.jpg") - assert.NoError(t, err) + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile("file", "test.jpg") + assert.NoError(t, err) - _, err = part.Write([]byte("test data")) - assert.NoError(t, err) + _, err = part.Write([]byte("test data")) + assert.NoError(t, err) - writer.Close() + writer.Close() - req := httptest.NewRequest("POST", "/author/photo/1", body) - req.Header.Set("Content-Type", writer.FormDataContentType()) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req := httptest.NewRequest("POST", "/author/photo/1", body) + req.Header.Set("Content-Type", writer.FormDataContentType()) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectExec(`UPDATE authors SET photo = \? WHERE id = \?`). - WithArgs("./upload/1/fullsize.jpg", 1). - WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectExec(`UPDATE authors SET photo = \? WHERE id = \?`). + WithArgs("./upload/1/fullsize.jpg", 1). + WillReturnResult(sqlmock.NewResult(1, 1)) - handler := http.HandlerFunc(app.AddAuthorPhoto) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddAuthorPhoto) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) - assert.Contains(t, rr.Body.String(), "File uploaded successfully") + assert.Equal(t, http.StatusOK, rr.Code) + assert.Contains(t, rr.Body.String(), "File uploaded successfully") } func TestAddAuthorPhoto_InvalidAuthorID(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - req := httptest.NewRequest("POST", "/author/photo/abc", nil) - req = mux.SetURLVars(req, map[string]string{"id": "abc"}) + req := httptest.NewRequest("POST", "/author/photo/abc", nil) + req = mux.SetURLVars(req, map[string]string{"id": "abc"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddAuthorPhoto) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddAuthorPhoto) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.Contains(t, rr.Body.String(), "Invalid author ID") + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "Invalid author ID") } func TestAddAuthorPhoto_ErrorSavingFile(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("file", "test.jpg") - assert.NoError(t, err) - _, err = part.Write([]byte("test data")) - assert.NoError(t, err) - writer.Close() + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile("file", "test.jpg") + assert.NoError(t, err) + _, err = part.Write([]byte("test data")) + assert.NoError(t, err) + writer.Close() - req := httptest.NewRequest("POST", "/author/photo/1", body) - req.Header.Set("Content-Type", writer.FormDataContentType()) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req := httptest.NewRequest("POST", "/author/photo/1", body) + req.Header.Set("Content-Type", writer.FormDataContentType()) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - originalCopy := ioCopy - ioCopy = func(dst io.Writer, src io.Reader) (int64, error) { - return 0, fmt.Errorf("Error saving file") - } - defer func() { ioCopy = originalCopy }() + originalCopy := ioCopy + ioCopy = func(dst io.Writer, src io.Reader) (int64, error) { + return 0, fmt.Errorf("Error saving file") + } + defer func() { ioCopy = originalCopy }() - handler := http.HandlerFunc(app.AddAuthorPhoto) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddAuthorPhoto) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Error saving file") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Error saving file") } - func TestAddAuthorPhoto_InvalidMethod(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - req := httptest.NewRequest("GET", "/author/photo/1", nil) - rr := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/author/photo/1", nil) + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddAuthorPhoto) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddAuthorPhoto) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusMethodNotAllowed, rr.Code) - assert.Contains(t, rr.Body.String(), "Only POST method is supported") + assert.Equal(t, http.StatusMethodNotAllowed, rr.Code) + assert.Contains(t, rr.Body.String(), "Only POST method is supported") } func TestAddAuthorPhoto_ErrorGettingFile(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - writer.Close() + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + writer.Close() - req := httptest.NewRequest("POST", "/author/photo/1", body) - req.Header.Set("Content-Type", writer.FormDataContentType()) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req := httptest.NewRequest("POST", "/author/photo/1", body) + req.Header.Set("Content-Type", writer.FormDataContentType()) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddAuthorPhoto) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddAuthorPhoto) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Error getting file from request") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Error getting file from request") } func TestAddAuthorPhoto_ErrorCreatingDirectories(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("file", "test.jpg") - assert.NoError(t, err) - _, err = part.Write([]byte("test data")) - assert.NoError(t, err) - writer.Close() + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile("file", "test.jpg") + assert.NoError(t, err) + _, err = part.Write([]byte("test data")) + assert.NoError(t, err) + writer.Close() - req := httptest.NewRequest("POST", "/author/photo/1", body) - req.Header.Set("Content-Type", writer.FormDataContentType()) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req := httptest.NewRequest("POST", "/author/photo/1", body) + req.Header.Set("Content-Type", writer.FormDataContentType()) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - originalMkdirAll := mkdirAll - mkdirAll = func(path string, perm os.FileMode) error { - return fmt.Errorf("Error creating directories") - } - defer func() { mkdirAll = originalMkdirAll }() + originalMkdirAll := mkdirAll + mkdirAll = func(path string, perm os.FileMode) error { + return fmt.Errorf("Error creating directories") + } + defer func() { mkdirAll = originalMkdirAll }() - handler := http.HandlerFunc(app.AddAuthorPhoto) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddAuthorPhoto) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Error creating directories") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Error creating directories") } func TestAddAuthorPhoto_ErrorCreatingFile(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("file", "test.jpg") - assert.NoError(t, err) - _, err = part.Write([]byte("test data")) - assert.NoError(t, err) - writer.Close() + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile("file", "test.jpg") + assert.NoError(t, err) + _, err = part.Write([]byte("test data")) + assert.NoError(t, err) + writer.Close() - req := httptest.NewRequest("POST", "/author/photo/1", body) - req.Header.Set("Content-Type", writer.FormDataContentType()) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req := httptest.NewRequest("POST", "/author/photo/1", body) + req.Header.Set("Content-Type", writer.FormDataContentType()) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - originalCreate := osCreate - osCreate = func(name string) (*os.File, error) { - return nil, fmt.Errorf("Error creating file on disk") - } - defer func() { osCreate = originalCreate }() + originalCreate := osCreate + osCreate = func(name string) (*os.File, error) { + return nil, fmt.Errorf("Error creating file on disk") + } + defer func() { osCreate = originalCreate }() - handler := http.HandlerFunc(app.AddAuthorPhoto) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddAuthorPhoto) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Error creating file on disk") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Error creating file on disk") } func TestAddAuthorPhoto_ErrorUpdatingAuthorPhotoInDB(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("file", "test.jpg") - assert.NoError(t, err) - _, err = part.Write([]byte("test data")) - assert.NoError(t, err) - writer.Close() + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile("file", "test.jpg") + assert.NoError(t, err) + _, err = part.Write([]byte("test data")) + assert.NoError(t, err) + writer.Close() - req := httptest.NewRequest("POST", "/author/photo/1", body) - req.Header.Set("Content-Type", writer.FormDataContentType()) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req := httptest.NewRequest("POST", "/author/photo/1", body) + req.Header.Set("Content-Type", writer.FormDataContentType()) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectExec(`UPDATE authors SET photo = ? WHERE id = ?`). - WithArgs("./upload/1/fullsize.jpg", 1). - WillReturnError(fmt.Errorf("Failed to update author photo")) + mock.ExpectExec(`UPDATE authors SET photo = ? WHERE id = ?`). + WithArgs("./upload/1/fullsize.jpg", 1). + WillReturnError(fmt.Errorf("Failed to update author photo")) - handler := http.HandlerFunc(app.AddAuthorPhoto) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddAuthorPhoto) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Failed to update author photo") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Failed to update author photo") } // TestAddAuthor tests the AddAuthor handler func TestAddAuthor_Success(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - author := Author{Firstname: "John", Lastname: "Doe"} - body, err := json.Marshal(author) - assert.NoError(t, err) + author := Author{Firstname: "John", Lastname: "Doe"} + body, err := json.Marshal(author) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/authors/new", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/authors/new", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectExec(`INSERT INTO authors \(lastname, firstname, photo\) VALUES \(\?, \?, \?\)`). - WithArgs("Doe", "John", ""). - WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectExec(`INSERT INTO authors \(lastname, firstname, photo\) VALUES \(\?, \?, \?\)`). + WithArgs("Doe", "John", ""). + WillReturnResult(sqlmock.NewResult(1, 1)) - handler := http.HandlerFunc(app.AddAuthor) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddAuthor) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusCreated, rr.Code) + assert.Equal(t, http.StatusCreated, rr.Code) - var response map[string]int - err = json.NewDecoder(rr.Body).Decode(&response) - assert.NoError(t, err) + var response map[string]int + err = json.NewDecoder(rr.Body).Decode(&response) + assert.NoError(t, err) - assert.Equal(t, 1, response["id"], "Expected inserted author ID to be 1") + assert.Equal(t, 1, response["id"], "Expected inserted author ID to be 1") } func TestAddAuthor_InvalidJSON(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - req := httptest.NewRequest("POST", "/authors/new", bytes.NewBuffer([]byte("invalid json"))) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/authors/new", bytes.NewBuffer([]byte("invalid json"))) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddAuthor) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddAuthor) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.Contains(t, rr.Body.String(), "Invalid JSON data") + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "Invalid JSON data") } func TestAddAuthor_FailedToInsert(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - author := Author{Firstname: "John", Lastname: "Doe"} - body, err := json.Marshal(author) - assert.NoError(t, err) + author := Author{Firstname: "John", Lastname: "Doe"} + body, err := json.Marshal(author) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/authors/new", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/authors/new", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectExec(`INSERT INTO authors \(lastname, firstname, photo\) VALUES \(\?, \?, \?\)`). - WillReturnError(fmt.Errorf("failed to insert author")) + mock.ExpectExec(`INSERT INTO authors \(lastname, firstname, photo\) VALUES \(\?, \?, \?\)`). + WillReturnError(fmt.Errorf("failed to insert author")) - handler := http.HandlerFunc(app.AddAuthor) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddAuthor) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Failed to insert author") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Failed to insert author") } func TestAddAuthor_FailedToGetLastInsertID(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - author := Author{Firstname: "John", Lastname: "Doe"} - body, err := json.Marshal(author) - assert.NoError(t, err) + author := Author{Firstname: "John", Lastname: "Doe"} + body, err := json.Marshal(author) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/authors/new", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/authors/new", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectExec(`INSERT INTO authors \(lastname, firstname, photo\) VALUES \(\?, \?, \?\)`). - WithArgs("Doe", "John", ""). - WillReturnResult(sqlmock.NewResult(0, 1)) + mock.ExpectExec(`INSERT INTO authors \(lastname, firstname, photo\) VALUES \(\?, \?, \?\)`). + WithArgs("Doe", "John", ""). + WillReturnResult(sqlmock.NewResult(0, 1)) - handler := http.HandlerFunc(app.AddAuthor) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddAuthor) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Failed to get last insert ID") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Failed to get last insert ID") } func TestAddAuthor_MethodNotAllowed(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - req := httptest.NewRequest("GET", "/authors/new", nil) - rr := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/authors/new", nil) + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddAuthor) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddAuthor) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusMethodNotAllowed, rr.Code) - assert.Contains(t, rr.Body.String(), "Only POST method is supported") + assert.Equal(t, http.StatusMethodNotAllowed, rr.Code) + assert.Contains(t, rr.Body.String(), "Only POST method is supported") } type errorWriter struct{} func (e *errorWriter) Header() http.Header { - return http.Header{} + return http.Header{} } func (e *errorWriter) Write([]byte) (int, error) { - return 0, fmt.Errorf("error encoding json") + return 0, fmt.Errorf("error encoding json") } func (e *errorWriter) WriteHeader(statusCode int) {} func TestAddAuthor_FailedToEncodeJSON(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - author := Author{Firstname: "John", Lastname: "Doe"} - body, err := json.Marshal(author) - assert.NoError(t, err) + author := Author{Firstname: "John", Lastname: "Doe"} + body, err := json.Marshal(author) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/authors/new", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/authors/new", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := &errorWriter{} + rr := &errorWriter{} - mock.ExpectExec(`INSERT INTO authors \(lastname, firstname, photo\) VALUES \(\?, \?, \?\)`). - WithArgs("Doe", "John", ""). - WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectExec(`INSERT INTO authors \(lastname, firstname, photo\) VALUES \(\?, \?, \?\)`). + WithArgs("Doe", "John", ""). + WillReturnResult(sqlmock.NewResult(1, 1)) - handler := http.HandlerFunc(app.AddAuthor) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddAuthor) + handler.ServeHTTP(rr, req) } func TestAddAuthor_InvalidAuthorData(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - author := Author{Firstname: "", Lastname: ""} - body, err := json.Marshal(author) - assert.NoError(t, err) + author := Author{Firstname: "", Lastname: ""} + body, err := json.Marshal(author) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/authors/new", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/authors/new", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddAuthor) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddAuthor) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.Contains(t, rr.Body.String(), "firstname and lastname are required fields") + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "firstname and lastname are required fields") } // TestAddBook tests the AddBook handler func TestAddBookPhoto_Success(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("file", "test.jpg") - assert.NoError(t, err) + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile("file", "test.jpg") + assert.NoError(t, err) - _, err = part.Write([]byte("test data")) - assert.NoError(t, err) + _, err = part.Write([]byte("test data")) + assert.NoError(t, err) - writer.Close() + writer.Close() - req := httptest.NewRequest("POST", "/book/photo/1", body) - req.Header.Set("Content-Type", writer.FormDataContentType()) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req := httptest.NewRequest("POST", "/book/photo/1", body) + req.Header.Set("Content-Type", writer.FormDataContentType()) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectExec(`UPDATE books SET photo = \? WHERE id = \?`). - WithArgs("./upload/books/1/fullsize.jpg", 1). - WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectExec(`UPDATE books SET photo = \? WHERE id = \?`). + WithArgs("./upload/books/1/fullsize.jpg", 1). + WillReturnResult(sqlmock.NewResult(1, 1)) - handler := http.HandlerFunc(app.AddBookPhoto) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddBookPhoto) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) - assert.Contains(t, rr.Body.String(), "File uploaded successfully") + assert.Equal(t, http.StatusOK, rr.Code) + assert.Contains(t, rr.Body.String(), "File uploaded successfully") } func TestAddBookPhoto_InvalidBookID(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - req := httptest.NewRequest("POST", "/book/photo/abc", nil) - req = mux.SetURLVars(req, map[string]string{"id": "abc"}) + req := httptest.NewRequest("POST", "/book/photo/abc", nil) + req = mux.SetURLVars(req, map[string]string{"id": "abc"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddBookPhoto) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddBookPhoto) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.Contains(t, rr.Body.String(), "Invalid book ID") + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "Invalid book ID") } func TestAddBookPhoto_ErrorSavingFile(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - originalIoCopy := ioCopy - ioCopy = func(dst io.Writer, src io.Reader) (int64, error) { - return 0, fmt.Errorf("mocked error saving file") - } - defer func() { ioCopy = originalIoCopy }() + originalIoCopy := ioCopy + ioCopy = func(dst io.Writer, src io.Reader) (int64, error) { + return 0, fmt.Errorf("mocked error saving file") + } + defer func() { ioCopy = originalIoCopy }() - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("file", "test.jpg") - assert.NoError(t, err) - _, err = part.Write([]byte("test data")) - assert.NoError(t, err) - writer.Close() + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile("file", "test.jpg") + assert.NoError(t, err) + _, err = part.Write([]byte("test data")) + assert.NoError(t, err) + writer.Close() - req := httptest.NewRequest("POST", "/book/photo/1", body) - req.Header.Set("Content-Type", writer.FormDataContentType()) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req := httptest.NewRequest("POST", "/book/photo/1", body) + req.Header.Set("Content-Type", writer.FormDataContentType()) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddBookPhoto) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddBookPhoto) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Error saving file") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Error saving file") } func TestAddBookPhoto_InvalidMethod(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - req := httptest.NewRequest("GET", "/book/photo/1", nil) - rr := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/book/photo/1", nil) + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddBookPhoto) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddBookPhoto) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusMethodNotAllowed, rr.Code) - assert.Contains(t, rr.Body.String(), "Only POST method is supported") + assert.Equal(t, http.StatusMethodNotAllowed, rr.Code) + assert.Contains(t, rr.Body.String(), "Only POST method is supported") } func TestAddBookPhoto_ErrorGettingFile(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - writer.Close() + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + writer.Close() - req := httptest.NewRequest("POST", "/book/photo/1", body) - req.Header.Set("Content-Type", writer.FormDataContentType()) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req := httptest.NewRequest("POST", "/book/photo/1", body) + req.Header.Set("Content-Type", writer.FormDataContentType()) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddBookPhoto) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddBookPhoto) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Error getting file from request") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Error getting file from request") } func TestAddBookPhoto_ErrorCreatingDirectories(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - originalMkdirAll := osMkdirAll - osMkdirAll = func(path string, perm os.FileMode) error { - return fmt.Errorf("mocked error creating directories") - } - defer func() { osMkdirAll = originalMkdirAll }() + originalMkdirAll := osMkdirAll + osMkdirAll = func(path string, perm os.FileMode) error { + return fmt.Errorf("mocked error creating directories") + } + defer func() { osMkdirAll = originalMkdirAll }() - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("file", "test.jpg") - assert.NoError(t, err) - _, err = part.Write([]byte("test data")) - assert.NoError(t, err) - writer.Close() + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile("file", "test.jpg") + assert.NoError(t, err) + _, err = part.Write([]byte("test data")) + assert.NoError(t, err) + writer.Close() - req := httptest.NewRequest("POST", "/book/photo/1", body) - req.Header.Set("Content-Type", writer.FormDataContentType()) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req := httptest.NewRequest("POST", "/book/photo/1", body) + req.Header.Set("Content-Type", writer.FormDataContentType()) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddBookPhoto) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddBookPhoto) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Error creating directories") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Error creating directories") } func TestAddBookPhoto_ErrorCreatingFile(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - originalOsCreate := osCreate - osCreate = func(name string) (*os.File, error) { - return nil, fmt.Errorf("mocked error creating file") - } - defer func() { osCreate = originalOsCreate }() + originalOsCreate := osCreate + osCreate = func(name string) (*os.File, error) { + return nil, fmt.Errorf("mocked error creating file") + } + defer func() { osCreate = originalOsCreate }() - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("file", "test.jpg") - assert.NoError(t, err) - _, err = part.Write([]byte("test data")) - assert.NoError(t, err) - writer.Close() + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile("file", "test.jpg") + assert.NoError(t, err) + _, err = part.Write([]byte("test data")) + assert.NoError(t, err) + writer.Close() - req := httptest.NewRequest("POST", "/book/photo/1", body) - req.Header.Set("Content-Type", writer.FormDataContentType()) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req := httptest.NewRequest("POST", "/book/photo/1", body) + req.Header.Set("Content-Type", writer.FormDataContentType()) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddBookPhoto) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddBookPhoto) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Error creating file on disk") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Error creating file on disk") } func TestAddBookPhoto_ErrorUpdatingBookPhotoInDB(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("file", "test.jpg") - assert.NoError(t, err) - _, err = part.Write([]byte("test data")) - assert.NoError(t, err) - writer.Close() + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile("file", "test.jpg") + assert.NoError(t, err) + _, err = part.Write([]byte("test data")) + assert.NoError(t, err) + writer.Close() - req := httptest.NewRequest("POST", "/book/photo/1", body) - req.Header.Set("Content-Type", writer.FormDataContentType()) - req = mux.SetURLVars(req, map[string]string{"id": "1"}) + req := httptest.NewRequest("POST", "/book/photo/1", body) + req.Header.Set("Content-Type", writer.FormDataContentType()) + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectExec(`UPDATE books SET photo = ? WHERE id = ?`). - WithArgs("./upload/books/1/fullsize.jpg", 1). - WillReturnError(fmt.Errorf("Failed to update book photo")) + mock.ExpectExec(`UPDATE books SET photo = ? WHERE id = ?`). + WithArgs("./upload/books/1/fullsize.jpg", 1). + WillReturnError(fmt.Errorf("Failed to update book photo")) - handler := http.HandlerFunc(app.AddBookPhoto) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddBookPhoto) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Failed to update book photo") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Failed to update book photo") } // Tests for AddBook handler func TestAddBook_Success(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - book := Book{ - Title: "Test Book", - Details: "Test Details", - AuthorID: 1, - IsBorrowed: false, - } - body, err := json.Marshal(book) - assert.NoError(t, err) + book := Book{ + Title: "Test Book", + Details: "Test Details", + AuthorID: 1, + IsBorrowed: false, + } + body, err := json.Marshal(book) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/books/new", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/books/new", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - - mock.ExpectExec(`INSERT INTO books \(title, photo, details, author_id, is_borrowed\) VALUES \(\?, \?, \?, \?, \?\)`). - WithArgs("Test Book", "", "Test Details", 1, false). - WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectExec(`INSERT INTO books \(title, photo, details, author_id, is_borrowed\) VALUES \(\?, \?, \?, \?, \?\)`). + WithArgs("Test Book", "", "Test Details", 1, false). + WillReturnResult(sqlmock.NewResult(1, 1)) - handler := http.HandlerFunc(app.AddBook) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddBook) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusCreated, rr.Code) + assert.Equal(t, http.StatusCreated, rr.Code) - var response map[string]int - err = json.NewDecoder(rr.Body).Decode(&response) - assert.NoError(t, err) - assert.Equal(t, 1, response["id"]) + var response map[string]int + err = json.NewDecoder(rr.Body).Decode(&response) + assert.NoError(t, err) + assert.Equal(t, 1, response["id"]) } func TestAddBook_InvalidJSON(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - req := httptest.NewRequest("POST", "/books/new", bytes.NewBuffer([]byte("invalid json"))) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/books/new", bytes.NewBuffer([]byte("invalid json"))) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddBook) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddBook) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.Contains(t, rr.Body.String(), "Invalid JSON data") + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "Invalid JSON data") } func TestAddBook_InvalidBookData(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - book := Book{Title: "", AuthorID: 0} - body, err := json.Marshal(book) - assert.NoError(t, err) + book := Book{Title: "", AuthorID: 0} + body, err := json.Marshal(book) + assert.NoError(t, err) + + req := httptest.NewRequest("POST", "/books/new", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - req := httptest.NewRequest("POST", "/books/new", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + rr := httptest.NewRecorder() - rr := httptest.NewRecorder() + handler := http.HandlerFunc(app.AddBook) + handler.ServeHTTP(rr, req) - handler := http.HandlerFunc(app.AddBook) - handler.ServeHTTP(rr, req) + assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.Equal(t, http.StatusBadRequest, rr.Code) - - assert.Contains(t, rr.Body.String(), "title and authorID are required fields") + assert.Contains(t, rr.Body.String(), "title and authorID are required fields") } func TestAddBook_FailedToInsertBook(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - book := Book{ - Title: "Test Book", - Details: "Test Details", - AuthorID: 1, - IsBorrowed: false, - } - body, err := json.Marshal(book) - assert.NoError(t, err) + book := Book{ + Title: "Test Book", + Details: "Test Details", + AuthorID: 1, + IsBorrowed: false, + } + body, err := json.Marshal(book) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/books/new", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/books/new", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectExec(`INSERT INTO books \(title, photo, details, author_id, is_borrowed\) VALUES \(\?, \?, \?, \?, \?\)`). - WithArgs("Test Book", "", "Test Details", 1, false). - WillReturnError(fmt.Errorf("SQL error")) + mock.ExpectExec(`INSERT INTO books \(title, photo, details, author_id, is_borrowed\) VALUES \(\?, \?, \?, \?, \?\)`). + WithArgs("Test Book", "", "Test Details", 1, false). + WillReturnError(fmt.Errorf("SQL error")) - handler := http.HandlerFunc(app.AddBook) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddBook) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Failed to insert book") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Failed to insert book") } func TestAddBook_FailedToGetLastInsertID(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - book := Book{ - Title: "Test Book", - Details: "Test Details", - AuthorID: 1, - IsBorrowed: false, - } - body, err := json.Marshal(book) - assert.NoError(t, err) + book := Book{ + Title: "Test Book", + Details: "Test Details", + AuthorID: 1, + IsBorrowed: false, + } + body, err := json.Marshal(book) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/books/new", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/books/new", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectExec(`INSERT INTO books \(title, photo, details, author_id, is_borrowed\) VALUES \(\?, \?, \?, \?, \?\)`). - WithArgs("Test Book", "", "Test Details", 1, false). - WillReturnResult(sqlmock.NewResult(0, 1)) + mock.ExpectExec(`INSERT INTO books \(title, photo, details, author_id, is_borrowed\) VALUES \(\?, \?, \?, \?, \?\)`). + WithArgs("Test Book", "", "Test Details", 1, false). + WillReturnResult(sqlmock.NewResult(0, 1)) - handler := http.HandlerFunc(app.AddBook) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddBook) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Failed to get last insert ID") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Failed to get last insert ID") } - func TestAddBook_JSONEncodingError(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() - - book := Book{ - Title: "Test Book", - Details: "Test Details", - AuthorID: 1, - IsBorrowed: false, - } - body, err := json.Marshal(book) - assert.NoError(t, err) - - req := httptest.NewRequest("POST", "/books/new", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") - - rr := &errorWriter{} - - mock.ExpectExec(`INSERT INTO books \(title, photo, details, author_id, is_borrowed\) VALUES \(\?, \?, \?, \?, \?\)`). - WithArgs("Test Book", "", "Test Details", 1, false). - WillReturnResult(sqlmock.NewResult(1, 1)) + app, mock := createTestApp(t) + defer app.DB.Close() - handler := http.HandlerFunc(app.AddBook) - handler.ServeHTTP(rr, req) + book := Book{ + Title: "Test Book", + Details: "Test Details", + AuthorID: 1, + IsBorrowed: false, + } + body, err := json.Marshal(book) + assert.NoError(t, err) + + req := httptest.NewRequest("POST", "/books/new", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + rr := &errorWriter{} - app.Logger.Printf("JSON encoding error") + mock.ExpectExec(`INSERT INTO books \(title, photo, details, author_id, is_borrowed\) VALUES \(\?, \?, \?, \?, \?\)`). + WithArgs("Test Book", "", "Test Details", 1, false). + WillReturnResult(sqlmock.NewResult(1, 1)) + + handler := http.HandlerFunc(app.AddBook) + handler.ServeHTTP(rr, req) + + app.Logger.Printf("JSON encoding error") } func TestAddBook_InvalidMethod(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - req := httptest.NewRequest("GET", "/books/new", nil) - rr := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/books/new", nil) + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddBook) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddBook) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusMethodNotAllowed, rr.Code) - assert.Contains(t, rr.Body.String(), "Only POST method is supported") + assert.Equal(t, http.StatusMethodNotAllowed, rr.Code) + assert.Contains(t, rr.Body.String(), "Only POST method is supported") } // TestAddSubscriber tests the AddSubscriber handler func TestAddSubscriber_Success(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - subscriber := Subscriber{Firstname: "John", Lastname: "Doe", Email: "john.doe@example.com"} - body, err := json.Marshal(subscriber) - assert.NoError(t, err) + subscriber := Subscriber{Firstname: "John", Lastname: "Doe", Email: "john.doe@example.com"} + body, err := json.Marshal(subscriber) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/subscribers/new", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/subscribers/new", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectExec(`INSERT INTO subscribers \(lastname, firstname, email\) VALUES \(\?, \?, \?\)`). - WithArgs("Doe", "John", "john.doe@example.com"). - WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectExec(`INSERT INTO subscribers \(lastname, firstname, email\) VALUES \(\?, \?, \?\)`). + WithArgs("Doe", "John", "john.doe@example.com"). + WillReturnResult(sqlmock.NewResult(1, 1)) - handler := http.HandlerFunc(app.AddSubscriber) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddSubscriber) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusCreated, rr.Code) + assert.Equal(t, http.StatusCreated, rr.Code) - var response map[string]int - err = json.NewDecoder(rr.Body).Decode(&response) - assert.NoError(t, err) - assert.Equal(t, 1, response["id"], "Expected inserted subscriber ID to be 1") + var response map[string]int + err = json.NewDecoder(rr.Body).Decode(&response) + assert.NoError(t, err) + assert.Equal(t, 1, response["id"], "Expected inserted subscriber ID to be 1") } func TestAddSubscriber_InvalidJSON(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - req := httptest.NewRequest("POST", "/subscribers/new", bytes.NewBuffer([]byte("invalid json"))) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/subscribers/new", bytes.NewBuffer([]byte("invalid json"))) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddSubscriber) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddSubscriber) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.Contains(t, rr.Body.String(), "Invalid JSON data") + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "Invalid JSON data") } func TestAddSubscriber_MissingFields(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - subscriber := Subscriber{Firstname: "", Lastname: "", Email: ""} - body, err := json.Marshal(subscriber) - assert.NoError(t, err) + subscriber := Subscriber{Firstname: "", Lastname: "", Email: ""} + body, err := json.Marshal(subscriber) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/subscribers/new", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/subscribers/new", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddSubscriber) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddSubscriber) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.Contains(t, rr.Body.String(), "Firstname, Lastname, and Email are required fields") + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "Firstname, Lastname, and Email are required fields") } func TestAddSubscriber_FailedToInsert(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - subscriber := Subscriber{Firstname: "John", Lastname: "Doe", Email: "john.doe@example.com"} - body, err := json.Marshal(subscriber) - assert.NoError(t, err) + subscriber := Subscriber{Firstname: "John", Lastname: "Doe", Email: "john.doe@example.com"} + body, err := json.Marshal(subscriber) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/subscribers/new", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/subscribers/new", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectExec(`INSERT INTO subscribers \(lastname, firstname, email\) VALUES \(\?, \?, \?\)`). - WillReturnError(fmt.Errorf("failed to insert subscriber")) + mock.ExpectExec(`INSERT INTO subscribers \(lastname, firstname, email\) VALUES \(\?, \?, \?\)`). + WillReturnError(fmt.Errorf("failed to insert subscriber")) - handler := http.HandlerFunc(app.AddSubscriber) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddSubscriber) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Failed to insert subscriber") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Failed to insert subscriber") } func TestAddSubscriber_FailedToGetLastInsertID(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - subscriber := Subscriber{Firstname: "John", Lastname: "Doe", Email: "john.doe@example.com"} - body, err := json.Marshal(subscriber) - assert.NoError(t, err) + subscriber := Subscriber{Firstname: "John", Lastname: "Doe", Email: "john.doe@example.com"} + body, err := json.Marshal(subscriber) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/subscribers/new", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/subscribers/new", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectExec(`INSERT INTO subscribers \(lastname, firstname, email\) VALUES \(\?, \?, \?\)`). - WillReturnResult(sqlmock.NewResult(0, 1)) + mock.ExpectExec(`INSERT INTO subscribers \(lastname, firstname, email\) VALUES \(\?, \?, \?\)`). + WillReturnResult(sqlmock.NewResult(0, 1)) - handler := http.HandlerFunc(app.AddSubscriber) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddSubscriber) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Failed to get last insert ID") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Failed to get last insert ID") } func TestAddSubscriber_InvalidMethod(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - req := httptest.NewRequest("GET", "/subscribers/new", nil) - rr := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/subscribers/new", nil) + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddSubscriber) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.AddSubscriber) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusMethodNotAllowed, rr.Code) - assert.Contains(t, rr.Body.String(), "Only POST method is supported") + assert.Equal(t, http.StatusMethodNotAllowed, rr.Code) + assert.Contains(t, rr.Body.String(), "Only POST method is supported") } // TestBorrowBook_Success tests the BorrowBook handler when borrowing is successful func TestBorrowBook_Success(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - requestBody := map[string]int{ - "subscriber_id": 1, - "book_id": 1, - } - body, err := json.Marshal(requestBody) - assert.NoError(t, err) + requestBody := map[string]int{ + "subscriber_id": 1, + "book_id": 1, + } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/borrow", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/borrow", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectQuery("SELECT is_borrowed FROM books WHERE id = ?"). - WithArgs(1). - WillReturnRows(sqlmock.NewRows([]string{"is_borrowed"}).AddRow(false)) + mock.ExpectQuery("SELECT is_borrowed FROM books WHERE id = ?"). + WithArgs(1). + WillReturnRows(sqlmock.NewRows([]string{"is_borrowed"}).AddRow(false)) - mock.ExpectExec("INSERT INTO borrowed_books"). - WithArgs(1, 1). - WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectExec("INSERT INTO borrowed_books"). + WithArgs(1, 1). + WillReturnResult(sqlmock.NewResult(1, 1)) - mock.ExpectExec("UPDATE books SET is_borrowed = TRUE WHERE id = ?"). - WithArgs(1). - WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectExec("UPDATE books SET is_borrowed = TRUE WHERE id = ?"). + WithArgs(1). + WillReturnResult(sqlmock.NewResult(1, 1)) - handler := http.HandlerFunc(app.BorrowBook) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.BorrowBook) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusCreated, rr.Code) - assert.Contains(t, rr.Body.String(), "Book borrowed successfully") + assert.Equal(t, http.StatusCreated, rr.Code) + assert.Contains(t, rr.Body.String(), "Book borrowed successfully") - err = mock.ExpectationsWereMet() - assert.NoError(t, err) + err = mock.ExpectationsWereMet() + assert.NoError(t, err) } func TestBorrowBook_BookAlreadyBorrowed(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - requestBody := map[string]int{ - "subscriber_id": 1, - "book_id": 1, - } - body, err := json.Marshal(requestBody) - assert.NoError(t, err) + requestBody := map[string]int{ + "subscriber_id": 1, + "book_id": 1, + } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/borrow", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/borrow", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectQuery("SELECT is_borrowed FROM books WHERE id = ?"). - WithArgs(1). - WillReturnRows(sqlmock.NewRows([]string{"is_borrowed"}).AddRow(true)) + mock.ExpectQuery("SELECT is_borrowed FROM books WHERE id = ?"). + WithArgs(1). + WillReturnRows(sqlmock.NewRows([]string{"is_borrowed"}).AddRow(true)) - handler := http.HandlerFunc(app.BorrowBook) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.BorrowBook) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusConflict, rr.Code) - assert.Contains(t, rr.Body.String(), "Book is already borrowed") + assert.Equal(t, http.StatusConflict, rr.Code) + assert.Contains(t, rr.Body.String(), "Book is already borrowed") - err = mock.ExpectationsWereMet() - assert.NoError(t, err) + err = mock.ExpectationsWereMet() + assert.NoError(t, err) } func TestBorrowBook_InvalidRequestBody(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - req := httptest.NewRequest("POST", "/borrow", bytes.NewBuffer([]byte("invalid body"))) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/borrow", bytes.NewBuffer([]byte("invalid body"))) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.BorrowBook) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.BorrowBook) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.Contains(t, rr.Body.String(), "Invalid request body") + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "Invalid request body") } func TestBorrowBook_MissingFields(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - requestBody := map[string]int{ - "subscriber_id": 0, - "book_id": 0, - } - body, err := json.Marshal(requestBody) - assert.NoError(t, err) + requestBody := map[string]int{ + "subscriber_id": 0, + "book_id": 0, + } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/borrow", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/borrow", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.BorrowBook) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.BorrowBook) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.Contains(t, rr.Body.String(), "Missing required fields") + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "Missing required fields") } func TestBorrowBook_DBError(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - requestBody := map[string]int{ - "subscriber_id": 1, - "book_id": 1, - } - body, err := json.Marshal(requestBody) - assert.NoError(t, err) + requestBody := map[string]int{ + "subscriber_id": 1, + "book_id": 1, + } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/borrow", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/borrow", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectQuery("SELECT is_borrowed FROM books WHERE id = ?"). - WithArgs(1). - WillReturnError(fmt.Errorf("DB error")) + mock.ExpectQuery("SELECT is_borrowed FROM books WHERE id = ?"). + WithArgs(1). + WillReturnError(fmt.Errorf("DB error")) - handler := http.HandlerFunc(app.BorrowBook) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.BorrowBook) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Database error") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Database error") - err = mock.ExpectationsWereMet() - assert.NoError(t, err) + err = mock.ExpectationsWereMet() + assert.NoError(t, err) } func TestBorrowBook_MethodNotAllowed(t *testing.T) { - app, _ := createTestApp(t) - defer app.DB.Close() + app, _ := createTestApp(t) + defer app.DB.Close() - req := httptest.NewRequest("GET", "/borrow", nil) - rr := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/borrow", nil) + rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.BorrowBook) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.BorrowBook) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusMethodNotAllowed, rr.Code) - assert.Contains(t, rr.Body.String(), "Method not allowed") + assert.Equal(t, http.StatusMethodNotAllowed, rr.Code) + assert.Contains(t, rr.Body.String(), "Method not allowed") } func TestBorrowBook_DBInsertError(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - requestBody := map[string]int{ - "subscriber_id": 1, - "book_id": 1, - } - body, err := json.Marshal(requestBody) - assert.NoError(t, err) + requestBody := map[string]int{ + "subscriber_id": 1, + "book_id": 1, + } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/borrow", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/borrow", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectQuery("SELECT is_borrowed FROM books WHERE id = ?"). - WithArgs(1). - WillReturnRows(sqlmock.NewRows([]string{"is_borrowed"}).AddRow(false)) + mock.ExpectQuery("SELECT is_borrowed FROM books WHERE id = ?"). + WithArgs(1). + WillReturnRows(sqlmock.NewRows([]string{"is_borrowed"}).AddRow(false)) - mock.ExpectExec("INSERT INTO borrowed_books"). - WithArgs(1, 1). - WillReturnError(fmt.Errorf("insert error")) + mock.ExpectExec("INSERT INTO borrowed_books"). + WithArgs(1, 1). + WillReturnError(fmt.Errorf("insert error")) - handler := http.HandlerFunc(app.BorrowBook) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.BorrowBook) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Database error") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Database error") - err = mock.ExpectationsWereMet() - assert.NoError(t, err) + err = mock.ExpectationsWereMet() + assert.NoError(t, err) } func TestBorrowBook_DBUpdateError(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - requestBody := map[string]int{ - "subscriber_id": 1, - "book_id": 1, - } - body, err := json.Marshal(requestBody) - assert.NoError(t, err) + requestBody := map[string]int{ + "subscriber_id": 1, + "book_id": 1, + } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/borrow", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/borrow", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectQuery("SELECT is_borrowed FROM books WHERE id = ?"). - WithArgs(1). - WillReturnRows(sqlmock.NewRows([]string{"is_borrowed"}).AddRow(false)) + mock.ExpectQuery("SELECT is_borrowed FROM books WHERE id = ?"). + WithArgs(1). + WillReturnRows(sqlmock.NewRows([]string{"is_borrowed"}).AddRow(false)) - mock.ExpectExec("INSERT INTO borrowed_books"). - WithArgs(1, 1). - WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectExec("INSERT INTO borrowed_books"). + WithArgs(1, 1). + WillReturnResult(sqlmock.NewResult(1, 1)) - mock.ExpectExec("UPDATE books SET is_borrowed = TRUE WHERE id = ?"). - WithArgs(1). - WillReturnError(fmt.Errorf("update error")) + mock.ExpectExec("UPDATE books SET is_borrowed = TRUE WHERE id = ?"). + WithArgs(1). + WillReturnError(fmt.Errorf("update error")) - handler := http.HandlerFunc(app.BorrowBook) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.BorrowBook) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusInternalServerError, rr.Code) - assert.Contains(t, rr.Body.String(), "Database error") + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Database error") - err = mock.ExpectationsWereMet() - assert.NoError(t, err) + err = mock.ExpectationsWereMet() + assert.NoError(t, err) } func TestBorrowBook_ContentTypeHeader(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + app, mock := createTestApp(t) + defer app.DB.Close() - requestBody := map[string]int{ - "subscriber_id": 1, - "book_id": 1, - } - body, err := json.Marshal(requestBody) - assert.NoError(t, err) + requestBody := map[string]int{ + "subscriber_id": 1, + "book_id": 1, + } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) - req := httptest.NewRequest("POST", "/borrow", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") + req := httptest.NewRequest("POST", "/borrow", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - rr := httptest.NewRecorder() + rr := httptest.NewRecorder() - mock.ExpectQuery("SELECT is_borrowed FROM books WHERE id = ?"). - WithArgs(1). - WillReturnRows(sqlmock.NewRows([]string{"is_borrowed"}).AddRow(false)) + mock.ExpectQuery("SELECT is_borrowed FROM books WHERE id = ?"). + WithArgs(1). + WillReturnRows(sqlmock.NewRows([]string{"is_borrowed"}).AddRow(false)) - mock.ExpectExec("INSERT INTO borrowed_books"). - WithArgs(1, 1). - WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectExec("INSERT INTO borrowed_books"). + WithArgs(1, 1). + WillReturnResult(sqlmock.NewResult(1, 1)) - mock.ExpectExec("UPDATE books SET is_borrowed = TRUE WHERE id = ?"). - WithArgs(1). - WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectExec("UPDATE books SET is_borrowed = TRUE WHERE id = ?"). + WithArgs(1). + WillReturnResult(sqlmock.NewResult(1, 1)) - handler := http.HandlerFunc(app.BorrowBook) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.BorrowBook) + handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusCreated, rr.Code) - assert.Equal(t, "application/json", rr.Header().Get("Content-Type")) + assert.Equal(t, http.StatusCreated, rr.Code) + assert.Equal(t, "application/json", rr.Header().Get("Content-Type")) } // Tests for ReturnBorrowedBook handler @@ -2728,61 +2722,513 @@ func TestUpdateAuthor(t *testing.T) { } } -// TestUpdateBook tests the UpdateBook handler -func TestUpdateBook(t *testing.T) { +// Tests for UpdateBook handler +func TestUpdateBook_Success(t *testing.T) { app, mock := createTestApp(t) defer app.DB.Close() - bookID := "1" + requestBody := Book{ + Title: "Updated Book Title", + AuthorID: 1, + Photo: "updated_photo.jpg", + Details: "Updated details", + IsBorrowed: false, + } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) + + req := httptest.NewRequest("PUT", "/books/1", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + vars := map[string]string{ + "id": "1", + } + req = mux.SetURLVars(req, vars) + + rr := httptest.NewRecorder() - // Set up SQL mock expectations for updating the book - mock.ExpectExec("^UPDATE books SET title = \\?, author_id = \\?, photo = \\?, details = \\?, is_borrowed = \\? WHERE id = \\?$"). - WithArgs("New Title", 1, "newphoto.jpg", "Some details", false, 1). + mock.ExpectExec(regexp.QuoteMeta(` + UPDATE books + SET title = ?, author_id = ?, photo = ?, details = ?, is_borrowed = ? + WHERE id = ?`)). + WithArgs("Updated Book Title", 1, "updated_photo.jpg", "Updated details", false, 1). WillReturnResult(sqlmock.NewResult(1, 1)) - // Create a new HTTP request with JSON body - book := struct { - Title string `json:"title"` - AuthorID int `json:"author_id"` - Photo string `json:"photo"` - Details string `json:"details"` - IsBorrowed bool `json:"is_borrowed"` - }{ - Title: "New Title", + handler := http.HandlerFunc(app.UpdateBook) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusOK, rr.Code) + assert.Contains(t, rr.Body.String(), "Book updated successfully") + + err = mock.ExpectationsWereMet() + assert.NoError(t, err) +} + +func TestUpdateBook_InvalidJSON(t *testing.T) { + app, _ := createTestApp(t) + defer app.DB.Close() + + req := httptest.NewRequest("PUT", "/books/1", bytes.NewBuffer([]byte("invalid json body"))) + req.Header.Set("Content-Type", "application/json") + + vars := map[string]string{ + "id": "1", + } + req = mux.SetURLVars(req, vars) + + rr := httptest.NewRecorder() + + handler := http.HandlerFunc(app.UpdateBook) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "Invalid JSON data") +} + +func TestUpdateBook_InvalidBookID(t *testing.T) { + app, _ := createTestApp(t) + defer app.DB.Close() + + requestBody := Book{ + Title: "Updated Book Title", AuthorID: 1, - Photo: "newphoto.jpg", - Details: "Some details", + Photo: "updated_photo.jpg", + Details: "Updated details", IsBorrowed: false, } - body, err := json.Marshal(book) - if err != nil { - t.Fatalf("Could not marshal book: %v", err) + body, err := json.Marshal(requestBody) + assert.NoError(t, err) + + req := httptest.NewRequest("PUT", "/books/invalid", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + rr := httptest.NewRecorder() + + handler := http.HandlerFunc(app.UpdateBook) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "Invalid book ID") +} + +func TestUpdateBook_ValidationError(t *testing.T) { + app, _ := createTestApp(t) + defer app.DB.Close() + + requestBody := Book{ + Title: "", + AuthorID: 0, + Photo: "", + Details: "Invalid details", + IsBorrowed: false, } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) - req, err := http.NewRequest("PUT", "/books/1", bytes.NewBuffer(body)) - if err != nil { - t.Fatalf("Could not create request: %v", err) + req := httptest.NewRequest("PUT", "/books/1", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + vars := map[string]string{ + "id": "1", } + req = mux.SetURLVars(req, vars) + + rr := httptest.NewRecorder() + + handler := http.HandlerFunc(app.UpdateBook) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "title and authorID are required fields") +} + +func TestUpdateBook_BookNotFound(t *testing.T) { + app, mock := createTestApp(t) + defer app.DB.Close() + + requestBody := Book{ + Title: "Updated Book Title", + AuthorID: 1, + Photo: "updated_photo.jpg", + Details: "Updated details", + IsBorrowed: false, + } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) + + req := httptest.NewRequest("PUT", "/books/1", bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") - req = mux.SetURLVars(req, map[string]string{"id": bookID}) - // Capture the response + vars := map[string]string{ + "id": "1", + } + req = mux.SetURLVars(req, vars) + rr := httptest.NewRecorder() + + mock.ExpectExec(regexp.QuoteMeta(` + UPDATE books + SET title = ?, author_id = ?, photo = ?, details = ?, is_borrowed = ? + WHERE id = ?`)). + WithArgs("Updated Book Title", 1, "updated_photo.jpg", "Updated details", false, 1). + WillReturnResult(sqlmock.NewResult(1, 0)) + handler := http.HandlerFunc(app.UpdateBook) handler.ServeHTTP(rr, req) - // Ensure the response status is 200 OK + assert.Equal(t, http.StatusNotFound, rr.Code) + assert.Contains(t, rr.Body.String(), "Book not found") + + err = mock.ExpectationsWereMet() + assert.NoError(t, err) +} + +func TestUpdateBook_DBError(t *testing.T) { + app, mock := createTestApp(t) + defer app.DB.Close() + + requestBody := Book{ + Title: "Updated Book Title", + AuthorID: 1, + Photo: "updated_photo.jpg", + Details: "Updated details", + IsBorrowed: false, + } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) + + req := httptest.NewRequest("PUT", "/books/1", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + vars := map[string]string{ + "id": "1", + } + req = mux.SetURLVars(req, vars) + + rr := httptest.NewRecorder() + + mock.ExpectExec(regexp.QuoteMeta(` + UPDATE books + SET title = ?, author_id = ?, photo = ?, details = ?, is_borrowed = ? + WHERE id = ?`)). + WithArgs("Updated Book Title", 1, "updated_photo.jpg", "Updated details", false, 1). + WillReturnError(fmt.Errorf("DB error")) + + handler := http.HandlerFunc(app.UpdateBook) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Failed to update book") + + err = mock.ExpectationsWereMet() + assert.NoError(t, err) +} + +func TestUpdateBook_MethodNotAllowed(t *testing.T) { + app, _ := createTestApp(t) + defer app.DB.Close() + + req := httptest.NewRequest("GET", "/books/1", nil) + rr := httptest.NewRecorder() + + vars := map[string]string{ + "id": "1", + } + req = mux.SetURLVars(req, vars) + + handler := http.HandlerFunc(app.UpdateBook) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusMethodNotAllowed, rr.Code) + assert.Contains(t, rr.Body.String(), "Only PUT or POST methods are supported") +} + +func TestUpdateBook_FailedToRetrieveAffectedRows(t *testing.T) { + app, mock := createTestApp(t) + defer app.DB.Close() + + requestBody := Book{ + Title: "Updated Book Title", + AuthorID: 1, + Photo: "updated_photo.jpg", + Details: "Updated details", + IsBorrowed: false, + } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) + + req := httptest.NewRequest("PUT", "/books/1", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + vars := map[string]string{ + "id": "1", + } + req = mux.SetURLVars(req, vars) + + rr := httptest.NewRecorder() + + mock.ExpectExec(regexp.QuoteMeta(` + UPDATE books + SET title = ?, author_id = ?, photo = ?, details = ?, is_borrowed = ? + WHERE id = ?`)). + WithArgs("Updated Book Title", 1, "updated_photo.jpg", "Updated details", false, 1). + WillReturnResult(sqlmock.NewErrorResult(fmt.Errorf("RowsAffected error"))) + + handler := http.HandlerFunc(app.UpdateBook) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Failed to retrieve affected rows") + + err = mock.ExpectationsWereMet() + assert.NoError(t, err) +} + +// Tests for UpdateSubscriber handler +func TestUpdateSubscriber_Success(t *testing.T) { + app, mock := createTestApp(t) + defer app.DB.Close() + + requestBody := Subscriber{ + Firstname: "Updated Firstname", + Lastname: "Updated Lastname", + Email: "updated.email@example.com", + } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) + + req := httptest.NewRequest("PUT", "/subscribers/1", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + vars := map[string]string{ + "id": "1", + } + req = mux.SetURLVars(req, vars) + + rr := httptest.NewRecorder() + + mock.ExpectExec(regexp.QuoteMeta(` + UPDATE subscribers + SET lastname = ?, firstname = ?, email = ? + WHERE id = ?`)). + WithArgs("Updated Lastname", "Updated Firstname", "updated.email@example.com", 1). + WillReturnResult(sqlmock.NewResult(1, 1)) + + handler := http.HandlerFunc(app.UpdateSubscriber) + handler.ServeHTTP(rr, req) + assert.Equal(t, http.StatusOK, rr.Code) + assert.Contains(t, rr.Body.String(), "Subscriber updated successfully") - // Check the response message - expected := "Book updated successfully" - assert.Equal(t, expected, rr.Body.String()) + err = mock.ExpectationsWereMet() + assert.NoError(t, err) +} - // Ensure all mock expectations were met - if err := mock.ExpectationsWereMet(); err != nil { - t.Errorf("Not all expectations were met: %v", err) +func TestUpdateSubscriber_InvalidJSON(t *testing.T) { + app, _ := createTestApp(t) + defer app.DB.Close() + + req := httptest.NewRequest("PUT", "/subscribers/1", bytes.NewBuffer([]byte("invalid json body"))) + req.Header.Set("Content-Type", "application/json") + + vars := map[string]string{ + "id": "1", + } + req = mux.SetURLVars(req, vars) + + rr := httptest.NewRecorder() + + handler := http.HandlerFunc(app.UpdateSubscriber) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "Invalid JSON data") +} + +func TestUpdateSubscriber_InvalidSubscriberID(t *testing.T) { + app, _ := createTestApp(t) + defer app.DB.Close() + + requestBody := Subscriber{ + Firstname: "Updated Firstname", + Lastname: "Updated Lastname", + Email: "updated.email@example.com", + } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) + + req := httptest.NewRequest("PUT", "/subscribers/invalid", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + rr := httptest.NewRecorder() + + handler := http.HandlerFunc(app.UpdateSubscriber) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "Invalid subscriber ID") +} + +func TestUpdateSubscriber_ValidationError(t *testing.T) { + app, _ := createTestApp(t) + defer app.DB.Close() + + requestBody := Subscriber{ + Firstname: "", + Lastname: "", + Email: "", + } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) + + req := httptest.NewRequest("PUT", "/subscribers/1", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + vars := map[string]string{ + "id": "1", + } + req = mux.SetURLVars(req, vars) + + rr := httptest.NewRecorder() + + handler := http.HandlerFunc(app.UpdateSubscriber) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Contains(t, rr.Body.String(), "firstname, lastname, and email are required fields") +} + +func TestUpdateSubscriber_SubscriberNotFound(t *testing.T) { + app, mock := createTestApp(t) + defer app.DB.Close() + + requestBody := Subscriber{ + Firstname: "Updated Firstname", + Lastname: "Updated Lastname", + Email: "updated.email@example.com", + } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) + + req := httptest.NewRequest("PUT", "/subscribers/1", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + vars := map[string]string{ + "id": "1", + } + req = mux.SetURLVars(req, vars) + + rr := httptest.NewRecorder() + + mock.ExpectExec(regexp.QuoteMeta(` + UPDATE subscribers + SET lastname = ?, firstname = ?, email = ? + WHERE id = ?`)). + WithArgs("Updated Lastname", "Updated Firstname", "updated.email@example.com", 1). + WillReturnResult(sqlmock.NewResult(1, 0)) + handler := http.HandlerFunc(app.UpdateSubscriber) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusNotFound, rr.Code) + assert.Contains(t, rr.Body.String(), "Subscriber not found") + + err = mock.ExpectationsWereMet() + assert.NoError(t, err) +} + +func TestUpdateSubscriber_DBError(t *testing.T) { + app, mock := createTestApp(t) + defer app.DB.Close() + + requestBody := Subscriber{ + Firstname: "Updated Firstname", + Lastname: "Updated Lastname", + Email: "updated.email@example.com", } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) + + req := httptest.NewRequest("PUT", "/subscribers/1", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + vars := map[string]string{ + "id": "1", + } + req = mux.SetURLVars(req, vars) + + rr := httptest.NewRecorder() + + mock.ExpectExec(regexp.QuoteMeta(` + UPDATE subscribers + SET lastname = ?, firstname = ?, email = ? + WHERE id = ?`)). + WithArgs("Updated Lastname", "Updated Firstname", "updated.email@example.com", 1). + WillReturnError(fmt.Errorf("DB error")) + + handler := http.HandlerFunc(app.UpdateSubscriber) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Failed to update subscriber") + + err = mock.ExpectationsWereMet() + assert.NoError(t, err) } +func TestUpdateSubscriber_MethodNotAllowed(t *testing.T) { + app, _ := createTestApp(t) + defer app.DB.Close() + + req := httptest.NewRequest("GET", "/subscribers/1", nil) + rr := httptest.NewRecorder() + + vars := map[string]string{ + "id": "1", + } + req = mux.SetURLVars(req, vars) + + handler := http.HandlerFunc(app.UpdateSubscriber) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusMethodNotAllowed, rr.Code) + assert.Contains(t, rr.Body.String(), "Only PUT or POST methods are supported") +} + +func TestUpdateSubscriber_FailedToRetrieveAffectedRows(t *testing.T) { + app, mock := createTestApp(t) + defer app.DB.Close() + + requestBody := Subscriber{ + Firstname: "John", + Lastname: "Doe", + Email: "johndoe@example.com", + } + body, err := json.Marshal(requestBody) + assert.NoError(t, err) + + req := httptest.NewRequest("PUT", "/subscribers/1", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + vars := map[string]string{ + "id": "1", + } + req = mux.SetURLVars(req, vars) + + rr := httptest.NewRecorder() + mock.ExpectExec(regexp.QuoteMeta(` + UPDATE subscribers + SET lastname = ?, firstname = ?, email = ? + WHERE id = ?`)). + WithArgs("Doe", "John", "johndoe@example.com", 1). + WillReturnResult(sqlmock.NewErrorResult(fmt.Errorf("RowsAffected error"))) + handler := http.HandlerFunc(app.UpdateSubscriber) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusInternalServerError, rr.Code) + assert.Contains(t, rr.Body.String(), "Failed to retrieve affected rows") + + err = mock.ExpectationsWereMet() + assert.NoError(t, err) +}