package handler import ( "bytes" "context" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/Tencent/WeKnora/internal/config" "github.com/Tencent/WeKnora/internal/types" "github.com/Tencent/WeKnora/internal/types/interfaces" "github.com/gin-gonic/gin" ) type usernameLoginService struct { interfaces.UserService received *types.LoginRequest } func (s *usernameLoginService) Login(_ context.Context, req *types.LoginRequest) (*types.LoginResponse, error) { s.received = req return &types.LoginResponse{Success: true, User: &types.User{ID: "test-user"}, Token: "test-token"}, nil } func TestLoginAcceptsUsernameAndLegacyEmailRequests(t *testing.T) { cases := []struct { name string body map[string]string status int }{ {"username without email", map[string]string{"username": "alice", "password": "123"}, http.StatusOK}, {"trim username", map[string]string{"username": " alice ", "password": "123"}, http.StatusOK}, {"Unicode password", map[string]string{"username": "alice", "password": "三个字"}, http.StatusOK}, {"legacy email", map[string]string{"email": "alice@example.com", "password": "123"}, http.StatusOK}, {"missing identifier", map[string]string{"password": "123"}, http.StatusBadRequest}, {"blank identifier", map[string]string{"username": " ", "password": "123"}, http.StatusBadRequest}, {"missing password", map[string]string{"username": "alice"}, http.StatusBadRequest}, } gin.SetMode(gin.TestMode) for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { svc := &usernameLoginService{} h := &AuthHandler{userService: svc} r := gin.New() r.Use(errorCapture()) r.POST("/auth/login", h.Login) payload, err := json.Marshal(tc.body) if err != nil { t.Fatal(err) } req := httptest.NewRequest(http.MethodPost, "/auth/login", bytes.NewReader(payload)) req.Header.Set("Content-Type", "application/json") response := httptest.NewRecorder() r.ServeHTTP(response, req) if response.Code != tc.status { t.Fatalf("status=%d body=%s, want %d", response.Code, response.Body, tc.status) } if tc.status != http.StatusOK { if svc.received != nil { t.Fatal("invalid input reached the login service") } return } if svc.received == nil || svc.received.Password != tc.body["password"] { t.Fatalf("password changed before authentication: %+v", svc.received) } if tc.body["username"] != "" && svc.received.Username != "alice" { t.Fatalf("username=%q, want alice", svc.received.Username) } }) } } func TestRegisterAcceptsThreeCharacterPassword(t *testing.T) { called := false svc := &stubRegisterUserService{register: func(_ context.Context, req *types.RegisterRequest) (*types.User, error) { called = true if req.Password != "123" { t.Fatalf("password=%q", req.Password) } return &types.User{ID: "created-user"}, nil }} h := &AuthHandler{userService: svc, configInfo: &config.Config{Auth: &config.AuthConfig{}}} body := validRegisterBody() body["password"] = "123" response := doRegister(t, newRegisterTestRouter(h), body) if response.Code != http.StatusCreated || !called { t.Fatalf("status=%d body=%s service_called=%v", response.Code, response.Body, called) } }