From 247bb821543aef79f654298ac5784c1634fdd930 Mon Sep 17 00:00:00 2001 From: chenanran555 Date: Fri, 24 Jul 2026 18:44:18 +0800 Subject: [PATCH] test(agent): cover config-write, profile, logout and error-mapping auth-chain scenarios --- .../managed-agent/_engine/credentials.ts | 9 +- .../commands/tests/credentials-bridge.test.ts | 20 ++++ .../managed-agent/agents-invalid.yaml | 7 ++ .../e2e/fixtures/managed-agent/agents.yaml | 24 +++++ .../e2e/managed-agent-auth-chain.e2e.test.ts | 99 +++++++++++++++++++ packages/commands/tests/e2e/topic-routes.ts | 1 + 6 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 packages/commands/tests/e2e/fixtures/managed-agent/agents-invalid.yaml create mode 100644 packages/commands/tests/e2e/fixtures/managed-agent/agents.yaml create mode 100644 packages/commands/tests/e2e/managed-agent-auth-chain.e2e.test.ts diff --git a/packages/commands/src/commands/managed-agent/_engine/credentials.ts b/packages/commands/src/commands/managed-agent/_engine/credentials.ts index cfa232c..f7a7e06 100644 --- a/packages/commands/src/commands/managed-agent/_engine/credentials.ts +++ b/packages/commands/src/commands/managed-agent/_engine/credentials.ts @@ -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) { diff --git a/packages/commands/tests/credentials-bridge.test.ts b/packages/commands/tests/credentials-bridge.test.ts index 93ade5c..37379d7 100644 --- a/packages/commands/tests/credentials-bridge.test.ts +++ b/packages/commands/tests/credentials-bridge.test.ts @@ -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( diff --git a/packages/commands/tests/e2e/fixtures/managed-agent/agents-invalid.yaml b/packages/commands/tests/e2e/fixtures/managed-agent/agents-invalid.yaml new file mode 100644 index 0000000..0b0449e --- /dev/null +++ b/packages/commands/tests/e2e/fixtures/managed-agent/agents-invalid.yaml @@ -0,0 +1,7 @@ +version: "1" + +providers: + bailian: + api_key: ${DASHSCOPE_API_KEY} + +agents: "not-a-map" diff --git a/packages/commands/tests/e2e/fixtures/managed-agent/agents.yaml b/packages/commands/tests/e2e/fixtures/managed-agent/agents.yaml new file mode 100644 index 0000000..7def726 --- /dev/null +++ b/packages/commands/tests/e2e/fixtures/managed-agent/agents.yaml @@ -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 diff --git a/packages/commands/tests/e2e/managed-agent-auth-chain.e2e.test.ts b/packages/commands/tests/e2e/managed-agent-auth-chain.e2e.test.ts new file mode 100644 index 0000000..1c5faa2 --- /dev/null +++ b/packages/commands/tests/e2e/managed-agent-auth-chain.e2e.test.ts @@ -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): 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"/); + }); +}); diff --git a/packages/commands/tests/e2e/topic-routes.ts b/packages/commands/tests/e2e/topic-routes.ts index fd8e7cf..ec5b263 100644 --- a/packages/commands/tests/e2e/topic-routes.ts +++ b/packages/commands/tests/e2e/topic-routes.ts @@ -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",