test: 后端自动化测试套件
Service 单元测试(SQLite in-memory): - auth: 登录成功/失败/禁用账号,token 刷新 - stock: 入库审核、出库审核(含库存不足)、单号生成 - license: 激活、设备绑定验证、过期检测 Handler 集成测试(httptest + SQLite): - auth: 登录 API 成功/401 场景 - product: CRUD 完整流程 + hotel_id 隔离验证 - stock_in: 创建→提交→审核完整流程 - stock_out: 完整流程 + 库存不足 400 - inventory: 库存查询/过滤/盘点创建 - warehouse: CRUD 覆盖率:service 90.4%,handler 56.1% 共 50 个测试用例,全部 PASS Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/wangjia/jiu/backend/internal/service"
|
||||
"github.com/wangjia/jiu/backend/testutil"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gin.SetMode(gin.TestMode)
|
||||
testutil.InitConfig()
|
||||
}
|
||||
|
||||
func newTestAuthRouter(t *testing.T) (*gin.Engine, *gin.Engine) {
|
||||
db := testutil.SetupTestDB()
|
||||
hotel := testutil.CreateTestHotel(db, "AUTHTEST")
|
||||
testutil.CreateTestUser(db, hotel.ID, "admin", "password123", "admin")
|
||||
testutil.CreateTestUser(db, hotel.ID, "disabled", "password123", "operator")
|
||||
// 禁用该用户
|
||||
db.Exec("UPDATE users SET is_active = 0 WHERE username = 'disabled' AND hotel_id = ?", hotel.ID)
|
||||
|
||||
svc := service.NewAuthService(db)
|
||||
h := NewAuthHandler(svc)
|
||||
|
||||
r := gin.New()
|
||||
r.POST("/api/v1/auth/login", h.Login)
|
||||
r.POST("/api/v1/auth/refresh", h.Refresh)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func TestAuthHandler_Login_Success(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
hotel := testutil.CreateTestHotel(db, "AH001")
|
||||
testutil.CreateTestUser(db, hotel.ID, "admin", "password123", "admin")
|
||||
|
||||
svc := service.NewAuthService(db)
|
||||
h := NewAuthHandler(svc)
|
||||
r := gin.New()
|
||||
r.POST("/api/v1/auth/login", h.Login)
|
||||
|
||||
body := map[string]string{
|
||||
"hotel_code": "AH001",
|
||||
"username": "admin",
|
||||
"password": "password123",
|
||||
}
|
||||
bodyBytes, _ := json.Marshal(body)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/api/v1/auth/login", bytes.NewBuffer(bodyBytes))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp map[string]interface{}
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
data := resp["data"].(map[string]interface{})
|
||||
assert.NotEmpty(t, data["access_token"])
|
||||
assert.NotEmpty(t, data["refresh_token"])
|
||||
}
|
||||
|
||||
func TestAuthHandler_Login_WrongPassword(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
hotel := testutil.CreateTestHotel(db, "AH002")
|
||||
testutil.CreateTestUser(db, hotel.ID, "admin", "password123", "admin")
|
||||
|
||||
svc := service.NewAuthService(db)
|
||||
h := NewAuthHandler(svc)
|
||||
r := gin.New()
|
||||
r.POST("/api/v1/auth/login", h.Login)
|
||||
|
||||
body := map[string]string{
|
||||
"hotel_code": "AH002",
|
||||
"username": "admin",
|
||||
"password": "wrongpassword",
|
||||
}
|
||||
bodyBytes, _ := json.Marshal(body)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/api/v1/auth/login", bytes.NewBuffer(bodyBytes))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
}
|
||||
|
||||
func TestAuthHandler_Login_MissingFields(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
svc := service.NewAuthService(db)
|
||||
h := NewAuthHandler(svc)
|
||||
r := gin.New()
|
||||
r.POST("/api/v1/auth/login", h.Login)
|
||||
|
||||
// 缺少必填字段
|
||||
body := map[string]string{
|
||||
"hotel_code": "AH003",
|
||||
}
|
||||
bodyBytes, _ := json.Marshal(body)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/api/v1/auth/login", bytes.NewBuffer(bodyBytes))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func TestAuthHandler_Refresh_Success(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
hotel := testutil.CreateTestHotel(db, "AH004")
|
||||
testutil.CreateTestUser(db, hotel.ID, "admin", "password123", "admin")
|
||||
|
||||
svc := service.NewAuthService(db)
|
||||
h := NewAuthHandler(svc)
|
||||
r := gin.New()
|
||||
r.POST("/api/v1/auth/login", h.Login)
|
||||
r.POST("/api/v1/auth/refresh", h.Refresh)
|
||||
|
||||
// 先登录获取 token
|
||||
loginBody := map[string]string{
|
||||
"hotel_code": "AH004",
|
||||
"username": "admin",
|
||||
"password": "password123",
|
||||
}
|
||||
loginBytes, _ := json.Marshal(loginBody)
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/api/v1/auth/login", bytes.NewBuffer(loginBytes))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
var loginResp map[string]interface{}
|
||||
json.Unmarshal(w.Body.Bytes(), &loginResp)
|
||||
data := loginResp["data"].(map[string]interface{})
|
||||
refreshToken := data["refresh_token"].(string)
|
||||
|
||||
// 刷新 token
|
||||
refreshBody := map[string]string{"refresh_token": refreshToken}
|
||||
refreshBytes, _ := json.Marshal(refreshBody)
|
||||
w2 := httptest.NewRecorder()
|
||||
req2, _ := http.NewRequest("POST", "/api/v1/auth/refresh", bytes.NewBuffer(refreshBytes))
|
||||
req2.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w2, req2)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w2.Code)
|
||||
var resp map[string]interface{}
|
||||
json.Unmarshal(w2.Body.Bytes(), &resp)
|
||||
newData := resp["data"].(map[string]interface{})
|
||||
assert.NotEmpty(t, newData["access_token"])
|
||||
}
|
||||
|
||||
func TestAuthHandler_Refresh_InvalidToken(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
svc := service.NewAuthService(db)
|
||||
h := NewAuthHandler(svc)
|
||||
r := gin.New()
|
||||
r.POST("/api/v1/auth/refresh", h.Refresh)
|
||||
|
||||
body := map[string]string{"refresh_token": "invalid.token.here"}
|
||||
bodyBytes, _ := json.Marshal(body)
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/api/v1/auth/refresh", bytes.NewBuffer(bodyBytes))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
||||
}
|
||||
Reference in New Issue
Block a user