diff --git a/api/coverage.out b/api/coverage.out index 64dba21..02823ca 100644 --- a/api/coverage.out +++ b/api/coverage.out @@ -181,135 +181,134 @@ mymodule/main.go:792.48,795.6 2 1 mymodule/main.go:797.5,798.68 2 1 mymodule/main.go:798.68,801.6 2 1 mymodule/main.go:803.5,804.66 2 1 -mymodule/main.go:809.65,812.36 2 1 -mymodule/main.go:812.36,815.6 2 1 -mymodule/main.go:817.5,818.65 2 1 -mymodule/main.go:818.65,821.6 2 1 -mymodule/main.go:822.5,824.50 2 1 -mymodule/main.go:824.50,827.6 2 1 -mymodule/main.go:829.5,831.19 3 1 -mymodule/main.go:831.19,834.6 2 1 -mymodule/main.go:836.5,837.30 2 1 -mymodule/main.go:837.30,840.6 2 1 -mymodule/main.go:842.5,845.63 4 1 -mymodule/main.go:845.63,848.6 2 1 -mymodule/main.go:855.71,856.33 1 1 -mymodule/main.go:856.33,859.3 2 0 -mymodule/main.go:862.2,864.16 3 1 -mymodule/main.go:864.16,867.3 2 0 -mymodule/main.go:868.2,871.87 2 1 -mymodule/main.go:871.87,874.3 2 0 -mymodule/main.go:877.2,881.16 3 1 -mymodule/main.go:881.16,885.3 3 0 -mymodule/main.go:888.2,889.16 2 1 -mymodule/main.go:889.16,893.3 3 0 -mymodule/main.go:895.2,899.60 4 1 -mymodule/main.go:899.60,902.3 2 0 -mymodule/main.go:906.68,907.33 1 1 -mymodule/main.go:907.33,910.3 2 0 -mymodule/main.go:913.2,918.16 3 1 -mymodule/main.go:918.16,921.3 2 0 -mymodule/main.go:923.2,923.62 1 1 -mymodule/main.go:923.62,926.3 2 0 -mymodule/main.go:929.2,931.16 3 1 -mymodule/main.go:931.16,935.3 3 0 -mymodule/main.go:936.2,936.16 1 1 -mymodule/main.go:936.16,939.3 2 0 -mymodule/main.go:942.2,943.16 2 1 -mymodule/main.go:943.16,947.3 3 0 -mymodule/main.go:950.2,951.16 2 1 -mymodule/main.go:951.16,955.3 3 0 -mymodule/main.go:957.2,959.61 3 1 -mymodule/main.go:963.76,964.33 1 1 -mymodule/main.go:964.33,967.3 2 0 -mymodule/main.go:970.2,975.16 3 1 -mymodule/main.go:975.16,978.3 2 0 -mymodule/main.go:981.2,983.16 3 1 -mymodule/main.go:983.16,984.27 1 0 -mymodule/main.go:984.27,986.4 1 0 -mymodule/main.go:986.9,989.4 2 0 -mymodule/main.go:990.3,990.9 1 0 -mymodule/main.go:993.2,993.17 1 1 -mymodule/main.go:993.17,996.3 2 0 -mymodule/main.go:999.2,1000.16 2 1 -mymodule/main.go:1000.16,1004.3 3 0 -mymodule/main.go:1007.2,1008.16 2 1 -mymodule/main.go:1008.16,1012.3 3 0 -mymodule/main.go:1014.2,1015.46 2 1 -mymodule/main.go:1019.70,1020.63 1 1 -mymodule/main.go:1020.63,1023.3 2 0 -mymodule/main.go:1025.2,1027.16 3 1 -mymodule/main.go:1027.16,1030.3 2 0 -mymodule/main.go:1032.2,1034.16 3 1 -mymodule/main.go:1034.16,1037.3 2 0 -mymodule/main.go:1038.2,1040.53 2 1 -mymodule/main.go:1040.53,1043.3 2 0 -mymodule/main.go:1045.2,1052.16 3 1 -mymodule/main.go:1052.16,1056.3 3 0 -mymodule/main.go:1058.2,1059.23 2 1 -mymodule/main.go:1059.23,1062.3 2 0 -mymodule/main.go:1064.2,1064.47 1 1 -mymodule/main.go:1068.68,1069.63 1 1 -mymodule/main.go:1069.63,1072.3 2 0 -mymodule/main.go:1075.2,1077.16 3 1 -mymodule/main.go:1077.16,1080.3 2 0 -mymodule/main.go:1083.2,1091.16 3 1 -mymodule/main.go:1091.16,1094.3 2 0 -mymodule/main.go:1095.2,1098.44 2 1 -mymodule/main.go:1098.44,1101.3 2 0 -mymodule/main.go:1104.2,1112.16 3 1 -mymodule/main.go:1112.16,1116.3 3 0 -mymodule/main.go:1119.2,1120.23 2 1 -mymodule/main.go:1120.23,1123.3 2 0 -mymodule/main.go:1125.2,1125.45 1 1 -mymodule/main.go:1129.74,1130.63 1 0 -mymodule/main.go:1130.63,1133.3 2 0 -mymodule/main.go:1136.2,1138.16 3 0 -mymodule/main.go:1138.16,1141.3 2 0 -mymodule/main.go:1144.2,1146.16 3 0 -mymodule/main.go:1146.16,1149.3 2 0 -mymodule/main.go:1150.2,1157.87 4 0 -mymodule/main.go:1157.87,1160.3 2 0 -mymodule/main.go:1163.2,1171.16 3 0 -mymodule/main.go:1171.16,1175.3 3 0 -mymodule/main.go:1178.2,1179.23 2 0 -mymodule/main.go:1179.23,1182.3 2 0 -mymodule/main.go:1184.2,1184.51 1 0 -mymodule/main.go:1188.70,1189.35 1 0 -mymodule/main.go:1189.35,1192.3 2 0 -mymodule/main.go:1195.2,1197.16 3 0 -mymodule/main.go:1197.16,1200.3 2 0 -mymodule/main.go:1203.2,1212.16 4 0 -mymodule/main.go:1212.16,1216.3 3 0 -mymodule/main.go:1219.2,1219.18 1 0 -mymodule/main.go:1219.18,1222.3 2 0 -mymodule/main.go:1225.2,1232.16 3 0 -mymodule/main.go:1232.16,1236.3 3 0 -mymodule/main.go:1239.2,1240.23 2 0 -mymodule/main.go:1240.23,1243.3 2 0 -mymodule/main.go:1245.2,1245.47 1 0 -mymodule/main.go:1249.68,1250.35 1 0 -mymodule/main.go:1250.35,1253.3 2 0 -mymodule/main.go:1256.2,1258.16 3 0 -mymodule/main.go:1258.16,1261.3 2 0 +mymodule/main.go:808.65,811.36 2 1 +mymodule/main.go:811.36,814.6 2 1 +mymodule/main.go:816.5,817.65 2 1 +mymodule/main.go:817.65,820.6 2 1 +mymodule/main.go:821.5,823.50 2 1 +mymodule/main.go:823.50,826.6 2 1 +mymodule/main.go:828.5,830.19 3 1 +mymodule/main.go:830.19,833.6 2 1 +mymodule/main.go:835.5,836.30 2 1 +mymodule/main.go:836.30,839.6 2 1 +mymodule/main.go:841.5,844.63 4 1 +mymodule/main.go:844.63,847.6 2 1 +mymodule/main.go:850.71,851.33 1 1 +mymodule/main.go:851.33,854.3 2 1 +mymodule/main.go:856.2,857.68 2 1 +mymodule/main.go:857.68,860.3 2 1 +mymodule/main.go:861.2,863.87 2 1 +mymodule/main.go:863.87,866.3 2 1 +mymodule/main.go:868.2,870.16 3 1 +mymodule/main.go:870.16,874.3 3 1 +mymodule/main.go:876.2,877.27 2 1 +mymodule/main.go:877.27,881.3 3 1 +mymodule/main.go:883.2,885.58 3 1 +mymodule/main.go:890.68,891.33 1 1 +mymodule/main.go:891.33,894.3 2 0 +mymodule/main.go:897.2,902.16 3 1 +mymodule/main.go:902.16,905.3 2 0 +mymodule/main.go:907.2,907.62 1 1 +mymodule/main.go:907.62,910.3 2 0 +mymodule/main.go:913.2,915.16 3 1 +mymodule/main.go:915.16,919.3 3 0 +mymodule/main.go:920.2,920.16 1 1 +mymodule/main.go:920.16,923.3 2 0 +mymodule/main.go:926.2,927.16 2 1 +mymodule/main.go:927.16,931.3 3 0 +mymodule/main.go:934.2,935.16 2 1 +mymodule/main.go:935.16,939.3 3 0 +mymodule/main.go:941.2,943.61 3 1 +mymodule/main.go:947.76,948.33 1 1 +mymodule/main.go:948.33,951.3 2 0 +mymodule/main.go:954.2,959.16 3 1 +mymodule/main.go:959.16,962.3 2 0 +mymodule/main.go:965.2,967.16 3 1 +mymodule/main.go:967.16,968.27 1 0 +mymodule/main.go:968.27,970.4 1 0 +mymodule/main.go:970.9,973.4 2 0 +mymodule/main.go:974.3,974.9 1 0 +mymodule/main.go:977.2,977.17 1 1 +mymodule/main.go:977.17,980.3 2 0 +mymodule/main.go:983.2,984.16 2 1 +mymodule/main.go:984.16,988.3 3 0 +mymodule/main.go:991.2,992.16 2 1 +mymodule/main.go:992.16,996.3 3 0 +mymodule/main.go:998.2,999.46 2 1 +mymodule/main.go:1003.70,1004.63 1 1 +mymodule/main.go:1004.63,1007.3 2 0 +mymodule/main.go:1009.2,1011.16 3 1 +mymodule/main.go:1011.16,1014.3 2 0 +mymodule/main.go:1016.2,1018.16 3 1 +mymodule/main.go:1018.16,1021.3 2 0 +mymodule/main.go:1022.2,1024.53 2 1 +mymodule/main.go:1024.53,1027.3 2 0 +mymodule/main.go:1029.2,1036.16 3 1 +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,1289.16 4 0 -mymodule/main.go:1289.16,1293.3 3 0 -mymodule/main.go:1296.2,1303.16 3 0 -mymodule/main.go:1303.16,1307.3 3 0 -mymodule/main.go:1310.2,1311.23 2 0 -mymodule/main.go:1311.23,1314.3 2 0 -mymodule/main.go:1317.2,1317.24 1 0 -mymodule/main.go:1317.24,1325.17 3 0 -mymodule/main.go:1325.17,1329.4 3 0 -mymodule/main.go:1332.2,1332.45 1 0 -mymodule/main.go:1336.74,1337.35 1 0 -mymodule/main.go:1337.35,1340.3 2 0 -mymodule/main.go:1343.2,1345.16 3 0 -mymodule/main.go:1345.16,1348.3 2 0 -mymodule/main.go:1351.2,1358.16 3 0 -mymodule/main.go:1358.16,1362.3 3 0 -mymodule/main.go:1365.2,1366.23 2 0 -mymodule/main.go:1366.23,1369.3 2 0 -mymodule/main.go:1371.2,1371.51 1 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 diff --git a/api/main.go b/api/main.go index 760304d..9dac908 100644 --- a/api/main.go +++ b/api/main.go @@ -846,7 +846,6 @@ func (app *App) AddBook(w http.ResponseWriter, r *http.Request) { http.Error(w, "Error encoding response", http.StatusInternalServerError) } } - // AddSubscriber adds a new subscriber to the database func (app *App) AddSubscriber(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { @@ -875,7 +874,7 @@ func (app *App) AddSubscriber(w http.ResponseWriter, r *http.Request) { } id, err := result.LastInsertId() - if err != nil { + if err != nil || id == 0 { app.Logger.Printf("Failed to get last insert ID: %v", err) http.Error(w, "Failed to get last insert ID", http.StatusInternalServerError) return diff --git a/api/main_test.go b/api/main_test.go index dc578d5..f1fca17 100644 --- a/api/main_test.go +++ b/api/main_test.go @@ -2046,52 +2046,132 @@ func TestAddBook_InvalidMethod(t *testing.T) { } // TestAddSubscriber tests the AddSubscriber handler -func TestAddSubscriber(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() +func TestAddSubscriber_Success(t *testing.T) { + app, mock := createTestApp(t) + defer app.DB.Close() - // Set up the SQL mock expectations - mock.ExpectExec("INSERT INTO subscribers"). - WithArgs("Doe", "John", "john.doe@example.com"). - WillReturnResult(sqlmock.NewResult(1, 1)) + subscriber := Subscriber{Firstname: "John", Lastname: "Doe", Email: "john.doe@example.com"} + body, err := json.Marshal(subscriber) + assert.NoError(t, err) - // Create a new HTTP request with JSON body - subscriber := Subscriber{Firstname: "John", Lastname: "Doe", Email: "john.doe@example.com"} - body, err := json.Marshal(subscriber) - if err != nil { - t.Fatalf("Could not marshal subscriber: %v", err) - } + req := httptest.NewRequest("POST", "/subscribers/new", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") - req, err := http.NewRequest("POST", "/subscribers/new", bytes.NewBuffer(body)) - if err != nil { - t.Fatalf("Could not create request: %v", err) - } - req.Header.Set("Content-Type", "application/json") + rr := httptest.NewRecorder() - // Capture the response - rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.AddSubscriber) - handler.ServeHTTP(rr, req) + mock.ExpectExec(`INSERT INTO subscribers \(lastname, firstname, email\) VALUES \(\?, \?, \?\)`). + WithArgs("Doe", "John", "john.doe@example.com"). + WillReturnResult(sqlmock.NewResult(1, 1)) - // Ensure the response status is 201 Created - assert.Equal(t, http.StatusCreated, rr.Code) + handler := http.HandlerFunc(app.AddSubscriber) + handler.ServeHTTP(rr, req) - // Check the JSON response - var response map[string]int - err = json.NewDecoder(rr.Body).Decode(&response) - if err != nil { - t.Fatalf("Could not decode response: %v", err) - } + assert.Equal(t, http.StatusCreated, rr.Code) - // Verify the response data - 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"], "Expected inserted subscriber ID to be 1") +} - // Ensure all mock expectations were met - if err := mock.ExpectationsWereMet(); err != nil { - t.Errorf("Not all expectations were met: %v", err) - } +func TestAddSubscriber_InvalidJSON(t *testing.T) { + 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") + + rr := httptest.NewRecorder() + + 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") +} + +func TestAddSubscriber_MissingFields(t *testing.T) { + app, _ := createTestApp(t) + defer app.DB.Close() + + 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") + + rr := httptest.NewRecorder() + + 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") +} + +func TestAddSubscriber_FailedToInsert(t *testing.T) { + 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) + + req := httptest.NewRequest("POST", "/subscribers/new", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + + rr := httptest.NewRecorder() + + 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) + + 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() + + 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") + + rr := httptest.NewRecorder() + + mock.ExpectExec(`INSERT INTO subscribers \(lastname, firstname, email\) VALUES \(\?, \?, \?\)`). + WillReturnResult(sqlmock.NewResult(0, 1)) + + 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") +} + +func TestAddSubscriber_InvalidMethod(t *testing.T) { + app, _ := createTestApp(t) + defer app.DB.Close() + + req := httptest.NewRequest("GET", "/subscribers/new", nil) + rr := httptest.NewRecorder() + + 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") +} + + + // TestBorrowBook tests the BorrowBook handler func TestBorrowBook(t *testing.T) { app, mock := createTestApp(t)