import { USAGE_CONTRACT_VERSION, type EnvironmentId, type UsageBucket, type UsageDay, type UsageProviderKind, type UsageSummary,} from "@t3tools/contracts";import { describe, expect, it } from "vite-plus/test"; import { isModelCostUnknown, mergeUsage, type EnvironmentUsage } from "./usageMerge.ts"; function bucket(overrides: Partial<UsageBucket> = {}): UsageBucket { return { day: "2026-08-07" as UsageDay, provider: "claude", model: "claude-fable-5", totals: { uncachedInputTokens: 100, cachedInputTokens: 1000, cacheCreationTokens: 10, outputTokens: 50, reasoningTokens: 0, }, costUsd: 10, cacheSavingsUsd: 2, costSource: "modelPriced", records: 5, unpricedRecords: 0, sessions: 1, ...overrides, };} function summary( buckets: readonly UsageBucket[], sources: readonly { provider: UsageProviderKind; hostId: string; homePath: string; volumeId?: string; distinctSessions?: number; }[], contractVersion: number = USAGE_CONTRACT_VERSION,): UsageSummary { return { contractVersion, readAt: "2026-08-07T00:00:00.000Z", timeZone: "UTC", sinceDay: "2026-08-01" as UsageDay, untilDay: "2026-08-31" as UsageDay, buckets, sources: sources.map((source) => ({ fingerprint: { hostId: source.hostId, provider: source.provider, resolvedHomePath: source.homePath, volumeId: source.volumeId ?? `vol-${source.hostId}`, }, status: "ok" as const, scannedFiles: 1, skippedFiles: 0, malformedRecords: 0, distinctSessions: source.distinctSessions ?? 1, message: null, })), pricing: { status: "fresh", source: "litellm", fetchedAt: null, knownModels: 10 }, scanDurationMs: 1, };} function environment(id: string, usageSummary: UsageSummary): EnvironmentUsage { return { environmentId: id as EnvironmentId, label: id, summary: usageSummary };} describe("mergeUsage", () => { it("sums environments that read different transcript directories", () => { const merged = mergeUsage( [ environment( "env-a", summary([bucket()], [{ provider: "claude", hostId: "mac", homePath: "/a/.claude" }]), ), environment( "env-b", summary([bucket()], [{ provider: "claude", hostId: "linux", homePath: "/b/.claude" }]), ), ], USAGE_CONTRACT_VERSION, ); expect(merged.costUsd).toBe(20); expect(merged.records).toBe(10); expect(merged.duplicateSources).toHaveLength(0); }); it("counts a shared transcript directory once", () => { // Two worktree servers on one machine resolve the same provider home. const shared = { provider: "claude" as const, hostId: "mac", homePath: "/home/theo/.claude" }; const merged = mergeUsage( [ environment("env-a", summary([bucket()], [shared])), environment("env-b", summary([bucket()], [shared])), ], USAGE_CONTRACT_VERSION, ); expect(merged.costUsd).toBe(10); expect(merged.records).toBe(5); expect(merged.sessions).toBe(1); expect(merged.duplicateSources).toHaveLength(1); expect(merged.contributingEnvironments).toEqual(["env-a"]); }); it("drops only the duplicated provider, keeping the environment's other one", () => { const sharedClaude = { provider: "claude" as const, hostId: "mac", homePath: "/home/theo/.claude", }; const merged = mergeUsage( [ environment("env-a", summary([bucket()], [sharedClaude])), environment( "env-b", summary( [bucket(), bucket({ provider: "codex", model: "gpt-5.6-sol", costUsd: 4 })], [sharedClaude, { provider: "codex", hostId: "mac", homePath: "/home/theo/.codex" }], ), ), ], USAGE_CONTRACT_VERSION, ); // env-b's claude bucket is dropped, its codex bucket survives. expect(merged.costUsd).toBe(14); expect(merged.providers.map((provider) => provider.provider).sort()).toEqual([ "claude", "codex", ]); expect(merged.sessions).toBe(2); expect( Object.fromEntries( merged.providers.map((provider) => [provider.provider, provider.sessions]), ), ).toEqual({ claude: 1, codex: 1 }); }); it("excludes an environment reporting an older contract version", () => { const merged = mergeUsage( [ environment( "env-a", summary([bucket()], [{ provider: "claude", hostId: "mac", homePath: "/a" }]), ), environment( "env-b", summary( [bucket()], [{ provider: "claude", hostId: "linux", homePath: "/b" }], USAGE_CONTRACT_VERSION - 2, ), ), ], USAGE_CONTRACT_VERSION, ); expect(merged.costUsd).toBe(10); expect(merged.staleEnvironments).toEqual(["env-b"]); }); it("keeps the previous compatible contract version so additive provider expansions still merge", () => { const merged = mergeUsage( [ environment( "env-a", summary( [bucket({ costUsd: 10 })], [{ provider: "claude", hostId: "mac", homePath: "/a" }], ), ), environment( "env-b", summary( [bucket({ costUsd: 4, provider: "codex", model: "gpt-5.6-sol" })], [{ provider: "codex", hostId: "linux", homePath: "/b" }], USAGE_CONTRACT_VERSION - 1, ), ), ], USAGE_CONTRACT_VERSION, ); expect(merged.costUsd).toBe(14); expect(merged.staleEnvironments).toEqual([]); }); it("derives provider shares and cost quality", () => { const merged = mergeUsage( [ environment( "env-a", summary( [ bucket({ costUsd: 75 }), bucket({ provider: "codex", model: "gpt-5.6-sol", costUsd: 25, unpricedRecords: 5 }), ], [ { provider: "claude", hostId: "mac", homePath: "/a/.claude" }, { provider: "codex", hostId: "mac", homePath: "/a/.codex" }, ], ), ), ], USAGE_CONTRACT_VERSION, ); expect(merged.providers[0]?.provider).toBe("claude"); expect(merged.providers[0]?.costShare).toBeCloseTo(0.75, 5); expect(merged.costQuality.unpricedShare).toBeCloseTo(0.5, 5); expect(merged.costQuality.cacheSavingsUsd).toBe(4); }); it("marks a model with no known rates as unpriced rather than free", () => { const merged = mergeUsage( [ environment( "env-a", summary( [ bucket({ costUsd: 75 }), bucket({ provider: "codex", model: "unknown-model", costUsd: 0, costSource: "unpriced", unpricedRecords: 5, }), ], [ { provider: "claude", hostId: "mac", homePath: "/a/.claude" }, { provider: "codex", hostId: "mac", homePath: "/a/.codex" }, ], ), ), ], USAGE_CONTRACT_VERSION, ); expect(merged.models.find((model) => model.model === "unknown-model")?.unpricedRecords).toBe(5); expect(merged.models.filter(isModelCostUnknown).map((model) => model.model)).toEqual([ "unknown-model", ]); }); it("keeps two machines apart when hostname and home path collide", () => { // Every Mac resolves /Users/theo/.claude, so a hostname clash used to make // one machine's usage vanish. Filesystem identity separates them. const shape = { provider: "claude" as const, hostId: "mac", homePath: "/Users/theo/.claude" }; const merged = mergeUsage( [ environment("env-a", summary([bucket()], [{ ...shape, volumeId: "16777220:1234" }])), environment("env-b", summary([bucket()], [{ ...shape, volumeId: "16777221:9999" }])), ], USAGE_CONTRACT_VERSION, ); expect(merged.costUsd).toBe(20); expect(merged.duplicateSources).toHaveLength(0); }); it("still collapses two servers reading the same directory", () => { const same = { provider: "claude" as const, hostId: "mac", homePath: "/Users/theo/.claude", volumeId: "16777220:1234", }; const merged = mergeUsage( [ environment("env-a", summary([bucket()], [same])), environment("env-b", summary([bucket()], [same])), ], USAGE_CONTRACT_VERSION, ); expect(merged.costUsd).toBe(10); expect(merged.duplicateSources).toHaveLength(1); }); it("totals sessions from per-directory distinct counts, not per-bucket sums", () => { // One session that spans two days appears in two buckets. Summing bucket // sessions would say 2; the source's distinct count says 1. const merged = mergeUsage( [ environment( "env-a", summary( [bucket({ day: "2026-08-06" as UsageDay }), bucket({ day: "2026-08-07" as UsageDay })], [ { provider: "claude", hostId: "mac", homePath: "/a/.claude", distinctSessions: 1, }, ], ), ), ], USAGE_CONTRACT_VERSION, ); expect(merged.sessions).toBe(1); expect(merged.providers[0]?.sessions).toBe(1); }); it("returns empty totals with no environments", () => { const merged = mergeUsage([], USAGE_CONTRACT_VERSION); expect(merged.costUsd).toBe(0); expect(merged.daily).toHaveLength(0); expect(merged.hourly).toHaveLength(0); }); it("omits providers with no sessions or usage", () => { const merged = mergeUsage( [ environment( "env-a", summary( [], [ { provider: "claude", hostId: "mac", homePath: "/a/.claude", distinctSessions: 0, }, ], ), ), ], USAGE_CONTRACT_VERSION, ); expect(merged.providers).toEqual([]); }); it("derives hourly totals without losing the daily rollup", () => { const merged = mergeUsage( [ environment( "env-a", summary( [ bucket({ hourStart: "2026-08-07T09:37:00.000Z", costUsd: 3 }), bucket({ hourStart: "2026-08-07T10:37:00.000Z", costUsd: 7 }), ], [{ provider: "claude", hostId: "mac", homePath: "/a/.claude" }], ), ), ], USAGE_CONTRACT_VERSION, ); expect(merged.hourly.map((hour) => [hour.hourStart, hour.costUsd])).toEqual([ ["2026-08-07T09:37:00.000Z", 3], ["2026-08-07T10:37:00.000Z", 7], ]); expect(merged.daily).toHaveLength(1); expect(merged.daily[0]?.costUsd).toBe(10); });});
import {
USAGE_CONTRACT_VERSION,
type EnvironmentId,
type UsageBucket,
type UsageDay,
type UsageProviderKind,
type UsageSummary,
} from "@t3tools/contracts";
import { describe, expect, it } from "vite-plus/test";
import { isModelCostUnknown, mergeUsage, type EnvironmentUsage } from "./usageMerge.ts";
function bucket(overrides: Partial<UsageBucket> = {}): UsageBucket {
return {
day: "2026-08-07" as UsageDay,
provider: "claude",
model: "claude-fable-5",
totals: {
uncachedInputTokens: 100,
cachedInputTokens: 1000,
cacheCreationTokens: 10,
outputTokens: 50,
reasoningTokens: 0,
},
costUsd: 10,
cacheSavingsUsd: 2,
costSource: "modelPriced",
records: 5,
unpricedRecords: 0,
sessions: 1,
...overrides,
};
}
function summary(
buckets: readonly UsageBucket[],
sources: readonly {
provider: UsageProviderKind;
hostId: string;
homePath: string;
volumeId?: string;
distinctSessions?: number;
}[],
contractVersion: number = USAGE_CONTRACT_VERSION,
): UsageSummary {
return {
contractVersion,
readAt: "2026-08-07T00:00:00.000Z",
timeZone: "UTC",
sinceDay: "2026-08-01" as UsageDay,
untilDay: "2026-08-31" as UsageDay,
buckets,
sources: sources.map((source) => ({
fingerprint: {
hostId: source.hostId,
provider: source.provider,
resolvedHomePath: source.homePath,
volumeId: source.volumeId ?? `vol-${source.hostId}`,
},
status: "ok" as const,
scannedFiles: 1,
skippedFiles: 0,
malformedRecords: 0,
distinctSessions: source.distinctSessions ?? 1,
message: null,
})),
pricing: { status: "fresh", source: "litellm", fetchedAt: null, knownModels: 10 },
scanDurationMs: 1,
};
}
function environment(id: string, usageSummary: UsageSummary): EnvironmentUsage {
return { environmentId: id as EnvironmentId, label: id, summary: usageSummary };
}
describe("mergeUsage", () => {
it("sums environments that read different transcript directories", () => {
const merged = mergeUsage(
[
environment(
"env-a",
summary([bucket()], [{ provider: "claude", hostId: "mac", homePath: "/a/.claude" }]),
),
environment(
"env-b",
summary([bucket()], [{ provider: "claude", hostId: "linux", homePath: "/b/.claude" }]),
),
],
USAGE_CONTRACT_VERSION,
);
expect(merged.costUsd).toBe(20);
expect(merged.records).toBe(10);
expect(merged.duplicateSources).toHaveLength(0);
});
it("counts a shared transcript directory once", () => {
// Two worktree servers on one machine resolve the same provider home.
const shared = { provider: "claude" as const, hostId: "mac", homePath: "/home/theo/.claude" };
const merged = mergeUsage(
[
environment("env-a", summary([bucket()], [shared])),
environment("env-b", summary([bucket()], [shared])),
],
USAGE_CONTRACT_VERSION,
);
expect(merged.costUsd).toBe(10);
expect(merged.records).toBe(5);
expect(merged.sessions).toBe(1);
expect(merged.duplicateSources).toHaveLength(1);
expect(merged.contributingEnvironments).toEqual(["env-a"]);
});
it("drops only the duplicated provider, keeping the environment's other one", () => {
const sharedClaude = {
provider: "claude" as const,
hostId: "mac",
homePath: "/home/theo/.claude",
};
const merged = mergeUsage(
[
environment("env-a", summary([bucket()], [sharedClaude])),
environment(
"env-b",
summary(
[bucket(), bucket({ provider: "codex", model: "gpt-5.6-sol", costUsd: 4 })],
[sharedClaude, { provider: "codex", hostId: "mac", homePath: "/home/theo/.codex" }],
),
),
],
USAGE_CONTRACT_VERSION,
);
// env-b's claude bucket is dropped, its codex bucket survives.
expect(merged.costUsd).toBe(14);
expect(merged.providers.map((provider) => provider.provider).sort()).toEqual([
"claude",
"codex",
]);
expect(merged.sessions).toBe(2);
expect(
Object.fromEntries(
merged.providers.map((provider) => [provider.provider, provider.sessions]),
),
).toEqual({ claude: 1, codex: 1 });
});
it("excludes an environment reporting an older contract version", () => {
const merged = mergeUsage(
[
environment(
"env-a",
summary([bucket()], [{ provider: "claude", hostId: "mac", homePath: "/a" }]),
),
environment(
"env-b",
summary(
[bucket()],
[{ provider: "claude", hostId: "linux", homePath: "/b" }],
USAGE_CONTRACT_VERSION - 2,
),
),
],
USAGE_CONTRACT_VERSION,
);
expect(merged.costUsd).toBe(10);
expect(merged.staleEnvironments).toEqual(["env-b"]);
});
it("keeps the previous compatible contract version so additive provider expansions still merge", () => {
const merged = mergeUsage(
[
environment(
"env-a",
summary(
[bucket({ costUsd: 10 })],
[{ provider: "claude", hostId: "mac", homePath: "/a" }],
),
),
environment(
"env-b",
summary(
[bucket({ costUsd: 4, provider: "codex", model: "gpt-5.6-sol" })],
[{ provider: "codex", hostId: "linux", homePath: "/b" }],
USAGE_CONTRACT_VERSION - 1,
),
),
],
USAGE_CONTRACT_VERSION,
);
expect(merged.costUsd).toBe(14);
expect(merged.staleEnvironments).toEqual([]);
});
it("derives provider shares and cost quality", () => {
const merged = mergeUsage(
[
environment(
"env-a",
summary(
[
bucket({ costUsd: 75 }),
bucket({ provider: "codex", model: "gpt-5.6-sol", costUsd: 25, unpricedRecords: 5 }),
],
[
{ provider: "claude", hostId: "mac", homePath: "/a/.claude" },
{ provider: "codex", hostId: "mac", homePath: "/a/.codex" },
],
),
),
],
USAGE_CONTRACT_VERSION,
);
expect(merged.providers[0]?.provider).toBe("claude");
expect(merged.providers[0]?.costShare).toBeCloseTo(0.75, 5);
expect(merged.costQuality.unpricedShare).toBeCloseTo(0.5, 5);
expect(merged.costQuality.cacheSavingsUsd).toBe(4);
});
it("marks a model with no known rates as unpriced rather than free", () => {
const merged = mergeUsage(
[
environment(
"env-a",
summary(
[
bucket({ costUsd: 75 }),
bucket({
provider: "codex",
model: "unknown-model",
costUsd: 0,
costSource: "unpriced",
unpricedRecords: 5,
}),
],
[
{ provider: "claude", hostId: "mac", homePath: "/a/.claude" },
{ provider: "codex", hostId: "mac", homePath: "/a/.codex" },
],
),
),
],
USAGE_CONTRACT_VERSION,
);
expect(merged.models.find((model) => model.model === "unknown-model")?.unpricedRecords).toBe(5);
expect(merged.models.filter(isModelCostUnknown).map((model) => model.model)).toEqual([
"unknown-model",
]);
});
it("keeps two machines apart when hostname and home path collide", () => {
// Every Mac resolves /Users/theo/.claude, so a hostname clash used to make
// one machine's usage vanish. Filesystem identity separates them.
const shape = { provider: "claude" as const, hostId: "mac", homePath: "/Users/theo/.claude" };
const merged = mergeUsage(
[
environment("env-a", summary([bucket()], [{ ...shape, volumeId: "16777220:1234" }])),
environment("env-b", summary([bucket()], [{ ...shape, volumeId: "16777221:9999" }])),
],
USAGE_CONTRACT_VERSION,
);
expect(merged.costUsd).toBe(20);
expect(merged.duplicateSources).toHaveLength(0);
});
it("still collapses two servers reading the same directory", () => {
const same = {
provider: "claude" as const,
hostId: "mac",
homePath: "/Users/theo/.claude",
volumeId: "16777220:1234",
};
const merged = mergeUsage(
[
environment("env-a", summary([bucket()], [same])),
environment("env-b", summary([bucket()], [same])),
],
USAGE_CONTRACT_VERSION,
);
expect(merged.costUsd).toBe(10);
expect(merged.duplicateSources).toHaveLength(1);
});
it("totals sessions from per-directory distinct counts, not per-bucket sums", () => {
// One session that spans two days appears in two buckets. Summing bucket
// sessions would say 2; the source's distinct count says 1.
const merged = mergeUsage(
[
environment(
"env-a",
summary(
[bucket({ day: "2026-08-06" as UsageDay }), bucket({ day: "2026-08-07" as UsageDay })],
[
{
provider: "claude",
hostId: "mac",
homePath: "/a/.claude",
distinctSessions: 1,
},
],
),
),
],
USAGE_CONTRACT_VERSION,
);
expect(merged.sessions).toBe(1);
expect(merged.providers[0]?.sessions).toBe(1);
});
it("returns empty totals with no environments", () => {
const merged = mergeUsage([], USAGE_CONTRACT_VERSION);
expect(merged.costUsd).toBe(0);
expect(merged.daily).toHaveLength(0);
expect(merged.hourly).toHaveLength(0);
});
it("omits providers with no sessions or usage", () => {
const merged = mergeUsage(
[
environment(
"env-a",
summary(
[],
[
{
provider: "claude",
hostId: "mac",
homePath: "/a/.claude",
distinctSessions: 0,
},
],
),
),
],
USAGE_CONTRACT_VERSION,
);
expect(merged.providers).toEqual([]);
});
it("derives hourly totals without losing the daily rollup", () => {
const merged = mergeUsage(
[
environment(
"env-a",
summary(
[
bucket({ hourStart: "2026-08-07T09:37:00.000Z", costUsd: 3 }),
bucket({ hourStart: "2026-08-07T10:37:00.000Z", costUsd: 7 }),
],
[{ provider: "claude", hostId: "mac", homePath: "/a/.claude" }],
),
),
],
USAGE_CONTRACT_VERSION,
);
expect(merged.hourly.map((hour) => [hour.hourStart, hour.costUsd])).toEqual([
["2026-08-07T09:37:00.000Z", 3],
["2026-08-07T10:37:00.000Z", 7],
]);
expect(merged.daily).toHaveLength(1);
expect(merged.daily[0]?.costUsd).toBe(10);
});
});