Files
modelstudioai__cli/packages/cli/tests/e2e/usage-stats.e2e.test.ts
T
2026-06-10 16:04:39 +08:00

300 lines
8.8 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.
import { describe, expect, test } from "vite-plus/test";
import { isBailianE2EEnabled, parseStdoutJson, runCli } from "./helpers.ts";
import { readConfigFile } from "bailian-cli-core";
function isConsoleE2EReady(): boolean {
if (!isBailianE2EEnabled()) return false;
if (process.env.DASHSCOPE_ACCESS_TOKEN?.trim()) return true;
try {
const config = readConfigFile();
return typeof config.access_token === "string" && config.access_token.length > 0;
} catch {
return false;
}
}
function getStaticWorkspaceId(): string | undefined {
if (process.env.BAILIAN_WORKSPACE_ID?.trim()) return process.env.BAILIAN_WORKSPACE_ID.trim();
try {
const config = readConfigFile();
if (config.workspace_id) return config.workspace_id;
} catch {}
return undefined;
}
async function fetchDefaultWorkspaceId(): Promise<string> {
const staticId = getStaticWorkspaceId();
if (staticId) return staticId;
const { stdout } = await runCli(["workspace", "list", "--output", "json"]);
const result = JSON.parse(stdout);
const data = result?.data?.DataV2?.data?.data?.data ?? [];
const defaultWs = data.find((ws: { defaultAgent?: boolean }) => ws.defaultAgent);
if (defaultWs?.workspaceId) return defaultWs.workspaceId;
if (data.length > 0 && data[0].workspaceId) return data[0].workspaceId;
throw new Error("No workspace found for e2e tests");
}
describe("e2e: usage stats", () => {
test("usage stats --help 正常退出", async () => {
const { stderr, exitCode } = await runCli(["usage", "stats", "--help"]);
expect(exitCode, stderr).toBe(0);
expect(stderr).toMatch(/--model|--days|stats/i);
});
test("usage stats --help 包含所有示例", async () => {
const { stderr, exitCode } = await runCli(["usage", "stats", "--help"]);
expect(exitCode, stderr).toBe(0);
expect(stderr).toContain("bl usage stats");
expect(stderr).toContain("bl usage stats --model qwen-turbo");
expect(stderr).toContain("bl usage stats --days 30");
});
test("usage stats --help 包含 --workspace-id 选项", async () => {
const { stderr, exitCode } = await runCli(["usage", "stats", "--help"]);
expect(exitCode, stderr).toBe(0);
expect(stderr).toContain("--workspace-id");
});
});
describe.skipIf(!isConsoleE2EReady())("e2e: usage stats(Console)", () => {
let wsId: string;
test("获取默认 workspace-id", async () => {
wsId = await fetchDefaultWorkspaceId();
expect(wsId).toBeTypeOf("string");
expect(wsId.length).toBeGreaterThan(0);
});
test("usage stats --dry-run 概览模式输出请求参数", async () => {
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
wsId,
"--dry-run",
"--output",
"json",
]);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<{
api?: string;
data?: {
reqDTO?: {
startTime?: number;
endTime?: number;
modelCallSource?: string;
filterWorkspaceId?: string;
};
};
}>(stdout);
expect(data.api).toContain("getModelUsageStatistic");
expect(data.data?.reqDTO?.modelCallSource).toBe("Online");
expect(data.data?.reqDTO?.startTime).toBeTypeOf("number");
expect(data.data?.reqDTO?.endTime).toBeTypeOf("number");
expect(data.data?.reqDTO?.filterWorkspaceId).toBe(wsId);
});
test("usage stats --dry-run --days 30 时间跨度约 30 天", async () => {
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
wsId,
"--dry-run",
"--days",
"30",
"--output",
"json",
]);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<{
data?: { reqDTO?: { startTime?: number; endTime?: number } };
}>(stdout);
const span = (data.data?.reqDTO?.endTime ?? 0) - (data.data?.reqDTO?.startTime ?? 0);
const thirtyDaysMs = 30 * 24 * 60 * 60 * 1000;
expect(span).toBeGreaterThan(thirtyDaysMs - 5000);
expect(span).toBeLessThan(thirtyDaysMs + 5000);
});
test("usage stats --dry-run --model 指定模型使用 list API", async () => {
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
wsId,
"--dry-run",
"--model",
"qwen-turbo",
"--output",
"json",
]);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<{
api?: string;
data?: { reqDTO?: { model?: string; filterWorkspaceId?: string } };
}>(stdout);
expect(data.api).toContain("listModelUsageStatisticData");
expect(data.data?.reqDTO?.model).toBe("qwen-turbo");
expect(data.data?.reqDTO?.filterWorkspaceId).toBe(wsId);
});
test("usage stats --dry-run --type Text 传递 obsModelType", async () => {
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
wsId,
"--dry-run",
"--type",
"Text",
"--output",
"json",
]);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<{
data?: { reqDTO?: { obsModelType?: string } };
}>(stdout);
expect(data.data?.reqDTO?.obsModelType).toBe("Text");
});
test("usage stats 概览模式返回 JSON 结果", async () => {
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
wsId,
"--output",
"json",
]);
expect(exitCode, stderr).toBe(0);
const data = parseStdoutJson<{
code?: string;
successResponse?: boolean;
}>(stdout);
expect(data.code).toBe("200");
expect(data.successResponse).toBe(true);
});
test("usage stats 概览文本输出包含中英文表头", async () => {
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
wsId,
"--output",
"text",
"--no-color",
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("时间范围 Period:");
expect(stdout).toContain("调用模型数");
expect(stdout).toContain("Models Called");
expect(stdout).toContain("调用成功次数");
expect(stdout).toContain("Successful Calls");
});
test("usage stats 概览文本输出包含 Token 用量", async () => {
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
wsId,
"--output",
"text",
"--no-color",
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("总 Token");
expect(stdout).toContain("Total Tokens");
expect(stdout).toContain("[tokens]");
});
test("usage stats --model 单模型文本输出包含双行表头", async () => {
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
wsId,
"--model",
"qwen3.6-plus",
"--output",
"text",
"--no-color",
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("模型");
expect(stdout).toContain("Model");
expect(stdout).toContain("调用次数");
expect(stdout).toContain("Calls");
expect(stdout).toContain("qwen3.6-plus");
});
test("usage stats --model 逗号分隔多模型返回多行", async () => {
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
wsId,
"--model",
"qwen3.6-plus,deepseek-v4-pro",
"--output",
"text",
"--no-color",
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("qwen3.6-plus");
expect(stdout).toContain("deepseek-v4-pro");
expect(stdout).toMatch(/共 2 个模型/);
});
test("usage stats --model 不存在的模型返回空表格", async () => {
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
wsId,
"--model",
"nonexistent-model-xyz-99999",
"--output",
"text",
"--no-color",
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("No usage data found");
});
test("usage stats --days 1 短时间范围正常返回", async () => {
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
wsId,
"--days",
"1",
"--output",
"text",
"--no-color",
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("(1 天)");
expect(stdout).toContain("调用模型数");
});
test("usage stats --type Vision 按类型过滤", async () => {
const { stdout, stderr, exitCode } = await runCli([
"usage",
"stats",
"--workspace-id",
wsId,
"--type",
"Vision",
"--output",
"text",
"--no-color",
]);
expect(exitCode, stderr).toBe(0);
expect(stdout).toContain("时间范围 Period:");
});
});