641 lines
22 KiB
Go
641 lines
22 KiB
Go
package handler
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/DATA-DOG/go-sqlmock"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/assert"
|
|
"gorm.io/driver/mysql"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
"starter-kit/internal/adapter/persistence"
|
|
"starter-kit/internal/domain/role"
|
|
"starter-kit/internal/service"
|
|
"starter-kit/internal/transport/http/dto"
|
|
"starter-kit/internal/transport/http/middleware"
|
|
)
|
|
|
|
// testDB chứa thông tin database test
|
|
type testDB struct {
|
|
db *gorm.DB
|
|
mock sqlmock.Sqlmock
|
|
}
|
|
|
|
// setupTestDB thiết lập database giả lập cho test
|
|
func setupTestDB(t *testing.T) *testDB {
|
|
// Tạo mock database với QueryMatcherRegexp để so khớp regexp
|
|
sqlDB, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherRegexp))
|
|
if err != nil {
|
|
t.Fatalf("Failed to create mock database: %v", err)
|
|
}
|
|
|
|
// Kết nối GORM với mock database
|
|
db, err := gorm.Open(mysql.New(mysql.Config{
|
|
Conn: sqlDB,
|
|
SkipInitializeWithVersion: true,
|
|
}), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Silent),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Failed to open gorm db: %v", err)
|
|
}
|
|
|
|
// Thiết lập kỳ vọng cho việc kết nối database
|
|
mock.ExpectQuery(`(?i)SELECT VERSION\(\)`).
|
|
WillReturnRows(sqlmock.NewRows([]string{"VERSION()"}).AddRow("5.7.0"))
|
|
|
|
// Thiết lập kỳ vọng cho việc kiểm tra bảng
|
|
mock.ExpectQuery(`(?i)SELECT\s+\*\s+FROM\s+information_schema\.tables`).
|
|
WillReturnRows(sqlmock.NewRows([]string{"table_name"}))
|
|
|
|
// Mock cho việc kiểm tra role mặc định
|
|
mock.ExpectQuery(`(?i)SELECT \* FROM "roles" WHERE name = \? AND "roles"\."deleted_at" IS NULL ORDER BY "roles"\."id" LIMIT 1`).
|
|
WithArgs("user").
|
|
WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(1, "user"))
|
|
|
|
// Mock cho việc kiểm tra email đã tồn tại
|
|
mock.ExpectQuery(`(?i)SELECT \* FROM "users" WHERE email = \? AND "users"\."deleted_at" IS NULL ORDER BY "users"\."id" LIMIT 1`).
|
|
WithArgs("test@example.com").
|
|
WillReturnRows(sqlmock.NewRows([]string{"id", "email"}).AddRow(1, "test@example.com"))
|
|
|
|
// Mock cho việc kiểm tra username đã tồn tại (trường hợp chưa tồn tại)
|
|
mock.ExpectQuery(`(?i)SELECT \* FROM "users" WHERE username = \? AND "users"\."deleted_at" IS NULL ORDER BY "users"\."id" LIMIT 1`).
|
|
WithArgs("testuser").
|
|
WillReturnRows(sqlmock.NewRows([]string{"id", "username"}))
|
|
|
|
// Mock cho việc kiểm tra email đã tồn tại (trường hợp chưa tồn tại)
|
|
mock.ExpectQuery(`(?i)SELECT \* FROM "users" WHERE email = \? AND "users"\."deleted_at" IS NULL ORDER BY "users"\."id" LIMIT 1`).
|
|
WithArgs("test@example.com").
|
|
WillReturnRows(sqlmock.NewRows([]string{"id", "email"}))
|
|
|
|
// Mock cho việc kiểm tra username đã tồn tại (trường hợp đã tồn tại)
|
|
mock.ExpectQuery(`(?i)SELECT \* FROM "users" WHERE username = \? AND "users"\."deleted_at" IS NULL ORDER BY "users"\."id" LIMIT 1`).
|
|
WithArgs("existinguser").
|
|
WillReturnRows(sqlmock.NewRows([]string{"id", "username"}).AddRow(1, "existinguser"))
|
|
|
|
// Mock cho việc kiểm tra email đã tồn tại (trường hợp đã tồn tại)
|
|
mock.ExpectQuery(`(?i)SELECT \* FROM "users" WHERE email = \? AND "users"\."deleted_at" IS NULL ORDER BY "users"\."id" LIMIT 1`).
|
|
WithArgs("existing@example.com").
|
|
WillReturnRows(sqlmock.NewRows([]string{"id", "email"}).AddRow(1, "existing@example.com"))
|
|
|
|
// Mock cho việc tạo user mới
|
|
mock.ExpectBegin()
|
|
mock.ExpectExec(`(?i)INSERT INTO "users"`).
|
|
WithArgs(
|
|
sqlmock.AnyArg(), // ID
|
|
"testuser",
|
|
sqlmock.AnyArg(), // password hash
|
|
"Test User",
|
|
"test@example.com",
|
|
sqlmock.AnyArg(), // created_at
|
|
sqlmock.AnyArg(), // updated_at
|
|
sqlmock.AnyArg(), // deleted_at
|
|
).
|
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
|
|
|
// Mock cho việc gán role cho user
|
|
mock.ExpectExec(`(?i)INSERT INTO "user_roles"`).
|
|
WithArgs(
|
|
sqlmock.AnyArg(), // ID
|
|
1, // user_id
|
|
1, // role_id
|
|
sqlmock.AnyArg(), // created_at
|
|
sqlmock.AnyArg(), // updated_at
|
|
nil, // deleted_at
|
|
).
|
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
|
|
|
mock.ExpectCommit()
|
|
|
|
// Mock cho việc lấy thông tin user sau khi tạo
|
|
mock.ExpectQuery(`(?i)SELECT \* FROM "users" WHERE id = \? AND "users"\."deleted_at" IS NULL ORDER BY "users"\."id" LIMIT 1`).
|
|
WithArgs(1).
|
|
WillReturnRows(sqlmock.NewRows(
|
|
[]string{"id", "username", "full_name", "email", "password_hash", "created_at", "updated_at", "deleted_at"},
|
|
).AddRow(
|
|
1, "testuser", "Test User", "test@example.com", "hashedpassword", time.Now(), time.Now(), nil,
|
|
))
|
|
|
|
// Mock cho việc đăng nhập: tìm user theo username
|
|
mock.ExpectQuery(`(?i)SELECT \* FROM "users" WHERE username = \? AND "users"\."deleted_at" IS NULL ORDER BY "users"\."id" LIMIT 1`).
|
|
WithArgs("testuser").
|
|
WillReturnRows(sqlmock.NewRows(
|
|
[]string{"id", "username", "full_name", "email", "password_hash", "is_active", "created_at", "updated_at"},
|
|
).AddRow(
|
|
1, "testuser", "Test User", "test@example.com", "$2a$10$somehashedpassword", true, time.Now(), time.Now(),
|
|
))
|
|
|
|
// Mock cho việc lấy roles của user khi đăng nhập
|
|
mock.ExpectQuery(`(?i)SELECT \* FROM "roles" INNER JOIN "user_roles" ON "user_roles"\."role_id" = "roles"\."id" WHERE "user_roles"\."user_id" = \?`).
|
|
WithArgs(1).
|
|
WillReturnRows(sqlmock.NewRows(
|
|
[]string{"id", "name"},
|
|
).AddRow(
|
|
1, "user",
|
|
))
|
|
|
|
// Thêm mock cho refresh token
|
|
mock.ExpectQuery(`(?i)SELECT \* FROM "refresh_tokens" WHERE user_id = \? AND "refresh_tokens"\."deleted_at" IS NULL ORDER BY "refresh_tokens"\."id" LIMIT 1`).
|
|
WithArgs(1).
|
|
WillReturnRows(sqlmock.NewRows([]string{"id", "user_id", "token", "expires_at", "created_at", "updated_at"}))
|
|
|
|
mock.ExpectBegin()
|
|
mock.ExpectExec(`(?i)INSERT INTO "refresh_tokens"`).
|
|
WithArgs(
|
|
sqlmock.AnyArg(), // ID
|
|
1, // user_id
|
|
sqlmock.AnyArg(), // token
|
|
sqlmock.AnyArg(), // expires_at
|
|
sqlmock.AnyArg(), // created_at
|
|
sqlmock.AnyArg(), // updated_at
|
|
nil, // deleted_at
|
|
).
|
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
|
mock.ExpectCommit()
|
|
mock.ExpectCommit()
|
|
|
|
// Mock cho việc kiểm tra refresh token
|
|
mock.ExpectQuery(`(?i)SELECT \* FROM "refresh_tokens" WHERE token = \? AND "refresh_tokens"\."deleted_at" IS NULL ORDER BY "refresh_tokens"\."id" LIMIT 1`).
|
|
WithArgs("valid-refresh-token").
|
|
WillReturnRows(sqlmock.NewRows(
|
|
[]string{"id", "user_id", "token", "expires_at", "created_at", "updated_at"},
|
|
).AddRow(
|
|
1, 1, "valid-refresh-token", time.Now().Add(time.Hour*24*7), time.Now(), time.Now(),
|
|
))
|
|
|
|
// Mock cho việc xóa refresh token cũ
|
|
mock.ExpectBegin()
|
|
mock.ExpectExec(`(?i)UPDATE "refresh_tokens" SET "deleted_at"=\? WHERE "refresh_tokens"\."deleted_at" IS NULL AND "user_id" = \?`).
|
|
WithArgs(sqlmock.AnyArg(), 1).
|
|
WillReturnResult(sqlmock.NewResult(0, 1))
|
|
|
|
// Mock cho việc tạo refresh token mới
|
|
mock.ExpectExec(`(?i)INSERT INTO "refresh_tokens"`).
|
|
WithArgs(
|
|
sqlmock.AnyArg(), // ID
|
|
1, // user_id
|
|
sqlmock.AnyArg(), // token
|
|
sqlmock.AnyArg(), // expires_at
|
|
sqlmock.AnyArg(), // created_at
|
|
sqlmock.AnyArg(), // updated_at
|
|
nil, // deleted_at
|
|
).
|
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
|
|
|
mock.ExpectCommit()
|
|
|
|
// Mock cho việc xóa refresh token khi đăng xuất
|
|
mock.ExpectBegin()
|
|
mock.ExpectExec(`(?i)UPDATE "refresh_tokens" SET "deleted_at"=\? WHERE "refresh_tokens"\."deleted_at" IS NULL AND "token" = \?`).
|
|
WithArgs(sqlmock.AnyArg(), "valid-refresh-token").
|
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
|
mock.ExpectCommit()
|
|
|
|
return &testDB{
|
|
db: db,
|
|
mock: mock,
|
|
}
|
|
}
|
|
|
|
// setupTestRouter thiết lập router cho test
|
|
func setupTestRouter(testDB *testDB, jwtSecret string, accessTokenExpire int) *gin.Engine {
|
|
// Khởi tạo router
|
|
r := gin.Default()
|
|
|
|
// Khởi tạo các repository
|
|
userRepo := persistence.NewUserRepository(testDB.db)
|
|
roleRepo := persistence.NewRoleRepository(testDB.db)
|
|
|
|
// Tạo role mặc định nếu chưa tồn tại
|
|
_, err := roleRepo.GetByName(context.Background(), "user")
|
|
if err == gorm.ErrRecordNotFound {
|
|
_ = roleRepo.Create(context.Background(), &role.Role{
|
|
Name: "user",
|
|
})
|
|
}
|
|
|
|
// Khởi tạo các service
|
|
authSvc := service.NewAuthService(userRepo, roleRepo, jwtSecret, time.Duration(accessTokenExpire)*time.Minute)
|
|
|
|
// Khởi tạo middleware
|
|
authMiddleware := middleware.NewAuthMiddleware(authSvc)
|
|
|
|
// Khởi tạo các handler
|
|
authHandler := NewAuthHandler(authSvc)
|
|
|
|
// Đăng ký các route
|
|
api := r.Group("/api/v1")
|
|
{
|
|
auth := api.Group("/auth")
|
|
{
|
|
auth.POST("/register", authHandler.Register)
|
|
auth.POST("/login", authHandler.Login)
|
|
auth.POST("/refresh", authHandler.RefreshToken)
|
|
auth.POST("/logout", authMiddleware.Authenticate(), authHandler.Logout)
|
|
}
|
|
}
|
|
|
|
return r
|
|
}
|
|
|
|
// TestMain chạy trước và sau các test case
|
|
func TestMain(m *testing.M) {
|
|
// Thiết lập chế độ test cho Gin
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
// Chạy các test case
|
|
code := m.Run()
|
|
|
|
// Thoát với mã trạng thái
|
|
os.Exit(code)
|
|
}
|
|
|
|
func TestAuthIntegration(t *testing.T) {
|
|
// Setup test database
|
|
testDB := setupTestDB(t)
|
|
|
|
// Setup router
|
|
jwtSecret := "test-secret-key"
|
|
accessTokenExpire := 15 // 15 phút
|
|
|
|
// Khởi tạo router cho test
|
|
r := setupTestRouter(testDB, jwtSecret, accessTokenExpire)
|
|
|
|
// Test data
|
|
registerData := dto.RegisterRequest{
|
|
Username: "testuser",
|
|
Email: "test@example.com",
|
|
Password: "password123",
|
|
FullName: "Test User",
|
|
}
|
|
|
|
// Test đăng ký tài khoản mới
|
|
t.Run("Register new user", func(t *testing.T) {
|
|
// In ra dữ liệu đăng ký
|
|
t.Logf("Register data: %+v", registerData)
|
|
|
|
jsonData, err := json.Marshal(registerData)
|
|
if err != nil {
|
|
t.Fatalf("Failed to marshal register data: %v", err)
|
|
}
|
|
t.Logf("Sending registration request: %s", string(jsonData))
|
|
|
|
req, err := http.NewRequest("POST", "/api/v1/auth/register", bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
t.Fatalf("Failed to create request: %v", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
t.Logf("Response status: %d, body: %s", w.Code, w.Body.String())
|
|
|
|
// In ra lỗi nếu có
|
|
if w.Code != http.StatusCreated {
|
|
t.Logf("Unexpected status code: %d, body: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
assert.Equal(t, http.StatusCreated, w.Code, "Expected status code 201")
|
|
|
|
var response dto.UserResponse
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
if err != nil {
|
|
t.Logf("Failed to unmarshal response: %v, body: %s", err, w.Body.String())
|
|
}
|
|
assert.NoError(t, err, "Should decode response without error")
|
|
|
|
t.Logf("Response user: %+v", response)
|
|
|
|
assert.Equal(t, registerData.Username, response.Username, "Username should match")
|
|
assert.Equal(t, registerData.Email, response.Email, "Email should match")
|
|
assert.Equal(t, registerData.FullName, response.FullName, "Full name should match")
|
|
})
|
|
|
|
// Test đăng nhập
|
|
t.Run("Login with valid credentials", func(t *testing.T) {
|
|
// Mock cho việc đăng nhập
|
|
testDB.mock.ExpectQuery(`(?i)SELECT.*FROM \` + "`" + `users\` + "`" + ` WHERE username = \?`).
|
|
WithArgs(registerData.Username).
|
|
WillReturnRows(sqlmock.NewRows(
|
|
[]string{"id", "username", "email", "password_hash", "full_name", "is_active"}).
|
|
AddRow(1, registerData.Username, registerData.Email, "$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi", registerData.FullName, true))
|
|
|
|
// Mock cho việc lấy roles của user
|
|
testDB.mock.ExpectQuery(`(?i)SELECT.*FROM \` + "`" + `roles\` + "`" + ``).
|
|
WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(1, "user"))
|
|
|
|
// Đăng nhập
|
|
loginData := dto.LoginRequest{
|
|
Username: registerData.Username,
|
|
Password: registerData.Password,
|
|
}
|
|
loginJSON, _ := json.Marshal(loginData)
|
|
t.Logf("Logging in with: %s", string(loginJSON))
|
|
|
|
req, _ := http.NewRequest("POST", "/api/v1/auth/login", bytes.NewBuffer(loginJSON))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
t.Logf("Login response: %d - %s", w.Code, w.Body.String())
|
|
assert.Equal(t, http.StatusOK, w.Code, "Expected status code 200 for login")
|
|
|
|
var response dto.AuthResponse
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err, "Should decode response without error")
|
|
assert.NotEmpty(t, response.AccessToken, "Access token should not be empty")
|
|
assert.NotEmpty(t, response.RefreshToken, "Refresh token should not be empty")
|
|
|
|
// Lưu lại token để sử dụng cho các test sau
|
|
accessToken := response.AccessToken
|
|
refreshToken := response.RefreshToken
|
|
|
|
// Test refresh token
|
|
t.Run("Refresh token", func(t *testing.T) {
|
|
// Mock cho việc validate refresh token
|
|
testDB.mock.ExpectQuery(`(?i)SELECT.*FROM \` + "`" + `refresh_tokens\` + "`" + ` WHERE token = \?`).
|
|
WithArgs(refreshToken).
|
|
WillReturnRows(sqlmock.NewRows(
|
|
[]string{"id", "user_id", "token", "expires_at", "created_at"}).
|
|
AddRow(1, 1, refreshToken, time.Now().Add(24*time.Hour), time.Now()))
|
|
|
|
// Mock cho việc lấy thông tin user
|
|
testDB.mock.ExpectQuery(`(?i)SELECT.*FROM \` + "`" + `users\` + "`" + ` WHERE id = \?`).
|
|
WithArgs(1).
|
|
WillReturnRows(sqlmock.NewRows(
|
|
[]string{"id", "username", "email", "full_name", "is_active"}).
|
|
AddRow(1, registerData.Username, registerData.Email, registerData.FullName, true))
|
|
|
|
// Mock cho việc lấy roles của user
|
|
testDB.mock.ExpectQuery(`(?i)SELECT.*FROM \` + "`" + `roles\` + "`" + ``).
|
|
WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(1, "user"))
|
|
|
|
// Mock cho việc xóa refresh token cũ
|
|
testDB.mock.ExpectExec(`(?i)DELETE FROM \` + "`" + `refresh_tokens\` + "`" + ` WHERE token = \?`).
|
|
WithArgs(refreshToken).
|
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
|
|
|
// Mock cho việc tạo refresh token mới
|
|
testDB.mock.ExpectExec(`(?i)INSERT INTO \` + "`" + `refresh_tokens\` + "`" + ``).
|
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
|
|
|
refreshData := map[string]string{
|
|
"refresh_token": refreshToken,
|
|
}
|
|
jsonData, _ := json.Marshal(refreshData)
|
|
|
|
req, _ := http.NewRequest("POST", "/api/v1/auth/refresh", bytes.NewBuffer(jsonData))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code, "Expected status code 200 for token refresh")
|
|
|
|
var refreshResponse map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &refreshResponse)
|
|
assert.NoError(t, err, "Should decode refresh response without error")
|
|
assert.NotEmpty(t, refreshResponse["access_token"], "New access token should not be empty")
|
|
assert.NotEmpty(t, refreshResponse["refresh_token"], "New refresh token should not be empty")
|
|
})
|
|
|
|
// Test logout
|
|
t.Run("Logout", func(t *testing.T) {
|
|
req, _ := http.NewRequest("POST", "/api/v1/auth/logout", nil)
|
|
req.Header.Set("Authorization", "Bearer "+accessToken)
|
|
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusNoContent, w.Code, "Expected status code 204 for logout")
|
|
})
|
|
})
|
|
|
|
// Test đăng nhập với thông tin không hợp lệ
|
|
t.Run("Login with invalid credentials", func(t *testing.T) {
|
|
loginData := map[string]string{
|
|
"username": "nonexistent",
|
|
"password": "wrongpassword",
|
|
}
|
|
jsonData, _ := json.Marshal(loginData)
|
|
|
|
req, _ := http.NewRequest("POST", "/api/v1/auth/login", bytes.NewBuffer(jsonData))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code, "Should return 401 for invalid credentials")
|
|
})
|
|
|
|
// Test đăng ký với tên người dùng đã tồn tại
|
|
t.Run("Register with existing username", func(t *testing.T) {
|
|
// Đăng ký user lần đầu
|
|
jsonData, _ := json.Marshal(registerData)
|
|
req, _ := http.NewRequest("POST", "/api/v1/auth/register", bytes.NewBuffer(jsonData))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
assert.Equal(t, http.StatusCreated, w.Code, "First registration should succeed")
|
|
|
|
// Thử đăng ký lại với cùng username
|
|
req, _ = http.NewRequest("POST", "/api/v1/auth/register", bytes.NewBuffer(jsonData))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w = httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusConflict, w.Code, "Should return 409 for existing username")
|
|
})
|
|
|
|
// Test cases
|
|
tests := []struct {
|
|
name string
|
|
payload interface{}
|
|
expectedStatus int
|
|
expectedError string
|
|
validateFunc func(t *testing.T, resp *http.Response)
|
|
}{
|
|
{
|
|
name: "Đăng ký thành công",
|
|
payload: map[string]string{
|
|
"username": "testuser",
|
|
"email": "test@example.com",
|
|
"password": "Test@123",
|
|
"full_name": "Test User",
|
|
},
|
|
expectedStatus: http.StatusCreated,
|
|
validateFunc: func(t *testing.T, resp *http.Response) {
|
|
var response dto.UserResponse
|
|
err := json.NewDecoder(resp.Body).Decode(&response)
|
|
assert.NoError(t, err)
|
|
assert.NotEmpty(t, response.ID)
|
|
assert.Equal(t, "testuser", response.Username)
|
|
assert.Equal(t, "test@example.com", response.Email)
|
|
assert.Equal(t, "Test User", response.FullName)
|
|
},
|
|
},
|
|
{
|
|
name: "Đăng ký với username đã tồn tại",
|
|
payload: map[string]string{
|
|
"username": "testuser",
|
|
"email": "test2@example.com",
|
|
"password": "Test@123",
|
|
"full_name": "Test User 2",
|
|
},
|
|
expectedStatus: http.StatusConflict,
|
|
expectedError: "already exists",
|
|
},
|
|
{
|
|
name: "Đăng ký với email đã tồn tại",
|
|
payload: map[string]string{
|
|
"username": "testuser2",
|
|
"email": "test@example.com",
|
|
"password": "Test@123",
|
|
"full_name": "Test User 2",
|
|
},
|
|
expectedStatus: http.StatusConflict,
|
|
expectedError: "already exists",
|
|
},
|
|
{
|
|
name: "Đăng ký với dữ liệu không hợp lệ",
|
|
payload: map[string]string{
|
|
"username": "",
|
|
"email": "invalid-email",
|
|
"password": "123",
|
|
},
|
|
expectedStatus: http.StatusBadRequest,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Chuyển đổi payload thành JSON
|
|
jsonData, err := json.Marshal(tt.payload)
|
|
assert.NoError(t, err)
|
|
|
|
// Tạo request
|
|
req, err := http.NewRequest("POST", "/api/v1/auth/register", bytes.NewBuffer(jsonData))
|
|
assert.NoError(t, err)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
// Ghi lại response
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
// Kiểm tra status code
|
|
assert.Equal(t, tt.expectedStatus, w.Code)
|
|
|
|
// Kiểm tra response body nếu có lỗi mong đợi
|
|
if tt.expectedError != "" {
|
|
var response map[string]string
|
|
err = json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, response["error"], tt.expectedError)
|
|
}
|
|
|
|
// Gọi hàm validate tùy chỉnh nếu có
|
|
if tt.validateFunc != nil {
|
|
tt.validateFunc(t, w.Result())
|
|
}
|
|
})
|
|
}
|
|
|
|
// Test đăng nhập sau khi đăng ký
|
|
t.Run("Đăng nhập sau khi đăng ký", func(t *testing.T) {
|
|
// Đăng ký tài khoản mới
|
|
registerPayload := map[string]string{
|
|
"username": "loginuser",
|
|
"email": "login@example.com",
|
|
"password": "Login@123",
|
|
"full_name": "Login Test User",
|
|
}
|
|
|
|
jsonData, err := json.Marshal(registerPayload)
|
|
assert.NoError(t, err)
|
|
|
|
// Gọi API đăng ký
|
|
registerReq, err := http.NewRequest("POST", "/api/v1/auth/register", bytes.NewBuffer(jsonData))
|
|
assert.NoError(t, err)
|
|
registerReq.Header.Set("Content-Type", "application/json")
|
|
|
|
registerW := httptest.NewRecorder()
|
|
r.ServeHTTP(registerW, registerReq)
|
|
assert.Equal(t, http.StatusCreated, registerW.Code)
|
|
|
|
// Test đăng nhập thành công
|
|
loginPayload := map[string]string{
|
|
"username": "loginuser",
|
|
"password": "Login@123",
|
|
}
|
|
|
|
loginData, err := json.Marshal(loginPayload)
|
|
assert.NoError(t, err)
|
|
|
|
loginReq, err := http.NewRequest("POST", "/api/v1/auth/login", bytes.NewBuffer(loginData))
|
|
assert.NoError(t, err)
|
|
loginReq.Header.Set("Content-Type", "application/json")
|
|
|
|
loginW := httptest.NewRecorder()
|
|
r.ServeHTTP(loginW, loginReq)
|
|
|
|
assert.Equal(t, http.StatusOK, loginW.Code)
|
|
|
|
var loginResponse struct {
|
|
AccessToken string `json:"access_token"`
|
|
RefreshToken string `json:"refresh_token"`
|
|
ExpiresAt time.Time `json:"expires_at"`
|
|
TokenType string `json:"token_type"`
|
|
}
|
|
|
|
err = json.Unmarshal(loginW.Body.Bytes(), &loginResponse)
|
|
assert.NoError(t, err)
|
|
assert.NotEmpty(t, loginResponse.AccessToken)
|
|
assert.NotEmpty(t, loginResponse.RefreshToken)
|
|
assert.Equal(t, "Bearer", loginResponse.TokenType)
|
|
assert.False(t, loginResponse.ExpiresAt.IsZero())
|
|
|
|
// Test refresh token
|
|
t.Run("Làm mới token", func(t *testing.T) {
|
|
refreshPayload := map[string]string{
|
|
"refresh_token": loginResponse.RefreshToken,
|
|
}
|
|
|
|
refreshData, err := json.Marshal(refreshPayload)
|
|
assert.NoError(t, err)
|
|
|
|
refreshReq, err := http.NewRequest("POST", "/api/v1/auth/refresh", bytes.NewBuffer(refreshData))
|
|
assert.NoError(t, err)
|
|
refreshReq.Header.Set("Content-Type", "application/json")
|
|
|
|
refreshW := httptest.NewRecorder()
|
|
r.ServeHTTP(refreshW, refreshReq)
|
|
|
|
assert.Equal(t, http.StatusOK, refreshW.Code)
|
|
|
|
})
|
|
|
|
// Test đăng xuất
|
|
t.Run("Đăng xuất", func(t *testing.T) {
|
|
logoutReq, err := http.NewRequest("POST", "/api/v1/auth/logout", nil)
|
|
assert.NoError(t, err)
|
|
logoutReq.Header.Set("Authorization", "Bearer "+loginResponse.AccessToken)
|
|
|
|
logoutW := httptest.NewRecorder()
|
|
r.ServeHTTP(logoutW, logoutReq)
|
|
|
|
assert.Equal(t, http.StatusNoContent, logoutW.Code)
|
|
|
|
})
|
|
})
|
|
}
|