import assert from "node:assert/strict"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import test from "node:test"; import { fileURLToPath } from "node:url"; import { cleanupServerJobDirectory, createServerJobDirectory } from "../../../tools/web/server-job-isolation.mjs"; import { cancelServerJobProcess, startServerJobProcess } from "../../../tools/web/server-job-process.mjs"; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); test("M13-04G cancels the real process group and cleans the job directory", async () => { const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "m13-04g-process-")); const job = await createServerJobDirectory(temporary, "server:cancel"); const childScript = "const {spawn}=require('node:child_process'); const c=spawn(process.execPath,['-e','setInterval(()=>{},1000)'],{stdio:'ignore'}); setInterval(()=>{},1000);"; const handle = startServerJobProcess(process.execPath, ["-e", childScript], { cwd: root }); let cleanupCount = 0; try { const receipt = await cancelServerJobProcess(handle, async () => { cleanupCount += 1; await cleanupServerJobDirectory(job); }); assert.equal(receipt.state, "CANCELLED"); assert.equal(receipt.cleanupCount, 1); assert.equal(cleanupCount, 1); assert.match(receipt.treeSignal, /GROUP|ALREADY_EXITED/); await assert.rejects(fs.stat(job.path), { code: "ENOENT" }); await assert.doesNotReject(handle.completion); } finally { await fs.rm(temporary, { recursive: true, force: true }); } }); test("M13-04G repeated cancellation is idempotent", async () => { const temporary = await fs.mkdtemp(path.join(os.tmpdir(), "m13-04g-idempotent-")); const job = await createServerJobDirectory(temporary, "server:repeat"); const handle = startServerJobProcess(process.execPath, ["-e", "setInterval(()=>{},1000)"], { cwd: root }); let cleanupCount = 0; const cleanup = async () => { cleanupCount += 1; await cleanupServerJobDirectory(job); }; try { const first = await cancelServerJobProcess(handle, cleanup); const second = await cancelServerJobProcess(handle, cleanup); assert.equal(first.state, "CANCELLED"); assert.equal(second.state, "CANCELLED"); assert.equal(first.cleanupCount, 1); assert.equal(second.cleanupCount, 1); assert.equal(cleanupCount, 1); } finally { await fs.rm(temporary, { recursive: true, force: true }); } });