279 lines · 8.8 KB · TypeScript
RawDownload
import { MessageId, ThreadId, TurnId } from "@t3tools/contracts";import { assert, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer"; import { ProjectionThreadMessageRepository } from "../Services/ProjectionThreadMessages.ts";import { ProjectionThreadMessageRepositoryLive } from "./ProjectionThreadMessages.ts";import { SqlitePersistenceMemory } from "./Sqlite.ts"; const layer = it.layer(  ProjectionThreadMessageRepositoryLive.pipe(Layer.provideMerge(SqlitePersistenceMemory)),); layer("ProjectionThreadMessageRepository", (it) => {  it.effect("finds the latest live user-message time within one thread", () =>    Effect.gen(function* () {      const repository = yield* ProjectionThreadMessageRepository;      const threadId = ThreadId.make("thread-latest-user-message");      assert.isNull(yield* repository.getLatestUserMessageAt({ threadId }));       yield* repository.upsert({        messageId: MessageId.make("import:codex:latest-user-message:000000"),        threadId,        turnId: null,        role: "user",        text: "Imported prompt",        isStreaming: false,        createdAt: "2026-02-28T19:05:06.000Z",        updatedAt: "2026-02-28T19:05:06.000Z",      });      assert.isNull(yield* repository.getLatestUserMessageAt({ threadId }));       const messages = [        { role: "user", createdAt: "2026-02-28T19:05:02.000Z" },        { role: "user", createdAt: "2026-02-28T19:05:01.000Z" },        { role: "assistant", createdAt: "2026-02-28T19:05:03.000Z" },        { role: "system", createdAt: "2026-02-28T19:05:04.000Z" },      ] as const;      for (const [index, message] of messages.entries()) {        yield* repository.upsert({          messageId: MessageId.make(`latest-user-message-${index}`),          threadId,          turnId: null,          ...message,          text: "Message body",          isStreaming: false,          updatedAt: "2026-02-28T19:06:00.000Z",        });      }      yield* repository.upsert({        messageId: MessageId.make("latest-user-message-other-thread"),        threadId: ThreadId.make("thread-latest-user-message-other"),        turnId: null,        role: "user",        text: "Other thread",        isStreaming: false,        createdAt: "2026-02-28T19:05:05.000Z",        updatedAt: "2026-02-28T19:05:05.000Z",      });       assert.strictEqual(        yield* repository.getLatestUserMessageAt({ threadId }),        "2026-02-28T19:05:02.000Z",      );      yield* repository.deleteByThreadId({ threadId });      assert.isNull(yield* repository.getLatestUserMessageAt({ threadId }));    }),  );   it.effect("appends streaming text and applies attachment updates", () =>    Effect.gen(function* () {      const repository = yield* ProjectionThreadMessageRepository;      const threadId = ThreadId.make("thread-streaming-append");      const messageId = MessageId.make("message-streaming-append");      const createdAt = "2026-02-28T19:05:00.000Z";      const attachments = [        {          type: "image" as const,          id: "thread-streaming-append-att-1",          name: "example.png",          mimeType: "image/png",          sizeBytes: 5,        },      ];       yield* repository.appendStreaming({        messageId,        threadId,        turnId: null,        role: "assistant",        text: "hello",        attachments,        createdAt,        updatedAt: createdAt,      });      yield* repository.appendStreaming({        messageId,        threadId,        turnId: null,        role: "assistant",        text: " world",        createdAt: "2026-02-28T19:05:01.000Z",        updatedAt: "2026-02-28T19:05:01.000Z",      });       const rowWithPreservedAttachments = yield* repository.getByMessageId({ messageId });      assert.equal(rowWithPreservedAttachments._tag, "Some");      if (rowWithPreservedAttachments._tag === "Some") {        assert.deepEqual(rowWithPreservedAttachments.value.attachments, attachments);      }       yield* repository.appendStreaming({        messageId,        threadId,        turnId: null,        role: "assistant",        text: "",        attachments: [],        createdAt: "2026-02-28T19:05:02.000Z",        updatedAt: "2026-02-28T19:05:02.000Z",      });       const row = yield* repository.getByMessageId({ messageId });      assert.equal(row._tag, "Some");      if (row._tag === "Some") {        assert.equal(row.value.text, "hello world");        assert.deepEqual(row.value.attachments, []);        assert.equal(row.value.createdAt, createdAt);        assert.equal(row.value.updatedAt, "2026-02-28T19:05:02.000Z");        assert.isTrue(row.value.isStreaming);      }    }),  );   it.effect("preserves existing attachments when upsert omits attachments", () =>    Effect.gen(function* () {      const repository = yield* ProjectionThreadMessageRepository;      const threadId = ThreadId.make("thread-preserve-attachments");      const messageId = MessageId.make("message-preserve-attachments");      const createdAt = "2026-02-28T19:00:00.000Z";      const updatedAt = "2026-02-28T19:00:01.000Z";      const persistedAttachments = [        {          type: "image" as const,          id: "thread-preserve-attachments-att-1",          name: "example.png",          mimeType: "image/png",          sizeBytes: 5,        },      ];       yield* repository.upsert({        messageId,        threadId,        turnId: null,        role: "user",        text: "initial",        attachments: persistedAttachments,        isStreaming: false,        createdAt,        updatedAt,      });       yield* repository.upsert({        messageId,        threadId,        turnId: null,        role: "user",        text: "updated",        isStreaming: false,        createdAt,        updatedAt: "2026-02-28T19:00:02.000Z",      });       const rows = yield* repository.listByThreadId({ threadId });      assert.equal(rows.length, 1);      assert.equal(rows[0]?.text, "updated");      assert.deepEqual(rows[0]?.attachments, persistedAttachments);       const rowById = yield* repository.getByMessageId({ messageId });      assert.equal(rowById._tag, "Some");      if (rowById._tag === "Some") {        assert.equal(rowById.value.text, "updated");        assert.deepEqual(rowById.value.attachments, persistedAttachments);      }    }),  );   it.effect("allows explicit attachment clearing with an empty array", () =>    Effect.gen(function* () {      const repository = yield* ProjectionThreadMessageRepository;      const threadId = ThreadId.make("thread-clear-attachments");      const messageId = MessageId.make("message-clear-attachments");      const createdAt = "2026-02-28T19:10:00.000Z";       yield* repository.upsert({        messageId,        threadId,        turnId: null,        role: "assistant",        text: "with attachment",        attachments: [          {            type: "image",            id: "thread-clear-attachments-att-1",            name: "example.png",            mimeType: "image/png",            sizeBytes: 5,          },        ],        isStreaming: false,        createdAt,        updatedAt: "2026-02-28T19:10:01.000Z",      });       yield* repository.upsert({        messageId,        threadId,        turnId: null,        role: "assistant",        text: "cleared",        attachments: [],        isStreaming: false,        createdAt,        updatedAt: "2026-02-28T19:10:02.000Z",      });       const rows = yield* repository.listByThreadId({ threadId });      assert.equal(rows.length, 1);      assert.equal(rows[0]?.text, "cleared");      assert.deepEqual(rows[0]?.attachments, []);    }),  );   it.effect("checks assistant turn state without hydrating message text", () =>    Effect.gen(function* () {      const repository = yield* ProjectionThreadMessageRepository;      const threadId = ThreadId.make("thread-assistant-turn-state");      const turnId = TurnId.make("turn-assistant-state");      const createdAt = "2026-03-01T00:00:00.000Z";       yield* repository.upsert({        messageId: MessageId.make("message-assistant-turn-state"),        threadId,        turnId,        role: "assistant",        text: "large text that the existence query must not select",        isStreaming: false,        createdAt,        updatedAt: createdAt,      });       assert.equal(        yield* repository.hasAssistantMessageForTurn({          threadId,          turnId,          streamingOnly: false,        }),        true,      );      assert.equal(        yield* repository.hasAssistantMessageForTurn({          threadId,          turnId,          streamingOnly: true,        }),        false,      );      assert.equal(        yield* repository.hasAssistantMessageForTurn({          threadId,          turnId: TurnId.make("turn-assistant-state-missing"),          streamingOnly: false,        }),        false,      );    }),  );});