import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { describe, expect, it } from "vitest"; import type { ClawdbotConfig } from "../config/config.js"; import { loadCostUsageSummary, loadSessionCostSummary } from "./session-cost-usage.js"; describe("session cost usage", () => { it("aggregates daily totals with log cost and pricing fallback", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-cost-")); const sessionsDir = path.join(root, "agents", "main", "sessions"); await fs.mkdir(sessionsDir, { recursive: true }); const sessionFile = path.join(sessionsDir, "sess-1.jsonl"); const now = new Date(); const older = new Date(Date.now() - 40 * 24 * 60 * 60 * 1000); const entries = [ { type: "message", timestamp: now.toISOString(), message: { role: "assistant", provider: "openai", model: "gpt-5.2", usage: { input: 10, output: 20, cacheRead: 0, cacheWrite: 0, totalTokens: 30, cost: { total: 0.03 }, }, }, }, { type: "message", timestamp: now.toISOString(), message: { role: "assistant", provider: "openai", model: "gpt-5.2", usage: { input: 10, output: 10, cacheRead: 0, cacheWrite: 0, totalTokens: 20, }, }, }, { type: "message", timestamp: older.toISOString(), message: { role: "assistant", provider: "openai", model: "gpt-5.2", usage: { input: 5, output: 5, totalTokens: 10, cost: { total: 0.01 }, }, }, }, ]; await fs.writeFile( sessionFile, entries.map((entry) => JSON.stringify(entry)).join("\n"), "utf-8", ); const config = { models: { providers: { openai: { models: [ { id: "gpt-5.2", cost: { input: 1, output: 2, cacheRead: 0, cacheWrite: 0, }, }, ], }, }, }, } as ClawdbotConfig; const originalState = process.env.CLAWDBOT_STATE_DIR; process.env.CLAWDBOT_STATE_DIR = root; try { const summary = await loadCostUsageSummary({ days: 30, config }); expect(summary.daily.length).toBe(1); expect(summary.totals.totalTokens).toBe(50); expect(summary.totals.totalCost).toBeCloseTo(0.03003, 5); } finally { if (originalState === undefined) delete process.env.CLAWDBOT_STATE_DIR; else process.env.CLAWDBOT_STATE_DIR = originalState; } }); it("summarizes a single session file", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-cost-session-")); const sessionFile = path.join(root, "session.jsonl"); const now = new Date(); await fs.writeFile( sessionFile, JSON.stringify({ type: "message", timestamp: now.toISOString(), message: { role: "assistant", provider: "openai", model: "gpt-5.2", usage: { input: 10, output: 20, totalTokens: 30, cost: { total: 0.03 }, }, }, }), "utf-8", ); const summary = await loadSessionCostSummary({ sessionFile, }); expect(summary?.totalCost).toBeCloseTo(0.03, 5); expect(summary?.totalTokens).toBe(30); expect(summary?.lastActivity).toBeGreaterThan(0); }); });