///
// The fake-clock harness (auto-advance mode) sorts every positive-delay
// `setTimeout` into a `src/` timer, which auto-advances so the runtime's own
// throttle/backoff windows elapse instantly, or a `test/` timer, which freezes
// so a stray wall-clock sleep written in test code deadlocks and the async-op
// sanitizer catches it. It decides by reading the scheduling frame's file path.
//
// SES's `errorTaming: "safe"` (the runner's production lockdown) blanks
// `new Error().stack` for the rest of the process from the first lockdown on.
// Reading the frame through `new Error().stack` therefore saw nothing after
// lockdown, and every test-scheduled sleep was misfiled as a `src/` timer and
// auto-advanced — silently defeating the freeze-and-catch guarantee for the
// whole suite. The harness reads the frames through SES's `getStackString`
// hook, which keeps returning them after lockdown (the same hook the runtime's
// own error mapping uses). These tests pin the classification across the
// lockdown boundary and the SES contract it stands on.
import { describe, it } from "@std/testing/bdd";
import { expect } from "@std/expect";
import { sleep } from "@commonfabric/utils/sleep";
import { ensureSESLockdown } from "../src/sandbox/ses-runtime.ts";
// One real macrotask turn. A zero-delay `setTimeout` fires on a real macrotask
// through the harness's kick. After this yield a frozen `test/` timer has not
// fired, and it never will on its own.
const macrotaskTurn = (): Promise =>
new Promise((resolve) => setTimeout(resolve, 0));
describe("fake-clock caller classification survives SES lockdown", () => {
it("SES blanks Error.stack yet exposes the frames through getStackString", () => {
ensureSESLockdown();
// The hazard: the tamed `stack` accessor reads back empty post-lockdown.
expect(new Error().stack ?? "").toBe("");
// The mechanism the harness stands on: the real frames, this file among them.
const getStackString = (globalThis as {
getStackString?: (error: Error) => string;
}).getStackString;
expect(typeof getStackString).toBe("function");
const frames = getStackString!(new Error());
expect(frames.length).toBeGreaterThan(0);
expect(frames).toContain(".test.ts");
});
it("a test-scheduled positive-delay setTimeout freezes after lockdown", async () => {
ensureSESLockdown();
let fired = false;
setTimeout(() => {
fired = true;
}, 500);
await macrotaskTurn();
// Frozen, not auto-advanced: the harness saw this file's `test/` frame.
expect(fired).toBe(false);
// Still a live timer — explicit time advancement fires it.
await clock.tick(500);
expect(fired).toBe(true);
});
it("a src/-scheduled positive-delay setTimeout still auto-advances after lockdown", async () => {
ensureSESLockdown();
// `sleep` arms the timer from `packages/utils/src`, a `src/` frame, so the
// harness auto-advances it — the runtime's own throttle and backoff timers
// are armed the same way and must keep elapsing on their own. The pump
// fires it once the loop is idle, so the wait is on the sleep itself:
// frozen, it would never return, and Deno would report the pending
// promise once the loop emptied.
const started = Date.now();
await sleep(500);
expect(Date.now() - started).toBe(500);
});
});