qiwei.api.test.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. /**
  2. * QiWei Express 接口测试(supertest,不启动真实端口)
  3. *
  4. * 运行:cd backend && pnpm test
  5. */
  6. import { describe, it, before, after, beforeEach } from 'node:test';
  7. import assert from 'node:assert';
  8. import fs from 'node:fs';
  9. import path from 'node:path';
  10. import os from 'node:os';
  11. import { fileURLToPath } from 'node:url';
  12. import request from 'supertest';
  13. import { createQiweiApp } from '../src/apps/qiwei/app.js';
  14. const __dirname = path.dirname(fileURLToPath(import.meta.url));
  15. const fixturePath = path.join(__dirname, 'fixtures', 'qiwei_callbacks_sample.json');
  16. const samplePayload = JSON.parse(fs.readFileSync(fixturePath, 'utf-8')) as Record<string, unknown>;
  17. function tempStorePath(): string {
  18. return path.join(os.tmpdir(), `qiwei-api-test-${process.pid}-${Date.now()}.json`);
  19. }
  20. describe('QiWei API /api/qiwei', () => {
  21. let storeFile: string;
  22. const app = createQiweiApp();
  23. before(() => {
  24. storeFile = tempStorePath();
  25. process.env.QIWEI_ROOM_DOC_STORE = storeFile;
  26. });
  27. after(() => {
  28. if (fs.existsSync(storeFile)) {
  29. fs.unlinkSync(storeFile);
  30. }
  31. delete process.env.QIWEI_ROOM_DOC_STORE;
  32. delete process.env.QIWEI_GUID;
  33. delete process.env.QIWEI_TOKEN;
  34. });
  35. beforeEach(() => {
  36. if (fs.existsSync(storeFile)) {
  37. fs.unlinkSync(storeFile);
  38. }
  39. });
  40. it('POST /webhook — 解析 docid 并写入台账', async () => {
  41. const res = await request(app)
  42. .post('/api/qiwei/webhook')
  43. .send(samplePayload)
  44. .expect(200);
  45. assert.strictEqual(res.body.ok, true);
  46. assert.ok(Array.isArray(res.body.docRefs));
  47. assert.ok(res.body.docRefs.length >= 2);
  48. const docids = res.body.docRefs.map((r: { docid: string | null }) => r.docid);
  49. assert.ok(docids.includes('DOC_TEST_abc123xyz'));
  50. assert.ok(docids.includes('DOC_OTHER_room2'));
  51. assert.ok(Array.isArray(res.body.saved));
  52. assert.strictEqual(res.body.saved.length, 2);
  53. });
  54. it('GET /room-docs — 查询群↔文档台账', async () => {
  55. await request(app).post('/api/qiwei/webhook').send(samplePayload).expect(200);
  56. const res = await request(app).get('/api/qiwei/room-docs').expect(200);
  57. assert.strictEqual(res.body.ok, true);
  58. assert.ok(res.body.items.length >= 2);
  59. const roomIds = res.body.items.map((i: { roomId: string | number }) => String(i.roomId));
  60. assert.ok(roomIds.includes('10791082136095292'));
  61. assert.ok(roomIds.includes('99999999999999'));
  62. });
  63. it('GET /room-docs/:roomId — 单个群文档', async () => {
  64. await request(app).post('/api/qiwei/webhook').send(samplePayload).expect(200);
  65. const res = await request(app)
  66. .get('/api/qiwei/room-docs/10791082136095292')
  67. .expect(200);
  68. assert.strictEqual(res.body.item.docid, 'DOC_TEST_abc123xyz');
  69. assert.ok(res.body.item.linkUrl.includes('doc.weixin.qq.com'));
  70. });
  71. it('GET /room-docs/:roomId — 未登记返回 404', async () => {
  72. await request(app).get('/api/qiwei/room-docs/00000000000000').expect(404);
  73. });
  74. it('POST /screen — 无法解析 docid 时命中异常模板', async () => {
  75. const payload = {
  76. data: [
  77. {
  78. cmd: 15000,
  79. msgType: 13,
  80. fromRoomId: 111,
  81. msgData: {
  82. linkUrl: 'https://doc.weixin.qq.com/some/page-without-docid',
  83. title: '无 docid 的链接',
  84. },
  85. },
  86. ],
  87. };
  88. const res = await request(app).post('/api/qiwei/screen').send(payload).expect(200);
  89. assert.strictEqual(res.body.ok, true);
  90. const codes = res.body.exceptions.map((e: { code: string }) => e.code);
  91. assert.ok(codes.includes('DOCID_PARSE_FAILED'));
  92. });
  93. it('POST /sync — 缺少 guid 返回 400', async () => {
  94. delete process.env.QIWEI_GUID;
  95. const res = await request(app).post('/api/qiwei/sync').send({}).expect(400);
  96. assert.strictEqual(res.body.ok, false);
  97. assert.ok(String(res.body.error).includes('guid'));
  98. });
  99. it('POST /sync — 无 QIWEI_TOKEN 返回 500', async () => {
  100. delete process.env.QIWEI_TOKEN;
  101. process.env.QIWEI_GUID = 'test-guid';
  102. const res = await request(app)
  103. .post('/api/qiwei/sync')
  104. .send({ guid: 'test-guid', msgSeq: 0 })
  105. .expect(500);
  106. assert.strictEqual(res.body.ok, false);
  107. });
  108. });