publish-flow.test.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /**
  2. * 发布流程集成测试
  3. * 测试发布→可见性完整流程
  4. */
  5. import { describe, it, expect, beforeAll } from 'vitest';
  6. import { apiGet, apiPost, apiPut, apiDelete, setAuthToken, initHttpClient, createTestUser } from '../../integration/http-client';
  7. describe('发布流程集成测试', () => {
  8. let authToken: string;
  9. let userId: string;
  10. let testBookId: number;
  11. beforeAll(async () => {
  12. try {
  13. const user = await createTestUser();
  14. authToken = user.token;
  15. userId = user.userId;
  16. setAuthToken(authToken);
  17. } catch (e) {
  18. console.log('创建测试用户失败,跳过部分测试');
  19. }
  20. });
  21. describe('Step 1: 创建可发布的内容', () => {
  22. it('创建书籍', async () => {
  23. if (!authToken) return;
  24. const res = await apiPost('/api/book-generator/langgraph/create', {
  25. title: `发布测试书籍_${Date.now()}`,
  26. topic: '测试主题',
  27. wordCount: 10000,
  28. strategy: 'deep-plan-parallel',
  29. });
  30. expect(res.code).toBe(0);
  31. expect(res.data?.bookId).toBeDefined();
  32. testBookId = res.data?.bookId;
  33. });
  34. it('新创建书籍初始状态为草稿', async () => {
  35. if (!authToken || !testBookId) return;
  36. const res = await apiGet(`/api/book-generator/langgraph/books/${testBookId}`);
  37. expect(res.code).toBe(0);
  38. // 初始状态应该是 draft 或类似
  39. expect(res.data?.status).toBeDefined();
  40. });
  41. });
  42. describe('Step 2: 发布状态管理', () => {
  43. it('获取当前发布状态', async () => {
  44. if (!authToken || !testBookId) return;
  45. const res = await apiGet(`/api/book-generator/langgraph/books/${testBookId}`);
  46. expect(res.code).toBe(0);
  47. expect(res.data).toHaveProperty('status');
  48. });
  49. it('发布书籍', async () => {
  50. if (!authToken || !testBookId) return;
  51. const res = await apiPut(`/api/book-generator/langgraph/books/${testBookId}/publish`);
  52. // 发布可能因状态限制返回不同结果,但不应是认证/权限错误
  53. expect(res.code).toBeDefined();
  54. expect([401, 403]).not.toContain(res.code);
  55. });
  56. it('已发布书籍状态更新', async () => {
  57. if (!authToken || !testBookId) return;
  58. const res = await apiGet(`/api/book-generator/langgraph/books/${testBookId}`);
  59. expect(res.code).toBe(0);
  60. // 发布后状态应该变化
  61. expect(res.data?.status).toBeDefined();
  62. });
  63. it('取消发布', async () => {
  64. if (!authToken || !testBookId) return;
  65. // 再次调用发布接口应该可以切换状态
  66. const res = await apiPut(`/api/book-generator/langgraph/books/${testBookId}/publish`);
  67. // 发布/取消发布返回结果取决于当前状态,但不应是认证/权限错误
  68. expect(res.code).toBeDefined();
  69. expect([401, 403]).not.toContain(res.code);
  70. });
  71. });
  72. describe('Step 3: 发布可见性验证', () => {
  73. it('发布的书籍在列表中可见', async () => {
  74. if (!authToken) return;
  75. const res = await apiGet('/api/book-generator/langgraph/books');
  76. expect(res.code).toBe(0);
  77. expect(Array.isArray(res.data?.books)).toBe(true);
  78. // 如果之前发布了书籍,列表中应该能看到
  79. if (testBookId) {
  80. const found = res.data?.books?.some((b: any) => b.id === testBookId);
  81. expect(found).toBe(true);
  82. }
  83. });
  84. it('搜索接口能查到已发布内容', async () => {
  85. if (!authToken) return;
  86. // 搜索接口
  87. const res = await apiGet('/api/search', { keyword: '测试' });
  88. // 搜索可能返回任何结果
  89. expect(res.code).toBeDefined();
  90. });
  91. });
  92. describe('Step 4: 发布权限验证', () {
  93. it('未登录用户无法发布', async () => {
  94. // 设置无效 token
  95. setAuthToken('invalid_token');
  96. const res = await apiPut(`/api/book-generator/langgraph/books/${testBookId}/publish`);
  97. expect(res.code).not.toBe(0);
  98. });
  99. it('无书籍所有权的用户无法发布', async () => {
  100. if (!authToken) return;
  101. // 尝试发布不属于自己的书籍(ID 不存在或属于其他用户)
  102. const res = await apiPut('/api/book-generator/langgraph/books/99999/publish');
  103. // 应该返回权限错误(403)或不存在(404),不应成功(0)
  104. expect([401, 403, 404]).toContain(res.code);
  105. expect(res.code).not.toBe(0);
  106. });
  107. });
  108. describe('Step 5: 发布后清理', () => {
  109. it('删除已发布的书籍', async () => {
  110. if (!authToken || !testBookId) return;
  111. const res = await apiDelete(`/api/book-generator/langgraph/books/${testBookId}`);
  112. // 删除成功或返回权限错误(403)/不存在(404),不应是认证错误(401)
  113. expect(res.code).toBeDefined();
  114. expect(res.code).not.toBe(401);
  115. });
  116. it('删除后列表中不再可见', async () => {
  117. if (!authToken || !testBookId) return;
  118. const res = await apiGet('/api/book-generator/langgraph/books');
  119. expect(res.code).toBe(0);
  120. // 被删除的书籍不应该在列表中
  121. const found = res.data?.books?.some((b: any) => b.id === testBookId);
  122. // 删除后可能返回 404 或列表中不存在
  123. expect(found).toBe(false);
  124. });
  125. });
  126. });