import { expect, test } from "bun:test"; import * as Question from "@chopin/question"; import * as Questions from "./questions/service"; import * as Store from "./questions/store"; import * as room from "./plan/room"; import type { Server } from "bun"; import type { Plan } from "./plan/service"; import type { Socket, SocketData } from "./wire"; import { createQuestionServiceFixture } from "./question-service.test-fixtures"; let plans: Plan[]; let fixture = createQuestionServiceFixture(() => plans, value => { plans = value; }); plans = fixture.plans; let { opened, definition, asking, answer } = fixture; test("a batched ask creates independently addressed decision records and nodes", async () => { let plan = await opened(); let definition = Questions.identify({ questions: [ { header: "Storage", question: "Where should room state live?", multiple: false, options: [{ label: "MDX on disk", description: "Readable." }], }, { header: "Scope", question: "What belongs in the first cut?", multiple: true, options: [{ label: "Anchors", description: "Link prose." }], }, ], }); let server = { publish() {} } as unknown as Server; let asked = asking(plan, server, definition); await asked.created; let records = [...plan.records.values()]; expect(records).toHaveLength(2); expect(records.map(record => record.definition.questions)).toEqual([ [definition.questions[0]], [definition.questions[1]], ]); expect(room.project(plan.document).match(/ { let plan = await opened("Related prose.\n"); let questions = definition(); let digest = room.digests(plan.document)[0]!; let server = { publish() {} } as unknown as Server; let asked = asking(plan, server, questions, { revision: plan.revision, blocks: [[{ index: 0, digest }]], }); await asked.created; let anchors = Questions.anchors(plan)[0]!.questions[questions.questions[0]!.id]!; let source = room.project(plan.document); expect(source.indexOf("Related prose.")).toBeLessThan(source.indexOf(" { let plan = await opened("Related prose.\n"); let questions = definition(2); let digest = room.digests(plan.document)[0]!; let server = { publish() {} } as unknown as Server; let asked = asking(plan, server, questions, { revision: plan.revision, blocks: [[{ index: 0, digest }], [{ index: 0, digest }]], }); await asked.created; let source = room.project(plan.document); expect(source.indexOf("Related prose.")).toBeLessThan(source.indexOf("Decision 1")); expect(source.indexOf("Decision 1")).toBeLessThan(source.indexOf("Decision 2")); await answer(plan); await asked.waiting; }); test("a stale placement does not create records or questionnaire nodes", async () => { let plan = await opened("Related prose.\n"); let questions = definition(); let digest = room.digests(plan.document)[0]!; let server = { publish() {} } as unknown as Server; let waiting = Questions.ask(plan, server, "test", questions, { revision: plan.revision + 1, blocks: [[{ index: 0, digest }]], }); await expect(waiting).rejects.toThrow(/changed.*read/i); expect(plan.records.size).toBe(0); expect(room.project(plan.document)).not.toContain(" { let plan = await opened("Related prose.\n"); let questions = definition(); let server = { publish() {} } as unknown as Server; let waiting = Questions.ask(plan, server, "test", questions, { revision: plan.revision, blocks: [[{ index: 0, digest: room.digest("different") }]], }); await expect(waiting).rejects.toThrow(/changed.*read/i); expect(plan.records.size).toBe(0); expect(room.project(plan.document)).not.toContain(" { let plan = await opened("Related prose.\n"); let questions = definition(); let server = { publish() {} } as unknown as Server; let waiting = Questions.ask(plan, server, "test", questions, { revision: plan.revision, blocks: [[]], }); await expect(waiting).rejects.toThrow(/relate.*write/i); expect(plan.records.size).toBe(0); expect(room.project(plan.document)).not.toContain(" { let plan = await opened(); let server = { publish() {} } as unknown as Server; let first = asking(plan, server, definition(), { revision: plan.revision, blocks: [[]], }); await first.created; let second = asking(plan, server, definition(), { revision: plan.revision, blocks: [[]], }); await second.created; expect(plan.records.size).toBe(2); expect(room.project(plan.document).match(/ { let plan = await opened(); let server = { publish() {} } as unknown as Server; let asked = asking(plan, server, definition()); await asked.created; let id = [...plan.records.keys()][0]!; let replies: Array<{ kind: string; message?: string; rid?: string; ts?: number }> = []; let ws = { data: { handle: "ana", client: "client-ana", room: "test" }, send(raw: string) { replies.push(JSON.parse(raw)); }, } as unknown as Socket; plan.execution = { id: "run-1" } as never; await Questions.cancel(plan, server, "test", ws, { kind: "question:cancel", ts: 0, rid: "cancel", id, }); expect(replies).toEqual([{ kind: "session:error", message: "implementation is active", ts: expect.any(Number), rid: "cancel", }]); expect(plan.records.get(id)?.status).toBe("open"); expect(room.project(plan.document)).toContain(" { let plan = await opened(); let server = { publish() {} } as unknown as Server; let revision = plan.revision; plan.execution = { id: "run-1" } as never; await expect(Questions.ask(plan, server, "test", definition())) .rejects.toThrow("implementation is active"); expect(plan.revision).toBe(revision); expect(plan.records.size).toBe(0); expect(room.project(plan.document)).not.toContain("