9abea02420
Stats tests set PI_CONFIG_DIR + setAgentDir(home/<config>/agent), which resolves equal to the default agent dir and routes stats.db through $XDG_DATA_HOME/omp/stats.db whenever an XDG var is set, leaking rows across tests. The shared installStatsTestIsolation helper now snapshots and clears XDG_*_HOME with PI_CONFIG_DIR on each test, points the agent dir at a fresh TempDir, closes the DB handle, and restores the prior env after the test.
85 lines
2.5 KiB
TypeScript
85 lines
2.5 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "bun:test";
|
|
import * as fs from "node:fs/promises";
|
|
import * as path from "node:path";
|
|
import { syncAllSessions } from "@oh-my-pi/omp-stats/aggregator";
|
|
import { getOverallStats } from "@oh-my-pi/omp-stats/db";
|
|
import { getSessionsDir } from "@oh-my-pi/pi-utils";
|
|
import { installStatsTestIsolation } from "./helpers/temp-agent";
|
|
|
|
installStatsTestIsolation("@pi-stats-sync-serial-");
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
async function writeSessionFile(): Promise<void> {
|
|
const sessionDir = path.join(getSessionsDir(), "--tmp--sync-serial");
|
|
await fs.mkdir(sessionDir, { recursive: true });
|
|
const timestamp = new Date().toISOString();
|
|
const sessionFile = path.join(sessionDir, "session.jsonl");
|
|
const assistant = {
|
|
type: "message",
|
|
id: "assistant-1",
|
|
parentId: null,
|
|
timestamp,
|
|
message: {
|
|
role: "assistant",
|
|
content: [{ type: "text", text: "ok" }],
|
|
api: "openai-responses",
|
|
provider: "openai",
|
|
model: "gpt-5.4",
|
|
usage: {
|
|
input: 1,
|
|
output: 2,
|
|
cacheRead: 0,
|
|
cacheWrite: 0,
|
|
totalTokens: 3,
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
},
|
|
stopReason: "stop",
|
|
timestamp: Date.now(),
|
|
duration: 10,
|
|
ttft: 5,
|
|
},
|
|
};
|
|
await Bun.write(sessionFile, `${JSON.stringify(assistant)}\n`);
|
|
}
|
|
|
|
describe("stats sync serial mode", () => {
|
|
it("honors workers: 1 without spawning a worker", async () => {
|
|
await writeSessionFile();
|
|
const workerSpy = vi.spyOn(globalThis, "Worker");
|
|
|
|
const synced = await syncAllSessions({ workers: 1 });
|
|
const overall = getOverallStats();
|
|
|
|
expect(synced.files).toBe(1);
|
|
expect(overall.totalRequests).toBe(1);
|
|
expect(workerSpy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("uses the serial parser by default on macOS", async () => {
|
|
await writeSessionFile();
|
|
vi.spyOn(process, "platform", "get").mockReturnValue("darwin");
|
|
const workerSpy = vi.spyOn(globalThis, "Worker");
|
|
|
|
const synced = await syncAllSessions();
|
|
const overall = getOverallStats();
|
|
|
|
expect(synced.files).toBe(1);
|
|
expect(overall.totalRequests).toBe(1);
|
|
expect(workerSpy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("spawns a worker pool when callers explicitly request workers: 2 with a single file", async () => {
|
|
await writeSessionFile();
|
|
const workerProbe = new Error("worker probe");
|
|
const workerSpy = vi.spyOn(globalThis, "Worker").mockImplementation(() => {
|
|
throw workerProbe;
|
|
});
|
|
|
|
await expect(syncAllSessions({ workers: 2 })).rejects.toBe(workerProbe);
|
|
expect(workerSpy).toHaveBeenCalled();
|
|
});
|
|
});
|