v1.0.10 légal item 2. The signup endpoint /api/v1/auth/register and
its frontend form now require a date of birth and refuse registrations
where the registrant would be < 16 years old at registration time.
Threshold rationale :
COPPA (US, < 13 forbidden) + RGPD strict (< 16 needs parental
consent in every EU member state at the highest interpretation).
16 is the conservative single cutoff that satisfies both regimes
without per-jurisdiction branching. If a future market needs a
different threshold, change MinRegistrationAgeYears in
internal/core/auth/service.go ; the frontend reads the same
constant so they stay aligned.
Backend changes
* dto.RegisterRequest gets a `Birthdate string` field, validated
`required,datetime=2006-01-02` so swaggo / orval emit the right
OpenAPI schema and the validator catches malformed values
before the handler even runs.
* AuthService.Register signature is now
(ctx, email, username, password, birthdate *time.Time). The
pointer lets internal seed paths / tests pass nil while the
public handler always supplies a parsed value.
* Age check uses a yearsBetween helper that handles the "anniversary
hasn't passed yet this year" case correctly (someone born
2008-05-01 is 16 on 2008+16-05-01, not on 2008+16-01-01).
* New sentinel auth.ErrUnderage ; handler maps it to 400 with a
friendly message ("You must be at least 16 years old to register")
so the SPA can render the right copy without parsing the message.
* 11 test call sites updated : test-only paths pass nil ; the
public-handler test (TestRegister_Success) and the in-package
handler test pass a fixture Birthdate "2000-01-15".
Frontend changes
* RegisterFormData type + zod schema in RegisterForm.tsx + initial
form state get a `birthdate` field.
* RegisterPageForm.tsx renders an `<AuthInput type="date">` with a
`max=` attr 16 years ago today (UX guard ; legal floor stays in
the API).
* useRegisterPage's validate() computes age client-side with the
same algorithm as backend ; emits localised errors
`birthdateRequired` / `birthdateInvalid` / `birthdateUnderage`
so the user gets immediate feedback.
* services/api/auth.ts RegisterRequest interface + register()
body include the new field.
Tests : `go test ./internal/core/auth ./internal/handlers -short`
passes ; `tsc --noEmit` clean ; `eslint src` 754 (baseline unchanged).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
601 lines
18 KiB
Go
601 lines
18 KiB
Go
package handlers
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"veza-backend-api/internal/config"
|
|
"veza-backend-api/internal/core/auth"
|
|
"veza-backend-api/internal/database"
|
|
"veza-backend-api/internal/dto"
|
|
"veza-backend-api/internal/models"
|
|
"veza-backend-api/internal/repositories"
|
|
"veza-backend-api/internal/services"
|
|
"veza-backend-api/internal/validators"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap/zaptest"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// setupAuthTestRouter creates a test router with real auth service and mocked dependencies.
|
|
// Optional authMiddlewares are applied to the /auth group before routes (for tests needing user_id in context).
|
|
func setupAuthTestRouter(t *testing.T, authMiddlewares ...gin.HandlerFunc) (*gin.Engine, *auth.AuthService, *services.SessionService, *services.TwoFactorService, *services.UserService, *zap.Logger, func(), *gorm.DB) {
|
|
gin.SetMode(gin.TestMode)
|
|
logger := zaptest.NewLogger(t)
|
|
|
|
// Setup in-memory database
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
|
|
// Enable foreign keys for SQLite
|
|
db.Exec("PRAGMA foreign_keys = ON")
|
|
|
|
// Auto-migrate models
|
|
err = db.AutoMigrate(
|
|
&models.User{},
|
|
&models.RefreshToken{},
|
|
&models.Session{},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Create email_verification_tokens table manually (no GORM model)
|
|
err = db.Exec(`
|
|
CREATE TABLE IF NOT EXISTS email_verification_tokens (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
token TEXT NOT NULL UNIQUE,
|
|
token_hash TEXT NOT NULL,
|
|
email TEXT NOT NULL,
|
|
verified INTEGER NOT NULL DEFAULT 0,
|
|
used INTEGER NOT NULL DEFAULT 0,
|
|
verified_at TIMESTAMP,
|
|
expires_at TIMESTAMP NOT NULL,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
)
|
|
`).Error
|
|
require.NoError(t, err)
|
|
|
|
// Create database wrapper
|
|
dbWrapper := &database.Database{}
|
|
dbWrapper.GormDB = db
|
|
|
|
// Get underlying SQL DB for services that need it (like EmailVerificationService)
|
|
sqlDB, err := db.DB()
|
|
require.NoError(t, err)
|
|
dbWrapper.DB = sqlDB
|
|
|
|
// Add 2FA columns to users (SQLite test DB; GORM User model may not have them)
|
|
// Ignore errors if columns already exist (e.g. from a future GORM migration)
|
|
_, _ = sqlDB.Exec("ALTER TABLE users ADD COLUMN two_factor_enabled BOOLEAN DEFAULT 0")
|
|
_, _ = sqlDB.Exec("ALTER TABLE users ADD COLUMN two_factor_secret TEXT DEFAULT ''")
|
|
_, _ = sqlDB.Exec("ALTER TABLE users ADD COLUMN backup_codes TEXT DEFAULT '[]'")
|
|
|
|
// Setup services
|
|
emailValidator := validators.NewEmailValidator(db)
|
|
passwordValidator := validators.NewPasswordValidator()
|
|
passwordService := services.NewPasswordService(dbWrapper, logger)
|
|
jwtService, err := services.NewJWTService("", "", "test-secret-key-must-be-32-chars-long", "test-issuer", "test-audience")
|
|
require.NoError(t, err)
|
|
refreshTokenService := services.NewRefreshTokenService(db)
|
|
emailVerificationService := services.NewEmailVerificationService(dbWrapper, logger)
|
|
passwordResetService := services.NewPasswordResetService(dbWrapper, logger)
|
|
emailService := services.NewEmailService(dbWrapper, logger)
|
|
|
|
// Create AuthService
|
|
authService := auth.NewAuthService(
|
|
db,
|
|
emailValidator,
|
|
passwordValidator,
|
|
passwordService,
|
|
jwtService,
|
|
refreshTokenService,
|
|
emailVerificationService,
|
|
passwordResetService,
|
|
emailService,
|
|
nil, // jobWorker - not needed for unit tests
|
|
nil, // refreshLock - not needed for unit tests
|
|
logger,
|
|
)
|
|
|
|
// Create other services
|
|
sessionService := services.NewSessionService(dbWrapper, logger)
|
|
twoFactorService := services.NewTwoFactorService(dbWrapper, logger)
|
|
|
|
// Create UserService with GORM repository
|
|
userRepo := repositories.NewGormUserRepository(db)
|
|
userService := services.NewUserServiceWithDB(userRepo, db)
|
|
|
|
// Create default config
|
|
cfg := &config.Config{}
|
|
|
|
router := gin.New()
|
|
authGroup := router.Group("/auth")
|
|
for _, mw := range authMiddlewares {
|
|
authGroup.Use(mw)
|
|
}
|
|
{
|
|
authGroup.POST("/login", Login(authService, sessionService, twoFactorService, logger, cfg))
|
|
authGroup.POST("/register", Register(authService, sessionService, logger, cfg))
|
|
authGroup.POST("/refresh", Refresh(authService, sessionService, logger, cfg))
|
|
authGroup.POST("/logout", Logout(authService, sessionService, logger, cfg))
|
|
authGroup.POST("/verify-email", VerifyEmail(authService))
|
|
authGroup.POST("/resend-verification", ResendVerification(authService, logger))
|
|
authGroup.GET("/check-username", CheckUsername(authService))
|
|
authGroup.GET("/me", GetMe(userService))
|
|
}
|
|
|
|
cleanup := func() {
|
|
// Database cleanup handled by test
|
|
}
|
|
|
|
return router, authService, sessionService, twoFactorService, userService, logger, cleanup, db
|
|
}
|
|
|
|
func TestLogin_Success(t *testing.T) {
|
|
router, authService, _, _, _, _, cleanup, db := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Create a test user first
|
|
ctx := context.Background()
|
|
user, err := authService.Register(ctx, "test@example.com", "testuser", "SecurePassword123!", nil)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, user)
|
|
|
|
// Verify email to allow login
|
|
db.Model(&models.User{}).Where("id = ?", user.ID).Update("is_verified", true)
|
|
|
|
reqBody := dto.LoginRequest{
|
|
Email: "test@example.com",
|
|
Password: "SecurePassword123!",
|
|
RememberMe: false,
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest(http.MethodPost, "/auth/login", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
var response APIResponse
|
|
json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.True(t, response.Success)
|
|
}
|
|
|
|
func TestLogin_InvalidCredentials(t *testing.T) {
|
|
router, authService, _, _, _, _, cleanup, db := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Create a test user first
|
|
ctx := context.Background()
|
|
user, err := authService.Register(ctx, "test@example.com", "testuser", "SecurePassword123!", nil)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, user)
|
|
|
|
// Verify email
|
|
db.Model(&models.User{}).Where("id = ?", user.ID).Update("is_verified", true)
|
|
|
|
reqBody := dto.LoginRequest{
|
|
Email: "test@example.com",
|
|
Password: "wrongpassword",
|
|
RememberMe: false,
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest(http.MethodPost, "/auth/login", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code) // invalid credentials -> 401 (auth.go returns 401)
|
|
}
|
|
|
|
func TestLogin_EmailNotVerified(t *testing.T) {
|
|
router, authService, _, _, _, _, cleanup, _ := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Create a test user but don't verify email
|
|
ctx := context.Background()
|
|
user, err := authService.Register(ctx, "test@example.com", "testuser", "SecurePassword123!", nil)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, user)
|
|
// User is not verified by default (v1.0.4: Register leaves is_verified=false).
|
|
|
|
reqBody := dto.LoginRequest{
|
|
Email: "test@example.com",
|
|
Password: "SecurePassword123!",
|
|
RememberMe: false,
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest(http.MethodPost, "/auth/login", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
// Login refuses unverified users; the user must POST /auth/verify-email first.
|
|
assert.Equal(t, http.StatusForbidden, w.Code)
|
|
}
|
|
|
|
func TestLogin_Requires2FA(t *testing.T) {
|
|
router, authService, _, twoFactorService, _, _, cleanup, db := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Create a test user
|
|
ctx := context.Background()
|
|
user, err := authService.Register(ctx, "test@example.com", "testuser", "SecurePassword123!", nil)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, user)
|
|
|
|
// Verify email - use GORM directly
|
|
db.Model(&models.User{}).Where("id = ?", user.ID).Update("is_verified", true)
|
|
|
|
// Enable 2FA for user - generate secret and enable
|
|
setup, err := twoFactorService.GenerateSecret(user)
|
|
require.NoError(t, err)
|
|
err = twoFactorService.EnableTwoFactor(ctx, user.ID, setup.Secret, setup.RecoveryCodes)
|
|
require.NoError(t, err)
|
|
|
|
reqBody := dto.LoginRequest{
|
|
Email: "test@example.com",
|
|
Password: "SecurePassword123!",
|
|
RememberMe: false,
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest(http.MethodPost, "/auth/login", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
var response APIResponse
|
|
json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.True(t, response.Success)
|
|
|
|
// Check that response contains requires_2fa flag
|
|
var loginResponse dto.LoginResponse
|
|
responseData, _ := json.Marshal(response.Data)
|
|
json.Unmarshal(responseData, &loginResponse)
|
|
assert.True(t, loginResponse.Requires2FA)
|
|
}
|
|
|
|
func TestLogin_InvalidRequest(t *testing.T) {
|
|
router, _, _, _, _, _, cleanup, _ := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Missing email
|
|
reqBody := map[string]interface{}{
|
|
"password": "password123",
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest(http.MethodPost, "/auth/login", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestRegister_Success(t *testing.T) {
|
|
router, _, _, _, _, _, cleanup, _ := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
reqBody := dto.RegisterRequest{
|
|
Email: "newuser@example.com",
|
|
Username: "newuser",
|
|
Password: "SecurePassword123!",
|
|
PasswordConfirm: "SecurePassword123!",
|
|
Birthdate: "2000-01-15", // adult, satisfies the 16+ age gate
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest(http.MethodPost, "/auth/register", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code)
|
|
var response APIResponse
|
|
json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.True(t, response.Success)
|
|
}
|
|
|
|
func TestRegister_UserAlreadyExists(t *testing.T) {
|
|
router, authService, _, _, _, _, cleanup, _ := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Create a user first
|
|
ctx := context.Background()
|
|
_, err := authService.Register(ctx, "existing@example.com", "existinguser", "SecurePassword123!", nil)
|
|
require.NoError(t, err)
|
|
|
|
// Try to register again with same email
|
|
reqBody := dto.RegisterRequest{
|
|
Email: "existing@example.com",
|
|
Username: "existinguser2",
|
|
Password: "SecurePassword123!",
|
|
PasswordConfirm: "SecurePassword123!",
|
|
Birthdate: "2000-01-15",
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest(http.MethodPost, "/auth/register", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusConflict, w.Code)
|
|
}
|
|
|
|
func TestRegister_InvalidEmail(t *testing.T) {
|
|
router, _, _, _, _, _, cleanup, _ := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Invalid email format will be caught by validation
|
|
reqBody := dto.RegisterRequest{
|
|
Email: "invalid-email",
|
|
Username: "newuser",
|
|
Password: "SecurePassword123!",
|
|
PasswordConfirm: "SecurePassword123!",
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest(http.MethodPost, "/auth/register", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
// Should fail validation before reaching service
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestRegister_WeakPassword(t *testing.T) {
|
|
router, _, _, _, _, _, cleanup, _ := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Weak password will be caught by validation (min=12)
|
|
reqBody := dto.RegisterRequest{
|
|
Email: "newuser@example.com",
|
|
Username: "newuser",
|
|
Password: "weak",
|
|
PasswordConfirm: "weak",
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest(http.MethodPost, "/auth/register", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
// Should fail validation before reaching service
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestRegister_InvalidRequest(t *testing.T) {
|
|
router, _, _, _, _, _, cleanup, _ := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Missing required fields
|
|
reqBody := map[string]interface{}{
|
|
"email": "test@example.com",
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest(http.MethodPost, "/auth/register", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestRefresh_InvalidRequest(t *testing.T) {
|
|
router, _, _, _, _, _, cleanup, _ := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Missing refresh_token
|
|
reqBody := map[string]interface{}{}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest(http.MethodPost, "/auth/refresh", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestLogout_Success(t *testing.T) {
|
|
userID := uuid.New()
|
|
router, _, _, _, _, _, cleanup, _ := setupAuthTestRouter(t, func(c *gin.Context) {
|
|
if c.Request.URL.Path == "/auth/logout" {
|
|
c.Set("user_id", userID)
|
|
}
|
|
c.Next()
|
|
})
|
|
defer cleanup()
|
|
|
|
reqBody := map[string]interface{}{
|
|
"refresh_token": "refresh_token",
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest(http.MethodPost, "/auth/logout", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Authorization", "Bearer access_token")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestLogout_Unauthorized(t *testing.T) {
|
|
router, _, _, _, _, _, cleanup, _ := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
reqBody := map[string]interface{}{
|
|
"refresh_token": "refresh_token",
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest(http.MethodPost, "/auth/logout", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
|
}
|
|
|
|
func TestVerifyEmail_MissingToken(t *testing.T) {
|
|
router, _, _, _, _, _, cleanup, _ := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/auth/verify-email", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestResendVerification_Success(t *testing.T) {
|
|
router, authService, _, _, _, _, cleanup, db := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Create a test user (not verified) - Register creates with is_verified=true by default, so we set false
|
|
ctx := context.Background()
|
|
user, err := authService.Register(ctx, "test@example.com", "testuser", "SecurePassword123!", nil)
|
|
require.NoError(t, err)
|
|
db.Model(&models.User{}).Where("id = ?", user.ID).Update("is_verified", false)
|
|
|
|
reqBody := dto.ResendVerificationRequest{
|
|
Email: "test@example.com",
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req := httptest.NewRequest(http.MethodPost, "/auth/resend-verification", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestCheckUsername_Available(t *testing.T) {
|
|
router, _, _, _, _, _, cleanup, _ := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/auth/check-username?username=newusername", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
var response APIResponse
|
|
json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.True(t, response.Success)
|
|
|
|
var data map[string]interface{}
|
|
responseData, _ := json.Marshal(response.Data)
|
|
json.Unmarshal(responseData, &data)
|
|
assert.True(t, data["available"].(bool))
|
|
}
|
|
|
|
func TestCheckUsername_Taken(t *testing.T) {
|
|
router, authService, _, _, _, _, cleanup, _ := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Create a user with username
|
|
ctx := context.Background()
|
|
_, err := authService.Register(ctx, "test@example.com", "existinguser", "SecurePassword123!", nil)
|
|
require.NoError(t, err)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/auth/check-username?username=existinguser", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
var response APIResponse
|
|
json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.True(t, response.Success)
|
|
|
|
var data map[string]interface{}
|
|
responseData, _ := json.Marshal(response.Data)
|
|
json.Unmarshal(responseData, &data)
|
|
assert.False(t, data["available"].(bool))
|
|
}
|
|
|
|
func TestCheckUsername_MissingUsername(t *testing.T) {
|
|
router, _, _, _, _, _, cleanup, _ := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/auth/check-username", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestGetMe_Success(t *testing.T) {
|
|
var userID uuid.UUID
|
|
router, authService, _, _, _, _, cleanup, _ := setupAuthTestRouter(t, func(c *gin.Context) {
|
|
if c.Request.URL.Path == "/auth/me" {
|
|
c.Set("user_id", userID)
|
|
}
|
|
c.Next()
|
|
})
|
|
defer cleanup()
|
|
|
|
// Create a test user
|
|
ctx := context.Background()
|
|
user, err := authService.Register(ctx, "test@example.com", "testuser", "SecurePassword123!", nil)
|
|
require.NoError(t, err)
|
|
userID = user.ID
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/auth/me", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestGetMe_Unauthorized(t *testing.T) {
|
|
router, _, _, _, _, _, cleanup, _ := setupAuthTestRouter(t)
|
|
defer cleanup()
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/auth/me", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
|
}
|