api-client.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import { createTestServer } from './test-server';
  2. export interface ApiClientOptions {
  3. baseURL?: string;
  4. headers?: Record<string, string>;
  5. authToken?: string;
  6. }
  7. export class ApiClient {
  8. private app: any;
  9. private options: ApiClientOptions;
  10. constructor(app: any, options: ApiClientOptions = {}) {
  11. this.app = app;
  12. this.options = {
  13. baseURL: 'http://localhost:3000',
  14. headers: {
  15. 'Content-Type': 'application/json',
  16. },
  17. ...options
  18. };
  19. if (this.options.authToken) {
  20. this.options.headers = {
  21. ...this.options.headers,
  22. 'Authorization': `Bearer ${this.options.authToken}`
  23. };
  24. }
  25. }
  26. /**
  27. * 发送GET请求
  28. */
  29. async get<T = any>(path: string, headers?: Record<string, string>): Promise<ApiResponse<T>> {
  30. const server = createTestServer(this.app);
  31. const response = await server.get(path, {
  32. ...this.options.headers,
  33. ...headers
  34. });
  35. return this.createResponse(response);
  36. }
  37. /**
  38. * 发送POST请求
  39. */
  40. async post<T = any>(path: string, data?: any, headers?: Record<string, string>): Promise<ApiResponse<T>> {
  41. const server = createTestServer(this.app);
  42. const response = await server.post(path, data, {
  43. ...this.options.headers,
  44. ...headers
  45. });
  46. return this.createResponse(response);
  47. }
  48. /**
  49. * 发送PUT请求
  50. */
  51. async put<T = any>(path: string, data?: any, headers?: Record<string, string>): Promise<ApiResponse<T>> {
  52. const server = createTestServer(this.app);
  53. const response = await server.put(path, data, {
  54. ...this.options.headers,
  55. ...headers
  56. });
  57. return this.createResponse(response);
  58. }
  59. /**
  60. * 发送DELETE请求
  61. */
  62. async delete<T = any>(path: string, headers?: Record<string, string>): Promise<ApiResponse<T>> {
  63. const server = createTestServer(this.app);
  64. const response = await server.delete(path, {
  65. ...this.options.headers,
  66. ...headers
  67. });
  68. return this.createResponse(response);
  69. }
  70. /**
  71. * 发送PATCH请求
  72. */
  73. async patch<T = any>(path: string, data?: any, headers?: Record<string, string>): Promise<ApiResponse<T>> {
  74. const server = createTestServer(this.app);
  75. const response = await server.patch(path, data, {
  76. ...this.options.headers,
  77. ...headers
  78. });
  79. return this.createResponse(response);
  80. }
  81. /**
  82. * 设置认证令牌
  83. */
  84. setAuthToken(token: string): void {
  85. this.options.authToken = token;
  86. if (this.options.headers) {
  87. this.options.headers.Authorization = `Bearer ${token}`;
  88. }
  89. }
  90. /**
  91. * 清除认证令牌
  92. */
  93. clearAuthToken(): void {
  94. this.options.authToken = undefined;
  95. if (this.options.headers) {
  96. delete this.options.headers.Authorization;
  97. }
  98. }
  99. /**
  100. * 添加请求头
  101. */
  102. setHeader(name: string, value: string): void {
  103. if (!this.options.headers) {
  104. this.options.headers = {};
  105. }
  106. this.options.headers[name] = value;
  107. }
  108. /**
  109. * 移除请求头
  110. */
  111. removeHeader(name: string): void {
  112. if (this.options.headers) {
  113. delete this.options.headers[name];
  114. }
  115. }
  116. private createResponse<T>(response: any): ApiResponse<T> {
  117. return {
  118. status: response.status,
  119. headers: response.headers,
  120. data: response.data,
  121. ok: response.status >= 200 && response.status < 300,
  122. json: async () => response.data,
  123. text: async () => typeof response.data === 'string' ? response.data : JSON.stringify(response.data)
  124. };
  125. }
  126. }
  127. export interface ApiResponse<T = any> {
  128. status: number;
  129. headers: Record<string, string>;
  130. data: T;
  131. ok: boolean;
  132. json: () => Promise<T>;
  133. text: () => Promise<string>;
  134. }
  135. /**
  136. * 创建API客户端工厂函数
  137. */
  138. export function createApiClient(app: any, options?: ApiClientOptions): ApiClient {
  139. return new ApiClient(app, options);
  140. }
  141. /**
  142. * 断言响应状态码
  143. */
  144. export function expectStatus(response: ApiResponse, expectedStatus: number): void {
  145. if (response.status !== expectedStatus) {
  146. throw new Error(`Expected status ${expectedStatus}, but got ${response.status}. Response: ${JSON.stringify(response.data)}`);
  147. }
  148. }
  149. /**
  150. * 断言响应包含特定字段
  151. */
  152. export function expectResponseToHave<T>(response: ApiResponse<T>, expectedFields: Partial<T>): void {
  153. for (const [key, value] of Object.entries(expectedFields)) {
  154. if ((response.data as any)[key] !== value) {
  155. throw new Error(`Expected field ${key} to be ${value}, but got ${(response.data as any)[key]}`);
  156. }
  157. }
  158. }
  159. /**
  160. * 断言响应包含特定结构
  161. */
  162. export function expectResponseStructure(response: ApiResponse, structure: Record<string, any>): void {
  163. for (const key of Object.keys(structure)) {
  164. if (!(key in response.data)) {
  165. throw new Error(`Expected response to have key: ${key}`);
  166. }
  167. }
  168. }