diff --git a/packages/opencode/test/util/context.test.ts b/packages/opencode/test/util/context.test.ts new file mode 100644 index 000000000..bcca717e3 --- /dev/null +++ b/packages/opencode/test/util/context.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, test } from "bun:test" +import { Context } from "../../src/util/context" + +describe("util.context", () => { + test("use returns provided value within scope", () => { + const userContext = Context.create<{ id: number }>("user") + + let seenId: number | undefined + + userContext.provide({ id: 123 }, () => { + seenId = userContext.use().id + }) + + expect(seenId).toBe(123) + }) + + test("use throws NotFound outside of provided scope", () => { + const ctx = Context.create("test-context") + + expect(() => ctx.use()).toThrowError(Context.NotFound) + }) +}) + + diff --git a/packages/opencode/test/util/eventloop.test.ts b/packages/opencode/test/util/eventloop.test.ts new file mode 100644 index 000000000..8bd9ab790 --- /dev/null +++ b/packages/opencode/test/util/eventloop.test.ts @@ -0,0 +1,20 @@ +import { describe, test } from "bun:test" +import { EventLoop } from "../../src/util/eventloop" + +describe("util.eventloop", () => { + test("wait resolves after active handles complete", async () => { + // Create an active timer handle + const timerPromise = new Promise((resolve) => { + setTimeout(resolve, 10) + }) + + // Start waiting for the event loop to become idle + const waitPromise = EventLoop.wait() + + // Let the timer finish, then ensure wait also completes + await timerPromise + await waitPromise + }) +}) + + diff --git a/packages/opencode/test/util/queue.test.ts b/packages/opencode/test/util/queue.test.ts new file mode 100644 index 000000000..85d7e3fa1 --- /dev/null +++ b/packages/opencode/test/util/queue.test.ts @@ -0,0 +1,63 @@ +import { describe, expect, test } from "bun:test" +import { AsyncQueue, work } from "../../src/util/queue" + +describe("util.queue", () => { + test("dequeues items in FIFO order", async () => { + const queue = new AsyncQueue() + queue.push(1) + queue.push(2) + queue.push(3) + + expect(await queue.next()).toBe(1) + expect(await queue.next()).toBe(2) + expect(await queue.next()).toBe(3) + }) + + test("resolves pending next() calls when items are pushed", async () => { + const queue = new AsyncQueue() + + const p1 = queue.next() + const p2 = queue.next() + + queue.push(1) + queue.push(2) + + await expect(p1).resolves.toBe(1) + await expect(p2).resolves.toBe(2) + }) + + test("supports async iteration", async () => { + const queue = new AsyncQueue() + queue.push(1) + queue.push(2) + queue.push(3) + + const result: number[] = [] + for await (const item of queue) { + result.push(item) + if (result.length === 3) break + } + + expect(result).toEqual([1, 2, 3]) + }) + + test("next() resolves when item is pushed after call", async () => { + const queue = new AsyncQueue() + const nextPromise = queue.next() + + setTimeout(() => queue.push(42), 10) + + await expect(nextPromise).resolves.toBe(42) + }) + + test("work() processes all items with concurrency", async () => { + const processed: number[] = [] + + await work(2, [1, 2, 3, 4], async (item) => { + await new Promise((resolve) => setTimeout(resolve, 5)) + processed.push(item) + }) + + expect(processed.sort()).toEqual([1, 2, 3, 4]) + }) +}) \ No newline at end of file