import { randomUUID } from "node:crypto"; import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; import { agents, companies, createDb, heartbeatRuns } from "@paperclipai/db"; import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase, } from "./helpers/embedded-postgres.js"; import { activityService } from "../services/activity.ts"; const embeddedPostgresSupport = await getEmbeddedPostgresTestSupport(); const describeEmbeddedPostgres = embeddedPostgresSupport.supported ? describe : describe.skip; if (!embeddedPostgresSupport.supported) { console.warn( `Skipping embedded Postgres activity service tests on this host: ${embeddedPostgresSupport.reason ?? "unsupported environment"}`, ); } describeEmbeddedPostgres("activity service", () => { let db!: ReturnType; let tempDb: Awaited> | null = null; beforeAll(async () => { tempDb = await startEmbeddedPostgresTestDatabase("paperclip-activity-service-"); db = createDb(tempDb.connectionString); }, 20_000); afterEach(async () => { await db.delete(heartbeatRuns); await db.delete(agents); await db.delete(companies); }); afterAll(async () => { await tempDb?.cleanup(); }); it("returns compact usage and result summaries for issue runs", async () => { const companyId = randomUUID(); const agentId = randomUUID(); const issueId = randomUUID(); const runId = 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: "running", adapterType: "codex_local", adapterConfig: {}, runtimeConfig: {}, permissions: {}, }); await db.insert(heartbeatRuns).values({ id: runId, companyId, agentId, invocationSource: "assignment", status: "succeeded", contextSnapshot: { issueId }, usageJson: { inputTokens: 11, output_tokens: 7, cache_read_input_tokens: 3, billingType: "metered", costUsd: 0.42, enormousBlob: "x".repeat(256_000), }, resultJson: { billing_type: "metered", total_cost_usd: 0.42, summary: "done", nestedHuge: { payload: "y".repeat(256_000) }, }, }); const runs = await activityService(db).runsForIssue(companyId, issueId); expect(runs).toHaveLength(1); expect(runs[0]).toMatchObject({ runId, agentId, invocationSource: "assignment", }); expect(runs[0]?.usageJson).toEqual({ inputTokens: 11, input_tokens: 11, outputTokens: 7, output_tokens: 7, cachedInputTokens: 3, cached_input_tokens: 3, cache_read_input_tokens: 3, billingType: "metered", billing_type: "metered", costUsd: 0.42, cost_usd: 0.42, total_cost_usd: 0.42, }); expect(runs[0]?.resultJson).toEqual({ billingType: "metered", billing_type: "metered", costUsd: 0.42, cost_usd: 0.42, total_cost_usd: 0.42, }); }); });