feie-api.integration.test.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. import { describe, it, expect, beforeEach } from 'vitest';
  2. import { testClient } from 'hono/testing';
  3. import { IntegrationTestDatabase, setupIntegrationDatabaseHooksWithEntities } from '@d8d/shared-test-util';
  4. import { JWTUtil } from '@d8d/shared-utils';
  5. import { UserEntityMt, RoleMt } from '@d8d/user-module-mt';
  6. import { FileMt } from '@d8d/file-module-mt';
  7. import createFeieRoutes from '../../src/routes/feie.routes';
  8. import { FeiePrinterMt, FeiePrintTaskMt, FeieConfigMt } from '../../src/entities';
  9. import { FeieTestFactory } from '../factories/feie-test-factory';
  10. // 设置集成测试钩子
  11. setupIntegrationDatabaseHooksWithEntities([
  12. UserEntityMt, RoleMt, FileMt, FeiePrinterMt, FeiePrintTaskMt, FeieConfigMt
  13. ])
  14. describe('飞鹅打印多租户API集成测试', () => {
  15. let client: ReturnType<typeof testClient<ReturnType<typeof createFeieRoutes>>>;
  16. let testFactory: FeieTestFactory;
  17. let userToken: string;
  18. let otherUserToken: string;
  19. let otherTenantUserToken: string;
  20. let testUser: UserEntityMt;
  21. let otherUser: UserEntityMt;
  22. let otherTenantUser: UserEntityMt;
  23. beforeEach(async () => {
  24. // 获取数据源
  25. const dataSource = await IntegrationTestDatabase.getDataSource();
  26. // 创建测试客户端
  27. client = testClient(createFeieRoutes(dataSource));
  28. // 创建测试工厂
  29. testFactory = new FeieTestFactory(dataSource);
  30. // 创建测试用户
  31. testUser = await testFactory.createTestUser(1);
  32. otherUser = await testFactory.createTestUser(1);
  33. otherTenantUser = await testFactory.createTestUser(2);
  34. // 生成JWT令牌
  35. userToken = JWTUtil.generateToken({ id: testUser.id, username: testUser.username, tenantId: 1 });
  36. otherUserToken = JWTUtil.generateToken({ id: otherUser.id, username: otherUser.username, tenantId: 1 });
  37. otherTenantUserToken = JWTUtil.generateToken({ id: otherTenantUser.id, username: otherTenantUser.username, tenantId: 2 });
  38. // 创建飞鹅API配置
  39. await testFactory.createFullFeieConfig(1);
  40. await testFactory.createFullFeieConfig(2);
  41. });
  42. describe('租户数据隔离验证', () => {
  43. it('应该只能访问自己租户的打印机', async () => {
  44. // 创建租户1的打印机
  45. const tenant1Printer = await testFactory.createTestPrinter(1);
  46. // 创建租户2的打印机
  47. const tenant2Printer = await testFactory.createTestPrinter(2);
  48. // 使用租户1的用户查询打印机列表
  49. const response = await client.printers.$get({
  50. query: {}
  51. }, {
  52. headers: {
  53. 'Authorization': `Bearer ${userToken}`
  54. }
  55. });
  56. expect(response.status).toBe(200);
  57. if (response.status === 200) {
  58. const data = await response.json();
  59. // 应该只返回租户1的打印机
  60. expect(data.success).toBe(true);
  61. expect(data.data.data).toHaveLength(1);
  62. expect(data.data.data[0].tenantId).toBe(1);
  63. expect(data.data.data[0].printerSn).toBe(tenant1Printer.printerSn);
  64. }
  65. });
  66. it('不应该访问其他租户的打印机详情', async () => {
  67. // 创建租户2的打印机
  68. const otherTenantPrinter = await testFactory.createTestPrinter(2);
  69. // 使用租户1的用户尝试访问租户2的打印机
  70. const response = await client['printers/:printerSn'].$get({
  71. param: { printerSn: otherTenantPrinter.printerSn }
  72. }, {
  73. headers: {
  74. 'Authorization': `Bearer ${userToken}`
  75. }
  76. });
  77. // 应该返回404,因为打印机不在当前租户
  78. expect(response.status).toBe(404);
  79. });
  80. it('应该正确过滤跨租户打印机访问', async () => {
  81. // 创建租户1的打印机
  82. const tenant1Printer = await testFactory.createTestPrinter(1);
  83. // 使用租户2的用户尝试访问租户1的打印机
  84. const response = await client['printers/:printerSn'].$get({
  85. param: { printerSn: tenant1Printer.printerSn }
  86. }, {
  87. headers: {
  88. 'Authorization': `Bearer ${otherTenantUserToken}`
  89. }
  90. });
  91. // 应该返回404,因为打印机不在当前租户
  92. expect(response.status).toBe(404);
  93. });
  94. });
  95. describe('打印机管理功能验证', () => {
  96. it('应该能够查询打印机列表', async () => {
  97. // 创建测试打印机
  98. const printer1 = await testFactory.createTestPrinter(1, { printerName: '打印机1' });
  99. const printer2 = await testFactory.createTestPrinter(1, { printerName: '打印机2' });
  100. // 查询打印机列表
  101. const response = await client.printers.$get({
  102. query: {}
  103. }, {
  104. headers: {
  105. 'Authorization': `Bearer ${userToken}`
  106. }
  107. });
  108. expect(response.status).toBe(200);
  109. if (response.status === 200) {
  110. const data = await response.json();
  111. expect(data.success).toBe(true);
  112. expect(data.data.data).toHaveLength(2);
  113. expect(data.data.total).toBe(2);
  114. }
  115. });
  116. it('应该能够根据名称搜索打印机', async () => {
  117. // 创建测试打印机
  118. const printer1 = await testFactory.createTestPrinter(1, { printerName: '测试打印机A' });
  119. const printer2 = await testFactory.createTestPrinter(1, { printerName: '其他打印机' });
  120. // 搜索打印机
  121. const response = await client.printers.$get({
  122. query: { search: '测试' }
  123. }, {
  124. headers: {
  125. 'Authorization': `Bearer ${userToken}`
  126. }
  127. });
  128. expect(response.status).toBe(200);
  129. if (response.status === 200) {
  130. const data = await response.json();
  131. expect(data.success).toBe(true);
  132. expect(data.data.data).toHaveLength(1);
  133. expect(data.data.data[0].printerName).toBe('测试打印机A');
  134. }
  135. });
  136. it('应该能够设置默认打印机', async () => {
  137. // 创建两个打印机
  138. const printer1 = await testFactory.createTestPrinter(1, { printerName: '打印机1', isDefault: 1 });
  139. const printer2 = await testFactory.createTestPrinter(1, { printerName: '打印机2', isDefault: 0 });
  140. // 设置打印机2为默认
  141. const response = await client['printers/:printerSn/set-default'].$post({
  142. param: { printerSn: printer2.printerSn }
  143. }, {
  144. headers: {
  145. 'Authorization': `Bearer ${userToken}`
  146. }
  147. });
  148. expect(response.status).toBe(200);
  149. if (response.status === 200) {
  150. const data = await response.json();
  151. expect(data.success).toBe(true);
  152. expect(data.data.isDefault).toBe(1);
  153. }
  154. // 验证打印机1不再是默认
  155. const dataSource = await IntegrationTestDatabase.getDataSource();
  156. const updatedPrinter1 = await dataSource.getRepository(FeiePrinterMt).findOne({
  157. where: { tenantId: 1, printerSn: printer1.printerSn }
  158. });
  159. expect(updatedPrinter1?.isDefault).toBe(0);
  160. });
  161. });
  162. describe('打印任务管理功能验证', () => {
  163. let testPrinter: FeiePrinterMt;
  164. beforeEach(async () => {
  165. // 创建测试打印机
  166. testPrinter = await testFactory.createTestPrinter(1);
  167. });
  168. it.skip('应该能够创建打印任务 - 需要实际飞鹅API连接', async () => {
  169. const taskData = {
  170. printerSn: testPrinter.printerSn,
  171. content: '<CB>测试打印内容</CB><BR>',
  172. printType: 'RECEIPT',
  173. delaySeconds: 0
  174. };
  175. const response = await client.tasks.$post({
  176. json: taskData
  177. }, {
  178. headers: {
  179. 'Authorization': `Bearer ${userToken}`
  180. }
  181. });
  182. console.debug('创建打印任务响应状态码:', response.status);
  183. if (response.status !== 200) {
  184. const errorResult = await response.json();
  185. console.debug('创建打印任务错误响应:', errorResult);
  186. }
  187. expect(response.status).toBe(200);
  188. if (response.status === 200) {
  189. const data = await response.json();
  190. expect(data.success).toBe(true);
  191. expect(data.data.taskId).toBeDefined();
  192. expect(data.data.printerSn).toBe(testPrinter.printerSn);
  193. expect(data.data.printType).toBe('RECEIPT');
  194. }
  195. });
  196. it('应该能够查询打印任务列表', async () => {
  197. // 创建测试打印任务
  198. const task1 = await testFactory.createTestPrintTask(1, testPrinter.printerSn);
  199. const task2 = await testFactory.createTestPrintTask(1, testPrinter.printerSn);
  200. // 查询打印任务列表
  201. const response = await client.tasks.$get({
  202. query: {}
  203. }, {
  204. headers: {
  205. 'Authorization': `Bearer ${userToken}`
  206. }
  207. });
  208. expect(response.status).toBe(200);
  209. if (response.status === 200) {
  210. const data = await response.json();
  211. expect(data.success).toBe(true);
  212. expect(data.data.data).toHaveLength(2);
  213. expect(data.data.total).toBe(2);
  214. }
  215. });
  216. it('应该能够根据打印机筛选打印任务', async () => {
  217. // 创建另一个打印机
  218. const otherPrinter = await testFactory.createTestPrinter(1);
  219. // 创建测试打印任务
  220. const task1 = await testFactory.createTestPrintTask(1, testPrinter.printerSn);
  221. const task2 = await testFactory.createTestPrintTask(1, otherPrinter.printerSn);
  222. // 根据打印机筛选
  223. const response = await client.tasks.$get({
  224. query: { printerSn: testPrinter.printerSn }
  225. }, {
  226. headers: {
  227. 'Authorization': `Bearer ${userToken}`
  228. }
  229. });
  230. expect(response.status).toBe(200);
  231. if (response.status === 200) {
  232. const data = await response.json();
  233. expect(data.success).toBe(true);
  234. expect(data.data.data).toHaveLength(1);
  235. expect(data.data.data[0].printerSn).toBe(testPrinter.printerSn);
  236. }
  237. });
  238. });
  239. describe('错误处理验证', () => {
  240. it('应该在打印机不存在时创建打印任务失败', async () => {
  241. const taskData = {
  242. printerSn: 'NONEXISTENT_PRINTER',
  243. content: '测试内容',
  244. printType: 'RECEIPT'
  245. };
  246. const response = await client.tasks.$post({
  247. json: taskData
  248. }, {
  249. headers: {
  250. 'Authorization': `Bearer ${userToken}`
  251. }
  252. });
  253. // 应该返回错误
  254. expect(response.status).toBe(500);
  255. if (response.status === 500) {
  256. const data = await response.json();
  257. expect(data.success).toBe(false);
  258. expect(data.message).toContain('打印机不存在');
  259. }
  260. });
  261. it('应该在打印任务不存在时查询状态失败', async () => {
  262. const response = await client['tasks/:taskId/status'].$get({
  263. param: { taskId: 'NONEXISTENT_TASK' }
  264. }, {
  265. headers: {
  266. 'Authorization': `Bearer ${userToken}`
  267. }
  268. });
  269. // 应该返回500(服务层抛出错误)
  270. expect(response.status).toBe(500);
  271. if (response.status === 500) {
  272. const data = await response.json();
  273. expect(data.success).toBe(false);
  274. expect(data.message).toBe('打印任务不存在');
  275. }
  276. });
  277. it('应该在缺少飞鹅API配置时返回错误', async () => {
  278. // 清理配置
  279. const dataSource = await IntegrationTestDatabase.getDataSource();
  280. await dataSource.getRepository(FeieConfigMt).delete({ tenantId: 1 });
  281. // 尝试查询打印机列表
  282. const response = await client.printers.$get({
  283. query: {}
  284. }, {
  285. headers: {
  286. 'Authorization': `Bearer ${userToken}`
  287. }
  288. });
  289. // 应该返回400,因为缺少配置
  290. expect(response.status).toBe(400);
  291. if (response.status === 400) {
  292. const data = await response.json();
  293. expect(data.success).toBe(false);
  294. expect(data.message).toBe('飞鹅API配置未找到或配置不完整');
  295. }
  296. });
  297. });
  298. describe('调度器管理功能验证', () => {
  299. it.skip('应该能够获取调度器状态 - 需要实际飞鹅API连接', async () => {
  300. const response = await client['scheduler/status'].$get({}, {
  301. headers: {
  302. 'Authorization': `Bearer ${userToken}`
  303. }
  304. });
  305. expect(response.status).toBe(200);
  306. if (response.status === 200) {
  307. const data = await response.json();
  308. expect(data.success).toBe(true);
  309. expect(data.data).toBeDefined();
  310. expect(data.data.isRunning).toBeDefined();
  311. expect(data.data.lastRunTime).toBeDefined();
  312. }
  313. });
  314. it.skip('应该能够启动和停止调度器 - 需要实际飞鹅API连接', async () => {
  315. // 启动调度器
  316. const startResponse = await client['scheduler/start'].$post({}, {
  317. headers: {
  318. 'Authorization': `Bearer ${userToken}`
  319. }
  320. });
  321. expect(startResponse.status).toBe(200);
  322. if (startResponse.status === 200) {
  323. const data = await startResponse.json();
  324. expect(data.success).toBe(true);
  325. expect(data.message).toBe('调度器已启动');
  326. }
  327. // 停止调度器
  328. const stopResponse = await client['scheduler/stop'].$post({}, {
  329. headers: {
  330. 'Authorization': `Bearer ${userToken}`
  331. }
  332. });
  333. expect(stopResponse.status).toBe(200);
  334. if (stopResponse.status === 200) {
  335. const data = await stopResponse.json();
  336. expect(data.success).toBe(true);
  337. expect(data.message).toBe('调度器已停止');
  338. }
  339. });
  340. it.skip('应该能够进行调度器健康检查 - 需要实际飞鹅API连接', async () => {
  341. const response = await client['scheduler/health'].$get({}, {
  342. headers: {
  343. 'Authorization': `Bearer ${userToken}`
  344. }
  345. });
  346. expect(response.status).toBe(200);
  347. if (response.status === 200) {
  348. const data = await response.json();
  349. expect(data.success).toBe(true);
  350. expect(data.data).toBeDefined();
  351. expect(data.data.healthy).toBeDefined();
  352. expect(data.data.message).toBeDefined();
  353. }
  354. });
  355. });
  356. describe('配置管理功能验证', () => {
  357. it('应该能够获取打印配置', async () => {
  358. const response = await client.config.$get({}, {
  359. headers: {
  360. 'Authorization': `Bearer ${userToken}`
  361. }
  362. });
  363. expect(response.status).toBe(200);
  364. if (response.status === 200) {
  365. const data = await response.json();
  366. expect(data.success).toBe(true);
  367. expect(data.data.data).toBeDefined();
  368. expect(Array.isArray(data.data.data)).toBe(true);
  369. // 应该包含飞鹅API配置
  370. const feieConfigs = data.data.data.filter((config: any) =>
  371. config.configKey.startsWith('feie.api.')
  372. );
  373. expect(feieConfigs.length).toBeGreaterThan(0);
  374. }
  375. });
  376. it('应该能够更新打印配置', async () => {
  377. const updateData = {
  378. configValue: 'new_test_user'
  379. };
  380. const response = await client['config/:configKey'].$put({
  381. param: { configKey: 'feie.api.user' },
  382. json: updateData
  383. }, {
  384. headers: {
  385. 'Authorization': `Bearer ${userToken}`
  386. }
  387. });
  388. expect(response.status).toBe(200);
  389. if (response.status === 200) {
  390. const data = await response.json();
  391. expect(data.success).toBe(true);
  392. expect(data.data.configKey).toBe('feie.api.user');
  393. expect(data.data.configValue).toBe('new_test_user');
  394. }
  395. // 验证配置已更新
  396. const dataSource = await IntegrationTestDatabase.getDataSource();
  397. const updatedConfig = await dataSource.getRepository(FeieConfigMt).findOne({
  398. where: { tenantId: 1, configKey: 'feie.api.user' }
  399. });
  400. expect(updatedConfig?.configValue).toBe('new_test_user');
  401. });
  402. it('应该在配置值为空时返回错误', async () => {
  403. const updateData = {
  404. configValue: ''
  405. };
  406. const response = await client['config/:configKey'].$put({
  407. param: { configKey: 'feie.api.user' },
  408. json: updateData
  409. }, {
  410. headers: {
  411. 'Authorization': `Bearer ${userToken}`
  412. }
  413. });
  414. // 应该返回400
  415. expect(response.status).toBe(400);
  416. if (response.status === 400) {
  417. const data = await response.json();
  418. expect(data.success).toBe(false);
  419. expect(data.message).toBe('配置值不能为空');
  420. }
  421. });
  422. });
  423. });