Files
team-api/tests/integration/admin/auth_test.go
T

152 lines
4.0 KiB
Go

//go:build integration
package admin_test
import (
"context"
"encoding/json"
"fmt"
"testing"
"time"
"github.com/redis/go-redis/v9"
"github.com/qianfree/team-api/tests/integration/admin/testinfra"
)
func TestLogin_Success(t *testing.T) {
result := testinfra.Authenticate(t)
if result.AccessToken == "" {
t.Fatal("expected non-empty access_token")
}
if result.RefreshToken == "" {
t.Fatal("expected non-empty refresh_token")
}
if result.ExpiresAt == "" {
t.Fatal("expected non-empty expires_at")
}
}
func TestLogin_InvalidPassword(t *testing.T) {
client := testinfra.NewAPIClient(testinfra.DefaultBaseURL)
// Step 1: Get captcha
captchaResp := client.Get("/api/captcha/", nil)
captchaResp.AssertSuccess(t)
var captchaData struct {
CaptchaKey string `json:"captcha_key"`
}
captchaResp.DecodeData(t, &captchaData)
// Step 2: Read captcha answer from Redis
rdb := redis.NewClient(&redis.Options{
Addr: testinfra.GetRedisAddr(),
DialTimeout: 3 * time.Second,
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
})
defer rdb.Close()
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
redisKey := fmt.Sprintf("captcha:state:%s", captchaData.CaptchaKey)
val, err := rdb.Get(ctx, redisKey).Result()
if err != nil {
t.Fatalf("read captcha from redis error: %v", err)
}
var answer struct {
X int `json:"x"`
Y int `json:"y"`
}
if err := json.Unmarshal([]byte(val), &answer); err != nil {
t.Fatalf("unmarshal captcha answer error: %v", err)
}
// Step 3: Login with wrong password
loginResp := client.Post("/api/admin/auth/login", map[string]any{
"username": testinfra.DefaultUsername,
"password": "WrongPassword123!",
"captcha_key": captchaData.CaptchaKey,
"captcha_x": answer.X,
})
if loginResp.Code == 0 {
t.Fatal("expected login to fail with wrong password, but got success")
}
}
func TestLogout_Success(t *testing.T) {
// Login
result := testinfra.Authenticate(t)
client := testinfra.NewAPIClient(testinfra.DefaultBaseURL).WithToken(result.AccessToken)
// Logout
logoutResp := client.Post("/api/admin/auth/logout", nil)
logoutResp.AssertSuccess(t)
// Verify token is invalidated by calling an auth-required endpoint
sessionResp := client.Get("/api/admin/auth/sessions", map[string]string{
"page": "1",
"page_size": "10",
})
sessionResp.AssertHTTPStatus(t, 401)
}
func TestTokenRefresh(t *testing.T) {
// Login to get initial tokens
result := testinfra.Authenticate(t)
// Use refresh_token to get new tokens
client := testinfra.NewAPIClient(testinfra.DefaultBaseURL)
refreshResp := client.Post("/api/admin/auth/refresh", map[string]any{
"refresh_token": result.RefreshToken,
})
refreshResp.AssertSuccess(t)
var refreshData struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresAt string `json:"expires_at"`
}
refreshResp.DecodeData(t, &refreshData)
if refreshData.AccessToken == "" {
t.Fatal("expected non-empty access_token after refresh")
}
if refreshData.RefreshToken == "" {
t.Fatal("expected non-empty refresh_token after refresh")
}
// Verify the new access_token works on an auth-required endpoint
authedClient := testinfra.NewAPIClient(testinfra.DefaultBaseURL).WithToken(refreshData.AccessToken)
sessionsResp := authedClient.Get("/api/admin/auth/sessions", map[string]string{
"page": "1",
"page_size": "10",
})
sessionsResp.AssertSuccess(t)
}
func TestSessionList(t *testing.T) {
client := testinfra.GetAuthedClient(t)
resp := client.Get("/api/admin/auth/sessions", map[string]string{
"page": "1",
"page_size": "10",
})
testinfra.AssertPaginatedList(t, resp, 1)
}
func TestChangePassword_Unauthorized(t *testing.T) {
// Verify endpoint requires auth (no token sent)
client := testinfra.NewAPIClient(testinfra.DefaultBaseURL)
resp := client.Put("/api/admin/auth/change-password", map[string]any{
"old_password": "anything",
"new_password": "anything",
})
resp.AssertHTTPStatus(t, 401)
}