test: isolate accounting fixtures and include all adapter suites

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Devin FoleyandPaperclip committed 2026-10-02 16:35:32 -07:00
1 parent 4abff286c2
commit 7eb38f881e
30 files changed
+98 -5

No files matched your search

@@ -1,3 +1,4 @@
import { execFileSync } from "node:child_process";
import { mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import os from "node:os";
import path from "node:path";
@@ -367,6 +368,9 @@ function isTestFile(relPath: string): boolean {
function listGuidanceFiles(rootDir = repoRoot): string[] {
const found: string[] = [];
// Include tracked files and new authored files, while respecting gitignored
// local settings. A tracked file remains scanned even if an ignore matches.
const authored = rootDir === repoRoot ? new Set(execFileSync("git", ["ls-files", "--cached", "--others", "--exclude-standard", "-z"], { cwd: rootDir, encoding: "utf8" }).split("\0")) : null;
function walk(absDir: string, relDir: string): void {
for (const entry of readdirSync(absDir, { withFileTypes: true })) {
@@ -382,7 +386,7 @@ function listGuidanceFiles(rootDir = repoRoot): string[] {
if (!SCAN_EXTENSIONS.has(path.extname(entry.name))) continue;
if (isTestFile(relPath)) continue;
if (SKIP_PATH_PREFIXES.some((prefix) => relPath.startsWith(prefix))) continue;
found.push(relPath);
if (!authored || authored.has(relPath)) found.push(relPath);
}
}
@@ -11,6 +11,7 @@ import {
stopSshEnvLabFixture,
} from "@paperclipai/adapter-utils/ssh";
import {
costEvents,
agents,
builtInManagedResources,
companies,
@@ -219,6 +220,7 @@ describeEmbeddedPostgres("environmentRuntimeService", () => {
}
await db.delete(environmentLeases);
await db.delete(issues);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(agents);
await db.delete(environments);
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import { eq } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
import {
costEvents,
activityLog,
agents,
agentWakeupRequests,
@@ -40,6 +41,7 @@ describeEmbeddedPostgres("execution lock orphan cleanup", () => {
await db.delete(activityLog);
await db.delete(heartbeatRunEvents);
await db.delete(issues);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(agents);
await db.delete(companies);
@@ -7,6 +7,7 @@ import { promisify } from "node:util";
import { eq, ne } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import {
costEvents,
activityLog,
agentRuntimeState,
agentTaskSessions,
@@ -136,6 +137,7 @@ describeEmbeddedPostgres("accepted plan workspace refresh", () => {
await db.delete(activityLog);
await db.delete(heartbeatRunEvents);
try {
await db.delete(costEvents);
await db.delete(heartbeatRuns);
break;
} catch (error) {
@@ -1,6 +1,7 @@
import { randomUUID } from "node:crypto";
import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
import {
costEvents,
agents,
agentWakeupRequests,
companies,
@@ -35,6 +36,7 @@ describeEmbeddedPostgres("heartbeat archived-company guard", () => {
afterEach(async () => {
await db.delete(heartbeatRunEvents);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(agentWakeupRequests);
await db.delete(issues);
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import { and, eq, inArray, sql } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import {
costEvents,
activityLog,
agents,
agentRuntimeState,
@@ -143,6 +144,7 @@ describeEmbeddedPostgres("heartbeat dependency-aware queued run selection", () =
await db.delete(issues);
await db.delete(heartbeatRunEvents);
await db.delete(activityLog);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(agentWakeupRequests);
await db.delete(agentRuntimeState);
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import { and, desc, eq, sql } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import {
costEvents,
activityLog,
agentRuntimeState,
agentWakeupRequests,
@@ -90,6 +91,7 @@ describeEmbeddedPostgres("heartbeat issue rewake throttle", () => {
await db.delete(issues);
await db.delete(heartbeatRunEvents);
await db.delete(activityLog);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(agentWakeupRequests);
await db.delete(agentRuntimeState);
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import { and, eq } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import {
costEvents,
agents,
agentRuntimeState,
agentWakeupRequests,
@@ -59,6 +60,7 @@ describeEmbeddedPostgres("heartbeat lock release on cross-agent reassignment", (
afterEach(async () => {
await drainHeartbeatRunsToQuiescence(db, heartbeat);
await db.delete(heartbeatRunEvents);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(agentWakeupRequests);
await db.delete(issues);
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import { eq } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import {
costEvents,
agents,
companies,
createDb,
@@ -62,6 +63,7 @@ describeEmbeddedPostgres("heartbeat sweepOrphanedActiveLeases", () => {
afterEach(async () => {
await db.delete(environmentLeases);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(environments);
await db.delete(agents);
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import { eq, sql } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import {
costEvents,
activityLog,
agents,
heartbeatRuns,
@@ -71,6 +72,7 @@ describeEmbeddedPostgres("heartbeat sweepPendingCleanupLeases", () => {
afterEach(async () => {
await db.delete(activityLog);
await db.delete(environmentLeases);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(environments);
await db.delete(agents);
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import { and, eq } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import {
costEvents,
activityLog,
agents,
agentRuntimeState,
@@ -62,6 +63,7 @@ async function deleteHeartbeatRunsAfterEvents(db: ReturnType<typeof createDb>) {
for (let attempt = 0; attempt < 5; attempt += 1) {
await db.delete(heartbeatRunEvents);
try {
await db.delete(costEvents);
await db.delete(heartbeatRuns);
return;
} catch (error) {
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import { and, eq, inArray, sql } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import {
costEvents,
agents,
approvals,
issueApprovals,
@@ -164,6 +165,7 @@ describeEmbeddedPostgres("heartbeat bounded retry scheduling", () => {
}
async function cleanupRetryFixtureOnce() {
await db.delete(costEvents);
await db.delete(activityLog);
await db.delete(environmentLeases);
await db.delete(issueRelations);
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import { eq } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import {
costEvents,
agents,
companies,
createDb,
@@ -42,6 +43,7 @@ describeEmbeddedPostgres("heartbeat terminalizeRunOnLeaseRelease", () => {
afterEach(async () => {
await db.delete(heartbeatRunEvents);
await db.delete(issues);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(agents);
await db.delete(companies);
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import { eq } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import {
costEvents,
agents,
companies,
createDb,
@@ -71,6 +72,7 @@ describeEmbeddedPostgres("heartbeat teardown terminalizes the run before releasi
afterEach(async () => {
await db.delete(heartbeatRunEvents);
await db.delete(issues);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(agents);
await db.delete(companies);
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import { eq } from "drizzle-orm";
import {
costEvents,
agents,
agentRuntimeState,
agentWakeupRequests,
@@ -52,6 +53,7 @@ describeEmbeddedPostgres("heartbeat runtime state deduplication", () => {
afterEach(async () => {
clearAllHeartbeatRunRuntimeStatuses();
await db.delete(heartbeatRunEvents);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(agentWakeupRequests);
await db.delete(agentRuntimeState);
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import { eq, sql } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
import {
costEvents,
activityLog,
agents,
agentRuntimeState,
@@ -51,6 +52,7 @@ describeEmbeddedPostgres("heartbeat task-drain admission release", () => {
await db.delete(heartbeatRunEvents);
await db.delete(activityLog);
try {
await db.delete(costEvents);
await db.delete(heartbeatRuns);
return;
} catch (error) {
@@ -7,6 +7,7 @@ import { promisify } from "node:util";
import { and, eq, inArray } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import {
costEvents,
activityLog,
agentRuntimeState,
agentWakeupRequests,
@@ -179,6 +180,7 @@ async function deleteHeartbeatRunsForCleanup(db: Db) {
await db.delete(heartbeatRunEvents);
await db.delete(activityLog);
try {
await db.delete(costEvents);
await db.delete(heartbeatRuns);
return;
} catch (error) {
@@ -5,6 +5,7 @@ import path from "node:path";
import { and, eq } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import {
costEvents,
agents,
agentRuntimeState,
agentWakeupRequests,
@@ -151,6 +152,7 @@ describeEmbeddedPostgres("shared-workspace run serialization", () => {
}
async function cleanupFixtureOnce() {
await db.delete(costEvents);
await db.delete(activityLog);
await db.delete(environmentLeases);
await db.delete(issueComments);
@@ -7,6 +7,7 @@ import { promisify } from "node:util";
import { and, asc, eq } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import {
costEvents,
activityLog,
agentRuntimeState,
agentTaskSessions,
@@ -121,6 +122,7 @@ async function deleteHeartbeatRowsAfterActivityLogDrains(db: Db) {
await db.delete(activityLog);
await db.delete(heartbeatRunEvents);
try {
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(agentWakeupRequests);
return;
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
import { eq, sql } from "drizzle-orm";
import {
costEvents,
activityLog,
agents,
agentWakeupRequests,
@@ -52,6 +53,7 @@ describeEmbeddedPostgres("heartbeat worktree suppression", () => {
await db.delete(heartbeatRunEvents);
await db.delete(activityLog);
try {
await db.delete(costEvents);
await db.delete(heartbeatRuns);
return;
} catch (error) {
@@ -3,6 +3,7 @@ import { eq, sql } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
import { PROVIDER_QUOTA_MONITOR_SERVICE_NAME } from "@paperclipai/shared";
import {
costEvents,
activityLog,
agentRuntimeState,
agentWakeupRequests,
@@ -111,6 +112,7 @@ describeEmbeddedPostgres("issue monitor scheduler", () => {
await db.delete(environmentLeases);
await db.delete(workspaceRuntimeServices);
await db.delete(issues);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(agentWakeupRequests);
await db.delete(agentRuntimeState);
@@ -4,6 +4,7 @@ import request from "supertest";
import { and, eq } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import {
costEvents,
activityLog,
agentRuntimeState,
agents,
@@ -94,6 +95,7 @@ describeEmbeddedPostgres("issue create onboarding first-task routes", () => {
await db.delete(issueThreadInteractions);
await db.delete(issueComments);
await db.delete(heartbeatRunEvents);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(agentWakeupRequests);
await db.delete(agentRuntimeState);
@@ -7,6 +7,7 @@ import request from "supertest";
import { and, eq } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import {
costEvents,
agents,
agentRuntimeState,
authUsers,
@@ -154,6 +155,7 @@ describeEmbeddedPostgres("issue recovery actions", () => {
await db.delete(issueComments);
await db.delete(environmentLeases);
await db.delete(activityLog);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(agentWakeupRequests);
await db.delete(environments);
@@ -4,6 +4,7 @@ import request from "supertest";
import { and, eq } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import {
costEvents,
activityLog,
agentRuntimeState,
agentWakeupRequests,
@@ -80,6 +81,7 @@ describeEmbeddedPostgres("issue watchdog routes", () => {
await db.delete(activityLog);
await db.delete(issueComments);
await db.delete(heartbeatRunEvents);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(agentWakeupRequests);
await db.delete(agentRuntimeState);
@@ -6,6 +6,7 @@ import { WebSocketServer } from "ws";
import { and, eq } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
import {
costEvents,
activityLog,
authUsers,
agentWakeupRequests,
@@ -103,6 +104,7 @@ async function deleteHeartbeatRunsAndWakeupsAfterActivityLogDrains(db: Db) {
await db.delete(activityLog);
try {
await db.delete(heartbeatRunEvents);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(agentWakeupRequests);
return;
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import { eq } from "drizzle-orm";
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import {
costEvents,
activityLog,
agents,
companies,
@@ -54,6 +55,7 @@ describeEmbeddedPostgres("recovery sweepStaleIssueLocks", () => {
await db.delete(issueRelations);
await db.delete(activityLog);
await db.delete(heartbeatRunEvents);
await db.delete(costEvents);
await db.delete(heartbeatRuns);
await db.delete(completionContracts);
await db.delete(issues);
+7
View File
@@ -21,6 +21,13 @@ type SupertestTestConstructor = {
};
};
// Receipt-spool recovery must never scan a developer's instance during tests.
const paperclipTestHome = fs.mkdtempSync(path.join(os.tmpdir(), "paperclip-vitest-home-"));
process.env.PAPERCLIP_HOME = paperclipTestHome;
// Setup-file afterAll hooks run before suites drain asynchronous heartbeats.
// Cleanup at worker exit, after all suite teardown, avoids deleting live state.
process.once("exit", () => fs.rmSync(paperclipTestHome, { recursive: true, force: true, maxRetries: 8, retryDelay: 50 }));
const require = createRequire(import.meta.url);
const SupertestTest = require("supertest/lib/test.js") as SupertestTestConstructor;
@@ -12,6 +12,7 @@ it("runs every active nested/parameterized fixture case exactly once through the
try {
const tests = path.join(root, "server/src/__tests__");
mkdirSync(tests, { recursive: true });
mkdirSync(path.join(root, "server/scripts"), { recursive: true });
symlinkSync(path.join(repoRoot, "node_modules"), path.join(root, "node_modules"), "junction");
writeFileSync(path.join(root, "package.json"), JSON.stringify({ private: true }));
writeFileSync(path.join(root, "vitest.config.mjs"), `export default {