"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var import_node_child_process = require("node:child_process");
var import_node_fs = require("node:fs");
var import_node_os = require("node:os");
var import_node_path = require("node:path");
var import_pidusage = __toESM(require("pidusage"), 1);
var import_vitest = require("vitest");
const childScript = (0, import_node_path.join)((0, import_node_os.tmpdir)(), "test_child.mjs");
(0, import_vitest.beforeAll)(() => {
  (0, import_node_fs.writeFileSync)(
    childScript,
    `process.on('message', (msg) => process.send?.({ echo: msg }));
     setInterval(() => {}, 1000);`
  );
});
(0, import_vitest.afterAll)(() => {
  try {
    (0, import_node_fs.unlinkSync)(childScript);
  } catch {
  }
});
async function getChildMemoryUsageMB(pid) {
  if (!pid) return 0;
  try {
    const stats = await (0, import_pidusage.default)(pid);
    return stats.memory / (1024 * 1024);
  } catch (err) {
    const code = err.code;
    if (code === "ENOENT" || code === "ESRCH") {
      return 0;
    }
    throw err;
  }
}
(0, import_vitest.describe)("pidusage on dead process", () => {
  (0, import_vitest.it)("raw pidusage throws on dead pid", async () => {
    const child = (0, import_node_child_process.spawn)("sleep", ["10"]);
    const pid = child.pid;
    child.kill("SIGKILL");
    await new Promise((r) => child.on("exit", r));
    await (0, import_vitest.expect)((0, import_pidusage.default)(pid)).rejects.toThrow();
  });
  (0, import_vitest.it)("fixed version returns 0 instead of crashing", async () => {
    const child = (0, import_node_child_process.spawn)("sleep", ["10"]);
    const pid = child.pid;
    child.kill("SIGKILL");
    await new Promise((r) => child.on("exit", r));
    const mem = await getChildMemoryUsageMB(pid);
    (0, import_vitest.expect)(mem).toBe(0);
  });
  (0, import_vitest.it)("handles concurrent calls on dying process", async () => {
    const child = (0, import_node_child_process.spawn)("sleep", ["10"]);
    const pid = child.pid;
    const exitPromise = new Promise((r) => child.on("exit", r));
    child.kill("SIGKILL");
    await exitPromise;
    const results = await Promise.all([
      getChildMemoryUsageMB(pid),
      getChildMemoryUsageMB(pid),
      getChildMemoryUsageMB(pid)
    ]);
    (0, import_vitest.expect)(results.every((r) => r === 0)).toBe(true);
  });
});
(0, import_vitest.describe)("IPC send on dead process", () => {
  (0, import_vitest.it)("child.connected becomes false when child dies", async () => {
    const child = (0, import_node_child_process.fork)(childScript, [], { stdio: ["pipe", "pipe", "pipe", "ipc"] });
    const exitPromise = new Promise((r) => child.on("exit", r));
    await new Promise((r) => setTimeout(r, 50));
    (0, import_vitest.expect)(child.connected).toBe(true);
    child.kill("SIGKILL");
    await exitPromise;
    (0, import_vitest.expect)(child.connected).toBe(false);
  });
  (0, import_vitest.it)("checking connected before send prevents crash", async () => {
    const child = (0, import_node_child_process.fork)(childScript, [], { stdio: ["pipe", "pipe", "pipe", "ipc"] });
    const exitPromise = new Promise((r) => child.on("exit", r));
    child.on("error", (err) => {
      if (err.code !== "EPIPE") throw err;
    });
    let sent = 0;
    let skipped = 0;
    const interval = setInterval(() => {
      if (child.connected) {
        child.send({ ping: Date.now() });
        sent++;
      } else {
        skipped++;
      }
    }, 20);
    await new Promise((r) => setTimeout(r, 60));
    child.kill("SIGKILL");
    await exitPromise;
    await new Promise((r) => setTimeout(r, 80));
    clearInterval(interval);
    (0, import_vitest.expect)(sent).toBeGreaterThan(0);
    (0, import_vitest.expect)(skipped).toBeGreaterThan(0);
  });
});
(0, import_vitest.describe)("init timeout rejection handling", () => {
  (0, import_vitest.it)("does not produce unhandled rejection when init times out", async () => {
    var _a;
    const unhandled = [];
    const handler = (reason) => unhandled.push(reason);
    process.on("unhandledRejection", handler);
    const slowScript = (0, import_node_path.join)((0, import_node_os.tmpdir)(), "test_slow_init_child.mjs");
    (0, import_node_fs.writeFileSync)(
      slowScript,
      `process.on('message', () => {
        setTimeout(() => process.send({ case: 'initializeResponse' }), 200);
      });
      setInterval(() => {}, 1000);`
    );
    const { SupervisedProc } = await import("./supervised_proc.cjs");
    class TestProc extends SupervisedProc {
      get processKind() {
        return "job";
      }
      createProcess() {
        return (0, import_node_child_process.fork)(slowScript, [], { stdio: ["pipe", "pipe", "pipe", "ipc"] });
      }
      async mainTask() {
      }
    }
    const proc = new TestProc(
      50,
      // initializeTimeout — fires before child responds at 200ms
      1e3,
      // closeTimeout
      0,
      // memoryWarnMB
      0,
      // memoryLimitMB
      5e3,
      // pingInterval
      6e4,
      // pingTimeout
      2500
      // highPingThreshold
    );
    await proc.start();
    await (0, import_vitest.expect)(proc.initialize()).rejects.toThrow("runner initialization timed out");
    await new Promise((r) => setTimeout(r, 200));
    process.off("unhandledRejection", handler);
    (_a = proc.proc) == null ? void 0 : _a.kill();
    try {
      (0, import_node_fs.unlinkSync)(slowScript);
    } catch {
    }
    (0, import_vitest.expect)(unhandled).toEqual([]);
  });
  (0, import_vitest.it)("join() resolves after init timeout instead of hanging forever", async () => {
    var _a;
    const slowScript = (0, import_node_path.join)((0, import_node_os.tmpdir)(), "test_slow_init_child_join.mjs");
    (0, import_node_fs.writeFileSync)(
      slowScript,
      `process.on('message', () => {
        setTimeout(() => process.send({ case: 'initializeResponse' }), 200);
      });
      setInterval(() => {}, 1000);`
    );
    const { SupervisedProc } = await import("./supervised_proc.cjs");
    class TestProc extends SupervisedProc {
      get processKind() {
        return "job";
      }
      createProcess() {
        return (0, import_node_child_process.fork)(slowScript, [], { stdio: ["pipe", "pipe", "pipe", "ipc"] });
      }
      async mainTask() {
      }
    }
    const proc = new TestProc(50, 1e3, 0, 0, 5e3, 6e4, 2500);
    await proc.start();
    await proc.initialize().catch(() => {
    });
    const result = await Promise.race([
      proc.join().then(() => "resolved"),
      new Promise((r) => setTimeout(() => r("timeout"), 2e3))
    ]);
    (_a = proc.proc) == null ? void 0 : _a.kill();
    try {
      (0, import_node_fs.unlinkSync)(slowScript);
    } catch {
    }
    (0, import_vitest.expect)(result).toBe("resolved");
  });
  (0, import_vitest.it)("kills the child when initialize times out", async () => {
    var _a;
    const hangScript = (0, import_node_path.join)((0, import_node_os.tmpdir)(), "test_hang_init_child.mjs");
    (0, import_node_fs.writeFileSync)(hangScript, `setInterval(() => {}, 1000);`);
    const { SupervisedProc } = await import("./supervised_proc.cjs");
    class TestProc extends SupervisedProc {
      createProcess() {
        return (0, import_node_child_process.fork)(hangScript, [], { stdio: ["pipe", "pipe", "pipe", "ipc"] });
      }
      async mainTask() {
      }
    }
    const proc = new TestProc(50, 1e3, 0, 0, 5e3, 6e4, 2500);
    await proc.start();
    const exited = new Promise((r) => proc.proc.on("exit", () => r()));
    await (0, import_vitest.expect)(proc.initialize()).rejects.toThrow("runner initialization timed out");
    const result = await Promise.race([
      exited.then(() => "exited"),
      new Promise((r) => setTimeout(() => r("timeout"), 2e3))
    ]);
    (_a = proc.proc) == null ? void 0 : _a.kill();
    try {
      (0, import_node_fs.unlinkSync)(hangScript);
    } catch {
    }
    (0, import_vitest.expect)(result).toBe("exited");
  });
});
(0, import_vitest.describe)("timer cleanup", () => {
  (0, import_vitest.it)("clearInterval stops the interval", async () => {
    let count = 0;
    const interval = setInterval(() => count++, 30);
    await new Promise((r) => setTimeout(r, 80));
    const countAtClear = count;
    clearInterval(interval);
    await new Promise((r) => setTimeout(r, 80));
    (0, import_vitest.expect)(count).toBe(countAtClear);
  });
  (0, import_vitest.it)("double clear is safe", () => {
    const interval = setInterval(() => {
    }, 100);
    const timeout = setTimeout(() => {
    }, 1e3);
    clearInterval(interval);
    clearTimeout(timeout);
    (0, import_vitest.expect)(() => {
      clearInterval(interval);
      clearTimeout(timeout);
    }).not.toThrow();
  });
});
(0, import_vitest.describe)("memory warning bookkeeping", () => {
  const MEMORY_WARN_COOLDOWN = 12e4;
  const MEMORY_WARN_RESET_DELTA_MB = 50;
  const makeProc = async (memoryWarnMB = 500, memoryLimitMB = 0) => {
    const { SupervisedProc } = await import("./supervised_proc.cjs");
    class FakeProc extends SupervisedProc {
      get processKind() {
        return "job";
      }
      createProcess() {
        throw new Error("not implemented");
      }
      async mainTask() {
      }
    }
    return new FakeProc(1e4, 1e4, memoryWarnMB, memoryLimitMB, 2500, 6e4, 500);
  };
  (0, import_vitest.it)("rate-limits the warning", async () => {
    const proc = await makeProc();
    (0, import_vitest.expect)(proc.shouldEmitMemoryWarning(520, 1e3)).toBe(true);
    (0, import_vitest.expect)(proc.shouldEmitMemoryWarning(521, 1005)).toBe(false);
    (0, import_vitest.expect)(proc.shouldEmitMemoryWarning(522, 1010)).toBe(false);
    (0, import_vitest.expect)(proc.shouldEmitMemoryWarning(522, 1e3 + MEMORY_WARN_COOLDOWN + 1)).toBe(true);
  });
  (0, import_vitest.it)("re-emits on significant growth within the cooldown", async () => {
    const proc = await makeProc();
    (0, import_vitest.expect)(proc.shouldEmitMemoryWarning(520, 1e3)).toBe(true);
    const grown = 520 + MEMORY_WARN_RESET_DELTA_MB + 1;
    (0, import_vitest.expect)(proc.shouldEmitMemoryWarning(grown, 1005)).toBe(true);
  });
  (0, import_vitest.it)("reports baseline and growth in the logging fields", async () => {
    const proc = await makeProc();
    let fields = proc.memoryLoggingFields(520);
    (0, import_vitest.expect)(fields.memoryUsageMB).toBe(520);
    (0, import_vitest.expect)(fields.memoryWarnMB).toBe(500);
    (0, import_vitest.expect)(fields.hasRunningJob).toBe(false);
    (0, import_vitest.expect)("uptime" in fields).toBe(true);
    (0, import_vitest.expect)("baselineMemoryMB" in fields).toBe(false);
    proc.memoryBaselineMB = 300;
    fields = proc.memoryLoggingFields(520);
    (0, import_vitest.expect)(fields.baselineMemoryMB).toBe(300);
    (0, import_vitest.expect)(fields.growthMemoryMB).toBe(220);
  });
  (0, import_vitest.it)("rounds reported memory to one decimal", async () => {
    const proc = await makeProc();
    (0, import_vitest.expect)(proc.memoryLoggingFields(520.567).memoryUsageMB).toBe(520.6);
  });
  (0, import_vitest.it)("uptime is 0 before start", async () => {
    const proc = await makeProc();
    (0, import_vitest.expect)(proc.uptime).toBe(0);
  });
  (0, import_vitest.it)("processKind renders into the message string", async () => {
    const proc = await makeProc();
    (0, import_vitest.expect)(`${proc.processKind} process`).toBe("job process");
  });
});
//# sourceMappingURL=supervised_proc.test.cjs.map