Files
modelstudioai__cli/packages/commands/tests/e2e/knowledge/knowledge-service.e2e.test.ts
T
2026-08-28 19:14:06 +08:00

1055 lines
30 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// service group, 7 commands: the static group covers help/missing-args/dry-run per
// command; the live group runs the full lifecycle (create → scalar update → get
// assertion → search --agent-version beta smoke → deploy → copy → delete×2).
import { mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, test } from "vite-plus/test";
import { isKbAdminE2EReady, parseStdoutJson, runCommandHelp, runCommandE2e } from "../helpers.ts";
import { KNOWLEDGE_SERVICE_ROUTES } from "../topic-routes.ts";
import { pickDifferentAgentModel } from "./verified-models.ts";
interface DryRunBody {
endpoint?: string;
request?: Record<string, unknown>;
}
describe("e2e: knowledge service list", () => {
test("--help 展示 flags", async () => {
const { stderr, exitCode } = await runCommandHelp(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"list",
"--help",
]);
expect(exitCode, stderr).toBe(0);
expect(stderr).toMatch(/--scene/i);
expect(stderr).toMatch(/--status/i);
expect(stderr).toMatch(/--index-id/i);
});
test("缺 --scene 报 USAGE (2)", async () => {
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"list",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
});
test("--scene 非法值报 USAGE (2)", async () => {
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"list",
"--scene",
"qa",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
});
test("--status 非法值报 USAGE (2)", async () => {
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"list",
"--scene",
"chat",
"--status",
"online",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
});
test("--page-size 101 报 USAGE (2)", async () => {
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"list",
"--scene",
"chat",
"--page-size",
"101",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
});
test("--dry-run 断言 body agent_scene 与分页走 body", async () => {
const { stdout, stderr, exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"list",
"--scene",
"chat",
"--index-id",
"idx_test",
"--workspace-id",
"ws_test",
"--dry-run",
"--output",
"json",
]);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<DryRunBody>(stdout);
expect(data.endpoint).toMatch(/api\/v1\/indices\/rag\/app\/list/);
expect(data.request?.agent_scene).toBe("chat");
expect(data.request?.pipeline_id).toBe("idx_test");
expect(data.request?.page_number).toBe(1);
});
test("--dry-run 断言 --status/--name/--agent-id 的 snake_case body 映射", async () => {
const { stdout, stderr, exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"list",
"--scene",
"search",
"--status",
"deployed",
"--name",
"demo",
"--agent-id",
"aid_test",
"--workspace-id",
"ws_test",
"--dry-run",
"--output",
"json",
]);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<DryRunBody>(stdout);
expect(data.request?.agent_status).toBe("deployed");
expect(data.request?.agent_name).toBe("demo");
expect(data.request?.agent_id).toBe("aid_test");
});
});
describe("e2e: knowledge service get / create / copy", () => {
test("get: 缺 --agent-id 报 USAGE (2)", async () => {
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"get",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
});
test("get: --dry-run 断言 agent_version 透传", async () => {
const { stdout, stderr, exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"get",
"--agent-id",
"aid_test",
"--agent-version",
"beta",
"--workspace-id",
"ws_test",
"--dry-run",
"--output",
"json",
]);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<DryRunBody>(stdout);
expect(data.endpoint).toMatch(/rag\/app\/get/);
expect(data.request?.agent_version).toBe("beta");
});
test("create: 缺 --scene 报 USAGE (2)", async () => {
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"create",
"--name",
"demo",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
});
test("create: --scene 非法值报 USAGE (2)", async () => {
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"create",
"--name",
"demo",
"--scene",
"qa",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
});
test("create: --name 201 字符报 USAGE (2)", async () => {
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"create",
"--name",
"x".repeat(201),
"--scene",
"chat",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
});
test("create: --description 1001 字符报 USAGE (2)", async () => {
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"create",
"--name",
"demo",
"--scene",
"chat",
"--description",
"x".repeat(1001),
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
});
test("create: --dry-run + --index-id 断言最简 kb_search_configs", async () => {
const { stdout, stderr, exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"create",
"--name",
"demo",
"--scene",
"search",
"--index-id",
"idx_test",
"--workspace-id",
"ws_test",
"--dry-run",
"--output",
"json",
]);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<DryRunBody>(stdout);
const agentConfig = data.request?.agent_config as
| { kb_search_configs?: Array<{ id: string }> }
| undefined;
expect(agentConfig?.kb_search_configs).toEqual([{ id: "idx_test" }]);
});
test("copy: --dry-run 断言 body agent_id", async () => {
const { stdout, stderr, exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"copy",
"--agent-id",
"aid_test",
"--workspace-id",
"ws_test",
"--dry-run",
"--output",
"json",
]);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<DryRunBody>(stdout);
expect(data.endpoint).toMatch(/rag\/app\/copy/);
expect(data.request?.agent_id).toBe("aid_test");
});
});
describe("e2e: knowledge service update", () => {
test("无修改项报 USAGE (2)", async () => {
const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"update",
"--agent-id",
"aid_test",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
expect(stderr).toMatch(/nothing to update/i);
});
test("--config-file 与标量 config flag 互斥 USAGE (2)", async () => {
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"update",
"--agent-id",
"aid_test",
"--temperature",
"0.5",
"--config-file",
"/tmp/whatever.json",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
});
test("已发布版本 + config 修改前置拦截 USAGE (2)", async () => {
const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"update",
"--agent-id",
"aid_test",
"--agent-version",
"1",
"--temperature",
"0.5",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
expect(stderr).toMatch(/beta draft/i);
});
test("--temperature 2.5 报 USAGE (2)", async () => {
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"update",
"--agent-id",
"aid_test",
"--temperature",
"2.5",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
});
test("--policy 非法值报 USAGE (2)", async () => {
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"update",
"--agent-id",
"aid_test",
"--policy",
"fast",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
});
test("--max-llm-calls 31 报 USAGE (2)", async () => {
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"update",
"--agent-id",
"aid_test",
"--max-llm-calls",
"31",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
});
test("--enable-session-file 非 true/false 报 USAGE (2)", async () => {
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"update",
"--agent-id",
"aid_test",
"--enable-session-file",
"maybe",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
});
test("--name 201 字符报 USAGE (2)", async () => {
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"update",
"--agent-id",
"aid_test",
"--name",
"x".repeat(201),
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
});
test("--description 1001 字符报 USAGE (2)", async () => {
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"update",
"--agent-id",
"aid_test",
"--description",
"x".repeat(1001),
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
});
test("--config-file 非法 JSON 报 USAGE (2)", async () => {
const fixtureDir = mkdtempSync(join(tmpdir(), "svc-update-e2e-"));
const badJson = join(fixtureDir, "bad.json");
writeFileSync(badJson, "{not json");
const { exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"update",
"--agent-id",
"aid_test",
"--config-file",
badJson,
"--workspace-id",
"ws_test",
"--dry-run",
]);
expect(exitCode).toBe(2);
});
test("--dry-run + --version-desc 断言 body 与标量合并说明", async () => {
const { stdout, stderr, exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"update",
"--agent-id",
"aid_test",
"--temperature",
"0.7",
"--workspace-id",
"ws_test",
"--dry-run",
"--output",
"json",
]);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<DryRunBody & { note?: string }>(stdout);
expect(data.endpoint).toMatch(/rag\/app\/update/);
const agentConfig = data.request?.agent_config as { temperature?: number } | undefined;
expect(agentConfig?.temperature).toBe(0.7);
expect(data.note).toMatch(/merged/i);
});
});
describe("e2e: knowledge service deploy / delete (危险)", () => {
test("deploy: 非 TTY 无 --yes 报 USAGE (2)", async () => {
const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"deploy",
"--agent-id",
"aid_test",
"--api-key",
"sk-fake",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
expect(stderr).toMatch(/--yes/);
});
test("deploy: --dry-run 断言 body", async () => {
const { stdout, stderr, exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"deploy",
"--agent-id",
"aid_test",
"--version-desc",
"v1 desc",
"--workspace-id",
"ws_test",
"--dry-run",
"--output",
"json",
]);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<DryRunBody>(stdout);
expect(data.endpoint).toMatch(/rag\/app\/deploy/);
expect(data.request?.agent_version_desc).toBe("v1 desc");
});
test("delete: 非 TTY 无 --yes 报 USAGE (2)", async () => {
const { stderr, exitCode } = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"delete",
"--agent-id",
"aid_test",
"--api-key",
"sk-fake",
"--workspace-id",
"ws_test",
]);
expect(exitCode).toBe(2);
expect(stderr).toMatch(/--yes/);
});
});
// Path gotcha: the actual gateway prefix is rag/app/; the rag/agent/ prefix from the
// public API docs is not routed (it returns console HTML).
describe.skipIf(!isKbAdminE2EReady())("e2e: knowledge service 生命周期 (live, 自清理)", () => {
const workspaceId = process.env.BAILIAN_WORKSPACE_ID!;
test("create → update → get 断言 → deploy → copy → delete×2", async () => {
const serviceName = `e2e-svc-${Date.now() % 100000000}`;
// 1) create (chat scene, no knowledge base bound — verifies server defaults)
const createRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"create",
"--name",
serviceName,
"--scene",
"chat",
"--workspace-id",
workspaceId,
"--quiet",
]);
expect(createRun.exitCode, createRun.stderr).toBe(0);
const agentId = createRun.stdout.trim().split("\n").pop()!;
expect(agentId).toMatch(/^aid-/);
// 1.5) list asserts the new agent shows up (filtered by agent_id)
const listRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"list",
"--scene",
"chat",
"--agent-id",
agentId,
"--workspace-id",
workspaceId,
"--quiet",
]);
expect(listRun.exitCode, listRun.stderr).toBe(0);
expect(listRun.stdout.trim().split("\n")).toContain(agentId);
// 2) scalar update (read-merge-write onto the beta draft)
const updateRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"update",
"--agent-id",
agentId,
"--temperature",
"0.55",
"--workspace-id",
workspaceId,
]);
expect(updateRun.exitCode, updateRun.stderr).toBe(0);
// 3) get asserts the scalars landed on beta
const getRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"get",
"--agent-id",
agentId,
"--agent-version",
"beta",
"--workspace-id",
workspaceId,
"--output",
"json",
]);
expect(getRun.exitCode, getRun.stderr).toBe(0);
const getData = parseStdoutJson<{
data: { agent_details: Array<{ agent_config?: { temperature?: number } }> };
}>(getRun.stdout);
expect(getData.data.agent_details[0]?.agent_config?.temperature).toBe(0.55);
// 4) deploy (--yes non-interactive)
const deployRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"deploy",
"--agent-id",
agentId,
"--yes",
"--workspace-id",
workspaceId,
"--quiet",
]);
expect(deployRun.exitCode, deployRun.stderr).toBe(0);
expect(deployRun.stdout.trim().split("\n").pop()).toBe("1");
// 4.5) bare get (no --agent-version) returns all versions: beta draft + published 1
const allVersionsRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"get",
"--agent-id",
agentId,
"--workspace-id",
workspaceId,
"--output",
"json",
]);
expect(allVersionsRun.exitCode, allVersionsRun.stderr).toBe(0);
const allVersionsData = parseStdoutJson<{
data: { agent_details: Array<{ agent_version?: string }> };
}>(allVersionsRun.stdout);
const versions = allVersionsData.data.agent_details.map((detail) => detail.agent_version);
expect(versions).toContain("beta");
expect(versions).toContain("1");
// 5) copy
const copyRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"copy",
"--agent-id",
agentId,
"--workspace-id",
workspaceId,
"--quiet",
]);
expect(copyRun.exitCode, copyRun.stderr).toBe(0);
const copiedAgentId = copyRun.stdout.trim().split("\n").pop()!;
expect(copiedAgentId).toMatch(/^aid-/);
expect(copiedAgentId).not.toBe(agentId);
// 6) delete both (idempotent soft delete)
for (const idToDelete of [copiedAgentId, agentId]) {
const deleteRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"delete",
"--agent-id",
idToDelete,
"--yes",
"--workspace-id",
workspaceId,
]);
expect(deleteRun.exitCode, deleteRun.stderr).toBe(0);
}
// 6.5) Verify both agents are gone from the server — not just that delete
// returned Success, but that a fresh list filtered by agent_id finds nothing
for (const idToVerify of [copiedAgentId, agentId]) {
const postDeleteListRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"list",
"--scene",
"chat",
"--agent-id",
idToVerify,
"--workspace-id",
workspaceId,
"--quiet",
]);
expect(postDeleteListRun.exitCode, postDeleteListRun.stderr).toBe(0);
expect(postDeleteListRun.stdout.trim()).toBe("");
}
}, 300_000);
});
// Live parameter coverage: every write parameter gets a read-back assertion
// via `service get --output json`. Read-back fields are confirmed present in
// the get response by the existing lifecycle test and printDetail().
describe.skipIf(!isKbAdminE2EReady())("e2e: knowledge service 参数全覆盖 (live, 自清理)", () => {
const workspaceId = process.env.BAILIAN_WORKSPACE_ID!;
test("create --description → update(name/model/policy×2/version-desc) → list(status×3/scene/name/index-id) → deploy → published update --version-desc → delete", async () => {
const serviceName = `e2e-svc-param-${Date.now() % 100000000}`;
let agentId: string | undefined;
try {
// ── P6: service-create --description ──
const createRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"create",
"--name",
serviceName,
"--scene",
"chat",
"--description",
"e2e param coverage",
"--workspace-id",
workspaceId,
"--quiet",
]);
expect(createRun.exitCode, createRun.stderr).toBe(0);
agentId = createRun.stdout.trim().split("\n").pop()!;
expect(agentId).toMatch(/^aid-/);
// get 读回验证 --description (agent_desc)
const createGetRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"get",
"--agent-id",
agentId,
"--workspace-id",
workspaceId,
"--output",
"json",
]);
expect(createGetRun.exitCode, createGetRun.stderr).toBe(0);
const createGetData = parseStdoutJson<{
data?: { agent_desc?: string };
}>(createGetRun.stdout);
expect(createGetData.data?.agent_desc).toBe("e2e param coverage");
// ── P1: list --status draft 验证新建 agent 可见 ──
const listDraftRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"list",
"--scene",
"chat",
"--status",
"draft",
"--agent-id",
agentId,
"--workspace-id",
workspaceId,
"--quiet",
]);
expect(listDraftRun.exitCode, listDraftRun.stderr).toBe(0);
expect(listDraftRun.stdout.trim().split("\n")).toContain(agentId);
// ── P1: list --scene search (live 合法值) ──
const listSearchSceneRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"list",
"--scene",
"search",
"--workspace-id",
workspaceId,
"--quiet",
]);
expect(listSearchSceneRun.exitCode, listSearchSceneRun.stderr).toBe(0);
// ── P1: list --name 精确过滤 ──
const listNameRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"list",
"--scene",
"chat",
"--name",
serviceName,
"--workspace-id",
workspaceId,
"--quiet",
]);
expect(listNameRun.exitCode, listNameRun.stderr).toBe(0);
expect(listNameRun.stdout.trim().split("\n")).toContain(agentId);
// ── P1: list --index-id (不报错验证; 无 kb fixture) ──
const listIndexIdRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"list",
"--scene",
"search",
"--index-id",
"idx_nonexistent_e2e",
"--workspace-id",
workspaceId,
"--quiet",
]);
expect(listIndexIdRun.exitCode, listIndexIdRun.stderr).toBe(0);
// ── P0 组1: update --name --version-desc --model → get 读回 3 个 scalar ──
// --model 取值自适应:先读回草稿当前模型,再挑一个不同的已验证模型写入,
// 断言才能证明「值真的变了」而不是把默认值原样写回
const baselineGetRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"get",
"--agent-id",
agentId,
"--agent-version",
"beta",
"--workspace-id",
workspaceId,
"--output",
"json",
]);
expect(baselineGetRun.exitCode, baselineGetRun.stderr).toBe(0);
const baselineModel = parseStdoutJson<{
data?: { agent_details?: Array<{ agent_config?: { agent_model?: string } }> };
}>(baselineGetRun.stdout).data?.agent_details?.[0]?.agent_config?.agent_model;
const targetModel = pickDifferentAgentModel(baselineModel);
if (targetModel === undefined) {
// 白名单缩到只剩当前模型 —— 跳过模型断言而不是断言一个空操作
process.stderr.write(
`skip --model assertion: no verified model differs from ${baselineModel}\n`,
);
}
const newName = `${serviceName}-renamed`;
const updateScalarRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"update",
"--agent-id",
agentId,
"--name",
newName,
"--version-desc",
"beta-v1",
...(targetModel === undefined ? [] : ["--model", targetModel]),
"--workspace-id",
workspaceId,
]);
expect(updateScalarRun.exitCode, updateScalarRun.stderr).toBe(0);
const scalarGetRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"get",
"--agent-id",
agentId,
"--agent-version",
"beta",
"--workspace-id",
workspaceId,
"--output",
"json",
]);
expect(scalarGetRun.exitCode, scalarGetRun.stderr).toBe(0);
const scalarGetData = parseStdoutJson<{
data?: {
agent_name?: string;
agent_details?: Array<{
agent_version_desc?: string | null;
agent_config?: { agent_model?: string };
}>;
};
}>(scalarGetRun.stdout);
expect(scalarGetData.data?.agent_name).toBe(newName);
if (targetModel !== undefined) {
expect(scalarGetData.data?.agent_details?.[0]?.agent_config?.agent_model).toBe(targetModel);
}
// --version-desc 在 beta 草稿上是服务端空操作update 返回 200 但存为 null。
// 版本说明只在 deploy 时传入、或对已发布版本 update 才能落库(下方 P0 组4 覆盖)。
expect(scalarGetData.data?.agent_details?.[0]?.agent_version_desc).toBeNull();
// ── P0 组2: update --policy turbo → get 读回 ──
const updateTurboRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"update",
"--agent-id",
agentId,
"--policy",
"turbo",
"--workspace-id",
workspaceId,
]);
expect(updateTurboRun.exitCode, updateTurboRun.stderr).toBe(0);
const turboGetRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"get",
"--agent-id",
agentId,
"--agent-version",
"beta",
"--workspace-id",
workspaceId,
"--output",
"json",
]);
expect(turboGetRun.exitCode, turboGetRun.stderr).toBe(0);
const turboData = parseStdoutJson<{
data?: {
agent_details?: Array<{
agent_config?: { agent_policy?: string };
}>;
};
}>(turboGetRun.stdout);
expect(turboData.data?.agent_details?.[0]?.agent_config?.agent_policy).toBe("turbo");
// ── P0 组3: update --policy agentic → get 读回 ──
// 注: --max-llm-calls 10 仅作为 agentic 切换的前置条件,不做读回验证
const updateAgenticRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"update",
"--agent-id",
agentId,
"--policy",
"agentic",
"--max-llm-calls",
"10",
"--workspace-id",
workspaceId,
]);
expect(updateAgenticRun.exitCode, updateAgenticRun.stderr).toBe(0);
const agenticGetRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"get",
"--agent-id",
agentId,
"--agent-version",
"beta",
"--workspace-id",
workspaceId,
"--output",
"json",
]);
expect(agenticGetRun.exitCode, agenticGetRun.stderr).toBe(0);
const agenticData = parseStdoutJson<{
data?: {
agent_details?: Array<{
agent_config?: { agent_policy?: string };
}>;
};
}>(agenticGetRun.stdout);
expect(agenticData.data?.agent_details?.[0]?.agent_config?.agent_policy).toBe("agentic");
// ── deploy → P1: list --status deployed ──
const deployRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"deploy",
"--agent-id",
agentId,
"--yes",
"--workspace-id",
workspaceId,
"--quiet",
]);
expect(deployRun.exitCode, deployRun.stderr).toBe(0);
const listDeployedRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"list",
"--scene",
"chat",
"--status",
"deployed",
"--agent-id",
agentId,
"--workspace-id",
workspaceId,
"--quiet",
]);
expect(listDeployedRun.exitCode, listDeployedRun.stderr).toBe(0);
expect(listDeployedRun.stdout.trim().split("\n")).toContain(agentId);
// ── P0 组4: update --agent-version 1 --version-desc (published version) ──
const updatePublishedRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"update",
"--agent-id",
agentId,
"--agent-version",
"1",
"--version-desc",
"published-v1-desc",
"--workspace-id",
workspaceId,
]);
expect(updatePublishedRun.exitCode, updatePublishedRun.stderr).toBe(0);
// get --agent-version 1 读回验证 --version-desc
const publishedGetRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"get",
"--agent-id",
agentId,
"--agent-version",
"1",
"--workspace-id",
workspaceId,
"--output",
"json",
]);
expect(publishedGetRun.exitCode, publishedGetRun.stderr).toBe(0);
const publishedData = parseStdoutJson<{
data?: {
agent_details?: Array<{ agent_version_desc?: string }>;
};
}>(publishedGetRun.stdout);
expect(publishedData.data?.agent_details?.[0]?.agent_version_desc).toBe("published-v1-desc");
// ── delete → P1: list --status deleted ──
const deleteRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"delete",
"--agent-id",
agentId,
"--yes",
"--workspace-id",
workspaceId,
]);
expect(deleteRun.exitCode, deleteRun.stderr).toBe(0);
const listDeletedRun = await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"list",
"--scene",
"chat",
"--status",
"deleted",
"--agent-id",
agentId,
"--workspace-id",
workspaceId,
"--quiet",
]);
expect(listDeletedRun.exitCode, listDeletedRun.stderr).toBe(0);
// 软验证: deleted 状态下 agent 应出现在列表中;
// 若服务端不返回 deleted 列表则仅验证不报错
if (listDeletedRun.stdout.trim()) {
expect(listDeletedRun.stdout.trim().split("\n")).toContain(agentId);
}
} finally {
// 兜底清理
if (agentId) {
await runCommandE2e(KNOWLEDGE_SERVICE_ROUTES, [
"knowledge",
"service",
"delete",
"--agent-id",
agentId,
"--yes",
"--workspace-id",
workspaceId,
]);
}
}
}, 300_000);
});