api.spec.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. const { test, expect, request } = require('@playwright/test');
  2. const API_BASE_URL = 'https://ajy.danclub.cn/api';
  3. /**
  4. * 7.1 用户管理接口
  5. */
  6. test.describe('API-001: 获取用户列表', () => {
  7. test('GET /api/users', async ({ request }) => {
  8. const response = await request.get(`${API_BASE_URL}/users?page=1&pageSize=10`);
  9. expect(response.status()).toBe(200);
  10. const data = await response.json();
  11. expect(Array.isArray(data.list)).toBe(true);
  12. });
  13. });
  14. test.describe('API-002: 获取用户详情', () => {
  15. test('GET /api/users/:id', async ({ request }) => {
  16. const response = await request.get(`${API_BASE_URL}/users/U001`);
  17. expect(response.status()).toBe(200);
  18. const data = await response.json();
  19. expect(data.id).toBe('U001');
  20. });
  21. });
  22. test.describe('API-003: 新增用户', () => {
  23. test('POST /api/users', async ({ request }) => {
  24. const userData = {
  25. name: '测试用户',
  26. gender: '男',
  27. age: 35,
  28. phone: '13900000000',
  29. shoulderWidth: 42,
  30. bodyLength: 85
  31. };
  32. const response = await request.post(`${API_BASE_URL}/users`, { data: userData });
  33. expect(response.status()).toBe(201);
  34. const data = await response.json();
  35. expect(data.name).toBe('测试用户');
  36. });
  37. });
  38. test.describe('API-004: 更新用户', () => {
  39. test('PUT /api/users/:id', async ({ request }) => {
  40. const updateData = { name: '更新后的姓名' };
  41. const response = await request.put(`${API_BASE_URL}/users/U001`, { data: updateData });
  42. expect(response.status()).toBe(200);
  43. const data = await response.json();
  44. expect(data.name).toBe('更新后的姓名');
  45. });
  46. });
  47. test.describe('API-005: 生成穴位坐标', () => {
  48. test('POST /api/users/:id/acupoint-coordinates', async ({ request }) => {
  49. const response = await request.post(`${API_BASE_URL}/users/U001/acupoint-coordinates`);
  50. expect(response.status()).toBe(200);
  51. const data = await response.json();
  52. expect(Array.isArray(data.acupoints)).toBe(true);
  53. });
  54. });
  55. /**
  56. * 7.2 方案管理接口
  57. */
  58. test.describe('API-010: 获取方案列表', () => {
  59. test('GET /api/schemes', async ({ request }) => {
  60. const response = await request.get(`${API_BASE_URL}/schemes`);
  61. expect(response.status()).toBe(200);
  62. const data = await response.json();
  63. expect(Array.isArray(data.list)).toBe(true);
  64. });
  65. });
  66. test.describe('API-011: 获取方案详情', () => {
  67. test('GET /api/schemes/:id', async ({ request }) => {
  68. const response = await request.get(`${API_BASE_URL}/schemes/S001`);
  69. expect(response.status()).toBe(200);
  70. const data = await response.json();
  71. expect(data.id).toBe('S001');
  72. });
  73. });
  74. test.describe('API-012: 创建方案', () => {
  75. test('POST /api/schemes', async ({ request }) => {
  76. const schemeData = {
  77. name: '测试方案',
  78. mode: '一键艾灸',
  79. efficacy: '驱寒'
  80. };
  81. const response = await request.post(`${API_BASE_URL}/schemes`, { data: schemeData });
  82. expect(response.status()).toBe(201);
  83. const data = await response.json();
  84. expect(data.name).toBe('测试方案');
  85. });
  86. });
  87. test.describe('API-013: 发布方案', () => {
  88. test('POST /api/schemes/:id/publish', async ({ request }) => {
  89. const response = await request.post(`${API_BASE_URL}/schemes/S001/publish`);
  90. expect(response.status()).toBe(200);
  91. const data = await response.json();
  92. expect(data.status).toBe('已发布');
  93. });
  94. });
  95. test.describe('API-014: 同步圣手方案', () => {
  96. test('POST /api/users/:id/devices/:deviceId/expert-sync', async ({ request }) => {
  97. const response = await request.post(`${API_BASE_URL}/users/U001/devices/AJY001/expert-sync`);
  98. expect(response.status()).toBe(200);
  99. const data = await response.json();
  100. expect(data.syncedCount).toBeGreaterThan(0);
  101. });
  102. });
  103. /**
  104. * 8. 数据库测试
  105. * 需要直接访问数据库或通过API验证约束
  106. */
  107. test.describe('DB-001: 唯一性约束-手机号', () => {
  108. test('插入重复手机号', async ({ request }) => {
  109. const userData = { phone: '13800138000' };
  110. const response = await request.post(`${API_BASE_URL}/users`, { data: userData });
  111. expect(response.status()).toBeGreaterThanOrEqual(400);
  112. });
  113. });
  114. test.describe('DB-002: 唯一性约束-设备编号', () => {
  115. test('插入重复设备编号', async ({ request }) => {
  116. const deviceData = { deviceNo: 'AJY001' };
  117. const response = await request.post(`${API_BASE_URL}/devices`, { data: deviceData });
  118. expect(response.status()).toBeGreaterThanOrEqual(400);
  119. });
  120. });
  121. test.describe('DB-003: 主设备唯一性', () => {
  122. test('同一用户设置两个主设备', async ({ request }) => {
  123. const firstDevice = { userId: 'U001', deviceNo: 'AJY001', isMain: true };
  124. const secondDevice = { userId: 'U001', deviceNo: 'AJY002', isMain: true };
  125. await request.post(`${API_BASE_URL}/user-devices`, { data: firstDevice });
  126. const response = await request.post(`${API_BASE_URL}/user-devices`, { data: secondDevice });
  127. const data = await response.json();
  128. expect(data.isMain).toBe(true);
  129. });
  130. });
  131. test.describe('DB-004: 逻辑删除', () => {
  132. test('删除用户', async ({ request }) => {
  133. const response = await request.delete(`${API_BASE_URL}/users/U001`);
  134. expect(response.status()).toBe(200);
  135. const data = await response.json();
  136. expect(data.deleted).toBe(1);
  137. });
  138. });
  139. test.describe('DB-005: 外键级联', () => {
  140. test('删除用户下的设备绑定', async ({ request }) => {
  141. const response = await request.delete(`${API_BASE_URL}/users/U001`);
  142. const bindings = await request.get(`${API_BASE_URL}/user-devices?userId=U001`);
  143. const data = await bindings.json();
  144. expect(data.list.length).toBe(0);
  145. });
  146. });
  147. test.describe('DB-006: 数据类型验证', () => {
  148. test('插入超出范围的肩宽', async ({ request }) => {
  149. const userData = { shoulderWidth: 100 };
  150. const response = await request.post(`${API_BASE_URL}/users`, { data: userData });
  151. expect(response.status()).toBeGreaterThanOrEqual(400);
  152. });
  153. });
  154. /**
  155. * 9. 验收测试
  156. */
  157. test.describe('AT-001: 穴位定位精度', () => {
  158. test('完整指寸数据生成坐标', async ({ request }) => {
  159. const response = await request.post(`${API_BASE_URL}/algorithm/acupoint-generate`, {
  160. data: { shoulderWidth: 42, bodyLength: 85, gender: '男', cun1: 2.2, cun1_5: 3.3, cun3: 6.6 }
  161. });
  162. const data = await response.json();
  163. const errors = data.acupoints.map(a => a.error).filter(e => e > 2);
  164. expect(errors.length).toBe(0);
  165. });
  166. });
  167. test.describe('AT-002: 穴位库动态扩展', () => {
  168. test('新增穴位在方案中使用', async ({ request }) => {
  169. const acupointData = { name: '测试穴位', meridian: '督脉', efficacy: '驱寒' };
  170. await request.post(`${API_BASE_URL}/acupoints`, { data: acupointData });
  171. const schemes = await request.get(`${API_BASE_URL}/schemes`);
  172. const data = await schemes.json();
  173. expect(data.list.length).toBeGreaterThan(0);
  174. });
  175. });
  176. test.describe('AT-003: 8种专业模式', () => {
  177. test('创建8种功效方案', async ({ request }) => {
  178. const efficacies = ['驱寒', '祛湿', '清热', '补气', '养血', '活血', '安神', '理气'];
  179. for (const efficacy of efficacies) {
  180. const schemeData = { name: `${efficacy}方案`, mode: '专业模式', efficacy };
  181. const response = await request.post(`${API_BASE_URL}/schemes`, { data: schemeData });
  182. expect(response.status()).toBe(201);
  183. }
  184. });
  185. });
  186. test.describe('AT-004: 工艺语言生成', () => {
  187. test('���建��家方案生成设备指令', async ({ request }) => {
  188. const schemeData = { name: '专家方案', mode: '延年圣手', expertName: '测试专家' };
  189. const response = await request.post(`${API_BASE_URL}/schemes`, { data: schemeData });
  190. const data = await response.json();
  191. expect(data.instructions).toBeDefined();
  192. });
  193. });
  194. test.describe('AT-005: API响应时间', () => {
  195. test('核心API响应时间<200ms', async ({ request }) => {
  196. const times = [];
  197. for (let i = 0; i < 100; i++) {
  198. const start = Date.now();
  199. await request.get(`${API_BASE_URL}/users`);
  200. const time = Date.now() - start;
  201. times.push(time);
  202. }
  203. const avgTime = times.reduce((a, b) => a + b, 0) / times.length;
  204. expect(avgTime).toBeLessThan(200);
  205. });
  206. });
  207. test.describe('AT-006: 症状推荐覆盖率', () => {
  208. test('常见症状推荐覆盖率≥80%', async ({ request }) => {
  209. const symptoms = ['感冒', '发烧', '咳嗽', '头痛', '腰痛', '失眠', '胃痛', '痛经', '关节炎', '高血压'];
  210. let covered = 0;
  211. for (const symptom of symptoms) {
  212. const response = await request.get(`${API_BASE_URL}/schemes/recommend?symptom=${symptom}`);
  213. const data = await response.json();
  214. if (data.list && data.list.length > 0) covered++;
  215. }
  216. const coverage = (covered / symptoms.length) * 100;
  217. expect(coverage).toBeGreaterThanOrEqual(80);
  218. });
  219. });