opencode/packages/core/test/form.test.ts
2026-07-03 03:55:15 -05:00

76 lines
3 KiB
TypeScript

import { describe, expect } from "bun:test"
import { Effect, Exit } from "effect"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { EventV2 } from "@opencode-ai/core/event"
import { Form } from "@opencode-ai/core/form"
import { SessionSchema } from "@opencode-ai/core/session/schema"
import { testEffect } from "./lib/effect"
const forms = AppNodeBuilder.build(LayerNode.group([EventV2.node, Form.node]))
const it = testEffect(forms)
const formID = Form.ID.create("frm_test")
const input = {
id: formID,
sessionID: SessionSchema.ID.make("ses_test"),
mode: "form",
fields: [{ key: "name", type: "string", required: true }],
} satisfies Form.CreateInput
describe("Form", () => {
it.effect("supports the temporary global mcp elicitation owner", () =>
Effect.gen(function* () {
const service = yield* Form.Service
const created = yield* service.create({
sessionID: "global",
mode: "form",
fields: [{ key: "name", type: "string", required: true }],
})
expect(created.sessionID).toBe("global")
const owned = yield* service.list({ sessionID: "global" })
expect(owned.map((form) => form.id)).toEqual([created.id])
expect(yield* service.list({ sessionID: "other" })).toEqual([])
yield* service.reply({ id: created.id, answer: { name: "Ava" } })
expect(yield* service.state(created.id)).toEqual({ status: "answered", answer: { name: "Ava" } })
}),
)
it.effect("cleans up created forms when event publication fails", () =>
Effect.gen(function* () {
const service = yield* Form.Service
const events = yield* EventV2.Service
const unsubscribe = yield* events.listen((event) =>
event.type === Form.Event.Created.type ? Effect.die("create listener failed") : Effect.void,
)
yield* Effect.addFinalizer(() => unsubscribe)
expect(Exit.isFailure(yield* Effect.exit(service.create(input)))).toBe(true)
expect(yield* service.get(formID).pipe(Effect.flip)).toEqual(new Form.NotFoundError({ id: formID }))
yield* unsubscribe
expect(yield* service.create(input)).toMatchObject({ id: formID })
}),
)
it.effect("keeps forms pending when reply event publication fails", () =>
Effect.gen(function* () {
const service = yield* Form.Service
const events = yield* EventV2.Service
yield* service.create(input)
const unsubscribe = yield* events.listen((event) =>
event.type === Form.Event.Replied.type ? Effect.die("reply listener failed") : Effect.void,
)
yield* Effect.addFinalizer(() => unsubscribe)
expect(Exit.isFailure(yield* Effect.exit(service.reply({ id: formID, answer: { name: "Ava" } })))).toBe(true)
expect(yield* service.state(formID)).toEqual({ status: "pending" })
yield* unsubscribe
yield* service.reply({ id: formID, answer: { name: "Ava" } })
expect(yield* service.state(formID)).toEqual({ status: "answered", answer: { name: "Ava" } })
}),
)
})