152 lines
4.0 KiB
Go
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)
|
|
}
|