system-config.routes.integration.test.ts 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. import { describe, it, expect, beforeEach, vi } from 'vitest';
  2. import { testClient } from 'hono/testing';
  3. import {
  4. IntegrationTestDatabase,
  5. setupIntegrationDatabaseHooksWithEntities
  6. } from '@d8d/shared-test-util';
  7. import { systemConfigRoutesMt } from '../../src/routes/system-config.routes.mt';
  8. import { SystemConfigMt } from '../../src/entities/system-config.entity.mt';
  9. import { SystemConfigServiceMt } from '../../src/services/system-config.service.mt';
  10. import { UserEntityMt, RoleMt } from '@d8d/core-module-mt/user-module-mt';
  11. import { FileMt } from '@d8d/core-module-mt/file-module-mt';
  12. import { TestDataFactory } from '../utils/integration-test-db';
  13. import { AuthService } from '@d8d/core-module-mt/auth-module-mt';
  14. import { UserServiceMt } from '@d8d/core-module-mt/user-module-mt';
  15. // 设置集成测试钩子
  16. setupIntegrationDatabaseHooksWithEntities([SystemConfigMt, UserEntityMt, RoleMt, FileMt])
  17. describe('系统配置路由API集成测试 (使用hono/testing)', () => {
  18. let client: ReturnType<typeof testClient<typeof systemConfigRoutesMt>>;
  19. let authService: AuthService;
  20. let userService: UserServiceMt;
  21. let testToken: string;
  22. let testUser: any;
  23. beforeEach(async () => {
  24. // 创建测试客户端
  25. client = testClient(systemConfigRoutesMt);
  26. // 获取数据源
  27. const dataSource = await IntegrationTestDatabase.getDataSource();
  28. if (!dataSource) throw new Error('Database not initialized');
  29. // 初始化服务
  30. userService = new UserServiceMt(dataSource);
  31. authService = new AuthService(userService);
  32. // 创建测试用户并生成token
  33. testUser = await TestDataFactory.createTestUser(dataSource, {
  34. username: 'testuser_systemconfig',
  35. password: 'TestPassword123!',
  36. email: 'testuser_systemconfig@example.com'
  37. });
  38. // 生成测试用户的token
  39. testToken = authService.generateToken(testUser);
  40. });
  41. describe('CRUD Operations', () => {
  42. it('应该成功创建系统配置', async () => {
  43. const configData = {
  44. configKey: 'app.login.enabled',
  45. configValue: 'true',
  46. description: '控制小程序登录功能是否开启'
  47. };
  48. const response = await client.index.$post({
  49. json: configData
  50. }, {
  51. headers: {
  52. 'Authorization': `Bearer ${testToken}`
  53. }
  54. });
  55. expect(response.status).toBe(201);
  56. const result = await response.json();
  57. if ('configKey' in result) {
  58. expect(result.configKey).toBe(configData.configKey);
  59. expect(result.configValue).toBe(configData.configValue);
  60. }
  61. });
  62. it('应该根据ID获取系统配置', async () => {
  63. // 先创建配置
  64. const dataSource = await IntegrationTestDatabase.getDataSource();
  65. if (!dataSource) throw new Error('Database not initialized');
  66. const systemConfigService = new SystemConfigServiceMt(dataSource);
  67. const config = await systemConfigService.create({
  68. configKey: 'app.payment.enabled',
  69. configValue: 'true',
  70. description: '控制支付功能是否开启',
  71. tenantId: testUser.tenantId
  72. } as SystemConfigMt);
  73. const response = await client[':id'].$get({
  74. param: { id: config.id }
  75. }, {
  76. headers: {
  77. 'Authorization': `Bearer ${testToken}`
  78. }
  79. });
  80. expect(response.status).toBe(200);
  81. const result = await response.json();
  82. if ('id' in result) {
  83. expect(result.id).toBe(config.id);
  84. expect(result.configKey).toBe(config.configKey);
  85. }
  86. });
  87. it('应该更新系统配置', async () => {
  88. // 先创建配置
  89. const dataSource = await IntegrationTestDatabase.getDataSource();
  90. if (!dataSource) throw new Error('Database not initialized');
  91. const systemConfigService = new SystemConfigServiceMt(dataSource);
  92. const config = await systemConfigService.create({
  93. configKey: 'app.notification.enabled',
  94. configValue: 'true',
  95. description: '控制通知功能是否开启',
  96. tenantId: testUser.tenantId
  97. } as SystemConfigMt);
  98. const updateData = {
  99. configValue: 'false',
  100. description: '通知功能已关闭'
  101. };
  102. const response = await client[':id'].$put({
  103. param: { id: config.id },
  104. json: updateData
  105. }, {
  106. headers: {
  107. 'Authorization': `Bearer ${testToken}`
  108. }
  109. });
  110. expect(response.status).toBe(200);
  111. const result = await response.json();
  112. if ('configValue' in result) {
  113. expect(result.configValue).toBe(updateData.configValue);
  114. expect(result.description).toBe(updateData.description);
  115. }
  116. });
  117. it('应该删除系统配置', async () => {
  118. // 先创建配置
  119. const dataSource = await IntegrationTestDatabase.getDataSource();
  120. if (!dataSource) throw new Error('Database not initialized');
  121. const systemConfigService = new SystemConfigServiceMt(dataSource);
  122. const config = await systemConfigService.create({
  123. configKey: 'app.analytics.enabled',
  124. configValue: 'true',
  125. description: '控制分析功能是否开启',
  126. tenantId: testUser.tenantId
  127. } as SystemConfigMt);
  128. const response = await client[':id'].$delete({
  129. param: { id: config.id }
  130. }, {
  131. headers: {
  132. 'Authorization': `Bearer ${testToken}`
  133. }
  134. });
  135. expect(response.status).toBe(204);
  136. // 验证配置已被删除
  137. const deletedConfig = await systemConfigService.getById(config.id);
  138. expect(deletedConfig).toBeNull();
  139. });
  140. it('应该列出系统配置列表', async () => {
  141. // 创建多个配置
  142. const dataSource = await IntegrationTestDatabase.getDataSource();
  143. if (!dataSource) throw new Error('Database not initialized');
  144. const systemConfigService = new SystemConfigServiceMt(dataSource);
  145. await systemConfigService.create({
  146. configKey: 'app.feature1.enabled',
  147. configValue: 'true',
  148. tenantId: testUser.tenantId
  149. } as SystemConfigMt);
  150. await systemConfigService.create({
  151. configKey: 'app.feature2.enabled',
  152. configValue: 'false',
  153. tenantId: testUser.tenantId
  154. } as SystemConfigMt);
  155. const response = await client.index.$get({
  156. query: {}
  157. }, {
  158. headers: {
  159. 'Authorization': `Bearer ${testToken}`
  160. }
  161. });
  162. expect(response.status).toBe(200);
  163. const result = await response.json();
  164. if ('data' in result) {
  165. expect(result.data).toHaveLength(2);
  166. }
  167. });
  168. });
  169. describe('Multi-tenant Isolation', () => {
  170. let tenant1User: any;
  171. let tenant2User: any;
  172. let tenant1Token: string;
  173. let tenant2Token: string;
  174. beforeEach(async () => {
  175. const dataSource = await IntegrationTestDatabase.getDataSource();
  176. if (!dataSource) throw new Error('Database not initialized');
  177. // 创建租户1的用户
  178. tenant1User = await TestDataFactory.createTestUser(dataSource, {
  179. username: 'tenant1_user_systemconfig',
  180. password: 'TestPassword123!',
  181. email: 'tenant1_systemconfig@example.com',
  182. tenantId: 1
  183. });
  184. // 创建租户2的用户
  185. tenant2User = await TestDataFactory.createTestUser(dataSource, {
  186. username: 'tenant2_user_systemconfig',
  187. password: 'TestPassword123!',
  188. email: 'tenant2_systemconfig@example.com',
  189. tenantId: 2
  190. });
  191. // 生成租户用户的token
  192. tenant1Token = authService.generateToken(tenant1User);
  193. tenant2Token = authService.generateToken(tenant2User);
  194. });
  195. it('应该按租户隔离配置', async () => {
  196. const dataSource = await IntegrationTestDatabase.getDataSource();
  197. if (!dataSource) throw new Error('Database not initialized');
  198. const systemConfigService = new SystemConfigServiceMt(dataSource);
  199. // 为租户1创建配置
  200. await systemConfigService.create({
  201. configKey: 'app.shared.config',
  202. configValue: 'tenant1-value',
  203. tenantId: 1
  204. } as SystemConfigMt);
  205. // 为租户2创建配置
  206. await systemConfigService.create({
  207. configKey: 'app.shared.config',
  208. configValue: 'tenant2-value',
  209. tenantId: 2
  210. } as SystemConfigMt);
  211. // 查询租户1的配置
  212. const response1 = await client.index.$get({
  213. query: {}
  214. }, {
  215. headers: {
  216. 'Authorization': `Bearer ${tenant1Token}`
  217. }
  218. });
  219. expect(response1.status).toBe(200);
  220. const result1 = await response1.json();
  221. if ('data' in result1) {
  222. expect(result1.data).toHaveLength(1);
  223. expect(result1.data[0].configValue).toBe('tenant1-value');
  224. }
  225. // 查询租户2的配置
  226. const response2 = await client.index.$get({
  227. query: {}
  228. }, {
  229. headers: {
  230. 'Authorization': `Bearer ${tenant2Token}`
  231. }
  232. });
  233. expect(response2.status).toBe(200);
  234. const result2 = await response2.json();
  235. if ('data' in result2) {
  236. expect(result2.data).toHaveLength(1);
  237. expect(result2.data[0].configValue).toBe('tenant2-value');
  238. }
  239. });
  240. });
  241. describe('Data Validation', () => {
  242. it('应该验证必填字段', async () => {
  243. const invalidData = {
  244. configKey: '', // 空字符串
  245. configValue: 'true'
  246. };
  247. const response = await client.index.$post({
  248. json: invalidData
  249. }, {
  250. headers: {
  251. 'Authorization': `Bearer ${testToken}`
  252. }
  253. });
  254. expect(response.status).toBe(400);
  255. });
  256. it('应该验证字段长度', async () => {
  257. const invalidData = {
  258. configKey: 'a'.repeat(256), // 超过255字符
  259. configValue: 'true'
  260. };
  261. const response = await client.index.$post({
  262. json: invalidData
  263. }, {
  264. headers: {
  265. 'Authorization': `Bearer ${testToken}`
  266. }
  267. });
  268. expect(response.status).toBe(400);
  269. });
  270. });
  271. });