mirror of
https://github.com/earendil-works/pi.git
synced 2026-06-18 15:54:04 +08:00
84 lines
3.0 KiB
TypeScript
84 lines
3.0 KiB
TypeScript
import { mkdirSync, mkdtempSync, rmSync, utimesSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import { buildSessionAnalyticsUpload } from "../src/core/session-analytics-reader.ts";
|
|
|
|
const tempDirs: string[] = [];
|
|
|
|
function createTempDir(): string {
|
|
const dir = mkdtempSync(join(tmpdir(), "pi-session-analytics-reader-"));
|
|
tempDirs.push(dir);
|
|
return dir;
|
|
}
|
|
|
|
afterEach(() => {
|
|
for (const dir of tempDirs.splice(0)) {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
function writeJsonl(path: string, lines: unknown[]): void {
|
|
writeFileSync(path, `${lines.map((line) => JSON.stringify(line)).join("\n")}\n`);
|
|
}
|
|
|
|
function sessionHeader(id: string, timestamp: string): unknown {
|
|
return { type: "session", version: 3, id, timestamp, cwd: `/work/${id}` };
|
|
}
|
|
|
|
function entry(id: string, timestamp: string): unknown {
|
|
return { type: "model_change", id, parentId: null, timestamp, provider: "anthropic", modelId: "model" };
|
|
}
|
|
|
|
describe("buildSessionAnalyticsUpload", () => {
|
|
it("selects files by mtime and includes old records before the scan cutoff", async () => {
|
|
const root = createTempDir();
|
|
const project = join(root, "--project--");
|
|
mkdirSync(project, { recursive: true });
|
|
const oldFile = join(project, "old.jsonl");
|
|
const changedFile = join(project, "changed.jsonl");
|
|
writeJsonl(oldFile, [
|
|
sessionHeader("old", "2026-01-01T00:00:00.000Z"),
|
|
entry("old-entry", "2026-01-01T00:00:01.000Z"),
|
|
]);
|
|
writeJsonl(changedFile, [
|
|
sessionHeader("changed", "2026-01-01T00:00:00.000Z"),
|
|
entry("included-old-entry", "2026-01-01T00:00:01.000Z"),
|
|
entry("future-entry", "2026-01-03T00:00:00.000Z"),
|
|
]);
|
|
utimesSync(oldFile, new Date("2026-01-01T00:10:00.000Z"), new Date("2026-01-01T00:10:00.000Z"));
|
|
utimesSync(changedFile, new Date("2026-01-02T00:10:00.000Z"), new Date("2026-01-02T00:10:00.000Z"));
|
|
|
|
const result = await buildSessionAnalyticsUpload({
|
|
sessionsRoot: root,
|
|
serverWatermark: "2026-01-02T00:00:00.000Z",
|
|
scanCutoff: new Date("2026-01-02T12:00:00.000Z"),
|
|
});
|
|
|
|
expect(result.filesScanned).toBe(1);
|
|
expect(result.malformedFiles).toBe(0);
|
|
expect(result.scanCutoff).toBe("2026-01-02T12:00:00.000Z");
|
|
expect(
|
|
result.records.map((record) => (record.recordType === "entry" ? record.entryId : record.sessionId)),
|
|
).toEqual(["changed", "included-old-entry"]);
|
|
});
|
|
|
|
it("skips malformed selected files", async () => {
|
|
const root = createTempDir();
|
|
const project = join(root, "--project--");
|
|
mkdirSync(project, { recursive: true });
|
|
const malformed = join(project, "malformed.jsonl");
|
|
writeFileSync(malformed, `${JSON.stringify(sessionHeader("bad", "2026-01-01T00:00:00.000Z"))}\nnot json\n`);
|
|
|
|
const result = await buildSessionAnalyticsUpload({
|
|
sessionsRoot: root,
|
|
serverWatermark: null,
|
|
scanCutoff: new Date("2026-01-02T00:00:00.000Z"),
|
|
});
|
|
|
|
expect(result.records).toEqual([]);
|
|
expect(result.filesScanned).toBe(1);
|
|
expect(result.malformedFiles).toBe(1);
|
|
});
|
|
});
|