refactor(backend): update test files for PoolProxy and per-test transactions
Migrate all test files from SetupTestDB/db.DB pattern to per-test transactions: - Replace SetupTestDB(t) with SetupTestTx(t) for context + transaction - Replace db.DB.Query/QueryRow/Exec with tx.Query/QueryRow/Exec - Replace context.Background() with context from SetupTestTx - Replace defer rows.Close() pattern with explicit rows.Close() - Add testdb.SeedBaseline(pool) to all TestMain functions - Wire db.Conn = db.NewPoolProxy(pool) in all TestMain functions Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -25,19 +25,18 @@ import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"crussell/db"
|
||||
"crussell/testutils"
|
||||
"crussell/mw"
|
||||
"crussell/testutils/fixtures"
|
||||
"crussell/testutils/jwt"
|
||||
"crussell/testutils/testdb"
|
||||
)
|
||||
|
||||
// TestProfile_Get verifies that an authenticated user can retrieve their own profile data.
|
||||
func TestProfile_Get(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
@@ -45,7 +44,7 @@ func TestProfile_Get(t *testing.T) {
|
||||
token := jwt.GenerateUserToken(userID)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/user/profile", nil)
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -68,7 +67,7 @@ func TestProfile_Get(t *testing.T) {
|
||||
|
||||
// TestProfile_Get_NoAuth verifies that an unauthenticated request to get profile returns 401 Unauthorized.
|
||||
func TestProfile_Get_NoAuth(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/user/profile", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -81,9 +80,10 @@ func TestProfile_Get_NoAuth(t *testing.T) {
|
||||
|
||||
// TestProfile_Update verifies that a user can update their profile with valid first name, last name, and phone.
|
||||
func TestProfile_Update(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
@@ -98,7 +98,7 @@ func TestProfile_Update(t *testing.T) {
|
||||
body, _ := json.Marshal(updateReq)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPut, "/api/user/profile", bytes.NewReader(body))
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
@@ -113,9 +113,10 @@ func TestProfile_Update(t *testing.T) {
|
||||
|
||||
// TestPasswordChange_Success verifies that a user can successfully change their password with valid credentials.
|
||||
func TestPasswordChange_Success(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
@@ -129,7 +130,7 @@ func TestPasswordChange_Success(t *testing.T) {
|
||||
body, _ := json.Marshal(changeReq)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPut, "/api/user/change-password", bytes.NewReader(body))
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
@@ -144,9 +145,10 @@ func TestPasswordChange_Success(t *testing.T) {
|
||||
|
||||
// TestPasswordChange_WrongOld verifies that providing an incorrect current password returns 401 Unauthorized.
|
||||
func TestPasswordChange_WrongOld(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
@@ -160,7 +162,7 @@ func TestPasswordChange_WrongOld(t *testing.T) {
|
||||
body, _ := json.Marshal(changeReq)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPut, "/api/user/change-password", bytes.NewReader(body))
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
@@ -175,9 +177,10 @@ func TestPasswordChange_WrongOld(t *testing.T) {
|
||||
// TestPasswordChange_InvalidNewPassword verifies that invalid new passwords (too short or too long for bcrypt)
|
||||
// are rejected with 400 Bad Request.
|
||||
func TestPasswordChange_InvalidNewPassword(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
@@ -201,7 +204,7 @@ func TestPasswordChange_InvalidNewPassword(t *testing.T) {
|
||||
body, _ := json.Marshal(changeReq)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPut, "/api/user/change-password", bytes.NewReader(body))
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
@@ -219,9 +222,10 @@ func TestPasswordChange_InvalidNewPassword(t *testing.T) {
|
||||
// TestAccount_Delete verifies that a registered user can delete their own account,
|
||||
// triggering anonymization and returning 204 No Content.
|
||||
func TestAccount_Delete(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
@@ -229,7 +233,7 @@ func TestAccount_Delete(t *testing.T) {
|
||||
token := jwt.GenerateUserToken(userID)
|
||||
|
||||
req := httptest.NewRequest(http.MethodDelete, "/api/user/account", nil)
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -241,7 +245,7 @@ func TestAccount_Delete(t *testing.T) {
|
||||
}
|
||||
|
||||
var firstName, accountRole string
|
||||
err = db.DB.QueryRow(context.Background(), `SELECT n_first_name, account_role FROM users WHERE id = $1`, userID).Scan(&firstName, &accountRole)
|
||||
err = tx.QueryRow(ctx, `SELECT n_first_name, account_role FROM users WHERE id = $1`, userID).Scan(&firstName, &accountRole)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query anonymized user: %v", err)
|
||||
}
|
||||
@@ -255,9 +259,10 @@ func TestAccount_Delete(t *testing.T) {
|
||||
|
||||
// TestAccount_DeleteGuest verifies that a guest user is fully deleted.
|
||||
func TestAccount_DeleteGuest(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestGuestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestGuestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test guest user: %v", err)
|
||||
}
|
||||
@@ -265,7 +270,7 @@ func TestAccount_DeleteGuest(t *testing.T) {
|
||||
token := jwt.GenerateUserToken(userID)
|
||||
|
||||
req := httptest.NewRequest(http.MethodDelete, "/api/user/account", nil)
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -277,7 +282,7 @@ func TestAccount_DeleteGuest(t *testing.T) {
|
||||
}
|
||||
|
||||
var count int
|
||||
err = db.DB.QueryRow(context.Background(), `SELECT COUNT(*) FROM users WHERE id = $1`, userID).Scan(&count)
|
||||
err = tx.QueryRow(ctx, `SELECT COUNT(*) FROM users WHERE id = $1`, userID).Scan(&count)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query user count: %v", err)
|
||||
}
|
||||
@@ -288,15 +293,16 @@ func TestAccount_DeleteGuest(t *testing.T) {
|
||||
|
||||
// TestLoyalty_Get verifies that a user can retrieve their loyalty stamps count and referral code.
|
||||
func TestLoyalty_Get(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
// Add some loyalty stamps
|
||||
_, err = db.DB.Exec(context.Background(), `UPDATE users SET loyalty_stamps = 10 WHERE id = $1`, userID)
|
||||
_, err = tx.Exec(ctx, `UPDATE users SET loyalty_stamps = 10 WHERE id = $1`, userID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to update loyalty stamps: %v", err)
|
||||
}
|
||||
@@ -304,7 +310,7 @@ func TestLoyalty_Get(t *testing.T) {
|
||||
token := jwt.GenerateUserToken(userID)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/user/loyalty", nil)
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -332,9 +338,10 @@ func TestLoyalty_Get(t *testing.T) {
|
||||
// TestProfile_Update_InvalidInput verifies that profile update validation rejects invalid inputs:
|
||||
// missing first name, missing last name, missing phone, invalid phone format, invalid characters in name, name too long.
|
||||
func TestProfile_Update_InvalidInput(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
@@ -383,7 +390,7 @@ func TestProfile_Update_InvalidInput(t *testing.T) {
|
||||
body, _ := json.Marshal(tt.req)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPut, "/api/user/profile", bytes.NewReader(body))
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
@@ -400,9 +407,10 @@ func TestProfile_Update_InvalidInput(t *testing.T) {
|
||||
|
||||
// TestProfile_Update_Success verifies that a valid profile update succeeds and the changes are persisted in the database.
|
||||
func TestProfile_Update_Success(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
@@ -417,7 +425,7 @@ func TestProfile_Update_Success(t *testing.T) {
|
||||
body, _ := json.Marshal(updateReq)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPut, "/api/user/profile", bytes.NewReader(body))
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
@@ -432,7 +440,7 @@ func TestProfile_Update_Success(t *testing.T) {
|
||||
|
||||
// Verify DB was updated
|
||||
var firstName, lastName, phone string
|
||||
err = db.DB.QueryRow(context.Background(),
|
||||
err = tx.QueryRow(ctx,
|
||||
"SELECT n_first_name, n_last_name, phone FROM users WHERE id = $1", userID).Scan(&firstName, &lastName, &phone)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query user: %v", err)
|
||||
@@ -451,9 +459,10 @@ func TestProfile_Update_Success(t *testing.T) {
|
||||
|
||||
// TestPasswordChange_SameAsOld verifies that attempting to change password to the same value returns 400 Bad Request.
|
||||
func TestPasswordChange_SameAsOld(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
@@ -468,7 +477,7 @@ func TestPasswordChange_SameAsOld(t *testing.T) {
|
||||
body, _ := json.Marshal(changeReq)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPut, "/api/user/change-password", bytes.NewReader(body))
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
@@ -484,9 +493,10 @@ func TestPasswordChange_SameAsOld(t *testing.T) {
|
||||
|
||||
// TestProfile_UploadPicture verifies that a user can upload a profile picture. May return 500 if S3 is not configured.
|
||||
func TestProfile_UploadPicture(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
@@ -539,7 +549,7 @@ func TestProfile_UploadPicture(t *testing.T) {
|
||||
writer.Close()
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/user/profile-picture", &b)
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
|
||||
@@ -569,16 +579,17 @@ func TestProfile_UploadPicture(t *testing.T) {
|
||||
|
||||
// TestContactInfo_ReturnsAdmin verifies that GetContactInfoHandler returns contact info for the first admin user.
|
||||
func TestContactInfo_ReturnsAdmin(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
// Create admin user with profile data
|
||||
adminID, err := fixtures.CreateTestAdminUser(db.DB)
|
||||
adminID, err := fixtures.CreateTestAdminUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create admin user: %v", err)
|
||||
}
|
||||
|
||||
// Update admin with specific profile data
|
||||
_, err = db.DB.Exec(context.Background(), `
|
||||
_, err = tx.Exec(ctx, `
|
||||
UPDATE users
|
||||
SET n_first_name = 'Jane', n_last_name = 'Smith', phone = '+447700900000', email = 'jane@example.com'
|
||||
WHERE id = $1
|
||||
@@ -589,6 +600,7 @@ func TestContactInfo_ReturnsAdmin(t *testing.T) {
|
||||
|
||||
// Call handler directly (no auth needed - public endpoint)
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/contact", nil)
|
||||
req = req.WithContext(ctx)
|
||||
rr := httptest.NewRecorder()
|
||||
GetContactInfoHandler(rr, req)
|
||||
|
||||
@@ -626,16 +638,16 @@ func TestContactInfo_ReturnsAdmin(t *testing.T) {
|
||||
|
||||
// TestContactInfo_NoAdmin verifies that GetContactInfoHandler returns 404 when no admin exists.
|
||||
func TestContactInfo_NoAdmin(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
testdb.TruncateTables(t, db.DB)
|
||||
|
||||
_, err := fixtures.CreateTestUser(db.DB)
|
||||
_, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/contact", nil)
|
||||
req = req.WithContext(ctx)
|
||||
rr := httptest.NewRecorder()
|
||||
GetContactInfoHandler(rr, req)
|
||||
|
||||
@@ -646,15 +658,16 @@ func TestContactInfo_NoAdmin(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNotificationPreferences_Get_Defaults(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/user/notification-preferences", nil)
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
GetNotificationPreferencesHandler(rr, req)
|
||||
@@ -680,9 +693,10 @@ func TestNotificationPreferences_Get_Defaults(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNotificationPreferences_Update(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
@@ -698,7 +712,7 @@ func TestNotificationPreferences_Update(t *testing.T) {
|
||||
|
||||
req := httptest.NewRequest(http.MethodPut, "/api/user/notification-preferences", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
UpdateNotificationPreferencesHandler(rr, req)
|
||||
@@ -708,7 +722,7 @@ func TestNotificationPreferences_Update(t *testing.T) {
|
||||
}
|
||||
|
||||
getReq := httptest.NewRequest(http.MethodGet, "/api/user/notification-preferences", nil)
|
||||
getReq = getReq.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
getReq = getReq.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
getRR := httptest.NewRecorder()
|
||||
GetNotificationPreferencesHandler(getRR, getReq)
|
||||
|
||||
@@ -729,9 +743,10 @@ func TestNotificationPreferences_Update(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNotificationPreferences_Update_Partial(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
@@ -744,7 +759,7 @@ func TestNotificationPreferences_Update_Partial(t *testing.T) {
|
||||
|
||||
req := httptest.NewRequest(http.MethodPut, "/api/user/notification-preferences", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
UpdateNotificationPreferencesHandler(rr, req)
|
||||
@@ -754,7 +769,7 @@ func TestNotificationPreferences_Update_Partial(t *testing.T) {
|
||||
}
|
||||
|
||||
getReq := httptest.NewRequest(http.MethodGet, "/api/user/notification-preferences", nil)
|
||||
getReq = getReq.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
getReq = getReq.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
getRR := httptest.NewRecorder()
|
||||
GetNotificationPreferencesHandler(getRR, getReq)
|
||||
|
||||
@@ -779,17 +794,17 @@ func TestNotificationPreferences_Update_Partial(t *testing.T) {
|
||||
// =============================================================================
|
||||
|
||||
func TestProfileUpdate_CreatesNameHistoryOnNameChange(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
ctx := context.Background()
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
// Fetch the user's current name from DB to verify against
|
||||
var origFirstName, origLastName string
|
||||
err = db.DB.QueryRow(ctx, `SELECT n_first_name, n_last_name FROM users WHERE id = $1`, userID).Scan(&origFirstName, &origLastName)
|
||||
err = tx.QueryRow(ctx, `SELECT n_first_name, n_last_name FROM users WHERE id = $1`, userID).Scan(&origFirstName, &origLastName)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query original name: %v", err)
|
||||
}
|
||||
@@ -804,7 +819,7 @@ func TestProfileUpdate_CreatesNameHistoryOnNameChange(t *testing.T) {
|
||||
}
|
||||
body, _ := json.Marshal(updateReq)
|
||||
req := httptest.NewRequest(http.MethodPut, "/api/user/profile", bytes.NewReader(body))
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -814,7 +829,7 @@ func TestProfileUpdate_CreatesNameHistoryOnNameChange(t *testing.T) {
|
||||
}
|
||||
|
||||
var count int
|
||||
err = db.DB.QueryRow(ctx, `SELECT COUNT(*) FROM name_history WHERE user_id = $1`, userID).Scan(&count)
|
||||
err = tx.QueryRow(ctx, `SELECT COUNT(*) FROM name_history WHERE user_id = $1`, userID).Scan(&count)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to count name_history: %v", err)
|
||||
}
|
||||
@@ -823,7 +838,7 @@ func TestProfileUpdate_CreatesNameHistoryOnNameChange(t *testing.T) {
|
||||
}
|
||||
|
||||
var prevFirstName, prevLastName string
|
||||
err = db.DB.QueryRow(ctx, `SELECT previous_first_name, previous_last_name FROM name_history WHERE user_id = $1`, userID).Scan(&prevFirstName, &prevLastName)
|
||||
err = tx.QueryRow(ctx, `SELECT previous_first_name, previous_last_name FROM name_history WHERE user_id = $1`, userID).Scan(&prevFirstName, &prevLastName)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query name_history: %v", err)
|
||||
}
|
||||
@@ -836,16 +851,16 @@ func TestProfileUpdate_CreatesNameHistoryOnNameChange(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProfileUpdate_NoNameHistoryOnSameName(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
ctx := context.Background()
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
var origFirstName, origLastName string
|
||||
err = db.DB.QueryRow(ctx, `SELECT n_first_name, n_last_name FROM users WHERE id = $1`, userID).Scan(&origFirstName, &origLastName)
|
||||
err = tx.QueryRow(ctx, `SELECT n_first_name, n_last_name FROM users WHERE id = $1`, userID).Scan(&origFirstName, &origLastName)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query original name: %v", err)
|
||||
}
|
||||
@@ -859,7 +874,7 @@ func TestProfileUpdate_NoNameHistoryOnSameName(t *testing.T) {
|
||||
}
|
||||
body, _ := json.Marshal(updateReq)
|
||||
req := httptest.NewRequest(http.MethodPut, "/api/user/profile", bytes.NewReader(body))
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -869,7 +884,7 @@ func TestProfileUpdate_NoNameHistoryOnSameName(t *testing.T) {
|
||||
}
|
||||
|
||||
var count int
|
||||
err = db.DB.QueryRow(ctx, `SELECT COUNT(*) FROM name_history WHERE user_id = $1`, userID).Scan(&count)
|
||||
err = tx.QueryRow(ctx, `SELECT COUNT(*) FROM name_history WHERE user_id = $1`, userID).Scan(&count)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to count name_history: %v", err)
|
||||
}
|
||||
@@ -879,16 +894,16 @@ func TestProfileUpdate_NoNameHistoryOnSameName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProfileUpdate_CreatesNameHistoryOnLastNameChange(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
ctx := context.Background()
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
var origFirstName, origLastName string
|
||||
err = db.DB.QueryRow(ctx, `SELECT n_first_name, n_last_name FROM users WHERE id = $1`, userID).Scan(&origFirstName, &origLastName)
|
||||
err = tx.QueryRow(ctx, `SELECT n_first_name, n_last_name FROM users WHERE id = $1`, userID).Scan(&origFirstName, &origLastName)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query original name: %v", err)
|
||||
}
|
||||
@@ -902,7 +917,7 @@ func TestProfileUpdate_CreatesNameHistoryOnLastNameChange(t *testing.T) {
|
||||
}
|
||||
body, _ := json.Marshal(updateReq)
|
||||
req := httptest.NewRequest(http.MethodPut, "/api/user/profile", bytes.NewReader(body))
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -912,7 +927,7 @@ func TestProfileUpdate_CreatesNameHistoryOnLastNameChange(t *testing.T) {
|
||||
}
|
||||
|
||||
var prevFirstName, prevLastName string
|
||||
err = db.DB.QueryRow(ctx, `SELECT previous_first_name, previous_last_name FROM name_history WHERE user_id = $1`, userID).Scan(&prevFirstName, &prevLastName)
|
||||
err = tx.QueryRow(ctx, `SELECT previous_first_name, previous_last_name FROM name_history WHERE user_id = $1`, userID).Scan(&prevFirstName, &prevLastName)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query name_history: %v", err)
|
||||
}
|
||||
@@ -925,14 +940,15 @@ func TestProfileUpdate_CreatesNameHistoryOnLastNameChange(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProfileGet_ReturnsPreviousNameWhenChanged(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
_, err = db.DB.Exec(context.Background(), `
|
||||
_, err = tx.Exec(ctx, `
|
||||
INSERT INTO name_history (user_id, previous_first_name, previous_last_name)
|
||||
VALUES ($1, 'OldFirst', 'OldLast')
|
||||
`, userID)
|
||||
@@ -941,7 +957,7 @@ func TestProfileGet_ReturnsPreviousNameWhenChanged(t *testing.T) {
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/user/profile", nil)
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
rr := httptest.NewRecorder()
|
||||
GetProfileHandler(rr, req)
|
||||
|
||||
@@ -963,20 +979,21 @@ func TestProfileGet_ReturnsPreviousNameWhenChanged(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProfileGet_OmitsPreviousNameWhenCurrentMatches(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
var origFirstName, origLastName string
|
||||
err = db.DB.QueryRow(context.Background(), `SELECT n_first_name, n_last_name FROM users WHERE id = $1`, userID).Scan(&origFirstName, &origLastName)
|
||||
err = tx.QueryRow(ctx, `SELECT n_first_name, n_last_name FROM users WHERE id = $1`, userID).Scan(&origFirstName, &origLastName)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query original name: %v", err)
|
||||
}
|
||||
|
||||
_, err = db.DB.Exec(context.Background(), `
|
||||
_, err = tx.Exec(ctx, `
|
||||
INSERT INTO name_history (user_id, previous_first_name, previous_last_name)
|
||||
VALUES ($1, $2, $3)
|
||||
`, userID, origFirstName, origLastName)
|
||||
@@ -985,7 +1002,7 @@ func TestProfileGet_OmitsPreviousNameWhenCurrentMatches(t *testing.T) {
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/user/profile", nil)
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
rr := httptest.NewRecorder()
|
||||
GetProfileHandler(rr, req)
|
||||
|
||||
@@ -1007,15 +1024,16 @@ func TestProfileGet_OmitsPreviousNameWhenCurrentMatches(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProfileGet_OmitsPreviousNameWhenNoHistory(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/user/profile", nil)
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
rr := httptest.NewRecorder()
|
||||
GetProfileHandler(rr, req)
|
||||
|
||||
@@ -1039,17 +1057,17 @@ func TestProfileGet_OmitsPreviousNameWhenNoHistory(t *testing.T) {
|
||||
// TestProfileGet_ReturnsReferralSavings verifies that the user profile
|
||||
// returns referralSavings reflecting applied referral discounts.
|
||||
func TestProfileGet_ReturnsReferralSavings(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
ctx := context.Background()
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
// Create a referral discount that's been applied to a booking
|
||||
var refID string
|
||||
err = db.DB.QueryRow(ctx, `
|
||||
err = tx.QueryRow(ctx, `
|
||||
INSERT INTO user_referrals (referrer_id, referred_id)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id
|
||||
@@ -1059,7 +1077,7 @@ func TestProfileGet_ReturnsReferralSavings(t *testing.T) {
|
||||
}
|
||||
|
||||
var rdID string
|
||||
err = db.DB.QueryRow(ctx, `
|
||||
err = tx.QueryRow(ctx, `
|
||||
INSERT INTO referral_discounts (user_id, referral_id, discount_percent, used)
|
||||
VALUES ($1, $2, 10.00, true)
|
||||
RETURNING id
|
||||
@@ -1068,33 +1086,28 @@ func TestProfileGet_ReturnsReferralSavings(t *testing.T) {
|
||||
t.Fatalf("failed to create referral discount: %v", err)
|
||||
}
|
||||
|
||||
// Record a booking_discount to simulate referral savings
|
||||
_, err = db.DB.Exec(ctx, `
|
||||
INSERT INTO booking_discounts (booking_id, user_id, discount_source, source_id, discount_percent, original_total, discount_amount)
|
||||
VALUES ((SELECT id FROM bookings LIMIT 1), $1, 'referral', $2, 10.00, 5000, 500)
|
||||
`, userID, rdID)
|
||||
// If no booking exists yet, create one
|
||||
// Create a booking first, since booking_discounts.booking_id is NOT NULL
|
||||
var bookingID string
|
||||
err = tx.QueryRow(ctx, `
|
||||
INSERT INTO bookings (user_id, start_time, status)
|
||||
VALUES ($1, NOW(), 'completed')
|
||||
RETURNING id
|
||||
`, userID).Scan(&bookingID)
|
||||
if err != nil {
|
||||
var bookingID string
|
||||
err = db.DB.QueryRow(ctx, `
|
||||
INSERT INTO bookings (user_id, start_time, status)
|
||||
VALUES ($1, NOW(), 'completed')
|
||||
RETURNING id
|
||||
`, userID).Scan(&bookingID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create booking: %v", err)
|
||||
}
|
||||
_, err = db.DB.Exec(ctx, `
|
||||
INSERT INTO booking_discounts (booking_id, user_id, discount_source, source_id, discount_percent, original_total, discount_amount)
|
||||
VALUES ($1, $2, 'referral', $3, 10.00, 5000, 500)
|
||||
`, bookingID, userID, rdID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to insert booking_discount: %v", err)
|
||||
}
|
||||
t.Fatalf("failed to create booking: %v", err)
|
||||
}
|
||||
|
||||
// Record a booking_discount to simulate referral savings
|
||||
_, err = tx.Exec(ctx, `
|
||||
INSERT INTO booking_discounts (booking_id, user_id, discount_source, source_id, discount_percent, original_total, discount_amount)
|
||||
VALUES ($1, $2, 'referral', $3, 10.00, 5000, 500)
|
||||
`, bookingID, userID, rdID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to insert booking_discount: %v", err)
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/user/profile", nil)
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
rr := httptest.NewRecorder()
|
||||
GetProfileHandler(rr, req)
|
||||
|
||||
@@ -1115,16 +1128,16 @@ func TestProfileGet_ReturnsReferralSavings(t *testing.T) {
|
||||
// TestProfileUpdate_NameHistoryRollback verifies that if the user update
|
||||
// fails after name_history is inserted, the name_history entry is rolled back.
|
||||
func TestProfileUpdate_NameHistoryRollback(t *testing.T) {
|
||||
testutils.SetupTestDB(t)
|
||||
ctx := context.Background()
|
||||
t.Parallel()
|
||||
ctx, tx := testutils.SetupTestTx(t)
|
||||
|
||||
userID, err := fixtures.CreateTestUser(db.DB)
|
||||
userID, err := fixtures.CreateTestUser(tx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
var origFirstName, origLastName string
|
||||
err = db.DB.QueryRow(ctx, `SELECT n_first_name, n_last_name FROM users WHERE id = $1`, userID).Scan(&origFirstName, &origLastName)
|
||||
err = tx.QueryRow(ctx, `SELECT n_first_name, n_last_name FROM users WHERE id = $1`, userID).Scan(&origFirstName, &origLastName)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query original name: %v", err)
|
||||
}
|
||||
@@ -1144,7 +1157,7 @@ func TestProfileUpdate_NameHistoryRollback(t *testing.T) {
|
||||
}
|
||||
body, _ := json.Marshal(updateReq)
|
||||
req := httptest.NewRequest(http.MethodPut, "/api/user/profile", bytes.NewReader(body))
|
||||
req = req.WithContext(context.WithValue(context.Background(), mw.UserIDKey, userID))
|
||||
req = req.WithContext(context.WithValue(ctx, mw.UserIDKey, userID))
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -1155,7 +1168,7 @@ func TestProfileUpdate_NameHistoryRollback(t *testing.T) {
|
||||
|
||||
// Verify name was updated
|
||||
var newFirstName string
|
||||
err = db.DB.QueryRow(ctx, `SELECT n_first_name FROM users WHERE id = $1`, userID).Scan(&newFirstName)
|
||||
err = tx.QueryRow(ctx, `SELECT n_first_name FROM users WHERE id = $1`, userID).Scan(&newFirstName)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query updated name: %v", err)
|
||||
}
|
||||
@@ -1165,7 +1178,7 @@ func TestProfileUpdate_NameHistoryRollback(t *testing.T) {
|
||||
|
||||
// Verify name_history has the original name recorded
|
||||
var prevFirstName string
|
||||
err = db.DB.QueryRow(ctx, `SELECT previous_first_name FROM name_history WHERE user_id = $1`, userID).Scan(&prevFirstName)
|
||||
err = tx.QueryRow(ctx, `SELECT previous_first_name FROM name_history WHERE user_id = $1`, userID).Scan(&prevFirstName)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to query name_history: %v", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user