74 lines
2.5 KiB
TypeScript
74 lines
2.5 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import type { Conversation, ConversationSummary } from "../domain/conversation";
|
|
import type { ConversationRepository } from "../ports/conversation-repository";
|
|
import { ConversationService } from "./conversation-service";
|
|
|
|
class MemoryConversationRepository implements ConversationRepository {
|
|
readonly values = new Map<string, Conversation>();
|
|
async listRecent(): Promise<readonly ConversationSummary[]> {
|
|
return [...this.values.values()];
|
|
}
|
|
async listByProject(projectId: string) {
|
|
return [...this.values.values()].filter(
|
|
(value) => value.projectId === projectId,
|
|
);
|
|
}
|
|
async deleteByProject(projectId: string) {
|
|
for (const [id, value] of this.values)
|
|
if (value.projectId === projectId) this.values.delete(id);
|
|
}
|
|
async getById(id: string): Promise<Conversation | null> {
|
|
return this.values.get(id) ?? null;
|
|
}
|
|
async create(conversation: Conversation): Promise<void> {
|
|
this.values.set(conversation.id, conversation);
|
|
}
|
|
async appendMessage(
|
|
id: string,
|
|
message: Conversation["messages"][number],
|
|
): Promise<Conversation> {
|
|
const current = this.values.get(id);
|
|
if (!current) throw new Error("not found");
|
|
const next = {
|
|
...current,
|
|
messages: [...current.messages, message],
|
|
updatedAt: message.createdAt,
|
|
};
|
|
this.values.set(id, next);
|
|
return next;
|
|
}
|
|
}
|
|
|
|
describe("ConversationService", () => {
|
|
test("首条有效消息才创建普通会话", async () => {
|
|
const conversations = new MemoryConversationRepository();
|
|
let nextId = 0;
|
|
const service = new ConversationService({
|
|
conversations,
|
|
clock: { now: () => new Date("2026-08-12T00:00:00.000Z") },
|
|
ids: { create: () => `id_${++nextId}` },
|
|
});
|
|
const created = await service.createWithFirstMessage(
|
|
" 第一次对话 ",
|
|
"run_1",
|
|
);
|
|
expect(created.projectId).toBeNull();
|
|
expect(created.messages[0]?.content).toBe("第一次对话");
|
|
expect(created.messages[0]?.runId).toBe("run_1");
|
|
expect(await service.listRecent()).toHaveLength(1);
|
|
});
|
|
|
|
test("拒绝空消息且不创建会话", async () => {
|
|
const conversations = new MemoryConversationRepository();
|
|
const service = new ConversationService({
|
|
conversations,
|
|
clock: { now: () => new Date() },
|
|
ids: { create: () => "unused" },
|
|
});
|
|
expect(
|
|
service.createWithFirstMessage(" \n ", "run_1"),
|
|
).rejects.toMatchObject({ code: "MESSAGE_EMPTY" });
|
|
expect(conversations.values.size).toBe(0);
|
|
});
|
|
});
|