1
7import { mkdtemp, writeFile, mkdir } from "node:fs/promises";
8import { tmpdir } from "node:os";
9import path from "node:path";
10import { describe, expect, it } from "vitest";
11import {
12 cleanupTempFiles,
13 createTempFile,
14 removeTempFile,
15 tempDir
16} from "../temp.js";
17
18async function freshDir(): Promise<string> {
19 const d = await mkdtemp(path.join(tmpdir(), "factory-files-test-"));
20 await mkdir(d, { recursive: true });
21 return d;
22}
23
24describe("files temp-file lifecycle (TASK-0102 §3/§7)", () => {
25 it("createTempFile writes into the pipeline temp dir", async () => {
26 const dir = await freshDir();
27 const p = await createTempFile({ FILES_TEMP_DIR: dir }, 0);
28 expect(path.dirname(p)).toBe(dir);
29 expect(p).toMatch(/file-/);
30 await removeTempFile(p);
31 });
32
33 it("removeTempFile is idempotent (missing file does not throw)", async () => {
34 await expect(removeTempFile(undefined)).resolves.toBeUndefined();
35 await expect(removeTempFile("/nonexistent/none-1234")).resolves.toBeUndefined();
36 });
37
38 it("cleanup reaps files older than max age, leaves fresh files", async () => {
39 const dir = await freshDir();
40 const oldFile = path.join(dir, "file-old");
41 const freshFile = path.join(dir, "file-fresh");
42 await writeFile(oldFile, "old payload");
43 await writeFile(freshFile, "fresh payload");
44
45 const past = new Date(Date.now() - 11 * 60_000);
46 await import("node:fs/promises").then((m) =>
47 Promise.all([m.utimes(oldFile, past, past), m.utimes(dir, past, past)]).catch(() => {})
48 );
49
50 const res = await cleanupTempFiles({ FILES_TEMP_DIR: dir, FILES_TEMP_MAX_AGE_MS: 10 * 60_000 });
51 expect(res.deleted).toBe(1);
52 expect(res.bytesFreed).toBe("old payload".length);
53
54 const { stat } = await import("node:fs/promises");
55 await expect(stat(freshFile)).resolves.toMatchObject({ size: "fresh payload".length });
56 });
57
58 it("cleanup on a missing dir is a no-op (0 deleted)", async () => {
59 const res = await cleanupTempFiles({
60 FILES_TEMP_DIR: path.join(tmpdir(), "factory-files-test-none-999"),
61 FILES_TEMP_MAX_AGE_MS: 600_000
62 });
63 expect(res).toEqual({ deleted: 0, bytesFreed: 0 });
64 });
65
66 it("tempDir falls back to os.tmpdir()/factory-files when unset", () => {
67 expect(tempDir({ FILES_TEMP_DIR: "" })).toBe(path.join(tmpdir(), "factory-files"));
68 expect(tempDir({ FILES_TEMP_DIR: "/custom" })).toBe("/custom");
69 });
70});
71