From 894ca055666579195af86d4e3ab4f65da48c393d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E7=94=B7?= Date: Thu, 30 Apr 2026 08:41:54 +0800 Subject: [PATCH] fix(course-package): extract role_key from JWT sub roles objects JWT sub.roles is an array of objects with role_key field, not plain strings. Fix extractRoleKeys to map role_key from each role object. Co-Authored-By: Claude Opus 4.7 --- .env.e2e.example | 3 +++ tests/course-package.spec.ts | 34 ++++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/.env.e2e.example b/.env.e2e.example index 6fedd65..b98d881 100644 --- a/.env.e2e.example +++ b/.env.e2e.example @@ -7,5 +7,8 @@ HUIKE_ADMIN_BASE_URL=https://admin.moicen.com MOICEN_ADMIN_USER= MOICEN_ADMIN_PASSWORD= +# 课包(course_package)测试依赖 MOICEN_E2E_UNIONID(共用 teacher/student unionid) +# 测试通过 JWT roles 验证只有 TEACHER/SUPERVISOR 角色可访问课包 API。 + # 可选:任意 HTTP 200 健康检查 URL(CI 可在仓库 Settings → Variables 配置 MOICEN_HEALTHCHECK_URL) MOICEN_HEALTHCHECK_URL= diff --git a/tests/course-package.spec.ts b/tests/course-package.spec.ts index 5b2601f..f76df51 100644 --- a/tests/course-package.spec.ts +++ b/tests/course-package.spec.ts @@ -6,21 +6,31 @@ const kcBase = process.env.HUIKE_ADMIN_BASE_URL?.trim() || 'https://admin.moicen.com'; /** - * 从 JWT payload 中提取 roles 数组(可能嵌在 `sub` JSON 字段内)。 + * 从 JWT payload 中提取 role_key 字符串数组(可能嵌在 `sub` JSON 字段内)。 + * roles 是对象数组(含 `role_key` 字段),也可能在顶层。 */ -function extractRoles(payload: Record | null): string[] { +function extractRoleKeys(payload: Record | null): string[] { if (!payload) return []; - if (Array.isArray(payload.roles)) return payload.roles as string[]; - const subRaw = payload.sub; - if (typeof subRaw === 'string') { - try { - const sub = JSON.parse(subRaw) as Record; - if (Array.isArray(sub.roles)) return sub.roles as string[]; - } catch { - /* 非 JSON sub,忽略 */ + const rawRoles: unknown[] = + (Array.isArray(payload.roles) ? payload.roles : []) as unknown[]; + if (rawRoles.length === 0) { + const subRaw = payload.sub; + if (typeof subRaw === 'string') { + try { + const sub = JSON.parse(subRaw) as Record; + if (Array.isArray(sub.roles)) { + rawRoles.push(...(sub.roles as unknown[])); + } + } catch { + /* 非 JSON sub,忽略 */ + } } } - return []; + return rawRoles + .map((r) => + typeof r === 'string' ? r : (r as Record)?.role_key as string | undefined, + ) + .filter((k): k is string => typeof k === 'string' && k.length > 0); } test.describe('课包(course_package)', () => { @@ -61,7 +71,7 @@ test.describe('课包(course_package)', () => { const payload = decodeJwtPayload(authToken!); expect(payload, 'JWT 应可解码').not.toBeNull(); - const roles = extractRoles(payload); + const roles = extractRoleKeys(payload); const hasCoursePkgRole = roles.some((r) => ['TEACHER', 'SUPERVISOR'].includes(r), );