|
- package controller
-
- import (
- "bytes"
- "encoding/json"
- "net/http"
- "net/http/httptest"
- "strings"
- "testing"
-
- "github.com/QuantumNous/new-api/common"
- "github.com/QuantumNous/new-api/model"
- "github.com/QuantumNous/new-api/service/region_sync"
- "github.com/gin-gonic/gin"
- "github.com/glebarez/sqlite"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "gorm.io/gorm"
- )
-
- func TestReceiveSyncedUserCreate_Success(t *testing.T) {
- gin.SetMode(gin.TestMode)
- router := gin.New()
- // 使用测试专用的认证中间件,绕过系统设置检查
- router.Use(testSyncAuthMiddleware())
- router.POST("/api/internal/sync/user/create", ReceiveSyncedUserCreate)
-
- origDB := model.DB
- defer func() {
- model.DB = origDB
- }()
-
- // 设置测试数据库
- db := setupSyncTestDB(t)
- model.DB = db
-
- req := region_sync.SyncUserRequest{
- Username: "testuser",
- Email: "test@example.com",
- PasswordHash: "hashedpassword",
- DisplayName: "Test User",
- Quota: 10000,
- RemoteUserId: 10000001,
- Group: "default",
- }
-
- reqBody, _ := json.Marshal(req)
- reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/user/create", bytes.NewBuffer(reqBody))
- reqHTTP.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- router.ServeHTTP(w, reqHTTP)
-
- assert.Equal(t, http.StatusOK, w.Code)
-
- var resp map[string]interface{}
- json.Unmarshal(w.Body.Bytes(), &resp)
- assert.True(t, resp["success"].(bool))
-
- // 验证用户已创建
- var user model.User
- err := db.Where("username = ?", "testuser").First(&user).Error
- require.NoError(t, err)
- assert.Equal(t, "testuser", user.Username)
- assert.Equal(t, 10000001, user.RemoteUserId)
- assert.Equal(t, "synced", user.Source)
- }
-
- func TestReceiveQuotaUpdate_Success(t *testing.T) {
- gin.SetMode(gin.TestMode)
- router := gin.New()
- router.Use(testSyncAuthMiddleware())
- router.POST("/api/internal/sync/quota/update", ReceiveQuotaUpdate)
-
- origDB := model.DB
- defer func() {
- model.DB = origDB
- }()
-
- // 设置测试数据库
- db := setupSyncTestDB(t)
- model.DB = db
-
- // 创建测试用户
- user := model.User{
- Username: "testuser",
- RemoteUserId: 10000001,
- Source: "synced",
- Quota: 5000,
- SyncedQuota: 5000,
- AffCode: "aff-qu-1",
- }
- db.Create(&user)
-
- req := region_sync.UpdateQuotaRequest{
- RemoteUserId: 10000001,
- Quota: 10000,
- }
-
- reqBody, _ := json.Marshal(req)
- reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/update", bytes.NewBuffer(reqBody))
- reqHTTP.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- router.ServeHTTP(w, reqHTTP)
-
- assert.Equal(t, http.StatusOK, w.Code)
-
- var resp map[string]interface{}
- json.Unmarshal(w.Body.Bytes(), &resp)
- assert.True(t, resp["success"].(bool))
-
- // 验证余额已更新
- var updatedUser model.User
- err := db.Where("remote_user_id = ?", 10000001).First(&updatedUser).Error
- require.NoError(t, err)
- assert.Equal(t, 10000, updatedUser.SyncedQuota)
- }
-
- func TestQueryUserQuota_Success(t *testing.T) {
- gin.SetMode(gin.TestMode)
- router := gin.New()
- router.Use(testSyncAuthMiddleware())
- router.POST("/api/internal/sync/quota/query", QueryUserQuota)
-
- origDB := model.DB
- defer func() {
- model.DB = origDB
- }()
-
- // 设置测试数据库
- db := setupSyncTestDB(t)
- model.DB = db
-
- // 创建测试用户
- user := model.User{
- Username: "testuser",
- Quota: 15000,
- AffCode: "aff-qq-1",
- }
- db.Create(&user)
-
- req := region_sync.QueryQuotaRequest{
- UserId: user.Id,
- }
-
- reqBody, _ := json.Marshal(req)
- reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/query", bytes.NewBuffer(reqBody))
- reqHTTP.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- router.ServeHTTP(w, reqHTTP)
-
- assert.Equal(t, http.StatusOK, w.Code)
-
- var resp region_sync.QueryQuotaResponse
- json.Unmarshal(w.Body.Bytes(), &resp)
- assert.True(t, resp.Success)
- assert.Equal(t, 15000, resp.Quota)
- }
-
- func TestBatchDeductQuota_Success(t *testing.T) {
- gin.SetMode(gin.TestMode)
- router := gin.New()
- router.Use(testSyncAuthMiddleware())
- router.POST("/api/internal/sync/quota/batch-deduct", BatchDeductQuota)
-
- origDB := model.DB
- defer func() {
- model.DB = origDB
- }()
-
- // 设置测试数据库
- db := setupSyncTestDB(t)
- model.DB = db
-
- // 创建测试用户
- user1 := model.User{Username: "user1", Quota: 1000, AffCode: "aff-bd-1"}
- user2 := model.User{Username: "user2", Quota: 500, AffCode: "aff-bd-2"}
- db.Create(&user1)
- db.Create(&user2)
-
- req := region_sync.BatchDeductRequest{
- Records: []region_sync.BatchDeductRecord{
- {UserId: user1.Id, RequestId: "req-1", Quota: 100},
- {UserId: user2.Id, RequestId: "req-2", Quota: 50},
- },
- }
-
- reqBody, _ := json.Marshal(req)
- reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/batch-deduct", bytes.NewBuffer(reqBody))
- reqHTTP.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- router.ServeHTTP(w, reqHTTP)
-
- assert.Equal(t, http.StatusOK, w.Code)
-
- var resp region_sync.BatchDeductResponse
- json.Unmarshal(w.Body.Bytes(), &resp)
- assert.True(t, resp.Success)
- assert.Len(t, resp.Results, 2)
-
- // 验证用户余额已更新
- var updatedUser1 model.User
- db.First(&updatedUser1, user1.Id)
- assert.Equal(t, 900, updatedUser1.Quota)
-
- var updatedUser2 model.User
- db.First(&updatedUser2, user2.Id)
- assert.Equal(t, 450, updatedUser2.Quota)
- }
-
- func TestBatchDeductQuota_InsufficientQuota(t *testing.T) {
- gin.SetMode(gin.TestMode)
- router := gin.New()
- router.Use(testSyncAuthMiddleware())
- router.POST("/api/internal/sync/quota/batch-deduct", BatchDeductQuota)
-
- origDB := model.DB
- defer func() {
- model.DB = origDB
- }()
-
- // 设置测试数据库
- db := setupSyncTestDB(t)
- model.DB = db
-
- // 创建余额不足的用户
- user := model.User{Username: "pooruser", Quota: 10, AffCode: "aff-poor-1"}
- db.Create(&user)
-
- req := region_sync.BatchDeductRequest{
- Records: []region_sync.BatchDeductRecord{
- {UserId: user.Id, RequestId: "req-1", Quota: 100},
- },
- }
-
- reqBody, _ := json.Marshal(req)
- reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/batch-deduct", bytes.NewBuffer(reqBody))
- reqHTTP.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- router.ServeHTTP(w, reqHTTP)
-
- assert.Equal(t, http.StatusOK, w.Code)
-
- var resp region_sync.BatchDeductResponse
- json.Unmarshal(w.Body.Bytes(), &resp)
- assert.True(t, resp.Success)
- assert.Len(t, resp.Results, 1)
- assert.False(t, resp.Results[0].Success)
- assert.Contains(t, resp.Results[0].Error, "insufficient quota")
- }
-
- // testSyncAuthMiddleware 测试专用的认证中间件,绕过系统设置
- func testSyncAuthMiddleware() gin.HandlerFunc {
- return func(c *gin.Context) {
- c.Set("sync_node", "test-node")
- c.Next()
- }
- }
-
- // setupSyncTestDB 设置同步测试数据库
- func setupSyncTestDB(t *testing.T) *gorm.DB {
- t.Helper()
- db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
- require.NoError(t, err)
- sqlDB, _ := db.DB()
- sqlDB.SetMaxOpenConns(1)
-
- err = db.AutoMigrate(&model.User{}, &model.QuotaSyncLog{})
- require.NoError(t, err)
-
- return db
- }
-
- // ---------------------------------------------------------------------------
- // 阶段 4:Controller 边界条件测试
- // ---------------------------------------------------------------------------
-
- func TestReceiveSyncedUserCreate_DuplicateUser(t *testing.T) {
- gin.SetMode(gin.TestMode)
- router := gin.New()
- router.Use(testSyncAuthMiddleware())
- router.POST("/api/internal/sync/user/create", ReceiveSyncedUserCreate)
-
- origDB := model.DB
- defer func() { model.DB = origDB }()
-
- db := setupSyncTestDB(t)
- model.DB = db
-
- // 先创建一个用户
- db.Create(&model.User{
- Username: "dupuser", RemoteUserId: 10000001,
- Source: common.UserSourceSynced, Password: "hash", AffCode: "aff-dup-1",
- })
-
- req := region_sync.SyncUserRequest{
- Username: "dupuser",
- RemoteUserId: 10000001,
- PasswordHash: "newhash",
- }
- reqBody, _ := json.Marshal(req)
- reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/user/create", bytes.NewBuffer(reqBody))
- reqHTTP.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- router.ServeHTTP(w, reqHTTP)
-
- assert.Equal(t, http.StatusOK, w.Code)
- var resp map[string]interface{}
- json.Unmarshal(w.Body.Bytes(), &resp)
- assert.True(t, resp["success"].(bool))
- assert.Contains(t, resp["message"], "already exists")
- }
-
- func TestReceiveSyncedUserCreate_InvalidBody(t *testing.T) {
- gin.SetMode(gin.TestMode)
- router := gin.New()
- router.Use(testSyncAuthMiddleware())
- router.POST("/api/internal/sync/user/create", ReceiveSyncedUserCreate)
-
- reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/user/create", strings.NewReader("not json"))
- reqHTTP.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- router.ServeHTTP(w, reqHTTP)
- assert.Equal(t, http.StatusBadRequest, w.Code)
- }
-
- func TestReceiveQuotaUpdate_UserNotFound(t *testing.T) {
- gin.SetMode(gin.TestMode)
- router := gin.New()
- router.Use(testSyncAuthMiddleware())
- router.POST("/api/internal/sync/quota/update", ReceiveQuotaUpdate)
-
- origDB := model.DB
- defer func() { model.DB = origDB }()
-
- db := setupSyncTestDB(t)
- model.DB = db
-
- req := region_sync.UpdateQuotaRequest{
- RemoteUserId: 99999999,
- Quota: 5000,
- }
- reqBody, _ := json.Marshal(req)
- reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/update", bytes.NewBuffer(reqBody))
- reqHTTP.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- router.ServeHTTP(w, reqHTTP)
- assert.Equal(t, http.StatusNotFound, w.Code)
- }
-
- func TestReceiveQuotaUpdate_InvalidBody(t *testing.T) {
- gin.SetMode(gin.TestMode)
- router := gin.New()
- router.Use(testSyncAuthMiddleware())
- router.POST("/api/internal/sync/quota/update", ReceiveQuotaUpdate)
-
- reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/update", strings.NewReader("invalid"))
- reqHTTP.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- router.ServeHTTP(w, reqHTTP)
- assert.Equal(t, http.StatusBadRequest, w.Code)
- }
-
- func TestQueryUserQuota_UserNotFound(t *testing.T) {
- gin.SetMode(gin.TestMode)
- router := gin.New()
- router.Use(testSyncAuthMiddleware())
- router.POST("/api/internal/sync/quota/query", QueryUserQuota)
-
- origDB := model.DB
- defer func() { model.DB = origDB }()
-
- db := setupSyncTestDB(t)
- model.DB = db
-
- req := region_sync.QueryQuotaRequest{UserId: 99999}
- reqBody, _ := json.Marshal(req)
- reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/query", bytes.NewBuffer(reqBody))
- reqHTTP.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- router.ServeHTTP(w, reqHTTP)
- assert.Equal(t, http.StatusNotFound, w.Code)
- }
-
- func TestBatchDeductQuota_EmptyRecords(t *testing.T) {
- gin.SetMode(gin.TestMode)
- router := gin.New()
- router.Use(testSyncAuthMiddleware())
- router.POST("/api/internal/sync/quota/batch-deduct", BatchDeductQuota)
-
- origDB := model.DB
- defer func() { model.DB = origDB }()
-
- db := setupSyncTestDB(t)
- model.DB = db
-
- req := region_sync.BatchDeductRequest{Records: []region_sync.BatchDeductRecord{}}
- reqBody, _ := json.Marshal(req)
- reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/batch-deduct", bytes.NewBuffer(reqBody))
- reqHTTP.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- router.ServeHTTP(w, reqHTTP)
- assert.Equal(t, http.StatusOK, w.Code)
-
- var resp region_sync.BatchDeductResponse
- json.Unmarshal(w.Body.Bytes(), &resp)
- assert.True(t, resp.Success)
- assert.Len(t, resp.Results, 0)
- }
-
- func TestBatchDeductQuota_UserNotFound(t *testing.T) {
- gin.SetMode(gin.TestMode)
- router := gin.New()
- router.Use(testSyncAuthMiddleware())
- router.POST("/api/internal/sync/quota/batch-deduct", BatchDeductQuota)
-
- origDB := model.DB
- defer func() { model.DB = origDB }()
-
- db := setupSyncTestDB(t)
- model.DB = db
-
- req := region_sync.BatchDeductRequest{
- Records: []region_sync.BatchDeductRecord{
- {UserId: 99999, RequestId: "req-nf", Quota: 100},
- },
- }
- reqBody, _ := json.Marshal(req)
- reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/batch-deduct", bytes.NewBuffer(reqBody))
- reqHTTP.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- router.ServeHTTP(w, reqHTTP)
- assert.Equal(t, http.StatusOK, w.Code)
-
- var resp region_sync.BatchDeductResponse
- json.Unmarshal(w.Body.Bytes(), &resp)
- assert.True(t, resp.Success)
- assert.Len(t, resp.Results, 1)
- assert.False(t, resp.Results[0].Success)
- assert.Contains(t, resp.Results[0].Error, "user not found")
- }
-
- func TestBatchDeductQuota_InvalidBody(t *testing.T) {
- gin.SetMode(gin.TestMode)
- router := gin.New()
- router.Use(testSyncAuthMiddleware())
- router.POST("/api/internal/sync/quota/batch-deduct", BatchDeductQuota)
-
- reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/batch-deduct", strings.NewReader("bad"))
- reqHTTP.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- router.ServeHTTP(w, reqHTTP)
- assert.Equal(t, http.StatusBadRequest, w.Code)
- }
|