feat(test):新增用户端API自动化测试

This commit is contained in:
qian committed 2026-05-27 23:12:01 +08:00
1 parent 104f1de0b3
commit 76d35c9d7d
18 files changed
+1932

No files matched your search

+117
View File
@@ -0,0 +1,117 @@
//go:build integration
package tenant_test
import (
"fmt"
"testing"
admintest "github.com/qianfree/team-api/tests/integration/admin/testinfra"
"github.com/qianfree/team-api/tests/integration/tenant/testinfra"
)
func TestApiKeyList(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
_, cleanup := testinfra.CreateTestApiKey(t, client)
defer cleanup()
resp := client.Get("/api/tenant/api-keys", map[string]string{
"page": "1",
"page_size": "10",
})
admintest.AssertPaginatedList(t, resp, 1)
}
func TestApiKeyCRUD(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
// --- Create ---
suffix := testinfra.RandomSuffix()
createResp := client.Post("/api/tenant/api-keys", map[string]any{
"name": fmt.Sprintf("crud-key-%s", suffix),
})
createResp.AssertSuccess(t)
keyID := createResp.GetID(t)
defer func() {
client.Delete(fmt.Sprintf("/api/tenant/api-keys/%d", keyID))
}()
var createData struct {
ID int64 `json:"id"`
Key string `json:"key"`
KeyPrefix string `json:"key_prefix"`
Name string `json:"name"`
}
createResp.DecodeData(t, &createData)
if createData.Key == "" {
t.Fatal("expected non-empty key on create")
}
if createData.KeyPrefix == "" {
t.Fatal("expected non-empty key_prefix")
}
// --- List should contain the key ---
listResp := client.Get("/api/tenant/api-keys", map[string]string{
"page": "1",
"page_size": "50",
})
listResp.AssertSuccess(t)
// --- Update ---
updateResp := client.Put(fmt.Sprintf("/api/tenant/api-keys/%d", keyID), map[string]any{
"name": fmt.Sprintf("updated-key-%s", suffix),
"status": "disabled",
})
updateResp.AssertSuccess(t)
// Verify update via list
verifyResp := client.Get("/api/tenant/api-keys", map[string]string{
"page": "1",
"page_size": "50",
})
verifyResp.AssertSuccess(t)
// --- Delete ---
deleteResp := client.Delete(fmt.Sprintf("/api/tenant/api-keys/%d", keyID))
deleteResp.AssertSuccess(t)
}
func TestApiKeyUpdateScopes(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
keyID, cleanup := testinfra.CreateTestApiKey(t, client)
defer cleanup()
// Update scopes
scopesResp := client.Put(fmt.Sprintf("/api/tenant/api-keys/%d/scopes", keyID), map[string]any{
"model_names": []string{"gpt-4", "claude-3"},
})
scopesResp.AssertSuccess(t)
// Verify model scopes
getScopesResp := client.Get(fmt.Sprintf("/api/tenant/api-keys/%d/model-scopes", keyID), nil)
getScopesResp.AssertSuccess(t)
var scopesData struct {
ModelNames []string `json:"model_names"`
}
getScopesResp.DecodeData(t, &scopesData)
if len(scopesData.ModelNames) != 2 {
t.Fatalf("expected 2 model names, got %d", len(scopesData.ModelNames))
}
}
func TestApiKeyExport(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
_, cleanup := testinfra.CreateTestApiKey(t, client)
defer cleanup()
resp := client.Get("/api/tenant/api-keys/export", map[string]string{
"format": "csv",
})
resp.AssertSuccess(t)
}
+107
View File
@@ -0,0 +1,107 @@
//go:build integration
package tenant_test
import (
"testing"
"github.com/qianfree/team-api/tests/integration/tenant/testinfra"
)
func TestAuditConfigGet(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/audit/config", nil)
resp.AssertSuccess(t)
var data struct {
AuditLevel string `json:"audit_level"`
}
resp.DecodeData(t, &data)
}
func TestAuditConfigUpdate(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
// Get current level
getResp := client.Get("/api/tenant/audit/config", nil)
getResp.AssertSuccess(t)
var getData struct {
AuditLevel string `json:"audit_level"`
}
getResp.DecodeData(t, &getData)
// Update to a different level
newLevel := "full_text"
if getData.AuditLevel == "full_text" {
newLevel = "masked"
}
updateResp := client.Put("/api/tenant/audit/config", map[string]any{
"audit_level": newLevel,
})
updateResp.AssertSuccess(t)
// Restore original level
client.Put("/api/tenant/audit/config", map[string]any{
"audit_level": getData.AuditLevel,
})
}
func TestAuditLogs(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/audit/logs", map[string]string{
"page": "1",
"page_size": "10",
})
resp.AssertSuccess(t)
var data struct {
List []map[string]any `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
resp.DecodeData(t, &data)
}
func TestRequestAuditLogs(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/audit/request-logs", map[string]string{
"page": "1",
"page_size": "10",
})
resp.AssertSuccess(t)
var data struct {
List []map[string]any `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
resp.DecodeData(t, &data)
}
func TestRequestAuditLogsWithFilters(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
// Filter by username
resp := client.Get("/api/tenant/audit/request-logs", map[string]string{
"page": "1",
"page_size": "10",
"username": "owner",
})
resp.AssertSuccess(t)
// Filter by date range
resp = client.Get("/api/tenant/audit/request-logs", map[string]string{
"page": "1",
"page_size": "10",
"start_date": "2025-01-01",
"end_date": "2026-12-31",
})
resp.AssertSuccess(t)
}
+203
View File
@@ -0,0 +1,203 @@
//go:build integration
package tenant_test
import (
"fmt"
"testing"
admintest "github.com/qianfree/team-api/tests/integration/admin/testinfra"
"github.com/qianfree/team-api/tests/integration/tenant/testinfra"
)
func TestTenantRegister_Success(t *testing.T) {
result := testinfra.RegisterTestTenant(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")
}
if result.Tenant.ID == 0 {
t.Fatal("expected tenant.id > 0")
}
if result.Tenant.Code == "" {
t.Fatal("expected tenant.code")
}
if result.User.Role != "owner" {
t.Fatalf("expected role=owner, got %s", result.User.Role)
}
}
func TestTenantRegister_DuplicateTenantCode(t *testing.T) {
result := testinfra.RegisterTestTenant(t)
// Try registering again with the same tenant_code
captchaKey, captchaX := testinfra.SolveCaptcha(t)
client := admintest.NewAPIClient(testinfra.DefaultBaseURL)
resp := client.Post("/api/tenant/auth/register", map[string]any{
"email": fmt.Sprintf("dup-%s@test.com", result.TenantCode),
"password": testinfra.TestPassword,
"tenant_name": "重复租户",
"tenant_code": result.TenantCode,
"username": "anotheruser",
"captcha_key": captchaKey,
"captcha_x": captchaX,
})
if resp.Code == 0 {
t.Fatal("expected error for duplicate tenant_code, but got success")
}
}
func TestTenantLogin_RAM_Success(t *testing.T) {
regResult := testinfra.RegisterTestTenant(t)
loginResult := testinfra.LoginTenant(t, regResult.Username, regResult.TenantCode, regResult.Password)
if loginResult.AccessToken == "" {
t.Fatal("expected non-empty access_token after RAM login")
}
if loginResult.RefreshToken == "" {
t.Fatal("expected non-empty refresh_token after RAM login")
}
}
func TestTenantLogin_InvalidPassword(t *testing.T) {
regResult := testinfra.RegisterTestTenant(t)
captchaKey, captchaX := testinfra.SolveCaptcha(t)
client := admintest.NewAPIClient(testinfra.DefaultBaseURL)
account := fmt.Sprintf("%s@%s", regResult.Username, regResult.TenantCode)
resp := client.Post("/api/tenant/auth/login", map[string]any{
"account": account,
"password": "WrongPassword123!",
"type": "ram",
"captcha_key": captchaKey,
"captcha_x": captchaX,
})
if resp.Code == 0 {
t.Fatal("expected login to fail with wrong password, but got success")
}
}
func TestTenantLogout_Success(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
logoutResp := client.Post("/api/tenant/auth/logout", nil)
logoutResp.AssertSuccess(t)
// Verify token is invalidated
sessionResp := client.Get("/api/tenant/auth/sessions", map[string]string{
"page": "1",
"page_size": "10",
})
sessionResp.AssertHTTPStatus(t, 401)
}
func TestTenantTokenRefresh(t *testing.T) {
regResult := testinfra.RegisterTestTenant(t)
client := admintest.NewAPIClient(testinfra.DefaultBaseURL)
refreshResp := client.Post("/api/tenant/auth/refresh", map[string]any{
"refresh_token": regResult.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")
}
// Verify new token works
authedClient := admintest.NewAPIClient(testinfra.DefaultBaseURL).WithToken(refreshData.AccessToken)
sessionsResp := authedClient.Get("/api/tenant/auth/sessions", map[string]string{
"page": "1",
"page_size": "10",
})
sessionsResp.AssertSuccess(t)
}
func TestTenantChangePassword(t *testing.T) {
regResult := testinfra.RegisterTestTenant(t)
client := admintest.NewAPIClient(testinfra.DefaultBaseURL).WithToken(regResult.AccessToken)
newPassword := "NewPass456!"
changeResp := client.Put("/api/tenant/auth/change-password", map[string]any{
"old_password": regResult.Password,
"new_password": newPassword,
})
changeResp.AssertSuccess(t)
// Verify can login with new password
loginResult := testinfra.LoginTenant(t, regResult.Username, regResult.TenantCode, newPassword)
if loginResult.AccessToken == "" {
t.Fatal("expected login with new password to succeed")
}
}
func TestTenantSessionList(t *testing.T) {
regResult := testinfra.RegisterTestTenant(t)
client := admintest.NewAPIClient(testinfra.DefaultBaseURL).WithToken(regResult.AccessToken)
resp := client.Get("/api/tenant/auth/sessions", map[string]string{
"page": "1",
"page_size": "10",
})
resp.AssertSuccess(t)
// Verify paginated response structure exists
var data struct {
List []any `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
resp.DecodeData(t, &data)
}
func TestTenantSessionRevoke(t *testing.T) {
regResult := testinfra.RegisterTestTenant(t)
client := admintest.NewAPIClient(testinfra.DefaultBaseURL).WithToken(regResult.AccessToken)
// List sessions
listResp := client.Get("/api/tenant/auth/sessions", map[string]string{
"page": "1",
"page_size": "10",
})
listResp.AssertSuccess(t)
var listData struct {
List []struct {
ID int64 `json:"id"`
} `json:"list"`
Total int `json:"total"`
}
listResp.DecodeData(t, &listData)
if len(listData.List) == 0 {
t.Skip("no sessions to revoke, session list empty")
}
// Revoke the first session
revokeResp := client.Delete(fmt.Sprintf("/api/tenant/auth/sessions/%d", listData.List[0].ID))
revokeResp.AssertSuccess(t)
}
func TestTenantAuth_Unauthorized(t *testing.T) {
client := admintest.NewAPIClient(testinfra.DefaultBaseURL)
resp := client.Get("/api/tenant/auth/sessions", map[string]string{
"page": "1",
"page_size": "10",
})
resp.AssertHTTPStatus(t, 401)
}
+65
View File
@@ -0,0 +1,65 @@
//go:build integration
package tenant_test
import (
"testing"
"github.com/qianfree/team-api/tests/integration/tenant/testinfra"
)
func TestUsageLogs(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/usage-logs", map[string]string{
"page": "1",
"page_size": "10",
})
resp.AssertSuccess(t)
var data struct {
List []map[string]any `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
resp.DecodeData(t, &data)
}
func TestUsageLogsWithFilters(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
// Filter by status
resp := client.Get("/api/tenant/usage-logs", map[string]string{
"page": "1",
"page_size": "10",
"status": "success",
})
resp.AssertSuccess(t)
// Filter by date range
resp = client.Get("/api/tenant/usage-logs", map[string]string{
"page": "1",
"page_size": "10",
"start_date": "2025-01-01",
"end_date": "2026-12-31",
})
resp.AssertSuccess(t)
// Filter by username
resp = client.Get("/api/tenant/usage-logs", map[string]string{
"page": "1",
"page_size": "10",
"username": "owner",
})
resp.AssertSuccess(t)
}
func TestUsageLogsExport(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/usage-logs/export", map[string]string{
"format": "csv",
})
resp.AssertSuccess(t)
}
@@ -0,0 +1,95 @@
//go:build integration
package tenant_test
import (
"testing"
"github.com/qianfree/team-api/tests/integration/tenant/testinfra"
)
func TestDashboardOverview(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/dashboard", nil)
resp.AssertSuccess(t)
var data struct {
Today map[string]any `json:"today"`
Month map[string]any `json:"month"`
Wallet map[string]any `json:"wallet"`
ActiveKeys int `json:"active_keys"`
MemberCount int `json:"member_count"`
}
resp.DecodeData(t, &data)
}
func TestDashboardTokenTrends(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/dashboard/token-trends", map[string]string{
"days": "7",
})
resp.AssertSuccess(t)
var data struct {
List []map[string]any `json:"list"`
}
resp.DecodeData(t, &data)
}
func TestDashboardModelDistribution(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/dashboard/model-distribution", map[string]string{
"days": "7",
})
resp.AssertSuccess(t)
var data struct {
List []map[string]any `json:"list"`
}
resp.DecodeData(t, &data)
}
func TestDashboardBalancePrediction(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/dashboard/balance-prediction", nil)
resp.AssertSuccess(t)
var data struct {
DailyAvgCost float64 `json:"daily_avg_cost"`
AvailableBalance float64 `json:"available_balance"`
WillExhaust bool `json:"will_exhaust"`
}
resp.DecodeData(t, &data)
}
func TestDashboardBudgetAlerts(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/dashboard/budget-alerts", nil)
resp.AssertSuccess(t)
var data struct {
Members []map[string]any `json:"members"`
Projects []map[string]any `json:"projects"`
}
resp.DecodeData(t, &data)
}
func TestDashboardMemberUsageRanking(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/dashboard/member-usage-ranking", map[string]string{
"days": "7",
"limit": "10",
})
resp.AssertSuccess(t)
var data struct {
List []map[string]any `json:"list"`
}
resp.DecodeData(t, &data)
}
+123
View File
@@ -0,0 +1,123 @@
//go:build integration
package tenant_test
import (
"fmt"
"testing"
"github.com/qianfree/team-api/tests/integration/tenant/testinfra"
)
func TestFeedbackCreate(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Post("/api/tenant/feedbacks", map[string]any{
"category": "bug_report",
"title": fmt.Sprintf("测试反馈 %s", testinfra.RandomSuffix()),
"description": "这是一个集成测试创建的反馈",
})
resp.AssertSuccess(t)
var data struct {
ID int64 `json:"id"`
}
resp.DecodeData(t, &data)
if data.ID == 0 {
t.Fatal("expected non-zero feedback id")
}
}
func TestFeedbackList(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
// Create a feedback first
createResp := client.Post("/api/tenant/feedbacks", map[string]any{
"category": "suggestion",
"title": fmt.Sprintf("列表测试反馈 %s", testinfra.RandomSuffix()),
"description": "测试反馈列表功能",
})
createResp.AssertSuccess(t)
// List feedbacks
resp := client.Get("/api/tenant/feedbacks", map[string]string{
"page": "1",
"page_size": "10",
})
resp.AssertSuccess(t)
var data struct {
List []struct {
ID int64 `json:"id"`
Category string `json:"category"`
Title string `json:"title"`
Status string `json:"status"`
} `json:"list"`
Total int `json:"total"`
}
resp.DecodeData(t, &data)
if data.Total < 1 {
t.Fatalf("expected at least 1 feedback, got total=%d", data.Total)
}
}
func TestFeedbackListWithFilters(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
// Create feedbacks with different categories
client.Post("/api/tenant/feedbacks", map[string]any{
"category": "bug_report",
"title": "Bug反馈过滤测试",
"description": "测试按分类过滤",
})
client.Post("/api/tenant/feedbacks", map[string]any{
"category": "feature_request",
"title": "功能请求过滤测试",
"description": "测试按分类过滤",
})
// Filter by category
resp := client.Get("/api/tenant/feedbacks", map[string]string{
"page": "1",
"page_size": "10",
"category": "bug_report",
})
resp.AssertSuccess(t)
}
func TestFeedbackGet(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
// Create a feedback
createResp := client.Post("/api/tenant/feedbacks", map[string]any{
"category": "complaint",
"title": "详情测试反馈",
"description": "测试获取反馈详情",
})
createResp.AssertSuccess(t)
var createData struct {
ID int64 `json:"id"`
}
createResp.DecodeData(t, &createData)
// Get detail
detailResp := client.Get(fmt.Sprintf("/api/tenant/feedbacks/%d", createData.ID), nil)
detailResp.AssertSuccess(t)
var detail struct {
ID int64 `json:"id"`
Category string `json:"category"`
Title string `json:"title"`
Description string `json:"description"`
Status string `json:"status"`
}
detailResp.DecodeData(t, &detail)
if detail.ID != createData.ID {
t.Fatalf("expected id=%d, got %d", createData.ID, detail.ID)
}
if detail.Category != "complaint" {
t.Fatalf("expected category=complaint, got %s", detail.Category)
}
}
+325
View File
@@ -0,0 +1,325 @@
//go:build integration
package tenant_test
import (
"fmt"
"testing"
admintest "github.com/qianfree/team-api/tests/integration/admin/testinfra"
"github.com/qianfree/team-api/tests/integration/tenant/testinfra"
)
func TestMemberList(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
memberID, cleanup := testinfra.CreateTestTenantMember(t, client)
defer cleanup()
resp := client.Get("/api/tenant/members", map[string]string{
"page": "1",
"page_size": "10",
})
admintest.AssertPaginatedList(t, resp, 2) // owner + member
// Verify the created member appears
var listData struct {
List []struct {
ID int64 `json:"id"`
Username string `json:"username"`
Role string `json:"role"`
} `json:"list"`
}
resp.DecodeData(t, &listData)
found := false
for _, m := range listData.List {
if m.ID == memberID {
found = true
if m.Role != "member" {
t.Fatalf("expected role=member, got %s", m.Role)
}
break
}
}
if !found {
t.Fatalf("created member %d not found in list", memberID)
}
}
func TestMemberListWithFilters(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
_, cleanup := testinfra.CreateTestTenantMember(t, client)
defer cleanup()
// Filter by role
resp := client.Get("/api/tenant/members", map[string]string{
"page": "1",
"page_size": "10",
"role": "member",
})
resp.AssertSuccess(t)
var memberList struct {
List []struct {
Role string `json:"role"`
} `json:"list"`
}
resp.DecodeData(t, &memberList)
for _, m := range memberList.List {
if m.Role != "member" {
t.Fatalf("expected role=member when filtering, got %s", m.Role)
}
}
// Filter by keyword
resp = client.Get("/api/tenant/members", map[string]string{
"page": "1",
"page_size": "10",
"keyword": "member",
})
resp.AssertSuccess(t)
}
func TestMemberCreate(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
memberID, cleanup := testinfra.CreateTestTenantMember(t, client)
defer cleanup()
// Get detail
detailResp := client.Get(fmt.Sprintf("/api/tenant/members/%d", memberID), nil)
detailResp.AssertSuccess(t)
var detail struct {
ID int64 `json:"id"`
Username string `json:"username"`
Role string `json:"role"`
Status string `json:"status"`
}
detailResp.DecodeData(t, &detail)
if detail.ID != memberID {
t.Fatalf("expected id=%d, got %d", memberID, detail.ID)
}
if detail.Role != "member" {
t.Fatalf("expected role=member, got %s", detail.Role)
}
if detail.Status != "active" {
t.Fatalf("expected status=active, got %s", detail.Status)
}
}
func TestMemberInvite(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
// Create invitation
inviteResp := client.Post("/api/tenant/members/invite", map[string]any{
"role": "member",
"expires_days": 7,
"max_uses": 5,
})
inviteResp.AssertSuccess(t)
var inviteData struct {
Code string `json:"code"`
InviteURL string `json:"invite_url"`
ExpiresAt string `json:"expires_at"`
MaxUses int `json:"max_uses"`
}
inviteResp.DecodeData(t, &inviteData)
if inviteData.Code == "" {
t.Fatal("expected non-empty invite code")
}
if inviteData.MaxUses != 5 {
t.Fatalf("expected max_uses=5, got %d", inviteData.MaxUses)
}
// List invitations
listResp := client.Get("/api/tenant/members/invitations", map[string]string{
"page": "1",
"page_size": "10",
})
admintest.AssertPaginatedList(t, listResp, 1)
}
func TestMemberInviteInfo(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
inviteResp := client.Post("/api/tenant/members/invite", map[string]any{
"role": "member",
})
inviteResp.AssertSuccess(t)
var inviteData struct {
Code string `json:"code"`
}
inviteResp.DecodeData(t, &inviteData)
// Query invite info (public endpoint)
pubClient := admintest.NewAPIClient(testinfra.DefaultBaseURL)
infoResp := pubClient.Get("/api/tenant/members/invite-info", map[string]string{
"code": inviteData.Code,
})
infoResp.AssertSuccess(t)
var info struct {
TenantName string `json:"tenant_name"`
Role string `json:"role"`
Valid bool `json:"valid"`
}
infoResp.DecodeData(t, &info)
if !info.Valid {
t.Fatal("expected invite to be valid")
}
if info.Role != "member" {
t.Fatalf("expected role=member, got %s", info.Role)
}
}
func TestMemberInviteRevoke(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
inviteResp := client.Post("/api/tenant/members/invite", map[string]any{
"role": "member",
})
inviteResp.AssertSuccess(t)
// List invitations to get the ID
listResp := client.Get("/api/tenant/members/invitations", map[string]string{
"page": "1",
"page_size": "10",
})
listResp.AssertSuccess(t)
var listData struct {
List []struct {
ID int64 `json:"id"`
} `json:"list"`
}
listResp.DecodeData(t, &listData)
if len(listData.List) == 0 {
t.Fatal("expected at least 1 invitation")
}
// Revoke
revokeResp := client.Delete(fmt.Sprintf("/api/tenant/members/invitations/%d", listData.List[0].ID))
revokeResp.AssertSuccess(t)
}
func TestMemberUpdateRole(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
memberID, cleanup := testinfra.CreateTestTenantMember(t, client)
defer cleanup()
// Update role to admin
updateResp := client.Put(fmt.Sprintf("/api/tenant/members/%d/role", memberID), map[string]any{
"role": "admin",
})
updateResp.AssertSuccess(t)
// Verify
detailResp := client.Get(fmt.Sprintf("/api/tenant/members/%d", memberID), nil)
detailResp.AssertSuccess(t)
var detail struct {
Role string `json:"role"`
}
detailResp.DecodeData(t, &detail)
if detail.Role != "admin" {
t.Fatalf("expected role=admin after update, got %s", detail.Role)
}
}
func TestMemberResetPassword(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
memberID, cleanup := testinfra.CreateTestTenantMember(t, client)
defer cleanup()
resetResp := client.Put(fmt.Sprintf("/api/tenant/members/%d/reset-password", memberID), map[string]any{
"password": "NewPass789!",
})
resetResp.AssertSuccess(t)
}
func TestMemberRemove(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
memberID, _ := testinfra.CreateTestTenantMember(t, client)
// Remove
removeResp := client.Delete(fmt.Sprintf("/api/tenant/members/%d", memberID))
removeResp.AssertSuccess(t)
// Verify removed - get detail should fail or return inactive status
detailResp := client.Get(fmt.Sprintf("/api/tenant/members/%d", memberID), nil)
detailResp.AssertSuccess(t)
var detail struct {
Status string `json:"status"`
}
detailResp.DecodeData(t, &detail)
if detail.Status == "active" {
t.Fatal("expected member to not be active after removal")
}
}
func TestMemberUsage(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
memberID, cleanup := testinfra.CreateTestTenantMember(t, client)
defer cleanup()
resp := client.Get(fmt.Sprintf("/api/tenant/members/%d/usage", memberID), nil)
resp.AssertSuccess(t)
var usage struct {
TodayRequests float64 `json:"today_requests"`
MonthRequests float64 `json:"month_requests"`
MonthInputTok float64 `json:"month_input_tokens"`
MonthOutputTok float64 `json:"month_output_tokens"`
MonthTotalCost float64 `json:"month_total_cost"`
}
resp.DecodeData(t, &usage)
// New member should have zero usage
if usage.TodayRequests != 0 {
t.Fatalf("expected today_requests=0, got %f", usage.TodayRequests)
}
}
func TestMemberApiKeys(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
memberID, cleanup := testinfra.CreateTestTenantMember(t, client)
defer cleanup()
resp := client.Get(fmt.Sprintf("/api/tenant/members/%d/api-keys", memberID), map[string]string{
"page": "1",
"page_size": "10",
})
resp.AssertSuccess(t)
var data struct {
List []any `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
resp.DecodeData(t, &data)
if data.Total != 0 {
t.Fatalf("expected 0 api keys for new member, got %d", data.Total)
}
}
func TestMemberExport(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/members/export", map[string]string{
"format": "csv",
})
resp.AssertSuccess(t)
}
+44
View File
@@ -0,0 +1,44 @@
//go:build integration
package tenant_test
import (
"testing"
"github.com/qianfree/team-api/tests/integration/tenant/testinfra"
)
func TestAvailableModels(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/models", nil)
resp.AssertSuccess(t)
var data struct {
List []struct {
ID int64 `json:"id"`
ModelID string `json:"model_id"`
ModelName string `json:"model_name"`
Category string `json:"category"`
} `json:"list"`
}
resp.DecodeData(t, &data)
}
func TestAvailableModelsFilterByCategory(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/models", map[string]string{
"category": "chat",
})
resp.AssertSuccess(t)
}
func TestAvailableModelsSearch(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/models", map[string]string{
"search": "gpt",
})
resp.AssertSuccess(t)
}
@@ -0,0 +1,106 @@
//go:build integration
package tenant_test
import (
"testing"
"github.com/qianfree/team-api/tests/integration/tenant/testinfra"
)
func TestNotificationList(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/notifications", map[string]string{
"page": "1",
"page_size": "10",
})
resp.AssertSuccess(t)
var data struct {
List []map[string]any `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
resp.DecodeData(t, &data)
}
func TestUnreadCount(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/notifications/unread-count", nil)
resp.AssertSuccess(t)
var data struct {
UnreadCount int `json:"unread_count"`
}
resp.DecodeData(t, &data)
}
func TestMarkAllRead(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Post("/api/tenant/notifications/read-all", nil)
resp.AssertSuccess(t)
}
func TestNotificationPreferences(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
// Get preferences
getResp := client.Get("/api/tenant/notification-preferences", nil)
getResp.AssertSuccess(t)
// Update preferences (user scope)
updateResp := client.Put("/api/tenant/notification-preferences", map[string]any{
"scope": "user",
"preferences": map[string]any{
"billing": map[string]any{
"email": true,
"in_app": true,
},
},
})
updateResp.AssertSuccess(t)
// Verify update
verifyResp := client.Get("/api/tenant/notification-preferences", nil)
verifyResp.AssertSuccess(t)
}
func TestNotificationPreferencesOrg(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
// Update preferences (org scope, owner can do this)
updateResp := client.Put("/api/tenant/notification-preferences", map[string]any{
"scope": "org",
"preferences": map[string]any{
"security": map[string]any{
"email": true,
},
},
})
updateResp.AssertSuccess(t)
}
func TestAnnouncements(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/announcements", nil)
resp.AssertSuccess(t)
var data struct {
List []map[string]any `json:"list"`
}
resp.DecodeData(t, &data)
}
func TestNotificationsExport(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/notifications/export", map[string]string{
"format": "csv",
})
resp.AssertSuccess(t)
}
@@ -0,0 +1,135 @@
//go:build integration
package tenant_test
import (
"fmt"
"testing"
"github.com/qianfree/team-api/tests/integration/tenant/testinfra"
)
func TestOrgInfo(t *testing.T) {
client, regResult := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/organization", nil)
resp.AssertSuccess(t)
var data struct {
ID int64 `json:"id"`
Name string `json:"name"`
Code string `json:"code"`
Status string `json:"status"`
MemberCount int `json:"member_count"`
}
resp.DecodeData(t, &data)
if data.Code != regResult.TenantCode {
t.Fatalf("expected code=%s, got %s", regResult.TenantCode, data.Code)
}
if data.Status != "active" {
t.Fatalf("expected status=active, got %s", data.Status)
}
if data.MemberCount < 1 {
t.Fatalf("expected member_count >= 1, got %d", data.MemberCount)
}
}
func TestOrgUpdate(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
newName := fmt.Sprintf("更新组织名 %s", testinfra.RandomSuffix())
updateResp := client.Put("/api/tenant/organization", map[string]any{
"name": newName,
})
updateResp.AssertSuccess(t)
// Verify update
verifyResp := client.Get("/api/tenant/organization", nil)
verifyResp.AssertSuccess(t)
var data struct {
Name string `json:"name"`
}
verifyResp.DecodeData(t, &data)
if data.Name != newName {
t.Fatalf("expected name=%s, got %s", newName, data.Name)
}
}
func TestProfile(t *testing.T) {
client, regResult := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/profile", nil)
resp.AssertSuccess(t)
var data struct {
ID int64 `json:"id"`
Username string `json:"username"`
Role string `json:"role"`
Status string `json:"status"`
}
resp.DecodeData(t, &data)
if data.Username != regResult.Username {
t.Fatalf("expected username=%s, got %s", regResult.Username, data.Username)
}
if data.Role != "owner" {
t.Fatalf("expected role=owner, got %s", data.Role)
}
}
func TestProfileUpdate(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
newDisplayName := fmt.Sprintf("测试用户 %s", testinfra.RandomSuffix())
updateResp := client.Put("/api/tenant/profile", map[string]any{
"display_name": newDisplayName,
})
updateResp.AssertSuccess(t)
// Verify update
verifyResp := client.Get("/api/tenant/profile", nil)
verifyResp.AssertSuccess(t)
var data struct {
DisplayName string `json:"display_name"`
}
verifyResp.DecodeData(t, &data)
if data.DisplayName != newDisplayName {
t.Fatalf("expected display_name=%s, got %s", newDisplayName, data.DisplayName)
}
}
func TestIPWhitelist(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
// Get current whitelist
getResp := client.Get("/api/tenant/security/ip-whitelist", nil)
getResp.AssertSuccess(t)
var getData struct {
Enabled bool `json:"enabled"`
Whitelist []string `json:"whitelist"`
}
getResp.DecodeData(t, &getData)
// Update whitelist (disable it to avoid locking ourselves out)
updateResp := client.Put("/api/tenant/security/ip-whitelist", map[string]any{
"enabled": false,
"whitelist": []string{},
})
updateResp.AssertSuccess(t)
// Verify update
verifyResp := client.Get("/api/tenant/security/ip-whitelist", nil)
verifyResp.AssertSuccess(t)
var verifyData struct {
Enabled bool `json:"enabled"`
}
verifyResp.DecodeData(t, &verifyData)
if verifyData.Enabled {
t.Fatal("expected ip whitelist to be disabled after update")
}
}
@@ -0,0 +1,65 @@
//go:build integration
package tenant_test
import (
"testing"
"github.com/qianfree/team-api/tests/integration/tenant/testinfra"
)
func TestPersonalDashboardOverview(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/personal-dashboard", nil)
resp.AssertSuccess(t)
var data struct {
Today map[string]any `json:"today"`
Month map[string]any `json:"month"`
ErrorRate map[string]any `json:"error_rate"`
}
resp.DecodeData(t, &data)
}
func TestPersonalDashboardTrends(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/personal-dashboard/trends", map[string]string{
"days": "7",
})
resp.AssertSuccess(t)
var data struct {
List []map[string]any `json:"list"`
}
resp.DecodeData(t, &data)
}
func TestPersonalDashboardModels(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/personal-dashboard/models", map[string]string{
"days": "7",
})
resp.AssertSuccess(t)
var data struct {
List []map[string]any `json:"list"`
}
resp.DecodeData(t, &data)
}
func TestPersonalDashboardApiKeyUsage(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/personal-dashboard/api-key-usage", map[string]string{
"days": "7",
})
resp.AssertSuccess(t)
var data struct {
List []map[string]any `json:"list"`
}
resp.DecodeData(t, &data)
}
+47
View File
@@ -0,0 +1,47 @@
//go:build integration
package tenant_test
import (
"testing"
"github.com/qianfree/team-api/tests/integration/tenant/testinfra"
)
func TestPlanList(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/plans", nil)
resp.AssertSuccess(t)
var data struct {
List []struct {
ID int64 `json:"id"`
Name string `json:"name"`
Identifier string `json:"identifier"`
} `json:"list"`
}
resp.DecodeData(t, &data)
}
func TestCurrentPlan(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/plan/current", nil)
// New tenant may not have a plan, accept either success or error
resp.AssertSuccess(t)
var data struct {
ID int64 `json:"id"`
Status string `json:"status"`
}
resp.DecodeData(t, &data)
}
func TestCancelAutoRenew(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Put("/api/tenant/plan/cancel-auto-renew", nil)
// New tenant likely has no active plan, so this may fail gracefully
resp.AssertSuccess(t)
}
+151
View File
@@ -0,0 +1,151 @@
//go:build integration
package tenant_test
import (
"fmt"
"testing"
admintest "github.com/qianfree/team-api/tests/integration/admin/testinfra"
"github.com/qianfree/team-api/tests/integration/tenant/testinfra"
)
func TestProjectList(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
_, cleanup := testinfra.CreateTestProject(t, client)
defer cleanup()
resp := client.Get("/api/tenant/projects", map[string]string{
"page": "1",
"page_size": "10",
})
admintest.AssertPaginatedList(t, resp, 1)
}
func TestProjectCRUD(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
// --- Create ---
suffix := testinfra.RandomSuffix()
createResp := client.Post("/api/tenant/projects", map[string]any{
"name": fmt.Sprintf("crud-project-%s", suffix),
"description": "Integration test project",
"budget": 100.0,
})
createResp.AssertSuccess(t)
projectID := createResp.GetID(t)
defer func() {
client.Post(fmt.Sprintf("/api/tenant/projects/%d/archive", projectID), nil)
}()
// --- Get detail ---
detailResp := client.Get(fmt.Sprintf("/api/tenant/projects/%d", projectID), nil)
detailResp.AssertSuccess(t)
var detail struct {
Data struct {
ID int64 `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Budget string `json:"budget"`
Status string `json:"status"`
} `json:"data"`
}
detailResp.DecodeData(t, &detail)
if detail.Data.ID != projectID {
t.Fatalf("expected id=%d, got %d", projectID, detail.Data.ID)
}
// --- Update ---
updateResp := client.Put(fmt.Sprintf("/api/tenant/projects/%d", projectID), map[string]any{
"name": fmt.Sprintf("updated-project-%s", suffix),
"description": "Updated description",
"budget": 200.0,
})
updateResp.AssertSuccess(t)
// Verify update
verifyResp := client.Get(fmt.Sprintf("/api/tenant/projects/%d", projectID), nil)
verifyResp.AssertSuccess(t)
}
func TestProjectArchiveUnarchive(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
projectID, _ := testinfra.CreateTestProject(t, client)
// Archive
archiveResp := client.Post(fmt.Sprintf("/api/tenant/projects/%d/archive", projectID), nil)
archiveResp.AssertSuccess(t)
// Verify archived via list
listResp := client.Get("/api/tenant/projects", map[string]string{
"page": "1",
"page_size": "50",
})
listResp.AssertSuccess(t)
// Unarchive
unarchiveResp := client.Post(fmt.Sprintf("/api/tenant/projects/%d/unarchive", projectID), nil)
unarchiveResp.AssertSuccess(t)
}
func TestProjectApiKeys(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
projectID, cleanup := testinfra.CreateTestProject(t, client)
defer cleanup()
// Create project API key
createKeyResp := client.Post(fmt.Sprintf("/api/tenant/projects/%d/api-keys", projectID), map[string]any{
"name": "project-test-key",
})
createKeyResp.AssertSuccess(t)
// List project API keys
listResp := client.Get(fmt.Sprintf("/api/tenant/projects/%d/api-keys", projectID), map[string]string{
"page": "1",
"page_size": "10",
})
admintest.AssertPaginatedList(t, listResp, 1)
var listData struct {
List []struct {
ID int64 `json:"id"`
} `json:"list"`
}
listResp.DecodeData(t, &listData)
if len(listData.List) == 0 {
t.Fatal("expected at least 1 project API key")
}
// Delete project API key
keyID := listData.List[0].ID
deleteResp := client.Delete(fmt.Sprintf("/api/tenant/projects/%d/api-keys/%d", projectID, keyID))
deleteResp.AssertSuccess(t)
}
func TestProjectUsageStats(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
projectID, cleanup := testinfra.CreateTestProject(t, client)
defer cleanup()
resp := client.Get(fmt.Sprintf("/api/tenant/projects/%d/usage-stats", projectID), nil)
resp.AssertSuccess(t)
}
func TestProjectUsageLogs(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
projectID, cleanup := testinfra.CreateTestProject(t, client)
defer cleanup()
resp := client.Get(fmt.Sprintf("/api/tenant/projects/%d/usage-logs", projectID), map[string]string{
"page": "1",
"page_size": "10",
})
resp.AssertSuccess(t)
}
+62
View File
@@ -0,0 +1,62 @@
//go:build integration
package tenant_test
import (
"testing"
"github.com/qianfree/team-api/tests/integration/tenant/testinfra"
)
func TestLoginHistory(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/security/login-history", map[string]string{
"page": "1",
"page_size": "10",
})
resp.AssertSuccess(t)
var data struct {
List []struct {
ID int64 `json:"id"`
Username string `json:"username"`
LoginMethod string `json:"login_method"`
IPAddress string `json:"ip_address"`
Success bool `json:"success"`
} `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
resp.DecodeData(t, &data)
}
func TestLoginHistoryWithFilters(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
// Filter by IP
resp := client.Get("/api/tenant/security/login-history", map[string]string{
"page": "1",
"page_size": "10",
"ip_address": "127.0.0.1",
})
resp.AssertSuccess(t)
// Filter by success status
resp = client.Get("/api/tenant/security/login-history", map[string]string{
"page": "1",
"page_size": "10",
"success": "true",
})
resp.AssertSuccess(t)
// Filter by date range
resp = client.Get("/api/tenant/security/login-history", map[string]string{
"page": "1",
"page_size": "10",
"start_time": "2025-01-01",
"end_time": "2026-12-31",
})
resp.AssertSuccess(t)
}
+153
View File
@@ -0,0 +1,153 @@
//go:build integration
package testinfra
import (
"context"
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"testing"
"time"
"github.com/redis/go-redis/v9"
admintest "github.com/qianfree/team-api/tests/integration/admin/testinfra"
)
func RandomSuffix() string {
b := make([]byte, 4)
_, _ = rand.Read(b)
return hex.EncodeToString(b)
}
func getRedisClient() *redis.Client {
return redis.NewClient(&redis.Options{
Addr: DefaultRedisAddr,
DB: 0,
DialTimeout: 3 * time.Second,
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
})
}
func SolveCaptcha(t *testing.T) (captchaKey string, captchaX int) {
t.Helper()
client := admintest.NewAPIClient(DefaultBaseURL)
captchaResp := client.Get("/api/captcha/", nil)
captchaResp.AssertSuccess(t)
var captchaData struct {
CaptchaKey string `json:"captcha_key"`
}
captchaResp.DecodeData(t, &captchaData)
rdb := getRedisClient()
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)
}
return captchaData.CaptchaKey, answer.X
}
type TenantRegisterResult struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresAt string `json:"expires_at"`
Tenant struct {
ID int64 `json:"id"`
Name string `json:"name"`
Code string `json:"code"`
} `json:"tenant"`
User struct {
ID int64 `json:"id"`
Username string `json:"username"`
Role string `json:"role"`
} `json:"user"`
// Stored for re-login tests
TenantCode string
Username string
Password string
Email string
}
func RegisterTestTenant(t *testing.T) *TenantRegisterResult {
t.Helper()
suffix := RandomSuffix()
tenantCode := fmt.Sprintf("t-%s", suffix)
username := fmt.Sprintf("owner%s", suffix)
email := fmt.Sprintf("%s@test.com", username)
captchaKey, captchaX := SolveCaptcha(t)
client := admintest.NewAPIClient(DefaultBaseURL)
resp := client.Post("/api/tenant/auth/register", map[string]any{
"email": email,
"password": TestPassword,
"tenant_name": fmt.Sprintf("测试租户 %s", suffix),
"tenant_code": tenantCode,
"username": username,
"captcha_key": captchaKey,
"captcha_x": captchaX,
})
resp.AssertSuccess(t)
var result TenantRegisterResult
resp.DecodeData(t, &result)
result.TenantCode = tenantCode
result.Username = username
result.Password = TestPassword
result.Email = email
return &result
}
func GetAuthedClient(t *testing.T) (*admintest.APIClient, *TenantRegisterResult) {
t.Helper()
result := RegisterTestTenant(t)
client := admintest.NewAPIClient(DefaultBaseURL).WithToken(result.AccessToken)
return client, result
}
func LoginTenant(t *testing.T, username, tenantCode, password string) *TenantRegisterResult {
t.Helper()
captchaKey, captchaX := SolveCaptcha(t)
client := admintest.NewAPIClient(DefaultBaseURL)
account := fmt.Sprintf("%s@%s", username, tenantCode)
resp := client.Post("/api/tenant/auth/login", map[string]any{
"account": account,
"password": password,
"type": "ram",
"captcha_key": captchaKey,
"captcha_x": captchaX,
})
resp.AssertSuccess(t)
var result TenantRegisterResult
resp.DecodeData(t, &result)
result.TenantCode = tenantCode
result.Username = username
result.Password = password
return &result
}
@@ -0,0 +1,9 @@
//go:build integration
package testinfra
const (
DefaultBaseURL = "http://127.0.0.1:18888"
DefaultRedisAddr = "192.168.50.22:16380"
TestPassword = "TestPass123!"
)
@@ -0,0 +1,53 @@
//go:build integration
package testinfra
import (
"fmt"
"testing"
admintest "github.com/qianfree/team-api/tests/integration/admin/testinfra"
)
func CreateTestTenantMember(t *testing.T, client *admintest.APIClient) (id int64, cleanup func()) {
t.Helper()
suffix := RandomSuffix()
username := fmt.Sprintf("member%s", suffix)
resp := client.Post("/api/tenant/members/create", map[string]any{
"username": username,
"password": TestPassword,
"email": fmt.Sprintf("%s@test.com", username),
"role": "member",
})
resp.AssertSuccess(t)
id = resp.GetID(t)
return id, func() {
client.Delete(fmt.Sprintf("/api/tenant/members/%d", id))
}
}
func CreateTestApiKey(t *testing.T, client *admintest.APIClient) (id int64, cleanup func()) {
t.Helper()
suffix := RandomSuffix()
resp := client.Post("/api/tenant/api-keys", map[string]any{
"name": fmt.Sprintf("test-key-%s", suffix),
})
resp.AssertSuccess(t)
id = resp.GetID(t)
return id, func() {
client.Delete(fmt.Sprintf("/api/tenant/api-keys/%d", id))
}
}
func CreateTestProject(t *testing.T, client *admintest.APIClient) (id int64, cleanup func()) {
t.Helper()
suffix := RandomSuffix()
resp := client.Post("/api/tenant/projects", map[string]any{
"name": fmt.Sprintf("test-project-%s", suffix),
})
resp.AssertSuccess(t)
id = resp.GetID(t)
return id, func() {
client.Post(fmt.Sprintf("/api/tenant/projects/%d/archive", id), nil)
}
}
+72
View File
@@ -0,0 +1,72 @@
//go:build integration
package tenant_test
import (
"testing"
"github.com/qianfree/team-api/tests/integration/tenant/testinfra"
)
func TestWalletInfo(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/wallet", nil)
resp.AssertSuccess(t)
var data struct {
Balance float64 `json:"balance"`
FrozenBalance float64 `json:"frozen_balance"`
AvailableBalance float64 `json:"available_balance"`
Currency string `json:"currency"`
Level int `json:"level"`
LevelName string `json:"level_name"`
CumulativeRecharge float64 `json:"cumulative_recharge"`
}
resp.DecodeData(t, &data)
// New tenant should have zero balance
if data.Currency == "" {
t.Fatal("expected non-empty currency")
}
}
func TestWalletTransactions(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/wallet/transactions", map[string]string{
"page": "1",
"page_size": "10",
})
resp.AssertSuccess(t)
// New tenant likely has no transactions, just verify structure
var data struct {
List []map[string]any `json:"list"`
Total int `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
resp.DecodeData(t, &data)
}
func TestWalletFrozenItems(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/wallet/frozen-items", nil)
resp.AssertSuccess(t)
var data struct {
Items []map[string]any `json:"items"`
}
resp.DecodeData(t, &data)
}
func TestWalletTransactionsExport(t *testing.T) {
client, _ := testinfra.GetAuthedClient(t)
resp := client.Get("/api/tenant/wallet/transactions/export", map[string]string{
"format": "csv",
})
resp.AssertSuccess(t)
}