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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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 { systemConfigRoutes } from '../../src/routes/system-config.routes';
  8. import { SystemConfig } from '../../src/entities/system-config.entity';
  9. import { SystemConfigService } from '../../src/services/system-config.service';
  10. import { UserEntity, Role } from '@d8d/core-module/user-module';
  11. import { File } from '@d8d/core-module/file-module';
  12. import { TestDataFactory } from '../utils/integration-test-db';
  13. import { AuthService } from '@d8d/core-module/auth-module';
  14. import { UserService } from '@d8d/core-module/user-module';
  15. import { redisUtil } from '@d8d/shared-utils';
  16. // 设置集成测试钩子
  17. setupIntegrationDatabaseHooksWithEntities([SystemConfig, UserEntity, Role, File])
  18. describe('系统配置路由API集成测试 (使用hono/testing)', () => {
  19. let client: ReturnType<typeof testClient<typeof systemConfigRoutes>>;
  20. let authService: AuthService;
  21. let userService: UserService;
  22. let testToken: string;
  23. let testUser: any;
  24. beforeEach(async () => {
  25. // 创建测试客户端
  26. client = testClient(systemConfigRoutes);
  27. // 获取数据源
  28. const dataSource = await IntegrationTestDatabase.getDataSource();
  29. if (!dataSource) throw new Error('Database not initialized');
  30. // 初始化服务
  31. userService = new UserService(dataSource);
  32. authService = new AuthService(userService);
  33. // 创建测试用户并生成token
  34. testUser = await TestDataFactory.createTestUser(dataSource, {
  35. username: 'testuser_systemconfig',
  36. password: 'TestPassword123!',
  37. email: 'testuser_systemconfig@example.com'
  38. });
  39. // 生成测试用户的token
  40. testToken = authService.generateToken(testUser);
  41. });
  42. describe('CRUD Operations', () => {
  43. it('应该成功创建系统配置', async () => {
  44. const configData = {
  45. configKey: 'app.login.enabled',
  46. configValue: 'true',
  47. description: '控制小程序登录功能是否开启'
  48. };
  49. const response = await client.index.$post({
  50. json: configData
  51. }, {
  52. headers: {
  53. 'Authorization': `Bearer ${testToken}`
  54. }
  55. });
  56. expect(response.status).toBe(201);
  57. const result = await response.json();
  58. if ('configKey' in result) {
  59. expect(result.configKey).toBe(configData.configKey);
  60. expect(result.configValue).toBe(configData.configValue);
  61. }
  62. });
  63. it('应该根据ID获取系统配置', async () => {
  64. // 先创建配置
  65. const dataSource = await IntegrationTestDatabase.getDataSource();
  66. if (!dataSource) throw new Error('Database not initialized');
  67. const systemConfigService = new SystemConfigService(dataSource);
  68. const config = await systemConfigService.create({
  69. configKey: 'app.payment.enabled',
  70. configValue: 'true',
  71. description: '控制支付功能是否开启',
  72. } as SystemConfig);
  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 SystemConfigService(dataSource);
  92. const config = await systemConfigService.create({
  93. configKey: 'app.notification.enabled',
  94. configValue: 'true',
  95. description: '控制通知功能是否开启',
  96. } as SystemConfig);
  97. const updateData = {
  98. configValue: 'false',
  99. description: '通知功能已关闭'
  100. };
  101. const response = await client[':id'].$put({
  102. param: { id: config.id },
  103. json: updateData
  104. }, {
  105. headers: {
  106. 'Authorization': `Bearer ${testToken}`
  107. }
  108. });
  109. expect(response.status).toBe(200);
  110. const result = await response.json();
  111. if ('configValue' in result) {
  112. expect(result.configValue).toBe(updateData.configValue);
  113. expect(result.description).toBe(updateData.description);
  114. }
  115. });
  116. it('应该删除系统配置', async () => {
  117. // 先创建配置
  118. const dataSource = await IntegrationTestDatabase.getDataSource();
  119. if (!dataSource) throw new Error('Database not initialized');
  120. const systemConfigService = new SystemConfigService(dataSource);
  121. const config = await systemConfigService.create({
  122. configKey: 'app.analytics.enabled',
  123. configValue: 'true',
  124. description: '控制分析功能是否开启',
  125. } as SystemConfig);
  126. const response = await client[':id'].$delete({
  127. param: { id: config.id }
  128. }, {
  129. headers: {
  130. 'Authorization': `Bearer ${testToken}`
  131. }
  132. });
  133. expect(response.status).toBe(204);
  134. // 验证配置已被删除
  135. const deletedConfig = await systemConfigService.getById(config.id);
  136. expect(deletedConfig).toBeNull();
  137. });
  138. it('应该列出系统配置列表', async () => {
  139. // 创建多个配置
  140. const dataSource = await IntegrationTestDatabase.getDataSource();
  141. if (!dataSource) throw new Error('Database not initialized');
  142. const systemConfigService = new SystemConfigService(dataSource);
  143. await systemConfigService.create({
  144. configKey: 'app.feature1.enabled',
  145. configValue: 'true',
  146. } as SystemConfig);
  147. await systemConfigService.create({
  148. configKey: 'app.feature2.enabled',
  149. configValue: 'false',
  150. } as SystemConfig);
  151. const response = await client.index.$get({
  152. query: {}
  153. }, {
  154. headers: {
  155. 'Authorization': `Bearer ${testToken}`
  156. }
  157. });
  158. expect(response.status).toBe(200);
  159. const result = await response.json();
  160. if ('data' in result) {
  161. expect(result.data).toHaveLength(2);
  162. }
  163. });
  164. });
  165. describe('Data Validation', () => {
  166. it('应该验证必填字段', async () => {
  167. const invalidData = {
  168. configKey: '', // 空字符串
  169. configValue: 'true'
  170. };
  171. const response = await client.index.$post({
  172. json: invalidData
  173. }, {
  174. headers: {
  175. 'Authorization': `Bearer ${testToken}`
  176. }
  177. });
  178. expect(response.status).toBe(400);
  179. });
  180. it('应该验证字段长度', async () => {
  181. const invalidData = {
  182. configKey: 'a'.repeat(256), // 超过255字符
  183. configValue: 'true'
  184. };
  185. const response = await client.index.$post({
  186. json: invalidData
  187. }, {
  188. headers: {
  189. 'Authorization': `Bearer ${testToken}`
  190. }
  191. });
  192. expect(response.status).toBe(400);
  193. });
  194. });
  195. describe('自定义路由缓存刷新验证', () => {
  196. beforeEach(async () => {
  197. // 清除测试缓存
  198. await redisUtil.deleteSystemConfig('app.login.enabled');
  199. await redisUtil.deleteSystemConfig('app.payment.enabled');
  200. await redisUtil.deleteSystemConfig('app.analytics.enabled');
  201. });
  202. it('应该通过自定义创建路由刷新缓存', async () => {
  203. const configData = {
  204. configKey: 'app.login.enabled',
  205. configValue: 'true',
  206. description: '控制小程序登录功能是否开启'
  207. };
  208. // 验证缓存初始为空
  209. const initialCache = await redisUtil.getSystemConfig(configData.configKey);
  210. expect(initialCache).toBeNull();
  211. // 通过自定义路由创建配置
  212. const response = await client.index.$post({
  213. json: configData
  214. }, {
  215. headers: {
  216. 'Authorization': `Bearer ${testToken}`
  217. }
  218. });
  219. if (response.status !== 201) {
  220. const errorText = await response.text();
  221. console.debug('Response error:', response.status, errorText);
  222. }
  223. expect(response.status).toBe(201);
  224. const result = await response.json() as any;
  225. expect(result.configKey).toBe(configData.configKey);
  226. expect(result.configValue).toBe(configData.configValue);
  227. // 验证缓存已被刷新(为空,因为setConfig会删除缓存)
  228. const afterCreateCache = await redisUtil.getSystemConfig(configData.configKey);
  229. expect(afterCreateCache).toBeNull();
  230. });
  231. it('应该通过自定义更新路由刷新缓存', async () => {
  232. // 先创建配置
  233. const dataSource = await IntegrationTestDatabase.getDataSource();
  234. if (!dataSource) throw new Error('Database not initialized');
  235. const systemConfigService = new SystemConfigService(dataSource);
  236. const config = await systemConfigService.create({
  237. configKey: 'app.payment.enabled',
  238. configValue: 'true',
  239. description: '控制支付功能是否开启',
  240. } as any);
  241. // 设置缓存
  242. await redisUtil.setSystemConfig('app.payment.enabled', 'cached-value');
  243. const initialCache = await redisUtil.getSystemConfig('app.payment.enabled');
  244. expect(initialCache).toBe('cached-value');
  245. // 通过自定义路由更新配置
  246. const updateData = {
  247. configValue: 'false',
  248. description: '支付功能已关闭'
  249. };
  250. const response = await client[':id'].$put({
  251. param: { id: config.id },
  252. json: updateData
  253. }, {
  254. headers: {
  255. 'Authorization': `Bearer ${testToken}`
  256. }
  257. });
  258. expect(response.status).toBe(200);
  259. const result = await response.json() as any;
  260. expect(result.configValue).toBe(updateData.configValue);
  261. // 验证缓存已被刷新
  262. const afterUpdateCache = await redisUtil.getSystemConfig('app.payment.enabled');
  263. expect(afterUpdateCache).toBeNull();
  264. });
  265. it('应该通过自定义删除路由刷新缓存', async () => {
  266. // 先创建配置
  267. const dataSource = await IntegrationTestDatabase.getDataSource();
  268. if (!dataSource) throw new Error('Database not initialized');
  269. const systemConfigService = new SystemConfigService(dataSource);
  270. const config = await systemConfigService.create({
  271. configKey: 'app.analytics.enabled',
  272. configValue: 'true',
  273. description: '控制分析功能是否开启',
  274. } as any);
  275. // 设置缓存
  276. await redisUtil.setSystemConfig('app.analytics.enabled', 'cached-value');
  277. const initialCache = await redisUtil.getSystemConfig('app.analytics.enabled');
  278. expect(initialCache).toBe('cached-value');
  279. // 通过自定义路由删除配置
  280. const response = await client[':id'].$delete({
  281. param: { id: config.id }
  282. }, {
  283. headers: {
  284. 'Authorization': `Bearer ${testToken}`
  285. }
  286. });
  287. expect(response.status).toBe(204);
  288. // 验证缓存已被刷新
  289. const afterDeleteCache = await redisUtil.getSystemConfig('app.analytics.enabled');
  290. expect(afterDeleteCache).toBeNull();
  291. });
  292. });
  293. });