Files
辰ing f3327e3f95 Merge pull request #6 from MoYiC6/fix/cross-platform-test-compatibility
fix: harden test suite for Windows/CRLF cross-platform compatibility
2026-03-13 10:59:04 +01:00

533 lines
17 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import type { ReadModelSnapshot } from "../src/types";
test("usage-cost snapshot marks disconnected sources with explicit placeholders", async () => {
const { computeUsageCostSnapshot } = await import("../src/runtime/usage-cost");
const snapshot = buildSnapshotFixture();
const usage = computeUsageCostSnapshot(snapshot, [], []);
const today = usage.periods.find((item) => item.key === "today");
assert(today, "Expected today usage period.");
assert.equal(today.requestCountStatus, "not_connected");
assert.equal(usage.contextWindows[0]?.thresholdState, "not_connected");
assert.equal(usage.budget.status, "not_connected");
assert(usage.connectors.todos.some((item) => item.id === "request_counter"));
assert(usage.connectors.todos.some((item) => item.id === "context_catalog"));
});
test("usage-cost snapshot computes context percent and burn-rate status when sources exist", async () => {
const { computeUsageCostSnapshot } = await import("../src/runtime/usage-cost");
const snapshot = buildSnapshotFixture({
model: "gpt-4o",
tokensIn: 64000,
tokensOut: 6000,
cost: 30,
costLimit: 100,
});
const todayIso = new Date().toISOString().slice(0, 10);
const usage = computeUsageCostSnapshot(
snapshot,
[
{
date: todayIso,
usage: {
statuses: 1,
totalTokensIn: 64000,
totalTokensOut: 6000,
totalCost: 90,
},
},
],
[{ match: "gpt-4o", contextWindowTokens: 128000, provider: "OpenAI" }],
);
assert.equal(usage.contextWindows[0]?.dataStatus, "connected");
assert.equal(Math.round(usage.contextWindows[0]?.usagePercent ?? 0), 55);
assert.equal(usage.budget.status, "warn");
assert.equal(usage.connectors.modelContextCatalog, "connected");
assert.equal(usage.periods.find((item) => item.key === "today")?.sourceStatus, "connected");
});
test("usage-cost snapshot uses runtime session events for real requests, trends, and breakdown rows", async () => {
const { computeUsageCostSnapshot } = await import("../src/runtime/usage-cost");
const snapshot = buildSnapshotFixture({
model: "gpt-5.3-codex",
tokensIn: 64000,
tokensOut: 6000,
cost: 10,
costLimit: 50,
});
const now = Date.now();
const usage = computeUsageCostSnapshot(snapshot, [], [], {
sourceStatus: "connected",
sessionContexts: [
{
sessionKey: "s-1",
sessionId: "sid-1",
agentId: "pandas",
model: "gpt-5.3-codex",
provider: "OpenAI",
contextWindowTokens: 100000,
},
],
events: [
runtimeEvent(now, 1000, 1),
runtimeEvent(now - 2 * 60 * 60 * 1000, 2000, 2),
runtimeEvent(now - 1 * 24 * 60 * 60 * 1000, 4000, 3),
runtimeEvent(now - 8 * 24 * 60 * 60 * 1000, 5000, 4),
],
});
const today = usage.periods.find((item) => item.key === "today");
const seven = usage.periods.find((item) => item.key === "7d");
const thirty = usage.periods.find((item) => item.key === "30d");
assert(today && seven && thirty);
assert.equal(today.tokens, 3000);
assert.equal(today.requestCount, 2);
assert.equal(today.requestCountStatus, "connected");
assert.equal(seven.tokens, 7000);
assert.equal(seven.requestCount, 3);
assert.equal(thirty.tokens, 12000);
assert.equal(thirty.requestCount, 4);
assert.equal(usage.contextWindows[0]?.dataStatus, "connected");
assert.equal(Math.round(usage.contextWindows[0]?.usagePercent ?? 0), 70);
assert.equal(usage.breakdown.byAgent[0]?.label, "pandas");
assert.equal(usage.breakdown.byAgent[0]?.requests, 4);
assert.equal(usage.breakdown.byTask[0]?.label, "p1/t1 · pandas · session");
assert.equal(usage.breakdown.byTask[0]?.tokens, 12000);
assert.equal(usage.breakdown.byTask[0]?.requests, 4);
assert.equal(usage.breakdownToday.byAgent[0]?.label, "pandas");
assert.equal(usage.breakdownToday.byAgent[0]?.tokens, 3000);
assert.equal(usage.breakdownToday.byAgent[0]?.requests, 2);
assert.equal(usage.breakdownToday.byTask[0]?.tokens, 3000);
assert.equal(usage.breakdownToday.bySessionType[0]?.label, "Main/内部会话");
assert.equal(usage.connectors.requestCounts, "connected");
assert(!usage.connectors.todos.some((item) => item.id === "request_counter"));
assert(!usage.connectors.todos.some((item) => item.id === "context_catalog"));
});
test("usage-cost snapshot surfaces connected subscription consumption and remaining contract", async () => {
const { computeUsageCostSnapshot } = await import("../src/runtime/usage-cost");
const usage = computeUsageCostSnapshot(buildSnapshotFixture(), [], [], undefined, {
status: "connected",
planLabel: "Pro Monthly",
consumed: 120,
remaining: 80,
limit: 200,
usagePercent: 60,
unit: "USD",
cycleStart: "2026-03-01",
cycleEnd: "2026-03-31",
sourcePath: "/tmp/subscription.json",
detail: "Used 120 USD. Remaining 80 USD.",
connectHint: "Provide one of: /tmp/subscription.json",
reasonCode: "provider_connected",
});
assert.equal(usage.subscription.status, "connected");
assert.equal(usage.subscription.remaining, 80);
assert.equal(usage.subscription.cycleEnd, "2026-03-31");
assert.equal(usage.connectors.subscriptionUsage, "connected");
assert(!usage.connectors.todos.some((item) => item.id === "subscription_usage"));
});
test("usage-cost snapshot surfaces unavailable subscription with explicit connector todo", async () => {
const { computeUsageCostSnapshot } = await import("../src/runtime/usage-cost");
const usage = computeUsageCostSnapshot(buildSnapshotFixture(), [], [], undefined, {
status: "not_connected",
planLabel: "Not connected",
unit: "USD",
detail: "Subscription usage source is not connected.",
connectHint: "Provide one of: /tmp/subscription.json",
});
assert.equal(usage.subscription.status, "not_connected");
assert.equal(usage.connectors.subscriptionUsage, "not_connected");
assert(usage.connectors.todos.some((item) => item.id === "subscription_usage"));
assert(usage.connectors.todos.some((item) => item.detail.includes("/tmp/subscription.json")));
});
test("usage-cost snapshot backfills subscription consumed from runtime usage when provider snapshot is missing", async () => {
const { computeUsageCostSnapshot } = await import("../src/runtime/usage-cost");
const now = Date.now();
const usage = computeUsageCostSnapshot(
buildSnapshotFixture({
cost: 0,
}),
[],
[],
{
sourceStatus: "connected",
sessionContexts: [
{
sessionKey: "s-1",
sessionId: "sid-1",
agentId: "pandas",
},
],
events: [runtimeEvent(now, 1000, 1), runtimeEvent(now - 30 * 60 * 1000, 1200, 2)],
},
);
assert.equal(usage.subscription.status, "not_connected");
assert.equal(usage.subscription.consumed, 3);
assert.equal(usage.subscription.remaining, undefined);
assert.equal(usage.subscription.limit, undefined);
assert(usage.subscription.detail.includes("Provider remaining/limit cannot be derived"));
assert.equal(usage.subscription.reasonCode, "runtime_backfill_only");
assert(usage.subscription.sourcePath?.replace(/\\/g, "/").includes("sessions/*.jsonl"));
assert.equal(usage.connectors.subscriptionUsage, "not_connected");
assert(
usage.connectors.todos.some(
(item) =>
item.id === "subscription_usage" &&
item.detail.includes("Provider remaining/limit cannot be derived"),
),
);
});
test("usage-cost snapshot backfills subscription limit and remaining from budget when provider snapshot is missing", async () => {
const { computeUsageCostSnapshot } = await import("../src/runtime/usage-cost");
const now = Date.now();
const usage = computeUsageCostSnapshot(
buildSnapshotFixture({
cost: 0,
costLimit: 10,
}),
[],
[],
{
sourceStatus: "connected",
sessionContexts: [
{
sessionKey: "s-1",
sessionId: "sid-1",
agentId: "pandas",
},
],
events: [runtimeEvent(now, 1000, 1), runtimeEvent(now - 30 * 60 * 1000, 1200, 2)],
},
);
assert.equal(usage.subscription.status, "not_connected");
assert.equal(usage.subscription.planLabel, "Estimated budget envelope");
assert.equal(usage.subscription.consumed, 3);
assert.equal(usage.subscription.limit, 10);
assert.equal(usage.subscription.remaining, 7);
assert.equal(usage.subscription.usagePercent, 30);
assert(usage.subscription.sourcePath?.includes("budgetSummary"));
assert(usage.subscription.detail.includes("Remaining/limit are derived from configured 30d cost budget"));
assert.equal(usage.connectors.subscriptionUsage, "not_connected");
assert(usage.connectors.todos.some((item) => item.id === "subscription_usage"));
});
test("usage-cost snapshot reports session-type share and cron-job share from deduped session totals", async () => {
const { computeUsageCostSnapshot } = await import("../src/runtime/usage-cost");
const usage = computeUsageCostSnapshot(
buildSnapshotFixture(),
[],
[],
{
sourceStatus: "connected",
sessionContexts: [
{
sessionKey: "agent:main:cron:job-a",
sessionId: "sid-1",
agentId: "main",
totalTokens: 1000,
},
{
sessionKey: "agent:main:cron:job-a:run:r-1",
sessionId: "sid-1",
agentId: "main",
totalTokens: 1000,
},
{
sessionKey: "agent:main:discord:channel:123",
sessionId: "sid-2",
agentId: "main",
totalTokens: 100,
channel: "discord",
},
{
sessionKey: "agent:coq:telegram:direct:123",
sessionId: "sid-3",
agentId: "coq",
totalTokens: 50,
channel: "telegram",
},
{
sessionKey: "agent:main:main",
sessionId: "sid-4",
agentId: "main",
totalTokens: 25,
},
],
events: [],
},
undefined,
new Map([["job-a", "job-alpha"]]),
);
const byType = usage.breakdown.bySessionType;
assert.equal(byType.find((item) => item.label === "Cron")?.tokens, 1000);
assert.equal(byType.find((item) => item.label === "Discord")?.tokens, 100);
assert.equal(byType.find((item) => item.label === "Telegram")?.tokens, 50);
assert.equal(byType.find((item) => item.label === "Main/内部会话")?.tokens, 25);
const cronTop = usage.breakdown.byCronJob[0];
assert(cronTop);
assert.equal(cronTop.tokens, 1000);
assert(cronTop.label.includes("job-alpha"));
assert(cronTop.label.includes("job-a"));
assert.equal(cronTop.sessions, 1);
assert.equal(usage.breakdown.byCronAgent[0]?.label, "main");
assert.equal(usage.breakdown.byCronAgent[0]?.tokens, 1000);
});
test("codex rate-limit parser prefers canonical codex window over model-specific window", async () => {
const { parseCodexRateLimitFromSessionLogForSmoke } = await import("../src/runtime/usage-cost");
const raw = [
JSON.stringify({
timestamp: "2026-03-06T11:38:10.057Z",
type: "event_msg",
payload: {
type: "token_count",
rate_limits: {
limit_id: "codex_bengalfox",
limit_name: "GPT-5.3-Codex-Spark",
primary: { used_percent: 0, window_minutes: 300, resets_at: 1772809458 },
secondary: { used_percent: 2, window_minutes: 10080, resets_at: 1773249479 },
plan_type: "pro",
},
},
}),
JSON.stringify({
timestamp: "2026-03-06T11:38:10.056Z",
type: "event_msg",
payload: {
type: "token_count",
rate_limits: {
limit_id: "codex",
limit_name: null,
primary: { used_percent: 2, window_minutes: 300, resets_at: 1772812807 },
secondary: { used_percent: 45, window_minutes: 10080, resets_at: 1773264817 },
plan_type: "pro",
},
},
}),
].join("\n");
const parsed = parseCodexRateLimitFromSessionLogForSmoke(raw, "/tmp/codex.jsonl", Date.parse("2026-03-06T11:40:00.000Z"));
assert(parsed, "expected parsed codex rate-limit snapshot");
assert.equal(parsed.primaryUsedPercent, 2);
assert.equal(parsed.secondaryUsedPercent, 45);
assert.equal(parsed.primaryResetAt, "2026-03-06T16:00:07.000Z");
assert.equal(parsed.secondaryResetAt, "2026-03-11T21:33:37.000Z");
assert.equal(parsed.detail, "codex");
});
test("codex rate-limit parser falls back to model-specific window when canonical codex window is absent", async () => {
const { parseCodexRateLimitFromSessionLogForSmoke } = await import("../src/runtime/usage-cost");
const raw = JSON.stringify({
timestamp: "2026-03-06T11:38:10.057Z",
type: "event_msg",
payload: {
type: "token_count",
rate_limits: {
limit_id: "codex_bengalfox",
limit_name: "GPT-5.3-Codex-Spark",
primary: { used_percent: 0, window_minutes: 300, resets_at: 1772809458 },
secondary: { used_percent: 2, window_minutes: 10080, resets_at: 1773249479 },
plan_type: "pro",
},
},
});
const parsed = parseCodexRateLimitFromSessionLogForSmoke(raw, "/tmp/codex.jsonl", Date.parse("2026-03-06T11:40:00.000Z"));
assert(parsed, "expected parsed model-specific codex rate-limit snapshot");
assert.equal(parsed.primaryUsedPercent, 0);
assert.equal(parsed.secondaryUsedPercent, 2);
assert.equal(parsed.detail, "codex_bengalfox");
});
test("codex rate-limit parser snaps near-week windows to a stable weekly label", async () => {
const { parseCodexRateLimitFromSessionLogForSmoke } = await import("../src/runtime/usage-cost");
const raw = JSON.stringify({
timestamp: "2026-03-10T23:25:35.000Z",
type: "event_msg",
payload: {
type: "token_count",
rate_limits: {
limit_id: "codex",
primary: { used_percent: 0, window_minutes: 300, resets_at: 1773228605 },
secondary: { used_percent: 1, window_minutes: 10081, resets_at: 1773815465 },
plan_type: "pro",
},
},
});
const parsed = parseCodexRateLimitFromSessionLogForSmoke(raw, "/tmp/codex.jsonl", Date.parse("2026-03-10T23:26:00.000Z"));
assert(parsed, "expected parsed codex rate-limit snapshot");
assert.equal(parsed.primaryWindowLabel, "5h");
assert.equal(parsed.secondaryWindowLabel, "7d");
});
function buildSnapshotFixture(overrides?: {
model?: string;
tokensIn?: number;
tokensOut?: number;
cost?: number;
costLimit?: number;
}): ReadModelSnapshot {
const now = new Date().toISOString();
const model = overrides?.model;
const tokensIn = overrides?.tokensIn ?? 2000;
const tokensOut = overrides?.tokensOut ?? 300;
const cost = overrides?.cost ?? 2;
const costLimit = overrides?.costLimit;
return {
sessions: [
{
sessionKey: "s-1",
agentId: "pandas",
label: "Pandas Session",
state: "running",
lastMessageAt: now,
},
],
statuses: [
{
sessionKey: "s-1",
model,
tokensIn,
tokensOut,
cost,
updatedAt: now,
},
],
cronJobs: [],
approvals: [],
projects: {
updatedAt: now,
projects: [
{
projectId: "p1",
title: "Project One",
status: "active",
owner: "pandas",
budget: {},
updatedAt: now,
},
],
},
projectSummaries: [
{
projectId: "p1",
title: "Project One",
status: "active",
owner: "pandas",
totalTasks: 1,
todo: 0,
inProgress: 1,
blocked: 0,
done: 0,
due: 0,
updatedAt: now,
},
],
tasks: {
updatedAt: now,
agentBudgets: [],
tasks: [
{
projectId: "p1",
taskId: "t1",
title: "Task One",
status: "in_progress",
owner: "pandas",
definitionOfDone: [],
artifacts: [],
rollback: {
strategy: "none",
steps: [],
},
sessionKeys: ["s-1"],
budget: {},
updatedAt: now,
},
],
},
tasksSummary: {
projects: 1,
tasks: 1,
todo: 0,
inProgress: 1,
blocked: 0,
done: 0,
owners: 1,
artifacts: 0,
},
budgetSummary: {
total: costLimit ? 1 : 0,
ok: costLimit ? 1 : 0,
warn: 0,
over: 0,
evaluations: costLimit
? [
{
scope: "agent",
scopeId: "pandas",
label: "Pandas",
thresholds: {
cost: costLimit,
},
usage: {
tokensIn,
tokensOut,
totalTokens: tokensIn + tokensOut,
cost,
},
metrics: [
{
metric: "cost",
used: cost,
limit: costLimit,
warnAt: costLimit * 0.8,
status: "ok",
},
],
status: "ok",
},
]
: [],
},
generatedAt: now,
};
}
function runtimeEvent(timestampMs: number, tokens: number, cost: number) {
const timestamp = new Date(timestampMs).toISOString();
return {
timestamp,
day: timestamp.slice(0, 10),
sessionId: "sid-1",
sessionKey: "s-1",
agentId: "pandas",
model: "gpt-5.3-codex",
provider: "OpenAI",
tokens,
cost,
};
}