Skip to content

Commit 196b2a1

Browse files
committed
fix(e2e): avoid live OpenAPI login with placeholder credentials
1 parent de9f1a3 commit 196b2a1

5 files changed

Lines changed: 116 additions & 57 deletions

File tree

docs/agents/cli-e2e-tests.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ describe.skipIf(<ready>)("e2e: <topic>(DashScope …)", () => {
6767
| 文本/搜索/记忆/配置 | `isDashScopeE2EReady()` |
6868
| 图像/语音 | `isBailianE2EMediaEnabled() && isDashScopeE2EReady()` |
6969
| 视频 | `isBailianE2EVideoEnabled() && isDashScopeE2EReady()` |
70+
| OpenAPI AK/SK | `isOpenApiE2EReady()``.env` 中必须同时提供完整 AK/SK) |
7071
| 视频 download/task | 另需 `BAILIAN_E2E_VIDEO_TASK_ID` |
7172
| 知识库 chat/search live | `isChatE2EReady()` / `isSearchE2EReady()``knowledge chat/search`,需 `BAILIAN_WORKSPACE_ID` + agent ID) |
7273

packages/commands/tests/e2e/auth.e2e.test.ts

Lines changed: 102 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { existsSync, readFileSync, writeFileSync } from "fs";
1+
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs";
22
import http from "node:http";
33
import type { AddressInfo } from "node:net";
4+
import { tmpdir } from "os";
45
import { join } from "path";
56
import { describe, expect, test } from "vite-plus/test";
67
import {
78
isDashScopeE2EReady,
9+
isOpenApiE2EReady,
810
makeE2eOutputDir,
911
parseStdoutJson,
1012
runCommandE2e,
@@ -47,9 +49,7 @@ async function startValidationServer(statusCode = 200): Promise<ValidationServer
4749
};
4850
}
4951

50-
/**
51-
* Auth 相关 E2E:只验证 CLI 进程能正常解析参数并退出。
52-
*/
52+
/** Auth E2E:本地参数/持久化契约默认执行;真实鉴权请求按对应 readiness gate 执行。 */
5353

5454
describe("e2e: auth", () => {
5555
test("auth login --help 正常退出", async () => {
@@ -116,6 +116,35 @@ describe("e2e: auth", () => {
116116
expect(stderr).toMatch(/Provide --access-key-id and --access-key-secret with --open-api/);
117117
});
118118

119+
test("auth login --open-api --dry-run 使用 placeholder 时不请求服务端、不写配置", async () => {
120+
const configDir = mkdtempSync(join(tmpdir(), "bl-auth-openapi-dry-run-"));
121+
try {
122+
const { stdout, stderr, exitCode } = await runCommandE2e(
123+
AUTH_ROUTES,
124+
[
125+
"auth",
126+
"login",
127+
"--open-api",
128+
"--access-key-id",
129+
"LTAI-e2e-placeholder",
130+
"--access-key-secret",
131+
"secret-e2e-placeholder",
132+
"--dry-run",
133+
],
134+
{
135+
BAILIAN_CONFIG_DIR: configDir,
136+
ALIBABA_CLOUD_ACCESS_KEY_ID: "",
137+
ALIBABA_CLOUD_ACCESS_KEY_SECRET: "",
138+
},
139+
);
140+
expect(exitCode, stderr).toBe(0);
141+
expect(stdout).toContain("Would save OpenAPI AK/SK credentials");
142+
expect(existsSync(join(configDir, "config.json"))).toBe(false);
143+
} finally {
144+
rmSync(configDir, { recursive: true, force: true });
145+
}
146+
});
147+
119148
test("auth logout --help 正常退出", async () => {
120149
const { stderr, exitCode } = await runCommandE2e(AUTH_ROUTES, ["auth", "logout", "--help"]);
121150
expect(exitCode, stderr).toBe(0);
@@ -458,57 +487,73 @@ describe("e2e: auth", () => {
458487
expect(denied.stderr).toMatch(/Unknown flag.*--access-key-id/);
459488
});
460489

461-
test("auth login --open-api 持久化 OpenAPI AK/SK 并支持单独 logout", async () => {
462-
const configDir = makeE2eOutputDir("auth-openapi-login");
463-
const env = {
464-
BAILIAN_CONFIG_DIR: configDir,
465-
ALIBABA_CLOUD_ACCESS_KEY_ID: "",
466-
ALIBABA_CLOUD_ACCESS_KEY_SECRET: "",
467-
};
468-
469-
const login = await runCommandE2e(
470-
AUTH_ROUTES,
471-
[
472-
"auth",
473-
"login",
474-
"--open-api",
475-
"--access-key-id",
476-
"LTAI-e2e-login-placeholder",
477-
"--access-key-secret",
478-
"secret-e2e-login-placeholder",
479-
],
480-
env,
481-
);
482-
expect(login.exitCode, login.stderr).toBe(0);
483-
expect(login.stderr).toMatch(/OpenAPI credentials saved/);
484-
485-
const config = JSON.parse(readFileSync(join(configDir, "config.json"), "utf8")) as Record<
486-
string,
487-
unknown
488-
>;
489-
expect(config.access_key_id).toBe("LTAI-e2e-login-placeholder");
490-
expect(config.access_key_secret).toBe("secret-e2e-login-placeholder");
491-
expect(config.openapi_access_key_id).toBeUndefined();
492-
expect(config.openapi_access_key_secret).toBeUndefined();
493-
494-
const status = await runCommandE2e(AUTH_ROUTES, ["auth", "status", "--output", "json"], env);
495-
expect(status.exitCode, status.stderr).toBe(0);
496-
const data = parseStdoutJson<{
497-
authenticated?: boolean;
498-
openapi?: { source?: string; access_key_id?: string; access_key_secret?: string };
499-
}>(status.stdout);
500-
expect(data.authenticated).toBe(true);
501-
expect(data.openapi?.source).toBe("config");
502-
expect(data.openapi?.access_key_id).not.toBe("LTAI-e2e-login-placeholder");
503-
expect(data.openapi?.access_key_secret).not.toBe("secret-e2e-login-placeholder");
504-
505-
const logout = await runCommandE2e(AUTH_ROUTES, ["auth", "logout", "--open-api"], env);
506-
expect(logout.exitCode, logout.stderr).toBe(0);
507-
expect(logout.stderr).toMatch(/Cleared access_key_id/);
508-
509-
const after = await runCommandE2e(AUTH_ROUTES, ["auth", "status", "--output", "json"], env);
510-
expect(after.exitCode, after.stderr).toBe(0);
511-
const afterData = parseStdoutJson<{ authenticated?: boolean; openapi?: unknown }>(after.stdout);
512-
expect(afterData.openapi).toBeUndefined();
513-
});
490+
test.skipIf(!isOpenApiE2EReady())(
491+
"auth login --open-api 使用环境中的真实 AK/SK,持久化后支持单独 logout",
492+
async () => {
493+
const accessKeyId = process.env.ALIBABA_CLOUD_ACCESS_KEY_ID!.trim();
494+
const accessKeySecret = process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET!.trim();
495+
const configDir = mkdtempSync(join(tmpdir(), "bl-auth-openapi-login-"));
496+
const env = {
497+
BAILIAN_CONFIG_DIR: configDir,
498+
ALIBABA_CLOUD_ACCESS_KEY_ID: "",
499+
ALIBABA_CLOUD_ACCESS_KEY_SECRET: "",
500+
};
501+
502+
try {
503+
const login = await runCommandE2e(
504+
AUTH_ROUTES,
505+
[
506+
"auth",
507+
"login",
508+
"--open-api",
509+
"--access-key-id",
510+
accessKeyId,
511+
"--access-key-secret",
512+
accessKeySecret,
513+
],
514+
env,
515+
);
516+
expect(login.exitCode, login.stderr).toBe(0);
517+
expect(login.stderr).toMatch(/OpenAPI credentials saved/);
518+
519+
const config = JSON.parse(readFileSync(join(configDir, "config.json"), "utf8")) as Record<
520+
string,
521+
unknown
522+
>;
523+
// 只断言布尔结果,避免失败 diff 把真实凭证打印到测试日志。
524+
expect(config.access_key_id === accessKeyId).toBe(true);
525+
expect(config.access_key_secret === accessKeySecret).toBe(true);
526+
expect(config.openapi_access_key_id).toBeUndefined();
527+
expect(config.openapi_access_key_secret).toBeUndefined();
528+
529+
const status = await runCommandE2e(
530+
AUTH_ROUTES,
531+
["auth", "status", "--output", "json"],
532+
env,
533+
);
534+
expect(status.exitCode, status.stderr).toBe(0);
535+
const data = parseStdoutJson<{
536+
authenticated?: boolean;
537+
openapi?: { source?: string; access_key_id?: string; access_key_secret?: string };
538+
}>(status.stdout);
539+
expect(data.authenticated).toBe(true);
540+
expect(data.openapi?.source).toBe("config");
541+
expect(data.openapi?.access_key_id === accessKeyId).toBe(false);
542+
expect(data.openapi?.access_key_secret === accessKeySecret).toBe(false);
543+
544+
const logout = await runCommandE2e(AUTH_ROUTES, ["auth", "logout", "--open-api"], env);
545+
expect(logout.exitCode, logout.stderr).toBe(0);
546+
expect(logout.stderr).toMatch(/Cleared access_key_id/);
547+
548+
const after = await runCommandE2e(AUTH_ROUTES, ["auth", "status", "--output", "json"], env);
549+
expect(after.exitCode, after.stderr).toBe(0);
550+
const afterData = parseStdoutJson<{ authenticated?: boolean; openapi?: unknown }>(
551+
after.stdout,
552+
);
553+
expect(afterData.openapi).toBeUndefined();
554+
} finally {
555+
rmSync(configDir, { recursive: true, force: true });
556+
}
557+
},
558+
);
514559
});

packages/commands/tests/e2e/helpers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export {
2828
isChatE2EReady,
2929
isConsoleE2EReady,
3030
isDashScopeE2EReady,
31+
isOpenApiE2EReady,
3132
isSearchE2EReady,
3233
} from "e2e/gating";
3334

packages/e2e/src/gating.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ export function isConsoleE2EReady(): boolean {
2828
}
2929
}
3030

31+
/** OpenAPI AK/SK 真实 E2E 就绪检查:只使用 `.env` / 进程环境中的完整凭证对。 */
32+
export function isOpenApiE2EReady(): boolean {
33+
if (!isBailianE2EEnabled()) return false;
34+
return Boolean(
35+
process.env.ALIBABA_CLOUD_ACCESS_KEY_ID?.trim() &&
36+
process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET?.trim(),
37+
);
38+
}
39+
3140
/** 语音与图像(可设 `BAILIAN_E2E_MEDIA=0` 跳过) */
3241
export function isBailianE2EMediaEnabled(): boolean {
3342
if (process.env.BAILIAN_E2E_MEDIA === "0") return false;

packages/e2e/src/global-setup.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ BAILIAN_E2E_VIDEO=1
2929
DASHSCOPE_BASE_URL=
3030
# DashScope API Key
3131
DASHSCOPE_API_KEY=
32+
# Alibaba Cloud OpenAPI AccessKey
33+
ALIBABA_CLOUD_ACCESS_KEY_ID=
34+
ALIBABA_CLOUD_ACCESS_KEY_SECRET=
3235
# -------------------------------
3336
BAILIAN_E2E_VIDEO_TASK_ID=b499a8cb-1fc4-4d43-9495-e23c7f78ae0d
3437
# -------------------------------

0 commit comments

Comments
 (0)