Нет описания
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

prompt_sync_test.go 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. package main
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "testing"
  9. "time"
  10. "git.x2erp.com/qdy/go-svc-code/internal/opencode"
  11. )
  12. // TestPromptSync 测试同步对话API
  13. func TestPromptSync(t *testing.T) {
  14. // 获取svc-code服务地址
  15. svcCodeURL := "http://localhost:8020"
  16. // 检查服务是否运行
  17. if !checkServiceRunningForPromptTest(t, svcCodeURL) {
  18. t.Skipf("svc-code服务未运行在 %s,跳过测试", svcCodeURL)
  19. }
  20. // 1. 用户登录获取token
  21. token, err := loginForPromptTest(t, svcCodeURL)
  22. if err != nil {
  23. t.Fatalf("登录失败: %v", err)
  24. }
  25. t.Logf("获取到Token: %s...", token[:minPromptInt(20, len(token))])
  26. // 2. 创建会话
  27. sessionID, err := createSessionForPromptTest(t, svcCodeURL, token, "同步对话测试")
  28. if err != nil {
  29. t.Logf("创建会话失败: %v (可能是OpenCode连接问题)", err)
  30. t.Skip("需要有效的OpenCode连接才能继续测试")
  31. }
  32. t.Logf("创建会话成功: %s", sessionID)
  33. // 3. 发送同步对话请求
  34. response, err := sendSyncPrompt(t, svcCodeURL, token, sessionID, "你好,请做一个简单的测试回复")
  35. if err != nil {
  36. t.Fatalf("发送同步对话失败: %v", err)
  37. }
  38. // 4. 验证响应
  39. t.Logf("同步对话响应成功")
  40. if response.Info.Content != "" {
  41. t.Logf("AI回复: %s", response.Info.Content)
  42. } else {
  43. t.Log("AI回复内容为空(可能是OpenCode响应格式问题)")
  44. }
  45. // 5. 验证响应结构
  46. if response.Info.ID == "" {
  47. t.Error("响应缺少ID字段")
  48. }
  49. if response.Info.Role != "assistant" {
  50. t.Errorf("预期role为'assistant',得到 %s", response.Info.Role)
  51. }
  52. if response.Info.SessionID != sessionID {
  53. t.Errorf("响应sessionID不匹配,预期 %s,得到 %s", sessionID, response.Info.SessionID)
  54. }
  55. }
  56. // sendSyncPrompt 发送同步对话请求
  57. func sendSyncPrompt(t *testing.T, svcCodeURL, token, sessionID, message string) (*opencode.PromptResponse, error) {
  58. url := svcCodeURL + "/api/prompt/sync"
  59. requestData := map[string]interface{}{
  60. "sessionID": sessionID,
  61. "parts": []opencode.TextPart{
  62. {
  63. Type: "text",
  64. Text: message,
  65. },
  66. },
  67. }
  68. jsonData, err := json.Marshal(requestData)
  69. if err != nil {
  70. return nil, fmt.Errorf("编码请求数据失败: %w", err)
  71. }
  72. req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
  73. if err != nil {
  74. return nil, fmt.Errorf("创建请求失败: %w", err)
  75. }
  76. req.Header.Set("Content-Type", "application/json")
  77. req.Header.Set("Authorization", "Bearer "+token)
  78. client := &http.Client{Timeout: 30 * time.Second}
  79. resp, err := client.Do(req)
  80. if err != nil {
  81. return nil, fmt.Errorf("HTTP请求失败: %w", err)
  82. }
  83. defer resp.Body.Close()
  84. if resp.StatusCode != http.StatusOK {
  85. bodyBytes, _ := io.ReadAll(resp.Body)
  86. return nil, fmt.Errorf("请求失败 (状态码 %d): %s", resp.StatusCode, string(bodyBytes))
  87. }
  88. var result struct {
  89. Success bool `json:"success"`
  90. Data opencode.PromptResponse `json:"data"`
  91. Message string `json:"message"`
  92. }
  93. bodyBytes, _ := io.ReadAll(resp.Body)
  94. if err := json.Unmarshal(bodyBytes, &result); err != nil {
  95. return nil, fmt.Errorf("解析响应失败: %w", err)
  96. }
  97. if !result.Success {
  98. return nil, fmt.Errorf("同步对话失败: %s", result.Message)
  99. }
  100. return &result.Data, nil
  101. }
  102. // createSessionForPromptTest 创建会话(提示测试专用)
  103. func createSessionForPromptTest(t *testing.T, svcCodeURL, token, title string) (string, error) {
  104. url := svcCodeURL + "/api/session/create"
  105. sessionData := map[string]string{
  106. "title": title,
  107. }
  108. jsonData, _ := json.Marshal(sessionData)
  109. req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
  110. if err != nil {
  111. return "", fmt.Errorf("创建请求失败: %v", err)
  112. }
  113. req.Header.Set("Authorization", "Bearer "+token)
  114. req.Header.Set("Content-Type", "application/json")
  115. client := &http.Client{Timeout: 10 * time.Second}
  116. resp, err := client.Do(req)
  117. if err != nil {
  118. return "", fmt.Errorf("HTTP请求失败: %v", err)
  119. }
  120. defer resp.Body.Close()
  121. if resp.StatusCode != http.StatusOK {
  122. bodyBytes, _ := io.ReadAll(resp.Body)
  123. return "", fmt.Errorf("创建会话失败 (状态码 %d): %s", resp.StatusCode, string(bodyBytes))
  124. }
  125. var result struct {
  126. Success bool `json:"success"`
  127. Data struct {
  128. ID string `json:"id"`
  129. } `json:"data"`
  130. Message string `json:"message"`
  131. }
  132. bodyBytes, _ := io.ReadAll(resp.Body)
  133. if err := json.Unmarshal(bodyBytes, &result); err != nil {
  134. return "", fmt.Errorf("解析响应失败: %v", err)
  135. }
  136. if !result.Success {
  137. return "", fmt.Errorf("创建会话失败: %s", result.Message)
  138. }
  139. return result.Data.ID, nil
  140. }
  141. // loginForPromptTest 登录获取token(提示测试专用)
  142. func loginForPromptTest(t *testing.T, svcCodeURL string) (string, error) {
  143. loginURL := svcCodeURL + "/api/auth/login"
  144. loginData := map[string]string{
  145. "user_id": "test-user-001",
  146. "password": "password123",
  147. }
  148. jsonData, _ := json.Marshal(loginData)
  149. resp, err := http.Post(loginURL, "application/json", bytes.NewBuffer(jsonData))
  150. if err != nil {
  151. return "", fmt.Errorf("登录请求失败: %v", err)
  152. }
  153. defer resp.Body.Close()
  154. if resp.StatusCode != http.StatusOK {
  155. bodyBytes, _ := io.ReadAll(resp.Body)
  156. return "", fmt.Errorf("登录失败 (状态码 %d): %s", resp.StatusCode, string(bodyBytes))
  157. }
  158. var result struct {
  159. Success bool `json:"success"`
  160. Data string `json:"data"`
  161. Message string `json:"message"`
  162. }
  163. bodyBytes, _ := io.ReadAll(resp.Body)
  164. if err := json.Unmarshal(bodyBytes, &result); err != nil {
  165. return "", fmt.Errorf("解析响应失败: %v", err)
  166. }
  167. if !result.Success {
  168. return "", fmt.Errorf("登录失败: %s", result.Message)
  169. }
  170. return result.Data, nil
  171. }
  172. // checkServiceRunningForPromptTest 检查服务是否运行(提示测试专用)
  173. func checkServiceRunningForPromptTest(t *testing.T, url string) bool {
  174. client := &http.Client{Timeout: 3 * time.Second}
  175. resp, err := client.Get(url + "/api/health")
  176. if err != nil {
  177. // 尝试其他端点
  178. resp, err = client.Get(url)
  179. if err != nil {
  180. return false
  181. }
  182. }
  183. defer resp.Body.Close()
  184. return resp.StatusCode == http.StatusOK || resp.StatusCode == 404
  185. }
  186. // minPromptInt 返回两个整数的最小值(提示测试专用)
  187. func minPromptInt(a, b int) int {
  188. if a < b {
  189. return a
  190. }
  191. return b
  192. }