Files
oh-my-pi/packages/coding-agent/test/memory-session-storage.test.ts
T
oldschoola 21d152c70c feat(coding-agent): async ConfigFile + ModelRegistry.create() factory; fix MemorySessionStorage O(N^2) appends
F6: defer the JSON -> YAML migration out of ConfigFile's constructor and
add an async path so the boot sequence stops blocking the event loop on
sync I/O. New ConfigFile.tryLoadAsync/loadAsync/loadOrDefaultAsync/
getMtimeMsAsync and static ConfigFile.warmup. The migration is now
idempotent (per-process cache) so relocate() does not re-run it.
ModelRegistry.create(authStorage, modelsPath?) is a new async factory
that runs the warmup before the sync constructor's bundled-model load.
Production call sites (main.ts, sdk.ts, task/executor.ts, commit
pipelines, SDK example) all switched. Sync new ModelRegistry(...)
constructor is still supported for tests.

F2: rewrite MemorySessionStorage's mirror as { chunks: string[]; byteLen;
mtimeMs } so writeLineSync appends a single chunk in O(1) instead of
read-modify-writing the entire file (which was O(N) per append, O(N^2)
per session). statSync now reports true UTF-8 byte length instead of
character count. readTextPrefix walks chunks until the byte budget is
exhausted instead of materialising the full mirror.

Also rolls in per-package CHANGELOG entries for F1-F8.
2026-05-31 04:46:06 +02:00

85 lines
3.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, test } from "bun:test";
import { MemorySessionStorage } from "../src/session/session-storage";
describe("MemorySessionStorage chunked mirror (F2)", () => {
test("writeLineSync builds the same content as a single writeTextSync of the join", async () => {
const storage = new MemorySessionStorage();
const path = "/virtual/session.jsonl";
const writer = storage.openWriter(path, { flags: "w" });
try {
const N = 1000;
for (let i = 0; i < N; i++) {
writer.writeLineSync(`{"i":${i}}\n`);
}
} finally {
await writer.close();
}
// Construct the baseline from the same parts.
const expected = Array.from({ length: 1000 }, (_, i) => `{"i":${i}}\n`).join("");
const actual = storage.readTextSync(path);
expect(actual).toBe(expected);
expect(actual.length).toBe(expected.length);
});
test("statSync reports UTF-8 byte length, not character count", () => {
const storage = new MemorySessionStorage();
const path = "/virtual/unicode.jsonl";
const writer = storage.openWriter(path, { flags: "w" });
try {
writer.writeLineSync("héllo\n"); // é = 2 bytes in UTF-8
writer.writeLineSync("日本語\n"); // 3 chars × 3 bytes = 9
} finally {
void writer.close();
}
const expectedBytes = Buffer.byteLength("héllo\n日本語\n", "utf-8");
expect(storage.statSync(path).size).toBe(expectedBytes);
});
test("readTextPrefix walks chunks until the byte budget is exhausted", async () => {
const storage = new MemorySessionStorage();
const path = "/virtual/prefix.jsonl";
const writer = storage.openWriter(path, { flags: "w" });
try {
writer.writeLineSync("alpha\n");
writer.writeLineSync("bravo\n");
writer.writeLineSync("charlie\n");
} finally {
void writer.close();
}
// Cap mid-second-chunk; first chunk = 6B, take 4 of the second.
const prefix = await storage.readTextPrefix(path, 10);
expect(prefix).toBe("alpha\nbrav");
});
test("subsequent writeLineSync after readTextSync stays O(1) (chunks preserved)", () => {
const storage = new MemorySessionStorage();
const path = "/virtual/cont.jsonl";
const writer = storage.openWriter(path, { flags: "w" });
try {
writer.writeLineSync("first\n");
writer.writeLineSync("second\n");
// Materialise once — implementation must NOT cache the joined string
// back into the entry, or subsequent appends collapse back to O(N).
expect(storage.readTextSync(path)).toBe("first\nsecond\n");
writer.writeLineSync("third\n");
expect(storage.readTextSync(path)).toBe("first\nsecond\nthird\n");
expect(storage.statSync(path).size).toBe(Buffer.byteLength("first\nsecond\nthird\n", "utf-8"));
} finally {
void writer.close();
}
});
test("writeTextSync resets the chunks and byte counter (overwrite semantics)", () => {
const storage = new MemorySessionStorage();
const path = "/virtual/overwrite.jsonl";
storage.writeTextSync(path, "abcdef");
expect(storage.statSync(path).size).toBe(6);
storage.writeTextSync(path, "xy");
expect(storage.statSync(path).size).toBe(2);
expect(storage.readTextSync(path)).toBe("xy");
});
});