From 196b2a1f510b80ab14e350c78de1b5a8801b9299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=A5=E9=BA=92?= Date: Thu, 16 Jul 2026 11:53:09 +0800 Subject: [PATCH] fix(e2e): avoid live OpenAPI login with placeholder credentials --- docs/agents/cli-e2e-tests.md | 1 + packages/commands/tests/e2e/auth.e2e.test.ts | 149 ++++++++++++------- packages/commands/tests/e2e/helpers.ts | 1 + packages/e2e/src/gating.ts | 9 ++ packages/e2e/src/global-setup.ts | 3 + 5 files changed, 111 insertions(+), 52 deletions(-) diff --git a/docs/agents/cli-e2e-tests.md b/docs/agents/cli-e2e-tests.md index 749329c..2ca45f0 100644 --- a/docs/agents/cli-e2e-tests.md +++ b/docs/agents/cli-e2e-tests.md @@ -67,6 +67,7 @@ describe.skipIf()("e2e: (DashScope …)", () => { | 文本/搜索/记忆/配置 | `isDashScopeE2EReady()` | | 图像/语音 | `isBailianE2EMediaEnabled() && isDashScopeE2EReady()` | | 视频 | `isBailianE2EVideoEnabled() && isDashScopeE2EReady()` | +| OpenAPI AK/SK | `isOpenApiE2EReady()`(`.env` 中必须同时提供完整 AK/SK) | | 视频 download/task | 另需 `BAILIAN_E2E_VIDEO_TASK_ID` | | 知识库 chat/search live | `isChatE2EReady()` / `isSearchE2EReady()`(`knowledge chat/search`,需 `BAILIAN_WORKSPACE_ID` + agent ID) | diff --git a/packages/commands/tests/e2e/auth.e2e.test.ts b/packages/commands/tests/e2e/auth.e2e.test.ts index 5841f86..2ee4da7 100644 --- a/packages/commands/tests/e2e/auth.e2e.test.ts +++ b/packages/commands/tests/e2e/auth.e2e.test.ts @@ -1,10 +1,12 @@ -import { existsSync, readFileSync, writeFileSync } from "fs"; +import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs"; import http from "node:http"; import type { AddressInfo } from "node:net"; +import { tmpdir } from "os"; import { join } from "path"; import { describe, expect, test } from "vite-plus/test"; import { isDashScopeE2EReady, + isOpenApiE2EReady, makeE2eOutputDir, parseStdoutJson, runCommandE2e, @@ -47,9 +49,7 @@ async function startValidationServer(statusCode = 200): Promise { test("auth login --help 正常退出", async () => { @@ -116,6 +116,35 @@ describe("e2e: auth", () => { expect(stderr).toMatch(/Provide --access-key-id and --access-key-secret with --open-api/); }); + test("auth login --open-api --dry-run 使用 placeholder 时不请求服务端、不写配置", async () => { + const configDir = mkdtempSync(join(tmpdir(), "bl-auth-openapi-dry-run-")); + try { + const { stdout, stderr, exitCode } = await runCommandE2e( + AUTH_ROUTES, + [ + "auth", + "login", + "--open-api", + "--access-key-id", + "LTAI-e2e-placeholder", + "--access-key-secret", + "secret-e2e-placeholder", + "--dry-run", + ], + { + BAILIAN_CONFIG_DIR: configDir, + ALIBABA_CLOUD_ACCESS_KEY_ID: "", + ALIBABA_CLOUD_ACCESS_KEY_SECRET: "", + }, + ); + expect(exitCode, stderr).toBe(0); + expect(stdout).toContain("Would save OpenAPI AK/SK credentials"); + expect(existsSync(join(configDir, "config.json"))).toBe(false); + } finally { + rmSync(configDir, { recursive: true, force: true }); + } + }); + test("auth logout --help 正常退出", async () => { const { stderr, exitCode } = await runCommandE2e(AUTH_ROUTES, ["auth", "logout", "--help"]); expect(exitCode, stderr).toBe(0); @@ -458,57 +487,73 @@ describe("e2e: auth", () => { expect(denied.stderr).toMatch(/Unknown flag.*--access-key-id/); }); - test("auth login --open-api 持久化 OpenAPI AK/SK 并支持单独 logout", async () => { - const configDir = makeE2eOutputDir("auth-openapi-login"); - const env = { - BAILIAN_CONFIG_DIR: configDir, - ALIBABA_CLOUD_ACCESS_KEY_ID: "", - ALIBABA_CLOUD_ACCESS_KEY_SECRET: "", - }; + test.skipIf(!isOpenApiE2EReady())( + "auth login --open-api 使用环境中的真实 AK/SK,持久化后支持单独 logout", + async () => { + const accessKeyId = process.env.ALIBABA_CLOUD_ACCESS_KEY_ID!.trim(); + const accessKeySecret = process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET!.trim(); + const configDir = mkdtempSync(join(tmpdir(), "bl-auth-openapi-login-")); + const env = { + BAILIAN_CONFIG_DIR: configDir, + ALIBABA_CLOUD_ACCESS_KEY_ID: "", + ALIBABA_CLOUD_ACCESS_KEY_SECRET: "", + }; - const login = await runCommandE2e( - AUTH_ROUTES, - [ - "auth", - "login", - "--open-api", - "--access-key-id", - "LTAI-e2e-login-placeholder", - "--access-key-secret", - "secret-e2e-login-placeholder", - ], - env, - ); - expect(login.exitCode, login.stderr).toBe(0); - expect(login.stderr).toMatch(/OpenAPI credentials saved/); + try { + const login = await runCommandE2e( + AUTH_ROUTES, + [ + "auth", + "login", + "--open-api", + "--access-key-id", + accessKeyId, + "--access-key-secret", + accessKeySecret, + ], + env, + ); + expect(login.exitCode, login.stderr).toBe(0); + expect(login.stderr).toMatch(/OpenAPI credentials saved/); - const config = JSON.parse(readFileSync(join(configDir, "config.json"), "utf8")) as Record< - string, - unknown - >; - expect(config.access_key_id).toBe("LTAI-e2e-login-placeholder"); - expect(config.access_key_secret).toBe("secret-e2e-login-placeholder"); - expect(config.openapi_access_key_id).toBeUndefined(); - expect(config.openapi_access_key_secret).toBeUndefined(); + const config = JSON.parse(readFileSync(join(configDir, "config.json"), "utf8")) as Record< + string, + unknown + >; + // 只断言布尔结果,避免失败 diff 把真实凭证打印到测试日志。 + expect(config.access_key_id === accessKeyId).toBe(true); + expect(config.access_key_secret === accessKeySecret).toBe(true); + expect(config.openapi_access_key_id).toBeUndefined(); + expect(config.openapi_access_key_secret).toBeUndefined(); - const status = await runCommandE2e(AUTH_ROUTES, ["auth", "status", "--output", "json"], env); - expect(status.exitCode, status.stderr).toBe(0); - const data = parseStdoutJson<{ - authenticated?: boolean; - openapi?: { source?: string; access_key_id?: string; access_key_secret?: string }; - }>(status.stdout); - expect(data.authenticated).toBe(true); - expect(data.openapi?.source).toBe("config"); - expect(data.openapi?.access_key_id).not.toBe("LTAI-e2e-login-placeholder"); - expect(data.openapi?.access_key_secret).not.toBe("secret-e2e-login-placeholder"); + const status = await runCommandE2e( + AUTH_ROUTES, + ["auth", "status", "--output", "json"], + env, + ); + expect(status.exitCode, status.stderr).toBe(0); + const data = parseStdoutJson<{ + authenticated?: boolean; + openapi?: { source?: string; access_key_id?: string; access_key_secret?: string }; + }>(status.stdout); + expect(data.authenticated).toBe(true); + expect(data.openapi?.source).toBe("config"); + expect(data.openapi?.access_key_id === accessKeyId).toBe(false); + expect(data.openapi?.access_key_secret === accessKeySecret).toBe(false); - const logout = await runCommandE2e(AUTH_ROUTES, ["auth", "logout", "--open-api"], env); - expect(logout.exitCode, logout.stderr).toBe(0); - expect(logout.stderr).toMatch(/Cleared access_key_id/); + const logout = await runCommandE2e(AUTH_ROUTES, ["auth", "logout", "--open-api"], env); + expect(logout.exitCode, logout.stderr).toBe(0); + expect(logout.stderr).toMatch(/Cleared access_key_id/); - const after = await runCommandE2e(AUTH_ROUTES, ["auth", "status", "--output", "json"], env); - expect(after.exitCode, after.stderr).toBe(0); - const afterData = parseStdoutJson<{ authenticated?: boolean; openapi?: unknown }>(after.stdout); - expect(afterData.openapi).toBeUndefined(); - }); + const after = await runCommandE2e(AUTH_ROUTES, ["auth", "status", "--output", "json"], env); + expect(after.exitCode, after.stderr).toBe(0); + const afterData = parseStdoutJson<{ authenticated?: boolean; openapi?: unknown }>( + after.stdout, + ); + expect(afterData.openapi).toBeUndefined(); + } finally { + rmSync(configDir, { recursive: true, force: true }); + } + }, + ); }); diff --git a/packages/commands/tests/e2e/helpers.ts b/packages/commands/tests/e2e/helpers.ts index 0ee7375..68e82b9 100644 --- a/packages/commands/tests/e2e/helpers.ts +++ b/packages/commands/tests/e2e/helpers.ts @@ -28,6 +28,7 @@ export { isChatE2EReady, isConsoleE2EReady, isDashScopeE2EReady, + isOpenApiE2EReady, isSearchE2EReady, } from "e2e/gating"; diff --git a/packages/e2e/src/gating.ts b/packages/e2e/src/gating.ts index 28916d9..a10c859 100644 --- a/packages/e2e/src/gating.ts +++ b/packages/e2e/src/gating.ts @@ -28,6 +28,15 @@ export function isConsoleE2EReady(): boolean { } } +/** OpenAPI AK/SK 真实 E2E 就绪检查:只使用 `.env` / 进程环境中的完整凭证对。 */ +export function isOpenApiE2EReady(): boolean { + if (!isBailianE2EEnabled()) return false; + return Boolean( + process.env.ALIBABA_CLOUD_ACCESS_KEY_ID?.trim() && + process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET?.trim(), + ); +} + /** 语音与图像(可设 `BAILIAN_E2E_MEDIA=0` 跳过) */ export function isBailianE2EMediaEnabled(): boolean { if (process.env.BAILIAN_E2E_MEDIA === "0") return false; diff --git a/packages/e2e/src/global-setup.ts b/packages/e2e/src/global-setup.ts index f12704c..005cf3a 100644 --- a/packages/e2e/src/global-setup.ts +++ b/packages/e2e/src/global-setup.ts @@ -29,6 +29,9 @@ BAILIAN_E2E_VIDEO=1 DASHSCOPE_BASE_URL= # DashScope API Key DASHSCOPE_API_KEY= +# Alibaba Cloud OpenAPI AccessKey +ALIBABA_CLOUD_ACCESS_KEY_ID= +ALIBABA_CLOUD_ACCESS_KEY_SECRET= # ------------------------------- BAILIAN_E2E_VIDEO_TASK_ID=b499a8cb-1fc4-4d43-9495-e23c7f78ae0d # -------------------------------