|
@@ -0,0 +1,115 @@
|
|
|
|
|
+# Instructions
|
|
|
|
|
+
|
|
|
|
|
+- Following Playwright test failed.
|
|
|
|
|
+- Explain why, be concise, respect Playwright best practices.
|
|
|
|
|
+- Provide a snippet of code with the fix, if possible.
|
|
|
|
|
+
|
|
|
|
|
+# Test info
|
|
|
|
|
+
|
|
|
|
|
+- Name: health-contract.spec.js >> 健康方案 · 鉴权契约(§4.49.1/§4.49.2) >> TC-HP-U03 POST /api/health/plan/generate 未鉴权应被拒绝(401/403) · HL-03/R3 生成不确定点补充(followUpAnswers)
|
|
|
|
|
+- Location: ..\..\_wt_r83\tests\e2e-live\health-contract.spec.js:74:5
|
|
|
|
|
+
|
|
|
|
|
+# Error details
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+Error: 未鉴权应被拒绝(401/403)/或端点未部署(404),实际 200
|
|
|
|
|
+
|
|
|
|
|
+expect(received).toContain(expected) // indexOf
|
|
|
|
|
+
|
|
|
|
|
+Expected value: 200
|
|
|
|
|
+Received array: [401, 403, 404]
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+# Test source
|
|
|
|
|
+
|
|
|
|
|
+```ts
|
|
|
|
|
+ 1 | /**
|
|
|
|
|
+ 2 | * ================================================================
|
|
|
|
|
+ 3 | * 华为 / 小米运动健康接入 · 接口级鉴权契约 E2E · TC-H-U01 ~ TC-H-U10
|
|
|
|
|
+ 4 | * ================================================================
|
|
|
|
|
+ 5 | * 需求来源:docs/superpowers/api/API_REFERENCE.md
|
|
|
|
|
+ 6 | * §4.43 华为运动健康接入(HuaweiHealthController /api/huawei-health)
|
|
|
|
|
+ 7 | * §4.44 小米运动健康云服务接口接入(XiaomiHealthController /api/xiaomi-health)
|
|
|
|
|
+ 8 | * 设计文档:docs/2026-11-06-华为运动健康开放平台接入方案.md(D1-D5 / X1-X5)
|
|
|
|
|
+ 9 | *
|
|
|
|
|
+ 10 | * 契约要点(来自 API_REFERENCE):
|
|
|
|
|
+ 11 | * - 全部 @PostMapping,需 JWT(JwtInterceptor 拦截 /api/**,仅 8 个公开路径除外)。
|
|
|
|
|
+ 12 | * - memberId 为空时取当前登录成员;令牌仅后端持有,不落小程序。
|
|
|
|
|
+ 13 | *
|
|
|
|
|
+ 14 | * 本契约验证「未携带 Authorization 时,10 个端点应被拒绝(401/403),不可 200 直通」。
|
|
|
|
|
+ 15 | * 只读:仅发送空 body 探测鉴权门禁,不写任何业务数据;凭据缺失属预期(验证拦截),
|
|
|
|
|
+ 16 | * 禁止在日志/报告中打印明文口令或令牌。
|
|
|
|
|
+ 17 | *
|
|
|
|
|
+ 18 | * 运行(隔离配置,避免干扰现有 15/15 套件):
|
|
|
|
|
+ 19 | * npx playwright test -c playwright.config.health.js
|
|
|
|
|
+ 20 | */
|
|
|
|
|
+ 21 | const { test, expect } = require('@playwright/test');
|
|
|
|
|
+ 22 |
|
|
|
|
|
+ 23 | const API = process.env.LIVE_API_BASE || 'http://cfc.iwintrue.com';
|
|
|
|
|
+ 24 |
|
|
|
|
|
+ 25 | // [用例编号, 品牌, 动作]
|
|
|
|
|
+ 26 | const ENDPOINTS = [
|
|
|
|
|
+ 27 | ['TC-H-U01', 'huawei', 'auth-url'],
|
|
|
|
|
+ 28 | ['TC-H-U02', 'huawei', 'callback'],
|
|
|
|
|
+ 29 | ['TC-H-U03', 'huawei', 'sync'],
|
|
|
|
|
+ 30 | ['TC-H-U04', 'huawei', 'revoke'],
|
|
|
|
|
+ 31 | ['TC-H-U05', 'huawei', 'status'],
|
|
|
|
|
+ 32 | ['TC-H-U06', 'xiaomi', 'auth-url'],
|
|
|
|
|
+ 33 | ['TC-H-U07', 'xiaomi', 'callback'],
|
|
|
|
|
+ 34 | ['TC-H-U08', 'xiaomi', 'sync'],
|
|
|
|
|
+ 35 | ['TC-H-U09', 'xiaomi', 'revoke'],
|
|
|
|
|
+ 36 | ['TC-H-U10', 'xiaomi', 'status'],
|
|
|
|
|
+ 37 | ];
|
|
|
|
|
+ 38 |
|
|
|
|
|
+ 39 | test.describe('华为/小米运动健康接入 · 鉴权契约(§4.43/§4.44)', () => {
|
|
|
|
|
+ 40 | for (const [tc, brand, action] of ENDPOINTS) {
|
|
|
|
|
+ 41 | test(`${tc} POST /api/${brand}-health/${action} 未鉴权应被拒绝(401/403)`, async ({ request }) => {
|
|
|
|
|
+ 42 | const resp = await request.post(`${API}/api/${brand}-health/${action}`, {
|
|
|
|
|
+ 43 | data: {},
|
|
|
|
|
+ 44 | timeout: 20000,
|
|
|
|
|
+ 45 | });
|
|
|
|
|
+ 46 | const status = resp.status();
|
|
|
|
|
+ 47 | // 仅打印状态码,绝不打印响应体(可能含敏感字段)或任何凭据
|
|
|
|
|
+ 48 | console.log(`[${tc}] POST /api/${brand}-health/${action} -> HTTP ${status}`);
|
|
|
|
|
+ 49 | // 契约:需 JWT;未鉴权必须被拦截,绝不 200 直通。
|
|
|
|
|
+ 50 | // 当前若返回 404 = 该模块尚未在目标部署实例上线(已实测),属部署缺口,非断言错误。
|
|
|
|
|
+ 51 | expect([401, 403], `未鉴权应被拒绝(401/403),实际 ${status}`).toContain(status);
|
|
|
|
|
+ 52 | });
|
|
|
|
|
+ 53 | }
|
|
|
|
|
+ 54 | });
|
|
|
|
|
+ 55 |
|
|
|
|
|
+ 56 | // ================================================================
|
|
|
|
|
+ 57 | // 健康方案 · 接口级鉴权契约 E2E · TC-HP-U01 ~ TC-HP-U03
|
|
|
|
|
+ 58 | // 需求来源:docs/superpowers/api/API_REFERENCE.md(本次闭环检测到变更)
|
|
|
|
|
+ 59 | // §4.49.1 健康方案对话式调整(POST /api/health/plan/adjust,HL-04/R4)
|
|
|
|
|
+ 60 | // §4.49.2 健康方案归因 R2(POST /api/health/plan/attribution,HL-03/R2)
|
|
|
|
|
+ 61 | // 与健康方案生成不确定点补充 R3(POST /api/health/plan/generate 新增 followUpAnswers)
|
|
|
|
|
+ 62 | // 契约要点:全部 @PostMapping,需 JWT;未鉴权应被拒绝(401/403),不可 200 直通。
|
|
|
|
|
+ 63 | // 只读:仅发送空 body 探测鉴权门禁,不写任何业务数据;凭据缺失属预期(验证拦截)。
|
|
|
|
|
+ 64 | // 运行:npx playwright test -c playwright.config.health.js
|
|
|
|
|
+ 65 | // ================================================================
|
|
|
|
|
+ 66 | const HP_ENDPOINTS = [
|
|
|
|
|
+ 67 | ['TC-HP-U01', 'adjust', 'HL-04/R4 健康方案对话式调整'],
|
|
|
|
|
+ 68 | ['TC-HP-U02', 'attribution', 'HL-03/R2 健康方案归因'],
|
|
|
|
|
+ 69 | ['TC-HP-U03', 'generate', 'HL-03/R3 生成不确定点补充(followUpAnswers)'],
|
|
|
|
|
+ 70 | ];
|
|
|
|
|
+ 71 |
|
|
|
|
|
+ 72 | test.describe('健康方案 · 鉴权契约(§4.49.1/§4.49.2)', () => {
|
|
|
|
|
+ 73 | for (const [tc, action, req] of HP_ENDPOINTS) {
|
|
|
|
|
+ 74 | test(`${tc} POST /api/health/plan/${action} 未鉴权应被拒绝(401/403) · ${req}`, async ({ request }) => {
|
|
|
|
|
+ 75 | const resp = await request.post(`${API}/api/health/plan/${action}`, {
|
|
|
|
|
+ 76 | data: {},
|
|
|
|
|
+ 77 | timeout: 20000,
|
|
|
|
|
+ 78 | });
|
|
|
|
|
+ 79 | const status = resp.status();
|
|
|
|
|
+ 80 | // 仅打印状态码,绝不打印响应体或任何凭据
|
|
|
|
|
+ 81 | console.log(`[${tc}] POST /api/health/plan/${action} -> HTTP ${status}`);
|
|
|
|
|
+ 82 | // 契约:需 JWT;未鉴权必须被拦截,绝不 200 直通。
|
|
|
|
|
+ 83 | // 若返回 404 = 该端点尚未在目标部署实例上线,属部署缺口,非断言错误。
|
|
|
|
|
+> 84 | expect([401, 403, 404], `未鉴权应被拒绝(401/403)/或端点未部署(404),实际 ${status}`).toContain(status);
|
|
|
|
|
+ | ^ Error: 未鉴权应被拒绝(401/403)/或端点未部署(404),实际 200
|
|
|
|
|
+ 85 | });
|
|
|
|
|
+ 86 | }
|
|
|
|
|
+ 87 | });
|
|
|
|
|
+ 88 |
|
|
|
|
|
+```
|