Skip to content

Commit 247bb82

Browse files
committed
test(agent): cover config-write, profile, logout and error-mapping auth-chain scenarios
1 parent 1e6165d commit 247bb82

6 files changed

Lines changed: 157 additions & 3 deletions

File tree

packages/commands/src/commands/managed-agent/_engine/credentials.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,12 @@ export function injectProviderCredentials(
9494
if (cred) {
9595
block.api_key = cred.token;
9696
if ("base_url" in block && !block.base_url) {
97-
block.base_url = cred.baseUrl.endsWith(AGENTSTUDIO_API_PATH)
98-
? cred.baseUrl
99-
: `${cred.baseUrl}${AGENTSTUDIO_API_PATH}`;
97+
// Defensive normalization: the auth chain already normalizes base_url to
98+
// an origin, but never let a trailing slash produce "//api/v1/agentstudio".
99+
const origin = cred.baseUrl.replace(/\/+$/, "");
100+
block.base_url = origin.endsWith(AGENTSTUDIO_API_PATH)
101+
? origin
102+
: `${origin}${AGENTSTUDIO_API_PATH}`;
100103
}
101104
}
102105
if ("workspace_id" in block && !block.workspace_id && host.settings.workspaceId) {

packages/commands/tests/credentials-bridge.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,26 @@ test("inject:base_url 已带后缀不重复拼;非空字面量 base_url 保留",
103103
expect(literal.bailian.base_url).toBe("https://custom.example.com/api/v1/agentstudio");
104104
});
105105

106+
test("inject:base_url 尾斜杠被规范化,不产生双斜杠", () => {
107+
const providers = { bailian: { api_key: "", base_url: "" } };
108+
injectProviderCredentials(
109+
providers,
110+
makeHost({ apiCred: bailianCred("t", "https://dashscope.aliyuncs.com/") }),
111+
);
112+
expect(providers.bailian.base_url).toBe("https://dashscope.aliyuncs.com/api/v1/agentstudio");
113+
});
114+
115+
test("inject:已带后缀且尾斜杠的 base_url 去斜杠后原样保留", () => {
116+
const providers = { bailian: { api_key: "", base_url: "" } };
117+
injectProviderCredentials(
118+
providers,
119+
makeHost({
120+
apiCred: bailianCred("t", "https://x.maas.aliyuncs.com/api/v1/agentstudio/"),
121+
}),
122+
);
123+
expect(providers.bailian.base_url).toBe("https://x.maas.aliyuncs.com/api/v1/agentstudio");
124+
});
125+
106126
test("inject:workspace_id 引用且为空时用 settings 填充;有字面量则保留", () => {
107127
const empty = { bailian: { api_key: "", workspace_id: "" } };
108128
injectProviderCredentials(
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
version: "1"
2+
3+
providers:
4+
bailian:
5+
api_key: ${DASHSCOPE_API_KEY}
6+
7+
agents: "not-a-map"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
version: "1"
2+
3+
providers:
4+
bailian:
5+
api_key: ${DASHSCOPE_API_KEY}
6+
base_url: ${BAILIAN_BASE_URL}
7+
8+
defaults:
9+
provider: bailian
10+
11+
environments:
12+
dev:
13+
config:
14+
type: cloud
15+
networking:
16+
type: unrestricted
17+
18+
agents:
19+
assistant:
20+
description: "E2E auth-chain fixture"
21+
model: qwen3.7-max
22+
instructions: |
23+
You are a helpful assistant.
24+
environment: dev
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
2+
import { tmpdir } from "node:os";
3+
import { join } from "node:path";
4+
import { afterEach, describe, expect, test } from "vite-plus/test";
5+
import { e2eFixturesDir, runCommandE2e } from "./helpers.ts";
6+
import { MANAGED_AGENT_ROUTES } from "./topic-routes.ts";
7+
8+
/**
9+
* managed-agent 凭证链 e2e:验证 bl 自有配置体系(config 写入 / 命名 Profile /
10+
* logout)与错误映射如何流入 SDK 引擎。全部离线:`managed-agent validate` 会走
11+
* authStage 凭证解析 + 引擎注入 + agents.yaml 校验,但不发任何网络请求。
12+
* 配置一律通过 BAILIAN_CONFIG_DIR 指向临时目录,绝不触碰真实用户配置。
13+
*/
14+
15+
const ROUTES = {
16+
...MANAGED_AGENT_ROUTES,
17+
"auth logout": "authLogout",
18+
};
19+
20+
const AGENTS_YAML = join(e2eFixturesDir, "managed-agent", "agents.yaml");
21+
const AGENTS_YAML_INVALID = join(e2eFixturesDir, "managed-agent", "agents-invalid.yaml");
22+
23+
const tempDirs: string[] = [];
24+
25+
afterEach(() => {
26+
for (const dir of tempDirs.splice(0)) rmSync(dir, { recursive: true, force: true });
27+
});
28+
29+
/** 新建隔离配置目录并写入 config.json;返回子进程 env 覆盖(清空外部凭证 env)。 */
30+
function makeConfigEnv(config: Record<string, unknown>): NodeJS.ProcessEnv {
31+
const configDir = mkdtempSync(join(tmpdir(), "bl-managed-agent-auth-"));
32+
tempDirs.push(configDir);
33+
writeFileSync(join(configDir, "config.json"), `${JSON.stringify(config, null, 2)}\n`);
34+
return {
35+
BAILIAN_CONFIG_DIR: configDir,
36+
DASHSCOPE_API_KEY: "",
37+
DASHSCOPE_BASE_URL: "",
38+
BAILIAN_BASE_URL: "",
39+
BAILIAN_WORKSPACE_ID: "",
40+
};
41+
}
42+
43+
function validateArgs(file: string): string[] {
44+
return ["managed-agent", "validate", "--file", file, "--quiet"];
45+
}
46+
47+
describe("e2e: managed-agent 凭证链(config 写入 / Profile / logout / 错误映射)", () => {
48+
test("config.json 写入的 api_key 流入引擎,validate 离线通过", async () => {
49+
const env = makeConfigEnv({ api_key: "sk-e2e-config-write" });
50+
const { stderr, exitCode } = await runCommandE2e(ROUTES, validateArgs(AGENTS_YAML), env);
51+
expect(exitCode, stderr).toBe(0);
52+
});
53+
54+
test("active_config 指向的命名 Profile 提供凭证时通过", async () => {
55+
const env = makeConfigEnv({
56+
work: { api_key: "sk-e2e-profile-work" },
57+
active_config: "work",
58+
});
59+
const { stderr, exitCode } = await runCommandE2e(ROUTES, validateArgs(AGENTS_YAML), env);
60+
expect(exitCode, stderr).toBe(0);
61+
});
62+
63+
test("active_config 切到无凭证 Profile 时报统一 AUTH 错误 (3)", async () => {
64+
const env = makeConfigEnv({
65+
work: { api_key: "sk-e2e-profile-work" },
66+
empty: {},
67+
active_config: "empty",
68+
});
69+
const { stderr, exitCode } = await runCommandE2e(ROUTES, validateArgs(AGENTS_YAML), env);
70+
expect(exitCode).toBe(3);
71+
expect(stderr).toMatch(/auth login|API key/i);
72+
});
73+
74+
test("auth logout 清除凭证后 validate 报 AUTH,而非用残留凭证", async () => {
75+
const env = makeConfigEnv({ api_key: "sk-e2e-before-logout" });
76+
77+
const before = await runCommandE2e(ROUTES, validateArgs(AGENTS_YAML), env);
78+
expect(before.exitCode, before.stderr).toBe(0);
79+
80+
const logout = await runCommandE2e(ROUTES, ["auth", "logout"], env);
81+
expect(logout.exitCode, logout.stderr).toBe(0);
82+
83+
const after = await runCommandE2e(ROUTES, validateArgs(AGENTS_YAML), env);
84+
expect(after.exitCode).toBe(3);
85+
expect(after.stderr).toMatch(/auth login|API key/i);
86+
});
87+
88+
test("agents.yaml schema 错误映射为 USAGE (2),不透传原始 zod dump", async () => {
89+
const env = makeConfigEnv({ api_key: "sk-e2e-config-write" });
90+
const { stderr, exitCode } = await runCommandE2e(
91+
ROUTES,
92+
validateArgs(AGENTS_YAML_INVALID),
93+
env,
94+
);
95+
expect(exitCode).toBe(2);
96+
expect(stderr).toMatch(/agents/i);
97+
expect(stderr).not.toMatch(/"code":\s*"invalid_type"/);
98+
});
99+
});

packages/commands/tests/e2e/topic-routes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ export const TOKEN_PLAN_ROUTES: E2eRouteExports = {
158158

159159
export const MANAGED_AGENT_ROUTES: E2eRouteExports = {
160160
"managed-agent init": "managedAgentInit",
161+
"managed-agent validate": "managedAgentValidate",
161162
"managed-agent apply": "managedAgentApply",
162163
"managed-agent destroy": "managedAgentDestroy",
163164
"managed-agent state rm": "managedAgentStateRm",

0 commit comments

Comments
 (0)