| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- package main
-
- import (
- "bytes"
- "context"
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "os/exec"
- "testing"
- "time"
-
- "git.x2erp.com/qdy/go-base/model/request/queryreq"
- "git.x2erp.com/qdy/go-base/sdk/configure"
- )
-
- // TestAuthLogin 测试认证登录功能
- func TestAuthLogin(t *testing.T) {
- // 清理测试缓存
- cleanTestCache(t)
-
- // 获取svc-code服务地址
- svcCodeURL := "http://localhost:8020"
-
- // 检查svc-code服务是否运行
- if !isServiceRunning(t, svcCodeURL) {
- t.Skipf("svc-code服务未运行在 %s,跳过测试", svcCodeURL)
- }
-
- // 检查配置中心服务是否运行
- configureURL := "http://localhost:8080"
- if !isServiceRunning(t, configureURL) {
- t.Skipf("配置中心服务未运行在 %s,跳过测试", configureURL)
- }
-
- // 测试1:使用SDK直接登录(验证SDK功能)
- t.Run("SDKLogin", func(t *testing.T) {
- testSDKLogin(t, configureURL)
- })
-
- // 测试2:通过svc-code API登录(验证集成功能)
- t.Run("APILogin", func(t *testing.T) {
- testAPILogin(t, svcCodeURL, configureURL)
- })
-
- // 测试3:无效凭证登录测试
- t.Run("InvalidCredentials", func(t *testing.T) {
- testInvalidCredentials(t, svcCodeURL)
- })
- }
-
- // testSDKLogin 测试直接使用SDK登录配置中心
- func testSDKLogin(t *testing.T, configureURL string) {
- // 创建SDK客户端(登录无需认证,但需要BaseURL)
- client, err := configure.NewBasicAuthClient(configureURL, "test", "test")
- if err != nil {
- t.Fatalf("创建SDK客户端失败: %v", err)
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- // 使用测试用户登录
- req := &configure.UserLoginRequest{
- UserID: "test-user-001",
- Password: "password123",
- }
-
- startTime := time.Now()
- token, err := client.LoginUser(ctx, req)
- elapsed := time.Since(startTime)
-
- if err != nil {
- if isConnectionError(err) {
- t.Skipf("配置中心连接失败,跳过测试: %v", err)
- }
- t.Fatalf("SDK用户登录失败: %v (耗时: %v)", err, elapsed)
- }
-
- if token == "" {
- t.Fatal("SDK用户登录返回空token")
- }
-
- t.Logf("SDK用户登录成功!获取Token: %s... (耗时: %v)",
- token[:min(50, len(token))], elapsed)
-
- // 验证token可用于创建新的认证客户端
- tokenClient, err := configure.NewTokenAuthClient(configureURL, token)
- if err != nil {
- t.Fatalf("使用获取的token创建客户端失败: %v", err)
- }
-
- t.Logf("Token客户端创建成功,BaseURL: %s", tokenClient.GetConfig().BaseURL)
- }
-
- // testAPILogin 测试通过svc-code API登录
- func testAPILogin(t *testing.T, svcCodeURL, configureURL string) {
- // 创建HTTP客户端
- httpClient := &http.Client{
- Timeout: 30 * time.Second,
- }
-
- // 准备登录请求
- loginURL := fmt.Sprintf("%s/api/auth/login", svcCodeURL)
- loginData := map[string]string{
- "user_id": "test-user-001",
- "password": "password123",
- }
- loginJSON, err := json.Marshal(loginData)
- if err != nil {
- t.Fatalf("JSON序列化失败: %v", err)
- }
-
- // 发送登录请求
- startTime := time.Now()
- req, err := http.NewRequest("POST", loginURL, bytes.NewReader(loginJSON))
- if err != nil {
- t.Fatalf("创建HTTP请求失败: %v", err)
- }
- req.Header.Set("Content-Type", "application/json")
-
- resp, err := httpClient.Do(req)
- if err != nil {
- t.Fatalf("发送登录请求失败: %v", err)
- }
- defer resp.Body.Close()
-
- elapsed := time.Since(startTime)
-
- // 检查响应状态码
- if resp.StatusCode != 200 {
- body, _ := io.ReadAll(resp.Body)
- t.Fatalf("登录API返回非200状态码: %d, 响应: %s", resp.StatusCode, string(body))
- }
-
- // 解析响应
- body, err := io.ReadAll(resp.Body)
- if err != nil {
- t.Fatalf("读取响应体失败: %v", err)
- }
-
- var result map[string]interface{}
- if err := json.Unmarshal(body, &result); err != nil {
- t.Fatalf("解析响应JSON失败: %v, 响应体: %s", err, string(body))
- }
-
- // 检查响应结构
- if !result["success"].(bool) {
- t.Fatalf("登录失败: %v", result)
- }
-
- token, ok := result["data"].(string)
- if !ok || token == "" {
- t.Fatalf("响应中未找到有效token: %v", result)
- }
-
- t.Logf("API用户登录成功!获取Token: %s... (耗时: %v)",
- token[:min(50, len(token))], elapsed)
-
- // 验证token可用于配置中心
- t.Run("ValidateTokenWithConfigure", func(t *testing.T) {
- validateTokenWithConfigure(t, configureURL, token)
- })
- }
-
- // testInvalidCredentials 测试无效凭证登录
- func testInvalidCredentials(t *testing.T, svcCodeURL string) {
- httpClient := &http.Client{
- Timeout: 10 * time.Second,
- }
-
- loginURL := fmt.Sprintf("%s/api/auth/login", svcCodeURL)
- invalidData := map[string]string{
- "user_id": "invalid-user",
- "password": "wrong-password",
- }
- invalidJSON, err := json.Marshal(invalidData)
- if err != nil {
- t.Fatalf("JSON序列化失败: %v", err)
- }
-
- req, err := http.NewRequest("POST", loginURL, bytes.NewReader(invalidJSON))
- if err != nil {
- t.Fatalf("创建HTTP请求失败: %v", err)
- }
- req.Header.Set("Content-Type", "application/json")
-
- resp, err := httpClient.Do(req)
- if err != nil {
- t.Fatalf("发送登录请求失败: %v", err)
- }
- defer resp.Body.Close()
-
- // 即使凭证无效,API也应该返回200(业务错误通过success字段表示)
- if resp.StatusCode != 200 {
- body, _ := io.ReadAll(resp.Body)
- t.Fatalf("无效凭证登录返回非200状态码: %d, 响应: %s", resp.StatusCode, string(body))
- }
-
- body, err := io.ReadAll(resp.Body)
- if err != nil {
- t.Fatalf("读取响应体失败: %v", err)
- }
-
- var result map[string]interface{}
- if err := json.Unmarshal(body, &result); err != nil {
- t.Fatalf("解析响应JSON失败: %v", err)
- }
-
- // 无效凭证应该返回success=false
- if result["success"].(bool) {
- t.Fatal("无效凭证登录应该失败,但成功了")
- }
-
- t.Logf("无效凭证登录失败(预期): %v", result)
- }
-
- // validateTokenWithConfigure 验证token可用于配置中心
- func validateTokenWithConfigure(t *testing.T, configureURL, token string) {
- // 使用token创建配置中心客户端
- client, err := configure.NewTokenAuthClient(configureURL, token)
- if err != nil {
- t.Fatalf("使用token创建配置中心客户端失败: %v", err)
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- // 尝试查询表列表(验证token权限)
- query := &configure.DicTableQueryRequest{
- QueryRequest: queryreq.QueryRequest{
- Page: 0,
- PageSize: 5,
- },
- }
-
- result, err := client.ListTables(ctx, query)
- if err != nil {
- // 如果token无效或权限不足,可能失败
- t.Logf("使用token查询表列表失败(可能是token权限问题): %v", err)
- } else {
- t.Logf("使用token查询表列表成功,总记录数: %d", result.TotalCount)
- }
- }
-
- // cleanTestCache 清理测试缓存
- func cleanTestCache(t *testing.T) {
- cmd := exec.Command("go", "clean", "-testcache")
- if err := cmd.Run(); err != nil {
- t.Logf("清除测试缓存失败: %v", err)
- }
- }
-
- // isServiceRunning 检查服务是否运行
- func isServiceRunning(t *testing.T, url string) bool {
- client := &http.Client{
- Timeout: 3 * time.Second,
- }
-
- // 尝试访问健康检查端点或根路径
- healthURL := url + "/health"
- resp, err := client.Get(healthURL)
- if err != nil {
- // 也尝试根路径
- resp, err = client.Get(url)
- if err != nil {
- return false
- }
- }
- defer resp.Body.Close()
-
- return resp.StatusCode == 200 || resp.StatusCode == 404
- }
-
- // isConnectionError 检查错误是否是连接错误
- func isConnectionError(err error) bool {
- errStr := err.Error()
- return contains(errStr, "connection refused") ||
- contains(errStr, "connect: connection refused") ||
- contains(errStr, "dial tcp") ||
- contains(errStr, "EOF") ||
- contains(errStr, "timeout")
- }
-
- // contains 检查字符串是否包含子串
- func contains(s, substr string) bool {
- return len(s) >= len(substr) && (s == substr || (len(s) > 0 && len(substr) > 0 && (s[0:len(substr)] == substr || contains(s[1:], substr))))
- }
-
- // min 返回两个整数的最小值
- func min(a, b int) int {
- if a < b {
- return a
- }
- return b
- }
-
- // TestHealthEndpoint 测试健康检查端点
- func TestHealthEndpoint(t *testing.T) {
- svcCodeURL := "http://localhost:8020"
-
- if !isServiceRunning(t, svcCodeURL) {
- t.Skipf("svc-code服务未运行在 %s,跳过测试", svcCodeURL)
- }
-
- client := &http.Client{
- Timeout: 10 * time.Second,
- }
-
- resp, err := client.Get(svcCodeURL + "/api/health")
- if err != nil {
- t.Fatalf("访问健康检查端点失败: %v", err)
- }
- defer resp.Body.Close()
-
- if resp.StatusCode != 200 {
- t.Fatalf("健康检查端点返回非200状态码: %d", resp.StatusCode)
- }
-
- body, err := io.ReadAll(resp.Body)
- if err != nil {
- t.Fatalf("读取响应体失败: %v", err)
- }
-
- var result map[string]interface{}
- if err := json.Unmarshal(body, &result); err != nil {
- t.Fatalf("解析响应JSON失败: %v", err)
- }
-
- if !result["success"].(bool) {
- t.Fatalf("健康检查返回success=false: %v", result)
- }
-
- data := result["data"].(map[string]interface{})
- if data["status"] != "healthy" {
- t.Fatalf("健康检查状态不是healthy: %v", data)
- }
-
- t.Logf("健康检查端点测试通过: %v", data)
- }
|