llm-to-agent/packages/agent-core/src/use-cases/project-service.test.ts
2026-08-14 14:34:27 +08:00

134 lines
3.6 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import type {
AgentRun,
Conversation,
ConversationRepository,
Project,
ProjectRepository,
RunEvent,
RunRepository,
} from "..";
import { ProjectService } from "..";
class Projects implements ProjectRepository {
values = new Map<string, Project>();
async list() {
return [...this.values.values()];
}
async getById(id: string) {
return this.values.get(id) ?? null;
}
async create(project: Project) {
this.values.set(project.id, project);
}
async update(project: Project) {
this.values.set(project.id, project);
}
async delete(id: string) {
this.values.delete(id);
}
}
class Conversations implements ConversationRepository {
values = new Map<string, Conversation>();
async listRecent() {
return [...this.values.values()].filter((value) => !value.projectId);
}
async listByProject(projectId: string) {
return [...this.values.values()].filter(
(value) => value.projectId === projectId,
);
}
async getById(id: string) {
return this.values.get(id) ?? null;
}
async create(value: Conversation) {
this.values.set(value.id, value);
}
async appendMessage(
_id: string,
_message: Conversation["messages"][number],
): Promise<Conversation> {
throw new Error("unused");
}
async deleteByProject(projectId: string) {
for (const [id, value] of this.values)
if (value.projectId === projectId) this.values.delete(id);
}
}
class Runs implements RunRepository {
active = false;
async create(_run: AgentRun) {}
async update(_run: AgentRun) {}
async getById(_id: string) {
return null;
}
async appendEvent(_event: RunEvent) {}
async listEvents(_id: string) {
return [];
}
async hasActiveForConversations() {
return this.active;
}
async deleteByConversations() {}
}
describe("ProjectService", () => {
test("创建项目时固化真实目录,重命名后仍保留目录", async () => {
const projects = new Projects();
const service = createService(projects, new Conversations(), new Runs());
const created = await service.create(" 我的项目 ", "/tmp/link");
expect(created).toMatchObject({
name: "我的项目",
workspaceRoot: "/tmp/real",
conversationCount: 0,
workspaceAvailable: true,
});
const renamed = await service.rename(created.id, "新名字");
expect(renamed.name).toBe("新名字");
expect(renamed.workspaceRoot).toBe("/tmp/real");
});
test("有运行中的项目任务时拒绝删除", async () => {
const projects = new Projects();
const conversations = new Conversations();
const runs = new Runs();
const service = createService(projects, conversations, runs);
const created = await service.create("项目", "/tmp/real");
conversations.values.set("conversation", {
id: "conversation",
projectId: created.id,
title: "对话",
messages: [],
createdAt: "now",
updatedAt: "now",
});
runs.active = true;
expect(service.delete(created.id)).rejects.toMatchObject({
code: "PROJECT_RUN_ACTIVE",
});
expect(await projects.getById(created.id)).not.toBeNull();
});
});
function createService(
projects: Projects,
conversations: Conversations,
runs: Runs,
) {
return new ProjectService({
projects,
conversations,
runs,
workspaces: {
async resolveForCreation() {
return "/tmp/real";
},
async isAvailable() {
return true;
},
},
clock: { now: () => new Date("2026-08-13T00:00:00Z") },
ids: { create: () => "project_1" },
});
}