Files
user-system/internal/integration/integration_test.go

223 lines
6.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package integration
import (
"context"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"sync/atomic"
"testing"
_ "modernc.org/sqlite" // 纯 Go SQLite注册 "sqlite" 驱动
gormsqlite "gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"github.com/user-management-system/internal/domain"
"github.com/user-management-system/internal/repository"
)
var integDBCounter int64
func setupTestDB(t *testing.T) *gorm.DB {
t.Helper()
id := atomic.AddInt64(&integDBCounter, 1)
dsn := fmt.Sprintf("file:integtestdb%d?mode=memory&cache=private", id)
db, err := gorm.Open(gormsqlite.New(gormsqlite.Config{
DriverName: "sqlite",
DSN: dsn,
}), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
if err != nil {
t.Fatalf("打开测试数据库失败: %v", err)
}
if err := db.AutoMigrate(&domain.User{}, &domain.Role{}, &domain.Permission{}, &domain.Device{}); err != nil {
t.Fatalf("数据库迁移失败: %v", err)
}
return db
}
func cleanupTestDB(t *testing.T, db *gorm.DB) {
t.Helper()
sqlDB, _ := db.DB()
sqlDB.Close()
}
// setupTestServer 测试服务器
func setupTestServer(t *testing.T) *httptest.Server {
t.Helper()
mux := http.NewServeMux()
mux.HandleFunc("/api/v1/auth/register", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"code":0,"message":"success","data":{"user_id":1}}`))
})
mux.HandleFunc("/api/v1/auth/login", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"code":0,"message":"success","data":{"access_token":"test-token"}}`))
})
mux.HandleFunc("/api/v1/users/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"code":0,"message":"success","data":{"id":1,"username":"testuser"}}`))
})
return httptest.NewServer(mux)
}
// TestDatabaseIntegration 测试数据库集成
func TestDatabaseIntegration(t *testing.T) {
db := setupTestDB(t)
defer cleanupTestDB(t, db)
repo := repository.NewUserRepository(db)
ctx := context.Background()
t.Run("CreateUser", func(t *testing.T) {
user := &domain.User{
Phone: domain.StrPtr("13800138000"),
Username: "integrationuser",
Password: "hashedpassword",
Status: domain.UserStatusActive,
}
if err := repo.Create(ctx, user); err != nil {
t.Fatalf("创建用户失败: %v", err)
}
if user.ID == 0 {
t.Error("用户ID不应为0")
}
})
t.Run("FindUser", func(t *testing.T) {
user, err := repo.GetByUsername(ctx, "integrationuser")
if err != nil {
t.Fatalf("查询用户失败: %v", err)
}
if domain.DerefStr(user.Phone) != "13800138000" {
t.Errorf("Phone = %v, want 13800138000", domain.DerefStr(user.Phone))
}
})
t.Run("UpdateUser", func(t *testing.T) {
user, _ := repo.GetByUsername(ctx, "integrationuser")
user.Nickname = "已更新"
if err := repo.Update(ctx, user); err != nil {
t.Fatalf("更新用户失败: %v", err)
}
found, _ := repo.GetByID(ctx, user.ID)
if found.Nickname != "已更新" {
t.Errorf("Nickname = %v, want 已更新", found.Nickname)
}
})
t.Run("DeleteUser", func(t *testing.T) {
user, _ := repo.GetByUsername(ctx, "integrationuser")
if err := repo.Delete(ctx, user.ID); err != nil {
t.Fatalf("删除用户失败: %v", err)
}
_, err := repo.GetByUsername(ctx, "integrationuser")
if err == nil {
t.Error("删除后查询应返回错误")
}
})
}
// TestTransactionIntegration 测试事务集成
func TestTransactionIntegration(t *testing.T) {
db := setupTestDB(t)
defer cleanupTestDB(t, db)
t.Run("TransactionRollback", func(t *testing.T) {
err := db.Transaction(func(tx *gorm.DB) error {
user := &domain.User{
Phone: domain.StrPtr("13811111111"),
Username: "txrollbackuser",
Password: "hashedpassword",
Status: domain.UserStatusActive,
}
if err := tx.Create(user).Error; err != nil {
return err
}
return errors.New("模拟错误,触发回滚")
})
if err == nil {
t.Error("事务应该失败")
}
var count int64
db.Model(&domain.User{}).Where("username = ?", "txrollbackuser").Count(&count)
if count > 0 {
t.Error("事务回滚后用户不应存在")
}
})
t.Run("TransactionCommit", func(t *testing.T) {
err := db.Transaction(func(tx *gorm.DB) error {
user := &domain.User{
Phone: domain.StrPtr("13822222222"),
Username: "txcommituser",
Password: "hashedpassword",
Status: domain.UserStatusActive,
}
return tx.Create(user).Error
})
if err != nil {
t.Fatalf("事务失败: %v", err)
}
var count int64
db.Model(&domain.User{}).Where("username = ?", "txcommituser").Count(&count)
if count != 1 {
t.Error("事务提交后用户应存在")
}
})
}
// TestAPIIntegration 测试HTTP API集成
func TestAPIIntegration(t *testing.T) {
server := setupTestServer(t)
defer server.Close()
t.Run("RegisterEndpoint", func(t *testing.T) {
resp, err := http.Post(server.URL+"/api/v1/auth/register", "application/json", nil)
if err != nil {
t.Fatalf("请求失败: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("StatusCode = %d, want 200", resp.StatusCode)
}
})
t.Run("LoginEndpoint", func(t *testing.T) {
resp, err := http.Post(server.URL+"/api/v1/auth/login", "application/json", nil)
if err != nil {
t.Fatalf("请求失败: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("StatusCode = %d, want 200", resp.StatusCode)
}
})
t.Run("GetUserEndpoint", func(t *testing.T) {
resp, err := http.Get(server.URL + "/api/v1/users/1")
if err != nil {
t.Fatalf("请求失败: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("StatusCode = %d, want 200", resp.StatusCode)
}
})
}