Download packages/codemode/test/promise.test.ts from SaylorTwift/opencode: direct link, hf CLI and curl.
- Browser
- Download file 15.9 kB
-
https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/codemode/test/promise.test.ts
- Command line
-
hf download hf://SaylorTwift/opencode/packages/codemode/test/promise.test.ts
-
curl -L -o promise.test.ts https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/codemode/test/promise.test.ts
15.9 kB
| import { describe, expect, test } from "bun:test" | |
| import { Effect, Schema } from "effect" | |
| import { CodeMode, Tool, toolError } from "../src/index.js" | |
| // Wave 5 acceptance suite: first-class promise values. Un-awaited tool calls start eagerly on | |
| // supervised fibers, `await` settles them, and Promise.all/allSettled/race/resolve/reject are | |
| // ordinary functions over arbitrary arrays mixing promises and plain values. | |
| type Trace = { | |
| starts: Array<number> | |
| active: number | |
| maxActive: number | |
| completed: number | |
| interrupted: number | |
| } | |
| const makeTrace = (): Trace => ({ starts: [], active: 0, maxActive: 0, completed: 0, interrupted: 0 }) | |
| /** Echoes `id` after `ms` milliseconds, recording start order, live concurrency, and interruption. */ | |
| const sleepyTool = (trace: Trace) => | |
| Tool.make({ | |
| description: "Echo an id after a delay", | |
| input: Schema.Struct({ id: Schema.Number, ms: Schema.optionalKey(Schema.Number) }), | |
| output: Schema.Number, | |
| run: ({ id, ms }) => | |
| Effect.gen(function* () { | |
| trace.starts.push(id) | |
| trace.active += 1 | |
| trace.maxActive = Math.max(trace.maxActive, trace.active) | |
| yield* Effect.sleep(ms ?? 20) | |
| trace.active -= 1 | |
| trace.completed += 1 | |
| return id | |
| }).pipe( | |
| Effect.onInterrupt(() => | |
| Effect.sync(() => { | |
| trace.active -= 1 | |
| trace.interrupted += 1 | |
| }), | |
| ), | |
| ), | |
| }) | |
| const failingTool = Tool.make({ | |
| description: "Always refuse", | |
| input: Schema.Struct({}), | |
| output: Schema.String, | |
| run: () => Effect.fail(toolError("Lookup refused")), | |
| }) | |
| const run = ( | |
| code: string, | |
| options: { trace?: Trace; limits?: CodeMode.ExecutionLimits } = {}, | |
| ): Promise<CodeMode.Result> => { | |
| const trace = options.trace ?? makeTrace() | |
| return Effect.runPromise( | |
| CodeMode.execute({ | |
| tools: { host: { sleepy: sleepyTool(trace), fail: failingTool } }, | |
| code, | |
| ...(options.limits ? { limits: options.limits } : {}), | |
| }), | |
| ) | |
| } | |
| const value = async (code: string, options: { trace?: Trace; limits?: CodeMode.ExecutionLimits } = {}) => { | |
| const result = await run(code, options) | |
| if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`) | |
| return result.value | |
| } | |
| const error = async (code: string, options: { trace?: Trace; limits?: CodeMode.ExecutionLimits } = {}) => { | |
| const result = await run(code, options) | |
| if (result.ok) throw new Error(`expected failure, got value ${JSON.stringify(result.value)}`) | |
| return result.error | |
| } | |
| describe("first-class promise values", () => { | |
| test("an un-awaited tool call starts eagerly, in call order, before any await", async () => { | |
| const trace = makeTrace() | |
| const result = await value( | |
| ` | |
| const a = tools.host.sleepy({ id: 1, ms: 40 }) | |
| const b = tools.host.sleepy({ id: 2, ms: 40 }) | |
| const rb = await b | |
| const ra = await a | |
| return [ra, rb] | |
| `, | |
| { trace }, | |
| ) | |
| expect(result).toEqual([1, 2]) | |
| expect(trace.starts).toEqual([1, 2]) | |
| // Both calls overlapped even though they were awaited sequentially. | |
| expect(trace.maxActive).toBeGreaterThan(1) | |
| }) | |
| test("awaiting the same promise twice settles once and never re-runs the call", async () => { | |
| const result = await run(` | |
| const p = tools.host.sleepy({ id: 7 }) | |
| const x = await p | |
| const y = await p | |
| return [x, y] | |
| `) | |
| expect(result.ok).toBe(true) | |
| if (!result.ok) return | |
| expect(result.value).toEqual([7, 7]) | |
| expect(result.toolCalls).toStrictEqual([{ name: "host.sleepy" }]) | |
| }) | |
| test("await of a non-promise value is a passthrough no-op", async () => { | |
| expect(await value(`return await 42`)).toBe(42) | |
| expect(await value(`const x = await "s"; return x`)).toBe("s") | |
| expect(await value(`return await null`)).toBeNull() | |
| expect(await value(`return (await [1, 2]).length`)).toBe(2) | |
| }) | |
| test("returning an un-awaited tool call resolves it (async-function return semantics)", async () => { | |
| expect(await value(`return tools.host.sleepy({ id: 9 })`)).toBe(9) | |
| }) | |
| test("typeof a promise is 'object', and console.log renders it sensibly", async () => { | |
| const result = await run(` | |
| const p = Promise.resolve(1) | |
| console.log(p) | |
| return typeof p | |
| `) | |
| expect(result.ok).toBe(true) | |
| if (!result.ok) return | |
| expect(result.value).toBe("object") | |
| expect(result.logs).toStrictEqual(["[Promise (await it to get its value)]"]) | |
| }) | |
| test("an awaited failure is catchable exactly like a synchronous throw", async () => { | |
| expect( | |
| await value(` | |
| const p = tools.host.fail({}) | |
| try { | |
| await p | |
| return "no" | |
| } catch (e) { | |
| return e.message | |
| } | |
| `), | |
| ).toBe("Lookup refused") | |
| }) | |
| test("a fire-and-forget call completes before the execution ends", async () => { | |
| const trace = makeTrace() | |
| const result = await value( | |
| ` | |
| tools.host.sleepy({ id: 1, ms: 30 }) | |
| return "done" | |
| `, | |
| { trace }, | |
| ) | |
| expect(result).toBe("done") | |
| expect(trace.completed).toBe(1) | |
| expect(trace.interrupted).toBe(0) | |
| }) | |
| test("a never-awaited failing call surfaces as an unhandled-rejection diagnostic", async () => { | |
| const diagnostic = await error(` | |
| tools.host.fail({}) | |
| return "done" | |
| `) | |
| expect(diagnostic.kind).toBe("ToolFailure") | |
| expect(diagnostic.message).toContain("Unhandled rejection from an un-awaited tool call") | |
| expect(diagnostic.message).toContain("Lookup refused") | |
| expect(diagnostic.suggestions?.join(" ")).toContain("await tools.ns.tool(...)") | |
| }) | |
| }) | |
| describe("promises at data boundaries", () => { | |
| test("returning an un-awaited promise inside data is a clear await-hinting diagnostic", async () => { | |
| const diagnostic = await error(`return { result: tools.host.sleepy({ id: 1 }) }`) | |
| expect(diagnostic.kind).toBe("InvalidDataValue") | |
| expect(diagnostic.message).toContain("un-awaited Promise") | |
| expect(diagnostic.message).toContain("await tools.ns.tool(...)") | |
| }) | |
| test("passing an un-awaited promise as a tool argument is a clear diagnostic", async () => { | |
| const diagnostic = await error(`return await tools.host.sleepy({ id: tools.host.sleepy({ id: 1 }) })`) | |
| expect(diagnostic.kind).toBe("InvalidDataValue") | |
| expect(diagnostic.message).toContain("un-awaited Promise") | |
| }) | |
| test("JSON.stringify of a promise is a diagnostic, not '{}'", async () => { | |
| const diagnostic = await error(`return JSON.stringify(Promise.resolve(1))`) | |
| expect(diagnostic.kind).toBe("InvalidDataValue") | |
| expect(diagnostic.message).toContain("un-awaited Promise") | |
| }) | |
| test("operators reject promise operands", async () => { | |
| const diagnostic = await error(`return Promise.resolve(1) + 1`) | |
| expect(diagnostic.kind).toBe("InvalidDataValue") | |
| }) | |
| }) | |
| describe("Promise.all over arbitrary arrays", () => { | |
| test("mixes promises and plain values, preserving order", async () => { | |
| expect( | |
| await value(` | |
| return await Promise.all([tools.host.sleepy({ id: 1 }), "plain", tools.host.sleepy({ id: 2 }), 42]) | |
| `), | |
| ).toEqual([1, "plain", 2, 42]) | |
| }) | |
| test("accepts arrays built beforehand, passed as identifiers, and spread elements", async () => { | |
| expect( | |
| await value(` | |
| const calls = [] | |
| calls.push(tools.host.sleepy({ id: 1 })) | |
| calls.push(7) | |
| const more = [tools.host.sleepy({ id: 2 })] | |
| const batch = [...calls, ...more, "x"] | |
| return await Promise.all(batch) | |
| `), | |
| ).toEqual([1, 7, 2, "x"]) | |
| }) | |
| test("runs items.map tool calls in parallel", async () => { | |
| const trace = makeTrace() | |
| const result = await value( | |
| ` | |
| const ids = [1, 2, 3, 4] | |
| return await Promise.all(ids.map((id) => tools.host.sleepy({ id, ms: 40 }))) | |
| `, | |
| { trace }, | |
| ) | |
| expect(result).toEqual([1, 2, 3, 4]) | |
| // maxActive counts truly-overlapping live executions, so > 1 proves real | |
| // parallelism deterministically - no wall-clock assertion needed. | |
| expect(trace.maxActive).toBeGreaterThan(1) | |
| }) | |
| test("caps live tool-call concurrency at the fixed internal constant (8)", async () => { | |
| const trace = makeTrace() | |
| const result = await value( | |
| ` | |
| const ids = [] | |
| for (let i = 0; i < 20; i += 1) ids.push(i) | |
| const results = await Promise.all(ids.map((id) => tools.host.sleepy({ id, ms: 10 }))) | |
| return results.length | |
| `, | |
| { trace }, | |
| ) | |
| expect(result).toBe(20) | |
| expect(trace.maxActive).toBeGreaterThan(1) | |
| expect(trace.maxActive).toBeLessThanOrEqual(8) | |
| }) | |
| test("resolves the empty array", async () => { | |
| expect(await value(`return await Promise.all([])`)).toEqual([]) | |
| }) | |
| test("rejects with the first failure, catchable in-program", async () => { | |
| expect( | |
| await value(` | |
| try { | |
| await Promise.all([tools.host.sleepy({ id: 1 }), tools.host.fail({})]) | |
| return "no" | |
| } catch (e) { | |
| return e.message | |
| } | |
| `), | |
| ).toBe("Lookup refused") | |
| }) | |
| test("a non-collection argument is a clear error", async () => { | |
| const diagnostic = await error(`return await Promise.all(42)`) | |
| expect(diagnostic.message).toContain("Promise.all expects an array") | |
| }) | |
| test("exceeding maxToolCalls inside Promise.all is a ToolCallLimitExceeded diagnostic", async () => { | |
| const diagnostic = await error( | |
| `return await Promise.all([tools.host.sleepy({ id: 1 }), tools.host.sleepy({ id: 2 }), tools.host.sleepy({ id: 3 })])`, | |
| { limits: { maxToolCalls: 2 } }, | |
| ) | |
| expect(diagnostic.kind).toBe("ToolCallLimitExceeded") | |
| }) | |
| }) | |
| describe("Promise.allSettled", () => { | |
| test("reports fulfilled and rejected outcomes with catch-normalized reasons", async () => { | |
| expect( | |
| await value(` | |
| return await Promise.allSettled([ | |
| tools.host.sleepy({ id: 5 }), | |
| tools.host.fail({}), | |
| "plain", | |
| Promise.reject(new Error("boom")), | |
| ]) | |
| `), | |
| ).toEqual([ | |
| { status: "fulfilled", value: 5 }, | |
| { status: "rejected", reason: { name: "Error", message: "Lookup refused" } }, | |
| { status: "fulfilled", value: "plain" }, | |
| { status: "rejected", reason: { name: "Error", message: "boom" } }, | |
| ]) | |
| }) | |
| test("never rejects for program-level failures", async () => { | |
| const result = await run(` | |
| const settled = await Promise.allSettled([tools.host.fail({}), tools.host.fail({})]) | |
| return settled.filter((s) => s.status === "rejected").length | |
| `) | |
| expect(result.ok).toBe(true) | |
| if (result.ok) expect(result.value).toBe(2) | |
| }) | |
| }) | |
| describe("Promise.race", () => { | |
| test("first settlement wins and losers are interrupted", async () => { | |
| const trace = makeTrace() | |
| const result = await value( | |
| ` | |
| const fast = tools.host.sleepy({ id: 1, ms: 10 }) | |
| const slow = tools.host.sleepy({ id: 2, ms: 5000 }) | |
| return await Promise.race([fast, slow]) | |
| `, | |
| { trace }, | |
| ) | |
| expect(result).toBe(1) | |
| expect(trace.interrupted).toBe(1) | |
| expect(trace.completed).toBe(1) | |
| }) | |
| test("awaiting an interrupted loser afterwards is a catchable program failure", async () => { | |
| expect( | |
| await value(` | |
| const fast = tools.host.sleepy({ id: 1, ms: 10 }) | |
| const slow = tools.host.sleepy({ id: 2, ms: 5000 }) | |
| const winner = await Promise.race([fast, slow]) | |
| try { | |
| await slow | |
| return "no" | |
| } catch (e) { | |
| return { winner, caught: e.message } | |
| } | |
| `), | |
| ).toEqual({ | |
| winner: 1, | |
| caught: "This tool call was interrupted because another value settled a Promise.race first.", | |
| }) | |
| }) | |
| test("a rejection can win the race", async () => { | |
| expect( | |
| await value(` | |
| try { | |
| await Promise.race([tools.host.fail({}), tools.host.sleepy({ id: 1, ms: 5000 })]) | |
| return "no" | |
| } catch (e) { | |
| return e.message | |
| } | |
| `), | |
| ).toBe("Lookup refused") | |
| }) | |
| test("a plain value wins over pending promises", async () => { | |
| const trace = makeTrace() | |
| expect( | |
| await value(`return await Promise.race([tools.host.sleepy({ id: 1, ms: 5000 }), "immediate"])`, { trace }), | |
| ).toBe("immediate") | |
| expect(trace.interrupted).toBe(1) | |
| }) | |
| test("an empty race is a clear error instead of hanging", async () => { | |
| const diagnostic = await error(`return await Promise.race([])`) | |
| expect(diagnostic.message).toContain("never settle") | |
| }) | |
| }) | |
| describe("Promise.resolve / Promise.reject", () => { | |
| test("resolve wraps plain values and passes promises through", async () => { | |
| expect(await value(`return await Promise.resolve(42)`)).toBe(42) | |
| expect(await value(`return await Promise.resolve(Promise.resolve("nested"))`)).toBe("nested") | |
| expect(await value(`return await Promise.resolve(tools.host.sleepy({ id: 3 }))`)).toBe(3) | |
| }) | |
| test("reject produces a promise whose await throws the reason", async () => { | |
| expect( | |
| await value(` | |
| try { | |
| await Promise.reject("nope") | |
| return "no" | |
| } catch (e) { | |
| return e | |
| } | |
| `), | |
| ).toBe("nope") | |
| }) | |
| }) | |
| describe("timeout interruption of forked calls", () => { | |
| test("the execution timeout interrupts in-flight forked fibers", async () => { | |
| const trace = makeTrace() | |
| const result = await run( | |
| ` | |
| const a = tools.host.sleepy({ id: 1, ms: 60000 }) | |
| const b = tools.host.sleepy({ id: 2, ms: 60000 }) | |
| return await a | |
| `, | |
| { trace, limits: { timeoutMs: 100 } }, | |
| ) | |
| expect(result.ok).toBe(false) | |
| if (result.ok) return | |
| expect(result.error.kind).toBe("TimeoutExceeded") | |
| // Both calls started; neither escaped the timeout - the awaited one AND the abandoned one. | |
| expect(trace.starts).toEqual([1, 2]) | |
| expect(trace.interrupted).toBe(2) | |
| expect(trace.completed).toBe(0) | |
| }) | |
| test("the timeout also interrupts calls inside Promise.all", async () => { | |
| const trace = makeTrace() | |
| const result = await run( | |
| `return await Promise.all([tools.host.sleepy({ id: 1, ms: 60000 }), tools.host.sleepy({ id: 2, ms: 60000 })])`, | |
| { trace, limits: { timeoutMs: 100 } }, | |
| ) | |
| expect(result.ok).toBe(false) | |
| if (result.ok) return | |
| expect(result.error.kind).toBe("TimeoutExceeded") | |
| expect(trace.interrupted).toBe(2) | |
| }) | |
| }) | |
| describe("unsupported promise surface", () => { | |
| test(".then/.catch/.finally give a clear await-instead error", async () => { | |
| for (const method of ["then", "catch", "finally"]) { | |
| const diagnostic = await error(`return tools.host.sleepy({ id: 1 }).${method}((x) => x)`) | |
| expect(diagnostic.kind).toBe("UnsupportedSyntax") | |
| expect(diagnostic.message).toContain(`Promise.prototype.${method} is not supported`) | |
| expect(diagnostic.message).toContain("await") | |
| } | |
| }) | |
| test("other property reads on a promise hint at the missing await", async () => { | |
| const diagnostic = await error(`return tools.host.sleepy({ id: 1 }).value`) | |
| expect(diagnostic.kind).toBe("InvalidDataValue") | |
| expect(diagnostic.message).toContain("un-awaited Promise") | |
| expect(diagnostic.message).toContain("await it first") | |
| }) | |
| test("unknown Promise statics list what is available", async () => { | |
| const diagnostic = await error(`return await Promise.any([tools.host.sleepy({ id: 1 })])`) | |
| expect(diagnostic.message).toContain("Promise.any is not available") | |
| expect(diagnostic.message).toContain("Promise.allSettled") | |
| }) | |
| test("new Promise(...) points at tool calls instead", async () => { | |
| const diagnostic = await error(`return new Promise((resolve) => resolve(1))`) | |
| expect(diagnostic.kind).toBe("UnsupportedSyntax") | |
| expect(diagnostic.message).toContain("new Promise(...) is not supported") | |
| expect(diagnostic.message).toContain("already return promises") | |
| }) | |
| }) | |