Files
oh-my-pi/packages/utils/test/logger-multiprocess.test.ts
T
can1357 fc6256372d test: fix local-midnight logger flake and stale kimi k3 thinking format pin
- logger-multiprocess asserted the UTC day (toISOString) while DailyRotateFile names files with the LOCAL date, failing nightly between 00:00 and 02:00 local (UTC+2); the per-pid rotation-file invariant now matches any dated name.
- kimi-code k3 bundled compat moved to thinkingFormat 'kimi' with the catalog regen; the K3 named-tool-choice downgrade gate keys on provider/id/baseUrl, so only the stale precondition needed updating.
2026-07-23 00:13:14 +02:00

89 lines
3.6 KiB
TypeScript

import { afterEach, describe, expect, it } from "bun:test";
import * as fs from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
import { pathToFileURL } from "node:url";
const loggerModuleUrl = pathToFileURL(path.join(import.meta.dir, "../src/logger.ts")).href;
const roots: string[] = [];
afterEach(async () => {
await Promise.all(roots.splice(0).map(root => fs.rm(root, { recursive: true, force: true })));
});
async function makeProbe(logsDir: string): Promise<string> {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "omp-logger-probe-"));
roots.push(root);
const probePath = path.join(root, "probe.ts");
await Bun.write(
probePath,
`import { info, setTransports } from ${JSON.stringify(loggerModuleUrl)};\n` +
`setTransports({ file: ${JSON.stringify(logsDir)} });\n` +
`info("multiprocess probe");\n` +
`console.log("ready");\n` +
`await new Response(Bun.stdin.stream()).text();\n` +
`setTransports({ file: false });\n`,
);
return probePath;
}
describe("multiprocess file logging", () => {
it("gives concurrent processes independent rotation files and audit state", async () => {
const logsDir = await fs.mkdtemp(path.join(os.tmpdir(), "omp-logger-output-"));
roots.push(logsDir);
const probePath = await makeProbe(logsDir);
const processes = [
Bun.spawn([process.execPath, probePath], { stdin: "pipe", stdout: "pipe", stderr: "pipe" }),
Bun.spawn([process.execPath, probePath], { stdin: "pipe", stdout: "pipe", stderr: "pipe" }),
];
const ready = await Promise.all(
processes.map(async proc => {
const reader = proc.stdout.getReader();
const result = await reader.read();
reader.releaseLock();
return result.value ? new TextDecoder().decode(result.value) : "";
}),
);
expect(ready).toEqual(["ready\n", "ready\n"]);
for (const proc of processes) proc.stdin.end();
expect(await Promise.all(processes.map(proc => proc.exited))).toEqual([0, 0]);
const entries = await fs.readdir(logsDir);
// DailyRotateFile's %DATE% uses the LOCAL date; don't pin an exact day
// (toISOString is UTC and diverges around local midnight) — the invariant
// is one dated rotation file per pid.
for (const proc of processes) {
const perPid = new RegExp(`^omp\\.\\d{4}-\\d{2}-\\d{2}\\.${proc.pid}\\.log$`);
expect(entries.some(name => perPid.test(name))).toBe(true);
}
expect(entries.filter(name => name.endsWith("-audit.json"))).toHaveLength(2);
});
it("prunes completed PID namespaces across short-lived invocations", async () => {
const logsDir = await fs.mkdtemp(path.join(os.tmpdir(), "omp-logger-retention-"));
roots.push(logsDir);
const exited = Array.from({ length: 7 }, () =>
Bun.spawn([process.execPath, "--version"], { stdout: "ignore", stderr: "ignore" }),
);
expect(await Promise.all(exited.map(proc => proc.exited))).toEqual(Array(7).fill(0));
const date = "2026-07-01";
for (const [index, proc] of exited.entries()) {
const logPath = path.join(logsDir, `omp.${date}.${proc.pid}.log`);
await Bun.write(logPath, `completed process ${proc.pid}`);
await fs.utimes(logPath, index + 1, index + 1);
await Bun.write(path.join(logsDir, `.omp.${proc.pid}-audit.json`), "{}");
}
const probePath = await makeProbe(logsDir);
const current = Bun.spawn([process.execPath, probePath], { stdout: "ignore", stderr: "pipe" });
expect(await current.exited).toBe(0);
const entries = await fs.readdir(logsDir);
const completedLogs = entries.filter(name => name.startsWith(`omp.${date}.`));
expect(completedLogs).toHaveLength(5);
expect(entries.filter(name => name.endsWith("-audit.json"))).toEqual([`.omp.${current.pid}-audit.json`]);
});
});