diff --git a/src/runtime.mjs b/src/runtime.mjs index 106fc99..903bc73 100644 --- a/src/runtime.mjs +++ b/src/runtime.mjs @@ -121,6 +121,9 @@ export async function runScript(source, opts = {}) { } const out = opts.out || ((s) => console.log(s)); const max = Number.isFinite(opts.max) ? opts.max : DEFAULT_MAX; + if (!Number.isInteger(max) || max < 1) { + throw new Error(`moshscript: max must be a positive integer, got ${JSON.stringify(opts.max)}`); + } const control = makeControl(max, out); const ctx = { diff --git a/test/runtime.test.mjs b/test/runtime.test.mjs index cea89f1..bfadcbc 100644 --- a/test/runtime.test.mjs +++ b/test/runtime.test.mjs @@ -112,6 +112,19 @@ test("DEFAULT_MAX bounds an unbounded while when no max is passed", async () => assert.equal(calls.length, DEFAULT_MAX); }); +test("runScript rejects invalid max values", async () => { + const { registry } = recorder(); + + await assert.rejects( + () => runScript(`while (alive) {}`, { commands: registry, max: 0 }), + /max must be a positive integer/, + ); + await assert.rejects( + () => runScript(`while (alive) {}`, { commands: registry, max: 1.5 }), + /max must be a positive integer/, + ); +}); + test("a rejecting fire-and-forget verb does not kill a script that keeps running", async () => { const calls = []; const registry = createRegistry([