Aucune description
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

api-test-standalone.ts 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. /**
  2. * 独立API测试模块 - 纯TypeScript/JavaScript
  3. * 可以直接在浏览器控制台或Node.js中运行
  4. * 不依赖Vite别名或前端代理配置
  5. * 直接调用后端API服务
  6. */
  7. interface ProjectInfo {
  8. id: string
  9. name: string
  10. description: string
  11. path: string
  12. tenant_id: string
  13. creator: string
  14. created_at: string
  15. }
  16. interface HealthCheckResponse {
  17. healthy: boolean
  18. version: string
  19. [key: string]: any
  20. }
  21. interface TestResult {
  22. success: boolean
  23. duration: number
  24. data?: any
  25. error?: string
  26. }
  27. /**
  28. * 配置
  29. */
  30. const CONFIG = {
  31. // 后端API基础URL
  32. BACKEND_BASE_URL: 'http://localhost:8020',
  33. // API前缀
  34. API_PREFIX: '/api',
  35. // 超时时间(毫秒)
  36. TIMEOUT: 10000,
  37. }
  38. /**
  39. * 获取完整的API URL
  40. */
  41. function getApiUrl(endpoint: string): string {
  42. // 确保endpoint以/开头
  43. const normalizedEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`
  44. return `${CONFIG.BACKEND_BASE_URL}${CONFIG.API_PREFIX}${normalizedEndpoint}`
  45. }
  46. /**
  47. * 发起API请求
  48. */
  49. async function apiRequest<T = any>(
  50. method: string,
  51. endpoint: string,
  52. data?: any
  53. ): Promise<T> {
  54. const url = getApiUrl(endpoint)
  55. const controller = new AbortController()
  56. const timeoutId = setTimeout(() => controller.abort(), CONFIG.TIMEOUT)
  57. try {
  58. const response = await fetch(url, {
  59. method,
  60. headers: {
  61. 'Content-Type': 'application/json',
  62. 'Accept': 'application/json',
  63. },
  64. body: data ? JSON.stringify(data) : undefined,
  65. signal: controller.signal,
  66. })
  67. clearTimeout(timeoutId)
  68. if (!response.ok) {
  69. let errorMessage = `HTTP ${response.status}`
  70. try {
  71. const errorData = await response.json()
  72. errorMessage += `: ${JSON.stringify(errorData)}`
  73. } catch {
  74. // 忽略JSON解析错误
  75. }
  76. throw new Error(errorMessage)
  77. }
  78. const responseData = await response.json()
  79. // 处理后端返回格式(假设是 {success: true, data: ...} 或直接是数据)
  80. if (responseData.success !== undefined) {
  81. if (!responseData.success) {
  82. throw new Error(responseData.message || responseData.error || '请求失败')
  83. }
  84. return responseData.data || responseData
  85. }
  86. return responseData
  87. } catch (error: any) {
  88. clearTimeout(timeoutId)
  89. if (error.name === 'AbortError') {
  90. throw new Error(`请求超时 (${CONFIG.TIMEOUT}ms)`)
  91. }
  92. throw error
  93. }
  94. }
  95. /**
  96. * API函数 - 测试专用(直接调用后端)
  97. */
  98. export const testApi = {
  99. /**
  100. * 获取项目列表
  101. */
  102. async getProjects(): Promise<ProjectInfo[]> {
  103. return apiRequest<ProjectInfo[]>('GET', '/projects')
  104. },
  105. /**
  106. * 健康检查
  107. */
  108. async checkHealth(): Promise<HealthCheckResponse> {
  109. return apiRequest<HealthCheckResponse>('GET', '/health')
  110. },
  111. /**
  112. * 测试连接
  113. */
  114. async testConnection(timeoutMs: number = 5000): Promise<TestResult> {
  115. const startTime = Date.now()
  116. try {
  117. const controller = new AbortController()
  118. const timeoutId = setTimeout(() => controller.abort(), timeoutMs)
  119. const response = await fetch(getApiUrl('/health'), {
  120. signal: controller.signal,
  121. })
  122. clearTimeout(timeoutId)
  123. if (!response.ok) {
  124. throw new Error(`HTTP ${response.status}`)
  125. }
  126. const data = await response.json()
  127. const duration = Date.now() - startTime
  128. return {
  129. success: true,
  130. duration,
  131. data
  132. }
  133. } catch (error: any) {
  134. const duration = Date.now() - startTime
  135. return {
  136. success: false,
  137. duration,
  138. error: error.message || '未知错误'
  139. }
  140. }
  141. },
  142. }
  143. /**
  144. * 测试函数
  145. */
  146. export async function testGetProjects(): Promise<boolean> {
  147. console.log('🧪 开始测试 getProjects API (直接调用后端)...')
  148. console.log(` 后端URL: ${CONFIG.BACKEND_BASE_URL}`)
  149. console.log(` API端点: ${getApiUrl('/projects')}`)
  150. try {
  151. const projects = await testApi.getProjects()
  152. console.log(`✅ 测试成功!获取到 ${projects.length} 个项目`)
  153. if (projects.length > 0) {
  154. console.log(' 项目列表:')
  155. projects.forEach((project: ProjectInfo, index: number) => {
  156. console.log(` ${index + 1}. ${project.name} (ID: ${project.id})`)
  157. console.log(` 描述: ${project.description}`)
  158. console.log(` 路径: ${project.path}`)
  159. })
  160. } else {
  161. console.log('⚠️ 警告: 项目列表为空')
  162. }
  163. return true
  164. } catch (error: any) {
  165. console.error('❌ 测试失败:', error.message || error)
  166. console.error(' 请检查:')
  167. console.error(' 1. 后端服务是否运行在端口8020')
  168. console.error(' 2. 网络连接是否正常')
  169. console.error(' 3. 跨域设置是否正确')
  170. return false
  171. }
  172. }
  173. export async function testHealthCheck(): Promise<boolean> {
  174. console.log('🧪 开始测试健康检查 API...')
  175. console.log(` 后端URL: ${CONFIG.BACKEND_BASE_URL}`)
  176. console.log(` API端点: ${getApiUrl('/health')}`)
  177. try {
  178. const health = await testApi.checkHealth()
  179. console.log(`✅ 健康检查成功!`)
  180. console.log(` 健康状态: ${health.healthy ? '健康' : '不健康'}`)
  181. console.log(` 版本: ${health.version}`)
  182. if (!health.healthy) {
  183. console.warn('⚠️ 警告: 后端报告不健康状态')
  184. }
  185. return true
  186. } catch (error: any) {
  187. console.error('❌ 健康检查失败:', error.message || error)
  188. return false
  189. }
  190. }
  191. export async function testBackendConnection(): Promise<boolean> {
  192. console.log('🧪 开始测试后端连接...')
  193. console.log(` 后端URL: ${CONFIG.BACKEND_BASE_URL}`)
  194. try {
  195. const result = await testApi.testConnection(3000)
  196. if (result.success) {
  197. console.log(`✅ 连接测试成功!`)
  198. console.log(` 响应时间: ${result.duration}ms`)
  199. console.log(` 健康状态: ${result.data?.healthy ? '健康' : '不健康'}`)
  200. return true
  201. } else {
  202. console.error(`❌ 连接测试失败: ${result.error}`)
  203. return false
  204. }
  205. } catch (error: any) {
  206. console.error('❌ 连接测试异常:', error.message || error)
  207. return false
  208. }
  209. }
  210. /**
  211. * 运行所有测试
  212. */
  213. export async function runAllTests() {
  214. console.log('='.repeat(60))
  215. console.log('🚀 开始运行独立API测试 (直接调用后端)')
  216. console.log('='.repeat(60))
  217. console.log(`后端服务: ${CONFIG.BACKEND_BASE_URL}`)
  218. console.log(`API前缀: ${CONFIG.API_PREFIX}`)
  219. console.log('='.repeat(60))
  220. const results = {
  221. healthCheck: false,
  222. connection: false,
  223. getProjects: false,
  224. }
  225. // 测试健康检查
  226. results.healthCheck = await testHealthCheck()
  227. // 测试连接
  228. results.connection = await testBackendConnection()
  229. // 测试获取项目列表
  230. if (results.connection) {
  231. results.getProjects = await testGetProjects()
  232. } else {
  233. console.log('⏭️ 跳过项目列表测试(连接测试失败)')
  234. }
  235. console.log('='.repeat(60))
  236. console.log('📊 测试结果汇总:')
  237. console.log('='.repeat(60))
  238. console.log(`健康检查: ${results.healthCheck ? '✅ 通过' : '❌ 失败'}`)
  239. console.log(`连接测试: ${results.connection ? '✅ 通过' : '❌ 失败'}`)
  240. console.log(`获取项目: ${results.getProjects ? '✅ 通过' : results.connection ? '❌ 失败' : '⏭️ 跳过'}`)
  241. const allPassed = Object.values(results).every(result => result === true)
  242. console.log(`\n${allPassed ? '🎉 所有测试通过!' : '⚠️ 部分测试失败'}`)
  243. return allPassed
  244. }
  245. /**
  246. * 浏览器控制台快捷方式
  247. */
  248. if (typeof window !== 'undefined') {
  249. // 在浏览器控制台中可以使用 window.testAPI 来运行测试
  250. ;(window as any).testAPI = {
  251. runAllTests,
  252. testGetProjects,
  253. testHealthCheck,
  254. testBackendConnection,
  255. testApi,
  256. }
  257. console.log(`
  258. 🛠️ API测试工具已加载!
  259. 在浏览器控制台中输入以下命令进行测试:
  260. 1. testAPI.runAllTests() - 运行所有测试
  261. 2. testAPI.testGetProjects() - 测试项目列表API
  262. 3. testAPI.testHealthCheck() - 测试健康检查API
  263. 4. testAPI.testBackendConnection() - 测试后端连接
  264. 5. testAPI.testApi.getProjects() - 直接调用API函数
  265. 后端配置:
  266. - URL: ${CONFIG.BACKEND_BASE_URL}
  267. - API前缀: ${CONFIG.API_PREFIX}
  268. `)
  269. }
  270. /**
  271. * Node.js环境支持
  272. */
  273. if (typeof module !== 'undefined' && module.exports) {
  274. module.exports = {
  275. runAllTests,
  276. testGetProjects,
  277. testHealthCheck,
  278. testBackendConnection,
  279. testApi,
  280. }
  281. }
  282. export default {
  283. runAllTests,
  284. testGetProjects,
  285. testHealthCheck,
  286. testBackendConnection,
  287. testApi,
  288. }