292 lines
8.1 KiB
Go
292 lines
8.1 KiB
Go
//go:build integration
|
|
|
|
package admin_test
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/qianfree/team-api/tests/integration/admin/testinfra"
|
|
admintest "github.com/qianfree/team-api/tests/integration/admin/testinfra"
|
|
tenanttest "github.com/qianfree/team-api/tests/integration/tenant/testinfra"
|
|
)
|
|
|
|
func TestTicketList(t *testing.T) {
|
|
client := testinfra.GetAuthedClient(t)
|
|
|
|
resp := client.Get("/api/admin/tickets", map[string]string{
|
|
"page": "1",
|
|
"page_size": "10",
|
|
})
|
|
testinfra.AssertPaginatedList(t, resp, 0)
|
|
}
|
|
|
|
func TestTicketListWithFilters(t *testing.T) {
|
|
client := testinfra.GetAuthedClient(t)
|
|
|
|
// Filter by status
|
|
resp := client.Get("/api/admin/tickets", map[string]string{
|
|
"page": "1",
|
|
"page_size": "10",
|
|
"status": "pending",
|
|
})
|
|
resp.AssertSuccess(t)
|
|
|
|
// Filter by category
|
|
resp = client.Get("/api/admin/tickets", map[string]string{
|
|
"page": "1",
|
|
"page_size": "10",
|
|
"category": "billing",
|
|
})
|
|
resp.AssertSuccess(t)
|
|
|
|
// Filter by tenant_id
|
|
tenantID, cleanup := createTestTenantWithTicket(t, client)
|
|
defer cleanup()
|
|
|
|
resp = client.Get("/api/admin/tickets", map[string]string{
|
|
"page": "1",
|
|
"page_size": "10",
|
|
"tenant_id": fmt.Sprintf("%d", tenantID),
|
|
})
|
|
resp.AssertSuccess(t)
|
|
|
|
// Multiple filters combined
|
|
resp = client.Get("/api/admin/tickets", map[string]string{
|
|
"page": "1",
|
|
"page_size": "10",
|
|
"status": "pending",
|
|
"category": "technical",
|
|
})
|
|
resp.AssertSuccess(t)
|
|
}
|
|
|
|
func TestTicketDetail(t *testing.T) {
|
|
client := testinfra.GetAuthedClient(t)
|
|
|
|
// Create test ticket via tenant
|
|
ticketID, cleanup := createTestTicketViaTenant(t, client)
|
|
defer cleanup()
|
|
|
|
// Get ticket detail
|
|
detailResp := client.Get(fmt.Sprintf("/api/admin/tickets/%d", ticketID), nil)
|
|
detailResp.AssertSuccess(t)
|
|
|
|
var detail map[string]any
|
|
detailResp.DecodeData(t, &detail)
|
|
|
|
// Verify essential fields exist
|
|
requiredFields := []string{"id", "title", "status", "category"}
|
|
for _, field := range requiredFields {
|
|
if _, ok := detail[field]; !ok {
|
|
t.Fatalf("ticket detail missing required field: %s", field)
|
|
}
|
|
}
|
|
|
|
// Verify id matches
|
|
if id, ok := detail["id"].(float64); !ok || int64(id) != ticketID {
|
|
t.Fatalf("expected ticket id=%d in detail response", ticketID)
|
|
}
|
|
|
|
t.Logf("Ticket detail retrieved: id=%d, status=%v", ticketID, detail["status"])
|
|
}
|
|
|
|
func TestTicketReply(t *testing.T) {
|
|
client := testinfra.GetAuthedClient(t)
|
|
|
|
// Create test ticket via tenant
|
|
ticketID, cleanup := createTestTicketViaTenant(t, client)
|
|
defer cleanup()
|
|
|
|
// Reply to the ticket
|
|
replyContent := fmt.Sprintf("管理员回复(集成测试)%s", randomSuffix())
|
|
replyResp := client.Post(fmt.Sprintf("/api/admin/tickets/%d/reply", ticketID), map[string]any{
|
|
"content": replyContent,
|
|
})
|
|
replyResp.AssertSuccess(t)
|
|
|
|
t.Logf("Replied to ticket %d", ticketID)
|
|
}
|
|
|
|
func TestTicketAssign(t *testing.T) {
|
|
client := testinfra.GetAuthedClient(t)
|
|
|
|
// Create test ticket via tenant
|
|
ticketID, cleanup := createTestTicketViaTenant(t, client)
|
|
defer cleanup()
|
|
|
|
// Get current admin user list to find an admin ID
|
|
adminListResp := client.Get("/api/admin/users", map[string]string{
|
|
"page": "1",
|
|
"page_size": "10",
|
|
})
|
|
adminListResp.AssertSuccess(t)
|
|
|
|
var adminData struct {
|
|
List []struct {
|
|
Id int64 `json:"id"`
|
|
} `json:"list"`
|
|
}
|
|
adminListResp.DecodeData(t, &adminData)
|
|
|
|
if len(adminData.List) == 0 {
|
|
t.Skip("No admin users found, skipping assign test")
|
|
}
|
|
|
|
adminID := adminData.List[0].Id
|
|
|
|
// Assign the ticket
|
|
assignResp := client.Put(fmt.Sprintf("/api/admin/tickets/%d/assign", ticketID), map[string]any{
|
|
"admin_id": adminID,
|
|
})
|
|
assignResp.AssertSuccess(t)
|
|
|
|
// Verify assignment via detail
|
|
detailResp := client.Get(fmt.Sprintf("/api/admin/tickets/%d", ticketID), nil)
|
|
detailResp.AssertSuccess(t)
|
|
|
|
var detail map[string]any
|
|
detailResp.DecodeData(t, &detail)
|
|
|
|
if assignedAdmin, ok := detail["assigned_admin_id"]; ok {
|
|
if adminIDFloat, ok := assignedAdmin.(float64); ok && int64(adminIDFloat) != adminID {
|
|
t.Fatalf("expected assigned_admin_id=%d, got %v", adminID, assignedAdmin)
|
|
}
|
|
}
|
|
|
|
t.Logf("Ticket %d assigned to admin %d", ticketID, adminID)
|
|
}
|
|
|
|
func TestTicketStatusUpdate(t *testing.T) {
|
|
client := testinfra.GetAuthedClient(t)
|
|
|
|
// Create test ticket via tenant
|
|
ticketID, cleanup := createTestTicketViaTenant(t, client)
|
|
defer cleanup()
|
|
|
|
// Update status to processing
|
|
updateResp := client.Put(fmt.Sprintf("/api/admin/tickets/%d/status", ticketID), map[string]any{
|
|
"status": "processing",
|
|
})
|
|
updateResp.AssertSuccess(t)
|
|
|
|
// Verify status change via detail
|
|
detailResp := client.Get(fmt.Sprintf("/api/admin/tickets/%d", ticketID), nil)
|
|
detailResp.AssertSuccess(t)
|
|
|
|
var detail struct {
|
|
Status string `json:"status"`
|
|
}
|
|
detailResp.DecodeData(t, &detail)
|
|
|
|
if detail.Status != "processing" {
|
|
t.Fatalf("expected status=processing after update, got %q", detail.Status)
|
|
}
|
|
|
|
// Update status to closed
|
|
closeResp := client.Put(fmt.Sprintf("/api/admin/tickets/%d/status", ticketID), map[string]any{
|
|
"status": "closed",
|
|
})
|
|
closeResp.AssertSuccess(t)
|
|
|
|
// Verify closed status
|
|
verifyResp := client.Get(fmt.Sprintf("/api/admin/tickets/%d", ticketID), nil)
|
|
verifyResp.AssertSuccess(t)
|
|
|
|
var verify struct {
|
|
Status string `json:"status"`
|
|
}
|
|
verifyResp.DecodeData(t, &verify)
|
|
|
|
if verify.Status != "closed" {
|
|
t.Fatalf("expected status=closed, got %q", verify.Status)
|
|
}
|
|
|
|
t.Logf("Ticket %d status updated: pending -> processing -> closed", ticketID)
|
|
}
|
|
|
|
func TestTicketNegative(t *testing.T) {
|
|
client := testinfra.GetAuthedClient(t)
|
|
|
|
// Detail for non-existent ticket
|
|
detailNonExistResp := client.Get("/api/admin/tickets/999999999", nil)
|
|
if detailNonExistResp.Code == 0 {
|
|
t.Fatal("expected error for non-existent ticket detail, got success")
|
|
}
|
|
|
|
// Reply to non-existent ticket
|
|
replyNonExistResp := client.Post("/api/admin/tickets/999999999/reply", map[string]any{
|
|
"content": "test",
|
|
})
|
|
if replyNonExistResp.Code == 0 {
|
|
t.Fatal("expected error when replying to non-existent ticket, got success")
|
|
}
|
|
|
|
// Reply with empty content — test against a real ticket
|
|
ticketID, cleanup := createTestTicketViaTenant(t, client)
|
|
defer cleanup()
|
|
|
|
emptyReplyResp := client.Post(fmt.Sprintf("/api/admin/tickets/%d/reply", ticketID), map[string]any{
|
|
"content": "",
|
|
})
|
|
if emptyReplyResp.Code == 0 {
|
|
t.Fatal("expected error for empty reply content, got success")
|
|
}
|
|
|
|
// Invalid status value
|
|
invalidStatusResp := client.Put(fmt.Sprintf("/api/admin/tickets/%d/status", ticketID), map[string]any{
|
|
"status": "invalid_status",
|
|
})
|
|
if invalidStatusResp.Code == 0 {
|
|
t.Fatal("expected error for invalid ticket status, got success")
|
|
}
|
|
}
|
|
|
|
// createTestTicketViaTenant creates a test tenant, creates a ticket via the tenant API,
|
|
// and returns the ticket ID and cleanup.
|
|
func createTestTicketViaTenant(t *testing.T, adminClient *admintest.APIClient) (ticketID int64, cleanup func()) {
|
|
t.Helper()
|
|
|
|
result := tenanttest.RegisterTestTenant(t)
|
|
tenantClient := admintest.NewAPIClient(tenanttest.DefaultBaseURL).WithToken(result.AccessToken)
|
|
|
|
suffix := tenanttest.RandomSuffix()
|
|
resp := tenantClient.Post("/api/tenant/tickets", map[string]any{
|
|
"category": "technical",
|
|
"title": fmt.Sprintf("测试工单 %s", suffix),
|
|
"description": "集成测试自动创建的工单",
|
|
"urgency": "normal",
|
|
})
|
|
resp.AssertSuccess(t)
|
|
|
|
var data struct {
|
|
ID int64 `json:"id"`
|
|
}
|
|
resp.DecodeData(t, &data)
|
|
|
|
return data.ID, func() {
|
|
// Tenant cleanup is handled by t.Cleanup in RegisterTestTenant
|
|
}
|
|
}
|
|
|
|
// createTestTenantWithTicket creates a test tenant with a ticket for filter tests.
|
|
func createTestTenantWithTicket(t *testing.T, adminClient *admintest.APIClient) (tenantID int64, cleanup func()) {
|
|
t.Helper()
|
|
|
|
result := tenanttest.RegisterTestTenant(t)
|
|
tenantClient := admintest.NewAPIClient(tenanttest.DefaultBaseURL).WithToken(result.AccessToken)
|
|
|
|
suffix := tenanttest.RandomSuffix()
|
|
resp := tenantClient.Post("/api/tenant/tickets", map[string]any{
|
|
"category": "billing",
|
|
"title": fmt.Sprintf("过滤测试工单 %s", suffix),
|
|
"description": "过滤测试自动创建的工单",
|
|
"urgency": "normal",
|
|
})
|
|
resp.AssertSuccess(t)
|
|
|
|
return result.Tenant.ID, func() {
|
|
// Tenant cleanup is handled by t.Cleanup in RegisterTestTenant
|
|
}
|
|
}
|