utils/learnings.test.ts
Rama main · réplica de lectura del repositorio original.
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import {
LEARNINGS_FILE_NAME,
learningsFilePath,
readLearningsFile,
seedLearningsFile,
} from "./learnings.ts";
describe("learnings tmpfile round-trip", () => {
let dir: string;
beforeEach(async () => {
dir = await mkdtemp(join(tmpdir(), "pullfrog-learnings-test-"));
});
afterEach(async () => {
await rm(dir, { recursive: true, force: true });
});
it("writes the verbatim DB body to disk and reads it back unchanged", async () => {
const current = [
"## Build & test",
"",
"- run tests with `pnpm -r test`",
"",
"## Architecture",
"",
"- workers in `worker/`",
].join("\n");
const path = await seedLearningsFile({ tmpdir: dir, current });
expect(path).toBe(learningsFilePath(dir));
expect(path.endsWith(LEARNINGS_FILE_NAME)).toBe(true);
expect(await readFile(path, "utf8")).toBe(current);
expect(await readLearningsFile(path)).toBe(current);
});
it("seeds an empty file when the repo has no learnings yet, round-trip is empty string", async () => {
const path = await seedLearningsFile({ tmpdir: dir, current: null });
expect(await readFile(path, "utf8")).toBe("");
expect(await readLearningsFile(path)).toBe("");
});
it("returns null when the file is missing (treated as no-change by persist)", async () => {
expect(await readLearningsFile(learningsFilePath(dir))).toBeNull();
});
it("trims trailing whitespace so editor newlines never trigger a spurious PATCH", async () => {
const current = "## Build & test\n\n- one fact";
const path = await seedLearningsFile({ tmpdir: dir, current });
await writeFile(path, `${current}\n\n `, "utf8");
expect(await readLearningsFile(path)).toBe(current);
});
it("truncates over-cap bodies at the last newline boundary so the next-seed TOC parse stays clean", async () => {
const padding = `${"x".repeat(80)}\n`.repeat(1300);
const oversized = `## Build & test\n\n${padding}`;
const path = await seedLearningsFile({ tmpdir: dir, current: null });
await writeFile(path, oversized, "utf8");
const read = await readLearningsFile(path);
expect(read).toBeTruthy();
expect(read?.length).toBeLessThanOrEqual(100_000);
const tailLine = read?.split("\n").pop() ?? "";
expect(/^x+$/.test(tailLine)).toBe(true);
expect(tailLine.length).toBe(80);
});
it("falls back to a hard truncate when the only newline is far above the cap (giant single line)", async () => {
const oversized = `## Build & test\n${"x".repeat(110_000)}`;
const path = await seedLearningsFile({ tmpdir: dir, current: null });
await writeFile(path, oversized, "utf8");
const read = await readLearningsFile(path);
expect(read).toBeTruthy();
expect(read?.length).toBe(100_000);
expect(read?.startsWith("## Build & test\n")).toBe(true);
});
it("preserves legacy free-text without scaffolding or wrapping", async () => {
const legacy = "- this is some old free-text bullet\n- another one";
const path = await seedLearningsFile({ tmpdir: dir, current: legacy });
expect(await readFile(path, "utf8")).toBe(legacy);
expect(await readLearningsFile(path)).toBe(legacy);
});
});