| import { describe, expect, it, vi } from "vitest"; |
| import { |
| clearFinalizableDraftMessage, |
| createFinalizableDraftLifecycle, |
| createFinalizableDraftStreamControlsForState, |
| takeMessageIdAfterStop, |
| } from "./draft-stream-controls.js"; |
|
|
| describe("draft-stream-controls", () => { |
| it("takeMessageIdAfterStop stops, reads, and clears message id", async () => { |
| const events: string[] = []; |
| let messageId: string | undefined = "m-1"; |
|
|
| const result = await takeMessageIdAfterStop({ |
| stopForClear: async () => { |
| events.push("stop"); |
| }, |
| readMessageId: () => { |
| events.push("read"); |
| return messageId; |
| }, |
| clearMessageId: () => { |
| events.push("clear"); |
| messageId = undefined; |
| }, |
| }); |
|
|
| expect(result).toBe("m-1"); |
| expect(messageId).toBeUndefined(); |
| expect(events).toEqual(["stop", "read", "clear"]); |
| }); |
|
|
| it("clearFinalizableDraftMessage deletes valid message ids", async () => { |
| const deleteMessage = vi.fn(async () => {}); |
| const onDeleteSuccess = vi.fn(); |
|
|
| await clearFinalizableDraftMessage({ |
| stopForClear: async () => {}, |
| readMessageId: () => "m-2", |
| clearMessageId: () => {}, |
| isValidMessageId: (value): value is string => typeof value === "string", |
| deleteMessage, |
| onDeleteSuccess, |
| warnPrefix: "cleanup failed", |
| }); |
|
|
| expect(deleteMessage).toHaveBeenCalledWith("m-2"); |
| expect(onDeleteSuccess).toHaveBeenCalledWith("m-2"); |
| }); |
|
|
| it("clearFinalizableDraftMessage skips invalid message ids", async () => { |
| const deleteMessage = vi.fn(async () => {}); |
|
|
| await clearFinalizableDraftMessage<unknown>({ |
| stopForClear: async () => {}, |
| readMessageId: () => 123, |
| clearMessageId: () => {}, |
| isValidMessageId: (value): value is string => typeof value === "string", |
| deleteMessage, |
| warnPrefix: "cleanup failed", |
| }); |
|
|
| expect(deleteMessage).not.toHaveBeenCalled(); |
| }); |
|
|
| it("clearFinalizableDraftMessage warns when delete fails", async () => { |
| const warn = vi.fn(); |
|
|
| await clearFinalizableDraftMessage({ |
| stopForClear: async () => {}, |
| readMessageId: () => "m-3", |
| clearMessageId: () => {}, |
| isValidMessageId: (value): value is string => typeof value === "string", |
| deleteMessage: async () => { |
| throw new Error("boom"); |
| }, |
| warn, |
| warnPrefix: "cleanup failed", |
| }); |
|
|
| expect(warn).toHaveBeenCalledWith("cleanup failed: boom"); |
| }); |
|
|
| it("controls ignore updates after final", async () => { |
| const sendOrEditStreamMessage = vi.fn(async () => true); |
| const controls = createFinalizableDraftStreamControlsForState({ |
| throttleMs: 250, |
| state: { stopped: false, final: true }, |
| sendOrEditStreamMessage, |
| }); |
|
|
| controls.update("ignored"); |
| await controls.loop.flush(); |
|
|
| expect(sendOrEditStreamMessage).not.toHaveBeenCalled(); |
| }); |
|
|
| it("lifecycle clear marks stopped, clears id, and deletes preview message", async () => { |
| const state = { stopped: false, final: false }; |
| let messageId: string | undefined = "m-4"; |
| const deleteMessage = vi.fn(async () => {}); |
|
|
| const lifecycle = createFinalizableDraftLifecycle({ |
| throttleMs: 250, |
| state, |
| sendOrEditStreamMessage: async () => true, |
| readMessageId: () => messageId, |
| clearMessageId: () => { |
| messageId = undefined; |
| }, |
| isValidMessageId: (value): value is string => typeof value === "string", |
| deleteMessage, |
| warnPrefix: "cleanup failed", |
| }); |
|
|
| await lifecycle.clear(); |
|
|
| expect(state.stopped).toBe(true); |
| expect(messageId).toBeUndefined(); |
| expect(deleteMessage).toHaveBeenCalledWith("m-4"); |
| }); |
| }); |
|
|