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() shop := testutil.CreateTestShop(db, "AUTHTEST") testutil.CreateTestUser(db, shop.ID, "admin", "password123", "admin") testutil.CreateTestUser(db, shop.ID, "disabled", "password123", "operator") // 禁用该用户 db.Exec("UPDATE users SET is_active = 0 WHERE username = 'disabled' AND shop_id = ?", shop.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() shop := testutil.CreateTestShop(db, "AH001") testutil.CreateTestUser(db, shop.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{ "shop_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() shop := testutil.CreateTestShop(db, "AH002") testutil.CreateTestUser(db, shop.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{ "shop_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{ "shop_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() shop := testutil.CreateTestShop(db, "AH004") testutil.CreateTestUser(db, shop.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{ "shop_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) } func TestAuthHandler_Login_DisabledUser(t *testing.T) { db := testutil.SetupTestDB() shop := testutil.CreateTestShop(db, "AH005") user := testutil.CreateTestUser(db, shop.ID, "disabled_user", "password123", "operator") db.Model(user).Update("is_active", false) svc := service.NewAuthService(db) h := NewAuthHandler(svc) r := gin.New() r.POST("/api/v1/auth/login", h.Login) body := map[string]string{ "shop_code": "AH005", "username": "disabled_user", "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.StatusUnauthorized, w.Code) } func TestAuthHandler_Login_WrongShopCode(t *testing.T) { db := testutil.SetupTestDB() testutil.CreateTestShop(db, "AH006") svc := service.NewAuthService(db) h := NewAuthHandler(svc) r := gin.New() r.POST("/api/v1/auth/login", h.Login) body := map[string]string{ "shop_code": "NONEXISTENT", "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.StatusUnauthorized, w.Code) } func TestAuthHandler_Login_EmptyBody(t *testing.T) { db := testutil.SetupTestDB() svc := service.NewAuthService(db) h := NewAuthHandler(svc) r := gin.New() r.POST("/api/v1/auth/login", h.Login) w := httptest.NewRecorder() req, _ := http.NewRequest("POST", "/api/v1/auth/login", bytes.NewBuffer([]byte("{}"))) req.Header.Set("Content-Type", "application/json") r.ServeHTTP(w, req) assert.Equal(t, http.StatusBadRequest, w.Code) } func TestAuthHandler_Refresh_MissingToken(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{} 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) // 缺少 refresh_token,应返回 4xx assert.True(t, w.Code >= 400 && w.Code < 500) } func TestAuthHandler_Login_ResponseContainsUserInfo(t *testing.T) { db := testutil.SetupTestDB() shop := testutil.CreateTestShop(db, "AH007") testutil.CreateTestUser(db, shop.ID, "manager", "password123", "admin") svc := service.NewAuthService(db) h := NewAuthHandler(svc) r := gin.New() r.POST("/api/v1/auth/login", h.Login) body := map[string]string{ "shop_code": "AH007", "username": "manager", "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{}) // token 不为空 assert.NotEmpty(t, data["access_token"]) assert.NotEmpty(t, data["refresh_token"]) // 包含 shop_id assert.NotNil(t, data["shop_id"]) // 包含用户信息 userInfo, ok := data["user"].(map[string]interface{}) if ok { assert.Equal(t, "manager", userInfo["username"]) } }