Files
paperclip/server/src/__tests__/issues-service.test.ts
T
2026-03-26 11:12:39 -05:00

220 lines
5.7 KiB
TypeScript

import { randomUUID } from "node:crypto";
import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
import {
activityLog,
agents,
companies,
createDb,
issueComments,
issues,
} from "@paperclipai/db";
import {
getEmbeddedPostgresTestSupport,
startEmbeddedPostgresTestDatabase,
} from "./helpers/embedded-postgres.js";
import { issueService } from "../services/issues.ts";
const embeddedPostgresSupport = await getEmbeddedPostgresTestSupport();
const describeEmbeddedPostgres = embeddedPostgresSupport.supported ? describe : describe.skip;
if (!embeddedPostgresSupport.supported) {
console.warn(
`Skipping embedded Postgres issue service tests on this host: ${embeddedPostgresSupport.reason ?? "unsupported environment"}`,
);
}
describeEmbeddedPostgres("issueService.list participantAgentId", () => {
let db!: ReturnType<typeof createDb>;
let svc!: ReturnType<typeof issueService>;
let tempDb: Awaited<ReturnType<typeof startEmbeddedPostgresTestDatabase>> | null = null;
beforeAll(async () => {
tempDb = await startEmbeddedPostgresTestDatabase("paperclip-issues-service-");
db = createDb(tempDb.connectionString);
svc = issueService(db);
}, 20_000);
afterEach(async () => {
await db.delete(issueComments);
await db.delete(activityLog);
await db.delete(issues);
await db.delete(agents);
await db.delete(companies);
});
afterAll(async () => {
await tempDb?.cleanup();
});
it("returns issues an agent participated in across the supported signals", async () => {
const companyId = randomUUID();
const agentId = randomUUID();
const otherAgentId = randomUUID();
await db.insert(companies).values({
id: companyId,
name: "Paperclip",
issuePrefix: `T${companyId.replace(/-/g, "").slice(0, 6).toUpperCase()}`,
requireBoardApprovalForNewAgents: false,
});
await db.insert(agents).values([
{
id: agentId,
companyId,
name: "CodexCoder",
role: "engineer",
status: "active",
adapterType: "codex_local",
adapterConfig: {},
runtimeConfig: {},
permissions: {},
},
{
id: otherAgentId,
companyId,
name: "OtherAgent",
role: "engineer",
status: "active",
adapterType: "codex_local",
adapterConfig: {},
runtimeConfig: {},
permissions: {},
},
]);
const assignedIssueId = randomUUID();
const createdIssueId = randomUUID();
const commentedIssueId = randomUUID();
const activityIssueId = randomUUID();
const excludedIssueId = randomUUID();
await db.insert(issues).values([
{
id: assignedIssueId,
companyId,
title: "Assigned issue",
status: "todo",
priority: "medium",
assigneeAgentId: agentId,
createdByAgentId: otherAgentId,
},
{
id: createdIssueId,
companyId,
title: "Created issue",
status: "todo",
priority: "medium",
createdByAgentId: agentId,
},
{
id: commentedIssueId,
companyId,
title: "Commented issue",
status: "todo",
priority: "medium",
createdByAgentId: otherAgentId,
},
{
id: activityIssueId,
companyId,
title: "Activity issue",
status: "todo",
priority: "medium",
createdByAgentId: otherAgentId,
},
{
id: excludedIssueId,
companyId,
title: "Excluded issue",
status: "todo",
priority: "medium",
createdByAgentId: otherAgentId,
assigneeAgentId: otherAgentId,
},
]);
await db.insert(issueComments).values({
companyId,
issueId: commentedIssueId,
authorAgentId: agentId,
body: "Investigating this issue.",
});
await db.insert(activityLog).values({
companyId,
actorType: "agent",
actorId: agentId,
action: "issue.updated",
entityType: "issue",
entityId: activityIssueId,
agentId,
details: { changed: true },
});
const result = await svc.list(companyId, { participantAgentId: agentId });
const resultIds = new Set(result.map((issue) => issue.id));
expect(resultIds).toEqual(new Set([
assignedIssueId,
createdIssueId,
commentedIssueId,
activityIssueId,
]));
expect(resultIds.has(excludedIssueId)).toBe(false);
});
it("combines participation filtering with search", async () => {
const companyId = randomUUID();
const agentId = randomUUID();
await db.insert(companies).values({
id: companyId,
name: "Paperclip",
issuePrefix: `T${companyId.replace(/-/g, "").slice(0, 6).toUpperCase()}`,
requireBoardApprovalForNewAgents: false,
});
await db.insert(agents).values({
id: agentId,
companyId,
name: "CodexCoder",
role: "engineer",
status: "active",
adapterType: "codex_local",
adapterConfig: {},
runtimeConfig: {},
permissions: {},
});
const matchedIssueId = randomUUID();
const otherIssueId = randomUUID();
await db.insert(issues).values([
{
id: matchedIssueId,
companyId,
title: "Invoice reconciliation",
status: "todo",
priority: "medium",
createdByAgentId: agentId,
},
{
id: otherIssueId,
companyId,
title: "Weekly planning",
status: "todo",
priority: "medium",
createdByAgentId: agentId,
},
]);
const result = await svc.list(companyId, {
participantAgentId: agentId,
q: "invoice",
});
expect(result.map((issue) => issue.id)).toEqual([matchedIssueId]);
});
});