fix: harden auth flows and align api contracts
This commit is contained in:
@@ -24,7 +24,7 @@ func TestExportHandler_ExportUsers_Success(t *testing.T) {
|
||||
t.Fatal("bootstrap admin token should succeed")
|
||||
}
|
||||
|
||||
resp, _ := doGet(server.URL+"/api/v1/exports/users", token)
|
||||
resp, _ := doGet(server.URL+"/api/v1/admin/users/export", token)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusInternalServerError,
|
||||
@@ -42,16 +42,17 @@ func TestExportHandler_ExportUsers_WithFormat(t *testing.T) {
|
||||
}
|
||||
|
||||
// CSV format
|
||||
resp1, _ := doGet(server.URL+"/api/v1/exports/users?format=csv", token)
|
||||
resp1, _ := doGet(server.URL+"/api/v1/admin/users/export?format=csv", token)
|
||||
defer resp1.Body.Close()
|
||||
assert.True(t, resp1.StatusCode == http.StatusOK || resp1.StatusCode == http.StatusForbidden,
|
||||
"should export CSV, got %d", resp1.StatusCode)
|
||||
|
||||
// Excel format
|
||||
resp2, _ := doGet(server.URL+"/api/v1/exports/users?format=excel", token)
|
||||
// XLSX format
|
||||
resp2, _ := doGet(server.URL+"/api/v1/admin/users/export?format=xlsx", token)
|
||||
defer resp2.Body.Close()
|
||||
assert.True(t, resp2.StatusCode == http.StatusOK || resp2.StatusCode == http.StatusForbidden || resp2.StatusCode == http.StatusBadRequest,
|
||||
"should export Excel, got %d", resp2.StatusCode)
|
||||
"should export XLSX, got %d", resp2.StatusCode)
|
||||
|
||||
}
|
||||
|
||||
// TestExportHandler_ExportUsers_WithFields 验证指定字段导出
|
||||
@@ -64,7 +65,7 @@ func TestExportHandler_ExportUsers_WithFields(t *testing.T) {
|
||||
t.Fatal("bootstrap admin token should succeed")
|
||||
}
|
||||
|
||||
resp, _ := doGet(server.URL+"/api/v1/exports/users?fields=id,username,email&format=csv", token)
|
||||
resp, _ := doGet(server.URL+"/api/v1/admin/users/export?fields=id,username,email&format=csv", token)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusForbidden,
|
||||
@@ -81,13 +82,29 @@ func TestExportHandler_ExportUsers_WithFilter(t *testing.T) {
|
||||
t.Fatal("bootstrap admin token should succeed")
|
||||
}
|
||||
|
||||
resp, _ := doGet(server.URL+"/api/v1/exports/users?keyword=admin&status=1&format=csv", token)
|
||||
resp, _ := doGet(server.URL+"/api/v1/admin/users/export?keyword=admin&status=1&format=csv", token)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusBadRequest,
|
||||
"should export with filter, got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// TestExportHandler_ExportUsers_InvalidStatus 验证非法状态参数
|
||||
func TestExportHandler_ExportUsers_InvalidStatus(t *testing.T) {
|
||||
server, cleanup := setupHandlerTestServer(t)
|
||||
defer cleanup()
|
||||
|
||||
token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!")
|
||||
if token == "" {
|
||||
t.Fatal("bootstrap admin token should succeed")
|
||||
}
|
||||
|
||||
resp, _ := doGet(server.URL+"/api/v1/admin/users/export?status=abc&format=csv", token)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||
}
|
||||
|
||||
// TestExportHandler_ExportUsers_NonAdmin 验证非管理员导出
|
||||
func TestExportHandler_ExportUsers_NonAdmin(t *testing.T) {
|
||||
server, cleanup := setupHandlerTestServer(t)
|
||||
@@ -97,7 +114,7 @@ func TestExportHandler_ExportUsers_NonAdmin(t *testing.T) {
|
||||
token := getToken(server.URL, "regular", "Pass123!")
|
||||
assert.NotEmpty(t, token)
|
||||
|
||||
resp, _ := doGet(server.URL+"/api/v1/exports/users", token)
|
||||
resp, _ := doGet(server.URL+"/api/v1/admin/users/export", token)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.True(t, resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusOK,
|
||||
@@ -109,7 +126,7 @@ func TestExportHandler_ExportUsers_Unauthorized(t *testing.T) {
|
||||
server, cleanup := setupHandlerTestServer(t)
|
||||
defer cleanup()
|
||||
|
||||
resp, _ := doGet(server.URL+"/api/v1/exports/users", "")
|
||||
resp, _ := doGet(server.URL+"/api/v1/admin/users/export", "")
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.True(t, resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusForbidden,
|
||||
@@ -134,7 +151,7 @@ func TestExportHandler_ImportUsers_Success(t *testing.T) {
|
||||
part.Write([]byte(csvData))
|
||||
writer.Close()
|
||||
|
||||
req, _ := http.NewRequest("POST", server.URL+"/api/v1/exports/users?format=csv", &body)
|
||||
req, _ := http.NewRequest("POST", server.URL+"/api/v1/admin/users/import?format=csv", &body)
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
|
||||
@@ -165,16 +182,20 @@ func TestExportHandler_ImportUsers_NoFile(t *testing.T) {
|
||||
writer := multipart.NewWriter(&body)
|
||||
writer.Close()
|
||||
|
||||
req, _ := http.NewRequest("POST", server.URL+"/api/v1/exports/users", &body)
|
||||
req, _ := http.NewRequest("POST", server.URL+"/api/v1/admin/users/import", &body)
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, _ := client.Do(req)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("request failed: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.True(t, resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusOK,
|
||||
"should require file, got %d", resp.StatusCode)
|
||||
|
||||
}
|
||||
|
||||
// TestExportHandler_ImportUsers_InvalidFormat 验证无效格式导入
|
||||
@@ -193,16 +214,20 @@ func TestExportHandler_ImportUsers_InvalidFormat(t *testing.T) {
|
||||
part.Write([]byte("invalid content"))
|
||||
writer.Close()
|
||||
|
||||
req, _ := http.NewRequest("POST", server.URL+"/api/v1/exports/users?format=invalid", &body)
|
||||
req, _ := http.NewRequest("POST", server.URL+"/api/v1/admin/users/import?format=invalid", &body)
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, _ := client.Do(req)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("request failed: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.True(t, resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusForbidden,
|
||||
"should handle invalid format, got %d", resp.StatusCode)
|
||||
|
||||
}
|
||||
|
||||
// TestExportHandler_ImportUsers_NonAdmin 验证非管理员导入
|
||||
@@ -220,12 +245,15 @@ func TestExportHandler_ImportUsers_NonAdmin(t *testing.T) {
|
||||
part.Write([]byte("username,email\nuser1,user1@test.com"))
|
||||
writer.Close()
|
||||
|
||||
req, _ := http.NewRequest("POST", server.URL+"/api/v1/exports/users", &body)
|
||||
req, _ := http.NewRequest("POST", server.URL+"/api/v1/admin/users/import", &body)
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, _ := client.Do(req)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("request failed: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.True(t, resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusOK,
|
||||
@@ -242,7 +270,7 @@ func TestExportHandler_GetImportTemplate_Success(t *testing.T) {
|
||||
t.Fatal("bootstrap admin token should succeed")
|
||||
}
|
||||
|
||||
resp, _ := doGet(server.URL+"/api/v1/exports/template", token)
|
||||
resp, _ := doGet(server.URL+"/api/v1/admin/users/import/template", token)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusInternalServerError,
|
||||
@@ -259,7 +287,7 @@ func TestExportHandler_GetImportTemplate_CSV(t *testing.T) {
|
||||
t.Fatal("bootstrap admin token should succeed")
|
||||
}
|
||||
|
||||
resp, _ := doGet(server.URL+"/api/v1/exports/template?format=csv", token)
|
||||
resp, _ := doGet(server.URL+"/api/v1/admin/users/import/template?format=csv", token)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusForbidden,
|
||||
@@ -276,11 +304,11 @@ func TestExportHandler_GetImportTemplate_Excel(t *testing.T) {
|
||||
t.Fatal("bootstrap admin token should succeed")
|
||||
}
|
||||
|
||||
resp, _ := doGet(server.URL+"/api/v1/exports/template?format=excel", token)
|
||||
resp, _ := doGet(server.URL+"/api/v1/admin/users/import/template?format=xlsx", token)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusBadRequest,
|
||||
"should get Excel template, got %d", resp.StatusCode)
|
||||
"should get XLSX template, got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// TestExportHandler_GetImportTemplate_Unauthorized 验证未认证获取模板
|
||||
@@ -288,7 +316,7 @@ func TestExportHandler_GetImportTemplate_Unauthorized(t *testing.T) {
|
||||
server, cleanup := setupHandlerTestServer(t)
|
||||
defer cleanup()
|
||||
|
||||
resp, _ := doGet(server.URL+"/api/v1/exports/template", "")
|
||||
resp, _ := doGet(server.URL+"/api/v1/admin/users/import/template", "")
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.True(t, resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusForbidden,
|
||||
@@ -305,7 +333,7 @@ func TestExportHandler_ExportResponse_ContentType(t *testing.T) {
|
||||
t.Fatal("bootstrap admin token should succeed")
|
||||
}
|
||||
|
||||
resp, _ := doGet(server.URL+"/api/v1/exports/users?format=csv", token)
|
||||
resp, _ := doGet(server.URL+"/api/v1/admin/users/export?format=csv", token)
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
@@ -325,7 +353,7 @@ func TestExportHandler_ExportResponse_ContentDisposition(t *testing.T) {
|
||||
t.Fatal("bootstrap admin token should succeed")
|
||||
}
|
||||
|
||||
resp, _ := doGet(server.URL+"/api/v1/exports/users?format=csv", token)
|
||||
resp, _ := doGet(server.URL+"/api/v1/admin/users/export?format=csv", token)
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
|
||||
Reference in New Issue
Block a user