branch:
max-persisted-messages.test.ts
6017 bytesRaw
import { env } from "cloudflare:workers";
import { describe, it, expect } from "vitest";
import type { UIMessage as ChatMessage } from "ai";
import { connectChatWS } from "./test-utils";
import { getAgentByName } from "agents";

describe("maxPersistedMessages", () => {
  it("does not limit messages when maxPersistedMessages is undefined", async () => {
    const room = crypto.randomUUID();
    const { ws } = await connectChatWS(`/agents/test-chat-agent/${room}`);
    await new Promise((r) => setTimeout(r, 50));

    const agentStub = await getAgentByName(env.TestChatAgent, room);

    // Default: no limit
    const messages: ChatMessage[] = Array.from({ length: 20 }, (_, i) => ({
      id: `msg-nolimit-${i}`,
      role: "user" as const,
      parts: [{ type: "text" as const, text: `Message ${i}` }]
    }));

    await agentStub.persistMessages(messages);

    const count = await agentStub.getMessageCount();
    expect(count).toBe(20);

    ws.close(1000);
  });

  it("deletes oldest messages when count exceeds limit", async () => {
    const room = crypto.randomUUID();
    const { ws } = await connectChatWS(`/agents/test-chat-agent/${room}`);
    await new Promise((r) => setTimeout(r, 50));

    const agentStub = await getAgentByName(env.TestChatAgent, room);

    // Set limit to 5
    await agentStub.setMaxPersistedMessages(5);

    // Persist 10 messages
    const messages: ChatMessage[] = Array.from({ length: 10 }, (_, i) => ({
      id: `msg-limit-${i}`,
      role: "user" as const,
      parts: [{ type: "text" as const, text: `Message ${i}` }]
    }));

    await agentStub.persistMessages(messages);

    // Should have exactly 5 messages
    const count = await agentStub.getMessageCount();
    expect(count).toBe(5);

    // The kept messages should be the most recent 5
    const persisted = (await agentStub.getPersistedMessages()) as ChatMessage[];
    expect(persisted.length).toBe(5);
    expect(persisted[0].id).toBe("msg-limit-5");
    expect(persisted[4].id).toBe("msg-limit-9");

    ws.close(1000);
  });

  it("keeps all messages when count is under the limit", async () => {
    const room = crypto.randomUUID();
    const { ws } = await connectChatWS(`/agents/test-chat-agent/${room}`);
    await new Promise((r) => setTimeout(r, 50));

    const agentStub = await getAgentByName(env.TestChatAgent, room);

    await agentStub.setMaxPersistedMessages(100);

    const messages: ChatMessage[] = Array.from({ length: 5 }, (_, i) => ({
      id: `msg-under-${i}`,
      role: "user" as const,
      parts: [{ type: "text" as const, text: `Message ${i}` }]
    }));

    await agentStub.persistMessages(messages);

    const count = await agentStub.getMessageCount();
    expect(count).toBe(5);

    ws.close(1000);
  });

  it("enforces limit across multiple persist calls", async () => {
    const room = crypto.randomUUID();
    const { ws } = await connectChatWS(`/agents/test-chat-agent/${room}`);
    await new Promise((r) => setTimeout(r, 50));

    const agentStub = await getAgentByName(env.TestChatAgent, room);

    await agentStub.setMaxPersistedMessages(4);

    // First batch: 3 messages
    await agentStub.persistMessages([
      {
        id: "batch1-1",
        role: "user",
        parts: [{ type: "text", text: "First" }]
      },
      {
        id: "batch1-2",
        role: "assistant",
        parts: [{ type: "text", text: "Response 1" }]
      },
      {
        id: "batch1-3",
        role: "user",
        parts: [{ type: "text", text: "Second" }]
      }
    ]);

    expect(await agentStub.getMessageCount()).toBe(3);

    // Second batch: adds 3 more (total would be 6, limit is 4)
    await agentStub.persistMessages([
      {
        id: "batch1-1",
        role: "user",
        parts: [{ type: "text", text: "First" }]
      },
      {
        id: "batch1-2",
        role: "assistant",
        parts: [{ type: "text", text: "Response 1" }]
      },
      {
        id: "batch1-3",
        role: "user",
        parts: [{ type: "text", text: "Second" }]
      },
      {
        id: "batch2-1",
        role: "assistant",
        parts: [{ type: "text", text: "Response 2" }]
      },
      {
        id: "batch2-2",
        role: "user",
        parts: [{ type: "text", text: "Third" }]
      },
      {
        id: "batch2-3",
        role: "assistant",
        parts: [{ type: "text", text: "Response 3" }]
      }
    ]);

    // Should be capped at 4
    expect(await agentStub.getMessageCount()).toBe(4);

    // The kept messages should be the most recent 4
    const persisted = (await agentStub.getPersistedMessages()) as ChatMessage[];
    expect(persisted.map((m) => m.id)).toEqual([
      "batch1-3",
      "batch2-1",
      "batch2-2",
      "batch2-3"
    ]);

    ws.close(1000);
  });

  it("can be disabled by setting to null", async () => {
    const room = crypto.randomUUID();
    const { ws } = await connectChatWS(`/agents/test-chat-agent/${room}`);
    await new Promise((r) => setTimeout(r, 50));

    const agentStub = await getAgentByName(env.TestChatAgent, room);

    // Enable limit
    await agentStub.setMaxPersistedMessages(3);

    await agentStub.persistMessages(
      Array.from({ length: 5 }, (_, i) => ({
        id: `disable-${i}`,
        role: "user" as const,
        parts: [{ type: "text" as const, text: `Msg ${i}` }]
      }))
    );
    expect(await agentStub.getMessageCount()).toBe(3);

    // Disable limit
    await agentStub.setMaxPersistedMessages(null);

    // Add more messages -- should keep all now
    await agentStub.persistMessages(
      Array.from({ length: 8 }, (_, i) => ({
        id: `disable-after-${i}`,
        role: "user" as const,
        parts: [{ type: "text" as const, text: `Msg after ${i}` }]
      }))
    );

    // All 8 new + 3 remaining from before (but IDs overlap detection may differ)
    // The key assertion: count is more than 3 (limit was removed)
    expect(await agentStub.getMessageCount()).toBeGreaterThan(3);

    ws.close(1000);
  });
});