users.integration.test.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
  2. import { OpenAPIHono } from '@hono/zod-openapi';
  3. import { createApiClient, ApiClient } from '../../__test_utils__/api-client';
  4. import { createMockDataSource } from '../../__test_utils__/test-db';
  5. // import '../../utils/generic-crud.service'
  6. // Mock 数据源
  7. vi.mock('../../../data-source', () => {
  8. const mockDataSource = createMockDataSource();
  9. return {
  10. AppDataSource: mockDataSource
  11. };
  12. });
  13. // Mock 用户服务
  14. vi.mock('../../modules/users/user.service', () => ({
  15. UserService: vi.fn().mockImplementation(() => ({
  16. getUserById: vi.fn().mockResolvedValue(null),
  17. createUser: vi.fn().mockResolvedValue({ id: 1, username: 'testuser' }),
  18. updateUser: vi.fn().mockResolvedValue({ affected: 1 }),
  19. deleteUser: vi.fn().mockResolvedValue({ affected: 1 })
  20. }))
  21. }));
  22. // Mock 认证中间件
  23. vi.mock('../../middleware/auth.middleware', () => ({
  24. authMiddleware: vi.fn().mockImplementation((_c, next) => next())
  25. }));
  26. // Mock 通用CRUD服务
  27. const mockGetList = vi.fn().mockResolvedValue([[], 0]);
  28. const mockGetById = vi.fn().mockResolvedValue(null);
  29. const mockCreate = vi.fn().mockResolvedValue({ id: 1, username: 'testuser' });
  30. const mockUpdate = vi.fn().mockResolvedValue({ affected: 1 });
  31. const mockDelete = vi.fn().mockResolvedValue({ affected: 1 });
  32. vi.mock('../../utils/generic-crud.service', () => ({
  33. GenericCrudService: vi.fn().mockImplementation(() => ({
  34. getList: mockGetList,
  35. getById: mockGetById,
  36. create: mockCreate,
  37. update: mockUpdate,
  38. delete: mockDelete
  39. }))
  40. }));
  41. describe('Users API Integration Tests', () => {
  42. let app: OpenAPIHono;
  43. let apiClient: ApiClient;
  44. beforeEach(async () => {
  45. vi.clearAllMocks();
  46. // 动态导入用户路由
  47. const userRoutes = await import('../users/index');
  48. // 使用导入的应用实例
  49. app = userRoutes.default;
  50. // 创建API客户端
  51. apiClient = createApiClient(app, {
  52. authToken: 'test-token-123'
  53. });
  54. });
  55. afterEach(() => {
  56. vi.resetAllMocks();
  57. });
  58. describe('GET /users', () => {
  59. it('应该返回用户列表和分页信息', async () => {
  60. const mockUsers = [
  61. {
  62. id: 1,
  63. username: 'user1',
  64. password: 'password123',
  65. phone: null,
  66. email: 'user1@example.com',
  67. nickname: null,
  68. name: null,
  69. avatar: null,
  70. isDisabled: 0,
  71. isDeleted: 0,
  72. roles: [],
  73. createdAt: new Date('2024-01-01T00:00:00.000Z'),
  74. updatedAt: new Date('2024-01-01T00:00:00.000Z')
  75. },
  76. {
  77. id: 2,
  78. username: 'user2',
  79. password: 'password123',
  80. phone: null,
  81. email: 'user2@example.com',
  82. nickname: null,
  83. name: null,
  84. avatar: null,
  85. isDisabled: 0,
  86. isDeleted: 0,
  87. roles: [],
  88. createdAt: new Date('2024-01-02T00:00:00.000Z'),
  89. updatedAt: new Date('2024-01-02T00:00:00.000Z')
  90. }
  91. ];
  92. mockGetList.mockResolvedValue([mockUsers, 2]);
  93. const response = await apiClient.get('/?page=1&pageSize=10');
  94. if (response.status !== 200) {
  95. // 使用 process.stderr.write 绕过 console mock
  96. process.stderr.write(`Response status: ${response.status}\n`);
  97. process.stderr.write(`Response data: ${JSON.stringify(response.data, null, 2)}\n`);
  98. }
  99. expect(response.status).toBe(200);
  100. expect(response.data).toEqual({
  101. data: mockUsers.map(user => ({
  102. ...user,
  103. createdAt: user.createdAt.toISOString(),
  104. updatedAt: user.updatedAt.toISOString()
  105. })),
  106. pagination: {
  107. total: 2,
  108. current: 1,
  109. pageSize: 10
  110. }
  111. });
  112. });
  113. it('应该验证分页参数', async () => {
  114. mockGetList.mockResolvedValue([[], 0]);
  115. const response = await apiClient.get('/?page=0&pageSize=0');
  116. expect(response.status).toBe(400);
  117. expect(response.data).toMatchObject({
  118. success: false,
  119. error: expect.any(Object)
  120. });
  121. });
  122. // it('应该支持关键词搜索', async () => {
  123. // const mockCrudService = require('../../../utils/generic-crud.service').GenericCrudService();
  124. // mockCrudService.getList.mockResolvedValue([[], 0]);
  125. // const response = await apiClient.get('/?page=1&pageSize=10&keyword=admin');
  126. // expect(response.status).toBe(200);
  127. // expect(mockCrudService.getList).toHaveBeenCalledWith(
  128. // expect.objectContaining({
  129. // page: 1,
  130. // pageSize: 10,
  131. // keyword: 'admin'
  132. // })
  133. // );
  134. // });
  135. });
  136. // describe('GET /users/:id', () => {
  137. // it('应该返回特定用户信息', async () => {
  138. // const mockUser = { id: 1, username: 'testuser', email: 'test@example.com' };
  139. // const mockCrudService = require('../../../utils/generic-crud.service').GenericCrudService();
  140. // mockCrudService.getById.mockResolvedValue(mockUser);
  141. // const response = await apiClient.get('/1');
  142. // expect(response.status).toBe(200);
  143. // expect(response.data).toEqual(mockUser);
  144. // expect(mockCrudService.getById).toHaveBeenCalledWith(1);
  145. // });
  146. // it('应该在用户不存在时返回404', async () => {
  147. // const mockCrudService = require('../../../utils/generic-crud.service').GenericCrudService();
  148. // mockCrudService.getById.mockResolvedValue(null);
  149. // const response = await apiClient.get('/999');
  150. // expect(response.status).toBe(404);
  151. // expect(response.data).toMatchObject({
  152. // code: 404,
  153. // message: expect.any(String)
  154. // });
  155. // });
  156. // it('应该验证用户ID格式', async () => {
  157. // const response = await apiClient.get('/invalid');
  158. // expect(response.status).toBe(400);
  159. // expect(response.data).toMatchObject({
  160. // success: false,
  161. // error: expect.any(Object)
  162. // });
  163. // });
  164. // });
  165. // describe('错误处理', () => {
  166. // it('应该在服务错误时返回500状态码', async () => {
  167. // const mockCrudService = require('../../../utils/generic-crud.service').GenericCrudService();
  168. // mockCrudService.getList.mockRejectedValue(new Error('Database error'));
  169. // const response = await apiClient.get('/?page=1&pageSize=10');
  170. // expect(response.status).toBe(500);
  171. // expect(response.data).toMatchObject({
  172. // code: 500,
  173. // message: 'Database error'
  174. // });
  175. // });
  176. // it('应该在未知错误时返回通用错误消息', async () => {
  177. // const mockCrudService = require('../../../utils/generic-crud.service').GenericCrudService();
  178. // mockCrudService.getList.mockRejectedValue('Unknown error');
  179. // const response = await apiClient.get('/?page=1&pageSize=10');
  180. // expect(response.status).toBe(500);
  181. // expect(response.data).toMatchObject({
  182. // code: 500,
  183. // message: '获取用户列表失败'
  184. // });
  185. // });
  186. // });
  187. // describe('认证和授权', () => {
  188. // it('应该在缺少认证令牌时返回401', async () => {
  189. // apiClient.clearAuthToken();
  190. // const response = await apiClient.get('/');
  191. // expect(response.status).toBe(401);
  192. // expect(response.data).toMatchObject({
  193. // code: 401,
  194. // message: expect.any(String)
  195. // });
  196. // });
  197. // it('应该在无效令牌时返回401', async () => {
  198. // // 模拟认证中间件验证失败
  199. // const authMiddleware = require('../../middleware/auth.middleware').authMiddleware;
  200. // authMiddleware.mockImplementation((c: any) => {
  201. // return c.json({ error: 'Invalid token' }, 401);
  202. // });
  203. // const response = await apiClient.get('/');
  204. // expect(response.status).toBe(401);
  205. // expect(response.data).toEqual({ error: 'Invalid token' });
  206. // });
  207. // });
  208. });