Files
EvoScientist-WebUI/src/lib/server/usageStore.test.ts
T
m4 c8e8305107 fix(webui): read usage deployment id from EVOSCIENTIST_USAGE_DEPLOYMENT_ID
Matches the backend split: usage attribution no longer shares
EVOSCIENTIST_DEPLOYMENT_ID with workspace scope partitioning.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-30 22:11:31 +08:00

268 lines
9.1 KiB
TypeScript

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
import type { UsageEventV1, UsageHeartbeatV1 } from "../usageTypes";
vi.mock("server-only", () => ({}));
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "evosci-usage-store-"));
process.env.EVOSCIENTIST_DATA_DIR = dataDir;
process.env.EVOSCIENTIST_USAGE_DEPLOYMENT_ID = "11111111-1111-4111-8111-111111111111";
process.env.EVOSCIENTIST_WORKSPACE_ID = "ws1_fixture";
const store = await import("./usageStore");
const fixtures = path.join(process.cwd(), "docs", "schemas", "fixtures");
function fixture(relative: string): UsageEventV1 {
return JSON.parse(fs.readFileSync(path.join(fixtures, relative), "utf8"));
}
function eventFrom(
base: UsageEventV1,
modelCallId: string,
patch: Partial<UsageEventV1> = {}
): UsageEventV1 {
const deploymentId = patch.deployment_id ?? base.deployment_id;
return {
...base,
...patch,
deployment_id: deploymentId,
model_call_id: modelCallId,
event_id: `${deploymentId}:${modelCallId}:callback_final:1`,
};
}
function range(filters: Record<string, string> = {}): URLSearchParams {
return new URLSearchParams({
from: "2026-07-01T00:00:00.000Z",
to: "2026-07-31T00:00:00.000Z",
...filters,
});
}
describe("usage store", () => {
const confirmed = fixture("accepted/confirmed.json");
const unknown = fixture("accepted/unknown.json");
beforeAll(() => {
expect(store.ingestUsageEvent(confirmed).status).toBe("accepted");
expect(store.ingestUsageEvent(unknown).status).toBe("accepted");
});
afterAll(() => {
store.closeUsageDb();
fs.rmSync(dataDir, { recursive: true, force: true });
});
it("handles duplicate and conflicting observations without changing projection", () => {
expect(store.ingestUsageEvent(confirmed).status).toBe("duplicate");
const conflict = fixture("projection/conflicting-confirmed.json");
expect(store.ingestUsageEvent(conflict).status).toBe("conflict");
const summary = store.usageSummary(range({ thread_id: "thread-fixture" }));
expect(summary.total_tokens).toBe("1200");
expect(summary.confirmed_call_count).toBe("1");
expect(summary.unknown_call_count).toBe("1");
});
it("includes async child calls through source_session_id", () => {
const child = eventFrom(confirmed, "44444444-4444-4444-8444-444444444444", {
thread_id: "worker-thread",
source_session_id: "thread-fixture",
scope: "async_subagent",
input_tokens: 50,
output_tokens: 5,
provider_total_tokens: 55,
});
expect(store.ingestUsageEvent(child).status).toBe("accepted");
const summary = store.usageSummary(
range({
deployment_id: confirmed.deployment_id,
workspace_id: confirmed.workspace_id,
thread_id: "thread-fixture",
})
);
expect(summary.total_tokens).toBe("1255");
expect(summary.confirmed_call_count).toBe("2");
expect(summary.by_scope).toEqual(
expect.arrayContaining([
expect.objectContaining({ key: "async_subagent", total_tokens: "55" }),
])
);
});
it("isolates identical call ids across deployments", () => {
const other = eventFrom(confirmed, confirmed.model_call_id, {
deployment_id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
workspace_id: "ws1_other",
});
expect(store.ingestUsageEvent(other).status).toBe("accepted");
const current = store.usageSummary(
range({
deployment_id: confirmed.deployment_id,
thread_id: "thread-fixture",
})
);
expect(current.confirmed_call_count).toBe("2");
});
it("returns exact aggregate values above the JavaScript safe integer", () => {
for (const id of [
"55555555-5555-4555-8555-555555555555",
"66666666-6666-4666-8666-666666666666",
]) {
expect(
store.ingestUsageEvent(
eventFrom(confirmed, id, {
thread_id: "bigint-thread",
turn_id: "bigint-turn",
input_tokens: Number.MAX_SAFE_INTEGER,
output_tokens: 0,
provider_total_tokens: null,
})
).status
).toBe("accepted");
}
const summary = store.usageSummary(range({ thread_id: "bigint-thread" }));
expect(summary.total_tokens).toBe("18014398509481982");
});
it("uses a total-order cursor across deployments", () => {
const first = store.usageCalls(range(), 1, null);
expect(first.next_cursor).not.toBeNull();
const decoded = JSON.parse(
Buffer.from(first.next_cursor!, "base64url").toString("utf8")
);
expect(decoded).toEqual({
at: expect.any(String),
deployment: expect.any(String),
id: expect.any(String),
});
const second = store.usageCalls(range(), 1, decoded);
expect(second.items[0]).not.toEqual(first.items[0]);
});
it("defaults status to the integrated launcher deployment", () => {
const heartbeat = (
deployment_id: string,
workspace_id: string
): UsageHeartbeatV1 => ({
deployment_id,
workspace_id,
emitter_version: "2.0",
schema_version: 1,
sender_status: "healthy",
spool_pending: 0,
spool_inflight: 0,
spool_quarantined: 0,
spool_bytes: 0,
first_loss_at: null,
tracking_degraded_reason: null,
last_error_code: null,
sent_at: new Date().toISOString(),
});
store.recordHeartbeat(
heartbeat(confirmed.deployment_id, confirmed.workspace_id)
);
store.recordHeartbeat(
heartbeat("bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb", "ws1_latest")
);
const status = store.usageStatus(new URLSearchParams());
expect(status.deployment_id).toBe(confirmed.deployment_id);
expect(status.workspace_id).toBe(confirmed.workspace_id);
});
it("expires inbox data while retaining idempotency tombstones", () => {
const db = store.usageDb();
db.prepare(
"UPDATE usage_event_inbox SET received_at=? WHERE event_id=?"
).run("2020-01-01T00:00:00.000Z", confirmed.event_id);
db.prepare(
"UPDATE usage_event_conflicts SET received_at=? WHERE event_id=?"
).run("2020-01-01T00:00:00.000Z", confirmed.event_id);
const result = store.runUsageMaintenance(
Date.parse("2026-07-16T00:00:00.000Z"),
db
);
expect(result.inbox_deleted).not.toBe("0");
expect(result.conflicts_deleted).not.toBe("0");
expect(
db
.prepare(
"SELECT event_id FROM usage_event_idempotency WHERE event_id=?"
)
.get(confirmed.event_id)
).toBeDefined();
expect(store.ingestUsageEvent(confirmed).status).toBe("duplicate");
});
it("creates a restorable online SQLite backup", async () => {
const destination = path.join(dataDir, "backups", "usage.db");
await store.backupUsageDatabase(destination);
expect(fs.statSync(destination).size).toBeGreaterThan(0);
const Database = (await import("better-sqlite3")).default;
const backup = new Database(destination, { readonly: true });
backup.defaultSafeIntegers(true);
try {
const row = backup
.prepare("SELECT COUNT(*) count FROM model_usage")
.get() as {
count: bigint;
};
expect(row.count).toBeGreaterThan(BigInt(0));
} finally {
backup.close();
}
});
it("stores aggregate-only provider reconciliation without rewriting calls", () => {
const aggregateEvent = eventFrom(
confirmed,
"88888888-8888-4888-8888-888888888888",
{
provider_profile_id: "aggregate-provider",
upstream_model_id: "aggregate-model",
model_alias: "aggregate-model",
input_tokens: 20,
output_tokens: 4,
provider_total_tokens: 24,
}
);
expect(store.ingestUsageEvent(aggregateEvent).status).toBe("accepted");
const observation = {
aggregate_id: "provider-window-1",
deployment_id: confirmed.deployment_id,
provider_profile_id: "aggregate-provider",
upstream_model_id: "aggregate-model",
window_start: "2026-07-16T00:00:00.000Z",
window_end: "2026-07-17T00:00:00.000Z",
input_tokens: "25",
output_tokens: "5",
provider_total_tokens: "30",
request_count: "1",
source_name: "provider-usage-api",
source_payload: { bucket: "fixture" },
observed_at: "2026-07-17T00:05:00.000Z",
};
expect(store.recordProviderAggregate(observation).status).toBe("accepted");
expect(store.recordProviderAggregate(observation).status).toBe("duplicate");
expect(
store.recordProviderAggregate({ ...observation, input_tokens: "26" })
.status
).toBe("conflict");
expect(store.providerAggregateDifference(observation.aggregate_id)).toEqual(
expect.objectContaining({
reconciliation_mode: "aggregate_only",
projection_updated: false,
local: expect.objectContaining({ total_tokens: "24" }),
provider: expect.objectContaining({ total_tokens: "30" }),
difference: expect.objectContaining({ total_tokens: "6" }),
})
);
const projection = store.usageSummary(
range({ provider_profile_id: "aggregate-provider" })
);
expect(projection.total_tokens).toBe("24");
});
});