fix: improve test infrastructure and add ID validation
- Add TestMain to set test env vars and testdb.TruncateTables for test isolation - Add chi routing context to test helpers for path parameter extraction - Fix SQL error handling to use errors.Is() instead of == - Add validators package with ID validation - Fix admin test middleware chain (RequireAdmin wrapper) - Update test user inserts to include phone and date_of_birth fields - Update service delete test to check soft-delete (is_active=false) - Update holiday hours test to use new schema (weekday, is_open) - Add phone number validation tests for UK mobile numbers
This commit is contained in:
@@ -3,6 +3,7 @@ package user
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -23,6 +24,7 @@ import (
|
||||
"crussell/db"
|
||||
"crussell/handlers/auth"
|
||||
"crussell/internal/s3"
|
||||
"crussell/internal/validators"
|
||||
"crussell/mw"
|
||||
)
|
||||
|
||||
@@ -247,13 +249,14 @@ func UpdateProfileHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Fetch user's email and DOB for CardDAV update
|
||||
var email string
|
||||
var dob time.Time
|
||||
var dob sql.NullTime
|
||||
var profilePicURL sql.NullString
|
||||
err = db.DB.QueryRow(r.Context(), `
|
||||
SELECT email, date_of_birth, profile_pic_url FROM users WHERE id = $1
|
||||
`, userID).Scan(&email, &dob, &profilePicURL)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Failed to fetch user %s: %v", userID, err)
|
||||
http.Error(w, "failed to fetch user data", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -272,7 +275,10 @@ func UpdateProfileHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Update CardDAV (non-blocking)
|
||||
go func() {
|
||||
dobStr := dob.Format("2006-01-02")
|
||||
var dobStr string
|
||||
if dob.Valid {
|
||||
dobStr = dob.Time.Format("2006-01-02")
|
||||
}
|
||||
if err := updateCardDAV(userID, req.FirstName, req.LastName, email, req.Phone, dobStr, profilePicURL.String); err != nil {
|
||||
fmt.Printf("Warning: Failed to update CardDAV contact for user %s: %v\n", userID, err)
|
||||
}
|
||||
@@ -284,8 +290,8 @@ func UpdateProfileHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// GET /api/admin/users/{id}
|
||||
func GetAdminUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID := chi.URLParam(r, "id")
|
||||
if userID == "" {
|
||||
http.Error(w, "User ID is required", http.StatusBadRequest)
|
||||
if userID == "" || !validators.IsValidID(userID) {
|
||||
http.Error(w, "User not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -313,7 +319,7 @@ func GetAdminUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
http.Error(w, "User not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
@@ -539,7 +545,7 @@ func ChangePasswordHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var passwordHash string
|
||||
err := db.DB.QueryRow(r.Context(), `SELECT password_hash FROM users WHERE id = $1`, userID).Scan(&passwordHash)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
http.Error(w, "user not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
@@ -579,8 +585,8 @@ type ServiceForPatchTest struct {
|
||||
// GET /api/admin/users/{id}/patch-tests/eligible
|
||||
func GetEligiblePatchTestServicesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID := chi.URLParam(r, "id")
|
||||
if userID == "" {
|
||||
http.Error(w, "User ID is required", http.StatusBadRequest)
|
||||
if userID == "" || !validators.IsValidID(userID) {
|
||||
http.Error(w, "User not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -633,8 +639,8 @@ type AddPatchTestRequest struct {
|
||||
// POST /api/admin/users/{id}/patch-tests
|
||||
func AddPatchTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID := chi.URLParam(r, "id")
|
||||
if userID == "" {
|
||||
http.Error(w, "User ID is required", http.StatusBadRequest)
|
||||
if userID == "" || !validators.IsValidID(userID) {
|
||||
http.Error(w, "User not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -652,7 +658,7 @@ func AddPatchTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var patchTestHours int
|
||||
err := db.DB.QueryRow(r.Context(), `SELECT patch_test_duration_hours FROM services WHERE id = $1 AND is_active = true AND patch_test_duration_hours > 0`, req.ServiceID).Scan(&patchTestHours)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
http.Error(w, "service not found or does not require patch test", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@@ -684,8 +690,8 @@ type UserPatchTest struct {
|
||||
|
||||
func GetUserPatchTestsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID := chi.URLParam(r, "user_id")
|
||||
if userID == "" {
|
||||
http.Error(w, "User ID is required", http.StatusBadRequest)
|
||||
if userID == "" || !validators.IsValidID(userID) {
|
||||
http.Error(w, "User not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -720,8 +726,12 @@ func GetUserPatchTestsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
func DeletePatchTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
userID := chi.URLParam(r, "user_id")
|
||||
testID := chi.URLParam(r, "test_id")
|
||||
if userID == "" || testID == "" {
|
||||
http.Error(w, "User ID and Test ID are required", http.StatusBadRequest)
|
||||
if userID == "" || !validators.IsValidID(userID) {
|
||||
http.Error(w, "User not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if testID == "" || !validators.IsValidID(testID) {
|
||||
http.Error(w, "Patch test not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -845,12 +855,12 @@ type ContactInfo struct {
|
||||
func GetContactInfoHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var contact ContactInfo
|
||||
err := db.DB.QueryRow(r.Context(), `
|
||||
SELECT
|
||||
SELECT
|
||||
COALESCE(n_first_name, '') || ' ' || COALESCE(n_last_name, '') as name,
|
||||
COALESCE(phone, ''),
|
||||
COALESCE(email, ''),
|
||||
profile_pic_url
|
||||
FROM users
|
||||
FROM users
|
||||
WHERE account_role = 'admin'
|
||||
ORDER BY created_at ASC
|
||||
LIMIT 1
|
||||
|
||||
Reference in New Issue
Block a user