Files
2026-07-13 13:39:12 +08:00

216 lines
6.5 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
const ANALYTICS_RESPONSE = {
byProvider: [
{
provider: "openai",
totalRequests: 150,
totalTokensIn: 50000,
totalTokensOut: 20000,
totalCost: 0.42,
},
{
provider: "anthropic",
totalRequests: 80,
totalTokensIn: 30000,
totalTokensOut: 12000,
totalCost: 0.18,
},
],
byModel: [
{
model: "gpt-4o",
totalRequests: 100,
totalTokensIn: 40000,
totalTokensOut: 16000,
totalCost: 0.35,
},
{
model: "claude-3-5-sonnet",
totalRequests: 80,
totalTokensIn: 30000,
totalTokensOut: 12000,
totalCost: 0.18,
},
],
byDay: [
{
date: "2026-05-14",
totalRequests: 50,
totalTokensIn: 20000,
totalTokensOut: 8000,
totalCost: 0.15,
},
{
date: "2026-05-15",
totalRequests: 60,
totalTokensIn: 22000,
totalTokensOut: 9000,
totalCost: 0.17,
},
],
};
function makeResp(data: unknown, status = 200) {
const obj = {
ok: status < 400,
status,
exitCode: status >= 200 && status < 300 ? 0 : 1,
json: () => Promise.resolve(data),
text: () => Promise.resolve(JSON.stringify(data)),
headers: new Headers(),
};
obj.json = obj.json.bind(obj);
obj.text = obj.text.bind(obj);
return obj;
}
function mockFetch() {
return (url: string) => {
if (url.includes("/api/usage/analytics")) {
return Promise.resolve(makeResp(ANALYTICS_RESPONSE));
}
return Promise.resolve(makeResp({}, 404));
};
}
async function captureOutput(fn: () => Promise<void>): Promise<{ stdout: string; stderr: string }> {
const out: string[] = [];
const err: string[] = [];
const origOut = process.stdout.write.bind(process.stdout);
const origErr = process.stderr.write.bind(process.stderr);
process.stdout.write = (c: string | Uint8Array) => {
out.push(typeof c === "string" ? c : c.toString());
return true;
};
process.stderr.write = (c: string | Uint8Array) => {
err.push(typeof c === "string" ? c : c.toString());
return true;
};
try {
await fn();
} finally {
process.stdout.write = origOut;
process.stderr.write = origErr;
}
return { stdout: out.join(""), stderr: err.join("") };
}
test("runCostCommand exibe tabela com provedores", async () => {
const origFetch = globalThis.fetch;
globalThis.fetch = mockFetch() as any;
const { runCostCommand } = await import("../../bin/cli/commands/cost.mjs");
const cmd = { optsWithGlobals: () => ({ output: "table", quiet: false }) };
const { stdout } = await captureOutput(() =>
runCostCommand({ period: "30d", groupBy: "provider", limit: 100 }, cmd as any)
);
globalThis.fetch = origFetch;
assert.ok(stdout.includes("openai") || stdout.includes("Group"));
});
test("runCostCommand --output json retorna array de rows", async () => {
const origFetch = globalThis.fetch;
globalThis.fetch = mockFetch() as any;
const { runCostCommand } = await import("../../bin/cli/commands/cost.mjs");
const cmd = { optsWithGlobals: () => ({ output: "json", quiet: true }) };
const { stdout } = await captureOutput(() =>
runCostCommand({ period: "30d", groupBy: "provider", limit: 100 }, cmd as any)
);
globalThis.fetch = origFetch;
const parsed = JSON.parse(stdout);
assert.ok(Array.isArray(parsed));
assert.equal(parsed.length, 2);
assert.equal(parsed[0].group, "openai");
assert.ok(parsed[0].costUsd > 0);
assert.ok(parsed[0].costPct > 0);
});
test("runCostCommand --group-by model usa byModel do response", async () => {
const origFetch = globalThis.fetch;
globalThis.fetch = mockFetch() as any;
const { runCostCommand } = await import("../../bin/cli/commands/cost.mjs");
const cmd = { optsWithGlobals: () => ({ output: "json", quiet: true }) };
const { stdout } = await captureOutput(() =>
runCostCommand({ period: "7d", groupBy: "model", limit: 100 }, cmd as any)
);
globalThis.fetch = origFetch;
const parsed = JSON.parse(stdout);
assert.ok(Array.isArray(parsed));
assert.ok(parsed.some((r: any) => r.group === "gpt-4o"));
});
test("runCostCommand --group-by day usa byDay do response", async () => {
const origFetch = globalThis.fetch;
globalThis.fetch = mockFetch() as any;
const { runCostCommand } = await import("../../bin/cli/commands/cost.mjs");
const cmd = { optsWithGlobals: () => ({ output: "json", quiet: true }) };
const { stdout } = await captureOutput(() =>
runCostCommand({ period: "7d", groupBy: "day", limit: 100 }, cmd as any)
);
globalThis.fetch = origFetch;
const parsed = JSON.parse(stdout);
assert.ok(Array.isArray(parsed));
assert.ok(parsed.some((r: any) => r.group.includes("2026-05")));
});
test("runCostCommand imprime total em stderr", async () => {
const origFetch = globalThis.fetch;
globalThis.fetch = mockFetch() as any;
const { runCostCommand } = await import("../../bin/cli/commands/cost.mjs");
const cmd = { optsWithGlobals: () => ({ output: "table", quiet: false }) };
const { stderr } = await captureOutput(() =>
runCostCommand({ period: "30d", groupBy: "provider", limit: 100 }, cmd as any)
);
globalThis.fetch = origFetch;
assert.ok(stderr.includes("Total:") && stderr.includes("$"));
});
test("runCostCommand --since/--until envia startDate/endDate na query", async () => {
let capturedUrl = "";
const origFetch = globalThis.fetch;
globalThis.fetch = ((url: string) => {
capturedUrl = url;
return Promise.resolve(makeResp(ANALYTICS_RESPONSE));
}) as any;
const { runCostCommand } = await import("../../bin/cli/commands/cost.mjs");
const cmd = { optsWithGlobals: () => ({ output: "json", quiet: true }) };
await captureOutput(() =>
runCostCommand(
{ since: "2026-01-01", until: "2026-05-01", groupBy: "provider", limit: 100 },
cmd as any
)
);
globalThis.fetch = origFetch;
assert.ok(capturedUrl.includes("startDate=2026-01-01"));
assert.ok(capturedUrl.includes("endDate=2026-05-01"));
assert.ok(!capturedUrl.includes("range="));
});
test("runCostCommand --limit trunca resultado", async () => {
const origFetch = globalThis.fetch;
globalThis.fetch = mockFetch() as any;
const { runCostCommand } = await import("../../bin/cli/commands/cost.mjs");
const cmd = { optsWithGlobals: () => ({ output: "json", quiet: true }) };
const { stdout } = await captureOutput(() =>
runCostCommand({ period: "30d", groupBy: "provider", limit: 1 }, cmd as any)
);
globalThis.fetch = origFetch;
const parsed = JSON.parse(stdout);
assert.equal(parsed.length, 1);
});