test(agent): cover config-write, profile, logout and error-mapping auth-chain scenarios

This commit is contained in:
chenanran555
2026-07-24 18:44:18 +08:00
parent 1e6165d7ff
commit 247bb82154
6 changed files with 157 additions and 3 deletions
@@ -94,9 +94,12 @@ export function injectProviderCredentials(
if (cred) {
block.api_key = cred.token;
if ("base_url" in block && !block.base_url) {
block.base_url = cred.baseUrl.endsWith(AGENTSTUDIO_API_PATH)
? cred.baseUrl
: `${cred.baseUrl}${AGENTSTUDIO_API_PATH}`;
// Defensive normalization: the auth chain already normalizes base_url to
// an origin, but never let a trailing slash produce "//api/v1/agentstudio".
const origin = cred.baseUrl.replace(/\/+$/, "");
block.base_url = origin.endsWith(AGENTSTUDIO_API_PATH)
? origin
: `${origin}${AGENTSTUDIO_API_PATH}`;
}
}
if ("workspace_id" in block && !block.workspace_id && host.settings.workspaceId) {
@@ -103,6 +103,26 @@ test("inject:base_url 已带后缀不重复拼;非空字面量 base_url 保留",
expect(literal.bailian.base_url).toBe("https://custom.example.com/api/v1/agentstudio");
});
test("inject:base_url 尾斜杠被规范化,不产生双斜杠", () => {
const providers = { bailian: { api_key: "", base_url: "" } };
injectProviderCredentials(
providers,
makeHost({ apiCred: bailianCred("t", "https://dashscope.aliyuncs.com/") }),
);
expect(providers.bailian.base_url).toBe("https://dashscope.aliyuncs.com/api/v1/agentstudio");
});
test("inject:已带后缀且尾斜杠的 base_url 去斜杠后原样保留", () => {
const providers = { bailian: { api_key: "", base_url: "" } };
injectProviderCredentials(
providers,
makeHost({
apiCred: bailianCred("t", "https://x.maas.aliyuncs.com/api/v1/agentstudio/"),
}),
);
expect(providers.bailian.base_url).toBe("https://x.maas.aliyuncs.com/api/v1/agentstudio");
});
test("inject:workspace_id 引用且为空时用 settings 填充;有字面量则保留", () => {
const empty = { bailian: { api_key: "", workspace_id: "" } };
injectProviderCredentials(
@@ -0,0 +1,7 @@
version: "1"
providers:
bailian:
api_key: ${DASHSCOPE_API_KEY}
agents: "not-a-map"
@@ -0,0 +1,24 @@
version: "1"
providers:
bailian:
api_key: ${DASHSCOPE_API_KEY}
base_url: ${BAILIAN_BASE_URL}
defaults:
provider: bailian
environments:
dev:
config:
type: cloud
networking:
type: unrestricted
agents:
assistant:
description: "E2E auth-chain fixture"
model: qwen3.7-max
instructions: |
You are a helpful assistant.
environment: dev
@@ -0,0 +1,99 @@
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, test } from "vite-plus/test";
import { e2eFixturesDir, runCommandE2e } from "./helpers.ts";
import { MANAGED_AGENT_ROUTES } from "./topic-routes.ts";
/**
* managed-agent 凭证链 e2e:验证 bl 自有配置体系(config 写入 / 命名 Profile /
* logout)与错误映射如何流入 SDK 引擎。全部离线:`managed-agent validate` 会走
* authStage 凭证解析 + 引擎注入 + agents.yaml 校验,但不发任何网络请求。
* 配置一律通过 BAILIAN_CONFIG_DIR 指向临时目录,绝不触碰真实用户配置。
*/
const ROUTES = {
...MANAGED_AGENT_ROUTES,
"auth logout": "authLogout",
};
const AGENTS_YAML = join(e2eFixturesDir, "managed-agent", "agents.yaml");
const AGENTS_YAML_INVALID = join(e2eFixturesDir, "managed-agent", "agents-invalid.yaml");
const tempDirs: string[] = [];
afterEach(() => {
for (const dir of tempDirs.splice(0)) rmSync(dir, { recursive: true, force: true });
});
/** 新建隔离配置目录并写入 config.json;返回子进程 env 覆盖(清空外部凭证 env)。 */
function makeConfigEnv(config: Record<string, unknown>): NodeJS.ProcessEnv {
const configDir = mkdtempSync(join(tmpdir(), "bl-managed-agent-auth-"));
tempDirs.push(configDir);
writeFileSync(join(configDir, "config.json"), `${JSON.stringify(config, null, 2)}\n`);
return {
BAILIAN_CONFIG_DIR: configDir,
DASHSCOPE_API_KEY: "",
DASHSCOPE_BASE_URL: "",
BAILIAN_BASE_URL: "",
BAILIAN_WORKSPACE_ID: "",
};
}
function validateArgs(file: string): string[] {
return ["managed-agent", "validate", "--file", file, "--quiet"];
}
describe("e2e: managed-agent 凭证链(config 写入 / Profile / logout / 错误映射)", () => {
test("config.json 写入的 api_key 流入引擎,validate 离线通过", async () => {
const env = makeConfigEnv({ api_key: "sk-e2e-config-write" });
const { stderr, exitCode } = await runCommandE2e(ROUTES, validateArgs(AGENTS_YAML), env);
expect(exitCode, stderr).toBe(0);
});
test("active_config 指向的命名 Profile 提供凭证时通过", async () => {
const env = makeConfigEnv({
work: { api_key: "sk-e2e-profile-work" },
active_config: "work",
});
const { stderr, exitCode } = await runCommandE2e(ROUTES, validateArgs(AGENTS_YAML), env);
expect(exitCode, stderr).toBe(0);
});
test("active_config 切到无凭证 Profile 时报统一 AUTH 错误 (3)", async () => {
const env = makeConfigEnv({
work: { api_key: "sk-e2e-profile-work" },
empty: {},
active_config: "empty",
});
const { stderr, exitCode } = await runCommandE2e(ROUTES, validateArgs(AGENTS_YAML), env);
expect(exitCode).toBe(3);
expect(stderr).toMatch(/auth login|API key/i);
});
test("auth logout 清除凭证后 validate 报 AUTH,而非用残留凭证", async () => {
const env = makeConfigEnv({ api_key: "sk-e2e-before-logout" });
const before = await runCommandE2e(ROUTES, validateArgs(AGENTS_YAML), env);
expect(before.exitCode, before.stderr).toBe(0);
const logout = await runCommandE2e(ROUTES, ["auth", "logout"], env);
expect(logout.exitCode, logout.stderr).toBe(0);
const after = await runCommandE2e(ROUTES, validateArgs(AGENTS_YAML), env);
expect(after.exitCode).toBe(3);
expect(after.stderr).toMatch(/auth login|API key/i);
});
test("agents.yaml schema 错误映射为 USAGE (2),不透传原始 zod dump", async () => {
const env = makeConfigEnv({ api_key: "sk-e2e-config-write" });
const { stderr, exitCode } = await runCommandE2e(
ROUTES,
validateArgs(AGENTS_YAML_INVALID),
env,
);
expect(exitCode).toBe(2);
expect(stderr).toMatch(/agents/i);
expect(stderr).not.toMatch(/"code":\s*"invalid_type"/);
});
});
@@ -158,6 +158,7 @@ export const TOKEN_PLAN_ROUTES: E2eRouteExports = {
export const MANAGED_AGENT_ROUTES: E2eRouteExports = {
"managed-agent init": "managedAgentInit",
"managed-agent validate": "managedAgentValidate",
"managed-agent apply": "managedAgentApply",
"managed-agent destroy": "managedAgentDestroy",
"managed-agent state rm": "managedAgentStateRm",