| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562 |
- package main
-
- import (
- "bytes"
- "context"
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "os/exec"
- "testing"
- "time"
-
- "git.x2erp.com/qdy/go-base/model/request/configreq"
- "git.x2erp.com/qdy/go-base/model/request/queryreq"
- "git.x2erp.com/qdy/go-base/sdk/configure"
- )
-
- // TestSDKTokenAuth 测试SDK使用Token认证进行数据库表字典操作
- func TestSDKTokenAuth(t *testing.T) {
- // 清除测试缓存
- cmd := exec.Command("go", "clean", "-testcache")
- if err := cmd.Run(); err != nil {
- t.Logf("清除测试缓存失败: %v", err)
- // 继续执行测试
- }
-
- // 1. 获取用户认证token
- authToken, err := getUserAuthToken(t)
- if err != nil {
- t.Fatalf("获取用户认证token失败: %v", err)
- }
- t.Logf("获取到用户认证token: %s...", authToken[:50])
-
- // 2. 使用认证token创建配置token
- configToken, err := createConfigToken(t, authToken)
- if err != nil {
- t.Fatalf("创建配置token失败: %v", err)
- }
- t.Logf("获取到配置token: %s...", configToken[:50])
-
- // 3. 使用配置token创建SDK客户端
- client, err := configure.NewTokenAuthClient("http://localhost:8080", configToken)
- if err != nil {
- t.Fatalf("创建SDK客户端失败: %v", err)
- }
- t.Log("SDK客户端创建成功")
-
- // 4. 测试SDK功能
- testTableID := fmt.Sprintf("test_sdk_table_%d", time.Now().Unix())
-
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
- defer cancel()
-
- // 4.1 创建数据库表字典
- t.Run("CreateTableWithSDK", func(t *testing.T) {
- createTableWithSDK(t, ctx, client, testTableID)
- })
-
- // 4.2 查询数据库表字典详情
- t.Run("GetTableWithSDK", func(t *testing.T) {
- getTableWithSDK(t, ctx, client, testTableID)
- })
-
- // 4.3 查询数据库表字典列表
- t.Run("ListTablesWithSDK", func(t *testing.T) {
- listTablesWithSDK(t, ctx, client)
- })
-
- // 4.4 删除数据库表字典
- t.Run("DeleteTableWithSDK", func(t *testing.T) {
- deleteTableWithSDK(t, ctx, client, testTableID)
- })
-
- // 4.5 验证数据库表字典已删除
- t.Run("VerifyTableDeletedWithSDK", func(t *testing.T) {
- verifyTableDeletedWithSDK(t, ctx, client, testTableID)
- })
-
- t.Log("SDK Token认证测试全部通过")
- }
-
- // getUserAuthToken 获取用户认证token
- func getUserAuthToken(t *testing.T) (string, error) {
- httpClient := &http.Client{}
-
- // 用户登录
- loginURL := "http://localhost:8080/api/login/user"
- loginData := map[string]string{
- "user_id": "test-user-001",
- "password": "password123",
- }
- loginJSON, _ := json.Marshal(loginData)
-
- loginReq, err := http.NewRequest("POST", loginURL, bytes.NewReader(loginJSON))
- if err != nil {
- return "", fmt.Errorf("创建登录请求失败: %w", err)
- }
- loginReq.Header.Set("Content-Type", "application/json")
-
- loginResp, err := httpClient.Do(loginReq)
- if err != nil {
- return "", fmt.Errorf("发送登录请求失败: %w", err)
- }
- defer loginResp.Body.Close()
-
- loginBody, err := io.ReadAll(loginResp.Body)
- if err != nil {
- return "", fmt.Errorf("读取登录响应失败: %w", err)
- }
-
- var loginResult map[string]interface{}
- if err := json.Unmarshal(loginBody, &loginResult); err != nil {
- return "", fmt.Errorf("解析登录响应失败: %w", err)
- }
-
- if !loginResult["success"].(bool) {
- return "", fmt.Errorf("登录失败: %v", loginResult)
- }
-
- return loginResult["data"].(string), nil
- }
-
- // createConfigToken 创建配置token
- func createConfigToken(t *testing.T, authToken string) (string, error) {
- httpClient := &http.Client{}
-
- // 使用认证token创建配置token
- url := "http://localhost:8080/api/create/config/token"
- data := configreq.ConfigTokenRequest{
- ProjectID: "test-project",
- ServiceName: "test-service",
- ExpiresDays: 30,
- }
- jsonData, err := json.Marshal(data)
- if err != nil {
- return "", fmt.Errorf("JSON序列化失败: %w", err)
- }
-
- req, err := http.NewRequest("POST", url, bytes.NewReader(jsonData))
- if err != nil {
- return "", fmt.Errorf("创建请求失败: %w", err)
- }
-
- // Token Auth 认证
- req.Header.Set("Authorization", "Bearer "+authToken)
- req.Header.Set("Content-Type", "application/json")
-
- resp, err := httpClient.Do(req)
- if err != nil {
- return "", fmt.Errorf("发送请求失败: %w", err)
- }
- defer resp.Body.Close()
-
- body, err := io.ReadAll(resp.Body)
- if err != nil {
- return "", fmt.Errorf("读取响应失败: %w", err)
- }
-
- var result map[string]interface{}
- if err := json.Unmarshal(body, &result); err != nil {
- return "", fmt.Errorf("解析响应失败: %w", err)
- }
-
- if resp.StatusCode != 200 {
- return "", fmt.Errorf("期望状态码200,实际: %d", resp.StatusCode)
- }
-
- if !result["success"].(bool) {
- return "", fmt.Errorf("创建配置token失败: %v", result)
- }
-
- if data, ok := result["data"].(string); !ok || data == "" {
- return "", fmt.Errorf("未返回有效的配置token")
- } else {
- return data, nil
- }
- }
-
- // createTableWithSDK 使用SDK创建数据库表字典
- func createTableWithSDK(t *testing.T, ctx context.Context, client *configure.Client, tableID string) {
- req := &configure.DicTableRequest{
- TableID: tableID,
- TableType: "实体表",
- Name: "SDK测试表",
- Description: "使用SDK Token认证创建的测试表",
- Fields: []configure.DicTableFieldRequest{
- {
- FieldID: tableID + ".id",
- TableID: tableID,
- FiledType: "实际字段",
- DataType: "数值型",
- FieldName: "id",
- FieldNameCN: "主键ID",
- Description: "主键字段",
- },
- {
- FieldID: tableID + ".name",
- TableID: tableID,
- FiledType: "实际字段",
- DataType: "字符型",
- FieldName: "name",
- FieldNameCN: "名称",
- Description: "名称字段",
- },
- {
- FieldID: tableID + ".created_at",
- TableID: tableID,
- FiledType: "实际字段",
- DataType: "日期型",
- FieldName: "created_at",
- FieldNameCN: "创建时间",
- Description: "创建时间字段",
- },
- },
- }
-
- detail, err := client.SaveTable(ctx, req)
- if err != nil {
- t.Fatalf("使用SDK创建表失败: %v", err)
- }
-
- if detail.Table.TableID != tableID {
- t.Errorf("期望表ID: %s, 实际: %s", tableID, detail.Table.TableID)
- }
-
- if len(detail.Fields) != 3 {
- t.Errorf("期望字段数: 3, 实际: %d", len(detail.Fields))
- }
-
- t.Logf("使用SDK创建表成功: %s, 包含 %d 个字段", tableID, len(detail.Fields))
- }
-
- // getTableWithSDK 使用SDK查询数据库表字典详情
- func getTableWithSDK(t *testing.T, ctx context.Context, client *configure.Client, tableID string) {
- detail, err := client.GetTable(ctx, tableID)
- if err != nil {
- t.Fatalf("使用SDK查询表详情失败: %v", err)
- }
-
- if detail.Table.TableID != tableID {
- t.Errorf("期望表ID: %s, 实际: %s", tableID, detail.Table.TableID)
- }
-
- if detail.Table.Name != "SDK测试表" {
- t.Errorf("期望表名称: SDK测试表, 实际: %s", detail.Table.Name)
- }
-
- if len(detail.Fields) != 3 {
- t.Errorf("期望字段数: 3, 实际: %d", len(detail.Fields))
- }
-
- t.Logf("使用SDK查询表详情成功: %s, 表名称: %s", tableID, detail.Table.Name)
- }
-
- // listTablesWithSDK 使用SDK查询数据库表字典列表
- func listTablesWithSDK(t *testing.T, ctx context.Context, client *configure.Client) {
- query := &configure.DicTableQueryRequest{
- QueryRequest: queryreq.QueryRequest{
- Page: 0,
- PageSize: 10,
- },
- }
-
- result, err := client.ListTables(ctx, query)
- if err != nil {
- t.Fatalf("使用SDK查询表列表失败: %v", err)
- }
-
- if result.TotalCount < 0 {
- t.Errorf("总记录数应该 >= 0, 实际: %d", result.TotalCount)
- }
-
- t.Logf("使用SDK查询表列表成功: 总记录数 %d, 最后页 %d", result.TotalCount, result.LastPage)
- }
-
- // deleteTableWithSDK 使用SDK删除数据库表字典
- func deleteTableWithSDK(t *testing.T, ctx context.Context, client *configure.Client, tableID string) {
- err := client.DeleteTable(ctx, tableID)
- if err != nil {
- t.Fatalf("使用SDK删除表失败: %v", err)
- }
-
- t.Logf("使用SDK删除表成功: %s", tableID)
- }
-
- // verifyTableDeletedWithSDK 使用SDK验证数据库表字典已删除
- func verifyTableDeletedWithSDK(t *testing.T, ctx context.Context, client *configure.Client, tableID string) {
- _, err := client.GetTable(ctx, tableID)
- if err == nil {
- t.Errorf("期望表 %s 已被删除,但查询成功", tableID)
- } else {
- // 检查错误是否为"未找到"
- if err.Error() != "not found" && !contains(err.Error(), "不存在") {
- t.Logf("表删除验证: %v", err)
- }
- t.Logf("表 %s 已成功删除(查询返回错误)", tableID)
- }
- }
-
- // 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))))
- }
-
- // TestBatchSaveWithSDK 测试SDK批量保存数据库表字典
- func TestBatchSaveWithSDK(t *testing.T) {
- // 1. 获取用户认证token
- authToken, err := getUserAuthToken(t)
- if err != nil {
- t.Fatalf("获取用户认证token失败: %v", err)
- }
- t.Logf("获取到用户认证token: %s...", authToken[:50])
-
- // 2. 使用认证token创建配置token
- configToken, err := createConfigToken(t, authToken)
- if err != nil {
- t.Fatalf("创建配置token失败: %v", err)
- }
- t.Logf("获取到配置token: %s...", configToken[:50])
-
- // 3. 使用配置token创建SDK客户端
- client, err := configure.NewTokenAuthClient("http://localhost:8080", configToken)
- if err != nil {
- t.Fatalf("创建SDK客户端失败: %v", err)
- }
- t.Log("SDK客户端创建成功")
-
- // 4. 准备批量保存测试数据
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
- defer cancel()
-
- // 生成唯一的表ID避免冲突
- timestamp := time.Now().Unix()
- table1ID := fmt.Sprintf("sdk_batch_table_001_%d", timestamp)
- table2ID := fmt.Sprintf("sdk_batch_table_002_%d", timestamp)
-
- // 创建批量保存请求
- req := &configure.BatchSaveDicTablesRequest{
- Tables: []configure.DicTableRequest{
- {
- TableID: table1ID,
- TableType: "实体表",
- Name: "SDK批量测试表001",
- Description: "SDK批量测试表001描述",
- },
- {
- TableID: table2ID,
- TableType: "视图",
- Name: "SDK批量测试表002",
- Description: "SDK批量测试表002描述",
- },
- },
- Fields: []configure.DicTableFieldRequest{
- // 表1的字段
- {
- FieldID: table1ID + ".id",
- TableID: table1ID,
- FiledType: "实际字段",
- DataType: "数值型",
- FieldName: "id",
- FieldNameCN: "主键ID",
- Description: "表1主键字段",
- },
- {
- FieldID: table1ID + ".name",
- TableID: table1ID,
- FiledType: "实际字段",
- DataType: "字符型",
- FieldName: "name",
- FieldNameCN: "名称",
- Description: "表1名称字段",
- },
- // 表2的字段
- {
- FieldID: table2ID + ".code",
- TableID: table2ID,
- FiledType: "实际字段",
- DataType: "字符型",
- FieldName: "code",
- FieldNameCN: "编码",
- Description: "表2编码字段",
- },
- {
- FieldID: table2ID + ".value",
- TableID: table2ID,
- FiledType: "计算字段",
- DataType: "数值型",
- FieldName: "value",
- FieldNameCN: "数值",
- Description: "表2数值字段",
- },
- },
- }
-
- // 5. 执行批量保存
- t.Run("BatchSaveTables", func(t *testing.T) {
- startTime := time.Now()
- if err := client.BatchSaveTables(ctx, req); err != nil {
- t.Fatalf("批量保存失败: %v", err)
- }
- elapsed := time.Since(startTime)
- t.Logf("批量保存成功!耗时: %v", elapsed)
- t.Logf("保存了 %d 个表,%d 个字段", len(req.Tables), len(req.Fields))
- })
-
- // 6. 验证批量保存结果
- t.Run("VerifyBatchSavedTables", func(t *testing.T) {
- // 验证表1
- detail1, err := client.GetTable(ctx, table1ID)
- if err != nil {
- t.Errorf("查询表1详情失败: %v", err)
- } else {
- if detail1.Table.TableID != table1ID {
- t.Errorf("表1 ID不匹配: 期望 %s, 实际 %s", table1ID, detail1.Table.TableID)
- }
- if detail1.Table.TableType != "实体表" {
- t.Errorf("表1 类型不匹配: 期望 实体表, 实际 %s", detail1.Table.TableType)
- }
- if len(detail1.Fields) != 2 {
- t.Errorf("表1 字段数不匹配: 期望 2, 实际 %d", len(detail1.Fields))
- }
- t.Logf("表1验证成功: %s, 包含 %d 个字段", table1ID, len(detail1.Fields))
- }
-
- // 验证表2
- detail2, err := client.GetTable(ctx, table2ID)
- if err != nil {
- t.Errorf("查询表2详情失败: %v", err)
- } else {
- if detail2.Table.TableID != table2ID {
- t.Errorf("表2 ID不匹配: 期望 %s, 实际 %s", table2ID, detail2.Table.TableID)
- }
- if detail2.Table.TableType != "视图" {
- t.Errorf("表2 类型不匹配: 期望 视图, 实际 %s", detail2.Table.TableType)
- }
- if len(detail2.Fields) != 2 {
- t.Errorf("表2 字段数不匹配: 期望 2, 实际 %d", len(detail2.Fields))
- }
- t.Logf("表2验证成功: %s, 包含 %d 个字段", table2ID, len(detail2.Fields))
- }
- })
-
- // 7. 清理测试数据
- t.Run("CleanupBatchTestData", func(t *testing.T) {
- // 删除表1
- if err := client.DeleteTable(ctx, table1ID); err != nil {
- t.Errorf("删除表1失败: %v", err)
- } else {
- t.Logf("表1删除成功: %s", table1ID)
- }
-
- // 删除表2
- if err := client.DeleteTable(ctx, table2ID); err != nil {
- t.Errorf("删除表2失败: %v", err)
- } else {
- t.Logf("表2删除成功: %s", table2ID)
- }
- })
-
- t.Log("SDK批量保存测试全部完成")
- }
-
- // TestSDKUserLogin 测试SDK用户登录功能
- func TestSDKUserLogin(t *testing.T) {
- // 创建客户端(登录端点无需认证,但需要BaseURL)
- // 使用任意认证凭证,因为登录时skipAuth=true
- client, err := configure.NewBasicAuthClient("http://localhost:8080", "test", "test")
- if err != nil {
- t.Fatalf("创建SDK客户端失败: %v", err)
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- // 测试登录功能
- t.Run("UserLoginWithSDK", func(t *testing.T) {
- // 尝试使用测试用户登录
- 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可用于创建新的认证客户端
- t.Run("CreateTokenClient", func(t *testing.T) {
- tokenClient, err := configure.NewTokenAuthClient("http://localhost:8080", token)
- if err != nil {
- t.Fatalf("使用获取的token创建客户端失败: %v", err)
- }
-
- t.Logf("Token客户端创建成功,BaseURL: %s", tokenClient.GetConfig().BaseURL)
-
- // 测试token客户端是否可正常工作(查询表列表)
- query := &configure.DicTableQueryRequest{
- QueryRequest: queryreq.QueryRequest{
- Page: 0,
- PageSize: 5,
- },
- }
-
- result, err := tokenClient.ListTables(ctx, query)
- if err != nil {
- // 如果token无效或权限不足,可能失败
- t.Logf("使用token查询表列表失败(可能是token权限问题): %v", err)
- } else {
- t.Logf("使用token查询表列表成功,总记录数: %d", result.TotalCount)
- }
- })
- })
-
- // 测试无效凭证登录
- t.Run("InvalidCredentialsLogin", func(t *testing.T) {
- req := &configure.UserLoginRequest{
- UserID: "invalid-user",
- Password: "wrong-password",
- }
-
- _, err := client.LoginUser(ctx, req)
- if err == nil {
- t.Fatal("使用无效凭证登录应该失败,但成功了")
- }
-
- // 检查错误类型
- t.Logf("无效凭证登录失败(预期): %v", err)
- })
- }
-
- // 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")
- }
-
- // min 返回两个整数的最小值
- func min(a, b int) int {
- if a < b {
- return a
- }
- return b
- }
|