import { convexTest } from "convex-test"; import { anyApi } from "convex/server"; import { describe, expect, test } from "vitest"; import schema from "./schema"; const modules = import.meta.glob("./**/*.ts"); const api = anyApi; type TestContext = ReturnType; interface Seeded { attemptId: string; runId: string; t: TestContext; workId: string; } // Seed an in-flight real attempt so each mutation under test starts from a // realistic running state: Work "executing", Run "running", Attempt "running". const seedRunningAttempt = async ( overrides: { attemptStatus?: "queued" | "claimed" | "running" | "terminal"; runStatus?: "ready" | "running" | "terminal" | "cancelled"; } = {} ): Promise => { const t = convexTest({ modules, schema }); const seeded = await t.run(async (ctx) => { const organizationId = await ctx.db.insert("organizations", { createdAt: 1, createdBy: "user", kind: "personal", name: "Personal", }); const projectId = await ctx.db.insert("projects", { createdAt: 1, name: "Zopu", normalizedSourceUrl: "https://github.com/puter/zopu", organizationId, repositoryPath: "puter/zopu", sourceHost: "github.com", sourceUrl: "https://github.com/puter/zopu", updatedAt: 1, }); const workId = await ctx.db.insert("works", { createdAt: 1, objective: "Implement a real slice", organizationId, projectId, status: "executing", title: "Real execution", updatedAt: 1, }); const sliceRowId = await ctx.db.insert("workSlices", { designVersion: 1, objective: "Change the repo", observableBehavior: "A diff exists", ordinal: 0, payloadJson: "{}", sliceId: "slice-1", status: "running", title: "Implementation", workId, }); const runId = await ctx.db.insert("workRuns", { createdAt: 1, designVersion: 1, executionKind: "real", kitId: "coding-v0", kitVersion: "1", scenario: "success", sliceId: "slice-1", sliceRowId, status: overrides.runStatus ?? "running", workId, }); const attemptId = await ctx.db.insert("workAttempts", { number: 1, runId, status: overrides.attemptStatus ?? "running", workId, workspaceKey: "workspace-1", }); return { attemptId, runId, workId }; }); return { ...seeded, t }; }; const successResult = { baseRevision: "base123", candidateRevision: "candidate456", changedFiles: ["src/index.ts"], diff: "+export const ready = true;", environmentId: "workspace-1", events: [ { kind: "runtime.completed", message: "completed", metadata: {}, occurredAt: 2, sequence: 0, }, ], summary: "Changed one file", }; describe("real work execution persistence", () => { test("records revisions, activity, diff, and terminal state atomically", async () => { const { t, ...seeded } = await seedRunningAttempt(); await t.mutation(api.workExecutionWorkflow.completeAttempt, { attemptId: seeded.attemptId, result: successResult, }); const state = await t.run(async (ctx) => ({ artifacts: await ctx.db.query("workArtifacts").collect(), attempt: await ctx.db.get(seeded.attemptId as any), run: await ctx.db.get(seeded.runId as any), work: await ctx.db.get(seeded.workId as any), })); expect(state.attempt?.classification).toBe("Succeeded"); expect(state.run).toMatchObject({ baseRevision: "base123", candidateRevision: "candidate456", terminalClassification: "Succeeded", }); expect(state.work?.status).toBe("completed"); expect(state.artifacts[0]).toMatchObject({ kind: "diff", sourceRevision: "candidate456", }); }); }); describe("classified failure mapping", () => { test("maps a transient runtime reason to a retryable classification", async () => { const { t, ...seeded } = await seedRunningAttempt(); await t.mutation(api.workExecutionWorkflow.failAttempt, { attemptId: seeded.attemptId, reason: "ProviderUnavailable", retryable: true, summary: "model provider timed out", }); const attempt = await t.run((ctx) => ctx.db.get(seeded.attemptId as any)); expect(attempt?.classification).toBe("RetryableFailure"); expect(attempt?.failureReason).toBe("ProviderUnavailable"); }); test("queues a new attempt without terminalizing a retryable run", async () => { const { t, ...seeded } = await seedRunningAttempt(); await t.mutation(api.workExecutionWorkflow.failAttempt, { attemptId: seeded.attemptId, reason: "ProviderUnavailable", retryable: true, summary: "provider unavailable", }); const state = await t.run(async (ctx) => ({ attempts: await ctx.db.query("workAttempts").collect(), run: await ctx.db.get(seeded.runId as any), work: await ctx.db.get(seeded.workId as any), })); expect(state.attempts).toHaveLength(2); expect(state.attempts[1]).toMatchObject({ number: 2, status: "queued", workspaceKey: "workspace-1", }); expect(state.run?.status).toBe("running"); expect(state.work?.status).toBe("executing"); }); test("maps an authentication reason to a permanent failure", async () => { const { t, ...seeded } = await seedRunningAttempt(); await t.mutation(api.workExecutionWorkflow.failAttempt, { attemptId: seeded.attemptId, reason: "Authentication", retryable: false, summary: "token rejected", }); const state = await t.run(async (ctx) => ({ attempt: await ctx.db.get(seeded.attemptId as any), run: await ctx.db.get(seeded.runId as any), work: await ctx.db.get(seeded.workId as any), })); expect(state.attempt?.classification).toBe("PermanentFailure"); expect(state.attempt?.failureReason).toBe("Authentication"); expect(state.run?.terminalClassification).toBe("PermanentFailure"); expect(state.work?.status).toBe("failed"); }); }); describe("cancellation fencing", () => { test("a cancelled attempt cannot later settle success", async () => { const { t, ...seeded } = await seedRunningAttempt(); // Simulate cancellation marking the attempt/run terminal as Cancelled. await t.run(async (ctx) => { await ctx.db.patch(seeded.attemptId as any, { classification: "Cancelled", endedAt: 5, status: "terminal", summary: "Execution cancelled", }); await ctx.db.patch(seeded.runId as any, { endedAt: 5, status: "cancelled", terminalClassification: "Cancelled", terminalSummary: "Execution cancelled", }); }); // A late-arriving completion must be ignored. await t.mutation(api.workExecutionWorkflow.completeAttempt, { attemptId: seeded.attemptId, result: successResult, }); const attempt = await t.run((ctx) => ctx.db.get(seeded.attemptId as any)); expect(attempt?.classification).toBe("Cancelled"); expect(attempt?.status).toBe("terminal"); }); test("a late failure does not overwrite a cancelled run", async () => { const { t, ...seeded } = await seedRunningAttempt(); await t.run(async (ctx) => { await ctx.db.patch(seeded.attemptId as any, { classification: "Cancelled", endedAt: 5, status: "terminal", }); await ctx.db.patch(seeded.runId as any, { status: "cancelled" }); }); await t.mutation(api.workExecutionWorkflow.failAttempt, { attemptId: seeded.attemptId, reason: "HarnessFailed", retryable: true, summary: "late failure", }); const attempt = await t.run((ctx) => ctx.db.get(seeded.attemptId as any)); expect(attempt?.classification).toBe("Cancelled"); }); }); describe("empty-change rejection", () => { test("a no-op result with no changed files fails as permanent", async () => { const { t, ...seeded } = await seedRunningAttempt(); await t.mutation(api.workExecutionWorkflow.completeAttempt, { attemptId: seeded.attemptId, result: { ...successResult, changedFiles: [], candidateRevision: "base123", }, }); const state = await t.run(async (ctx) => ({ attempt: await ctx.db.get(seeded.attemptId as any), work: await ctx.db.get(seeded.workId as any), })); expect(state.attempt?.classification).toBe("PermanentFailure"); expect(state.attempt?.failureReason).toBe("InvalidInput"); expect(state.work?.status).toBe("failed"); }); test("identical base and candidate revisions are rejected", async () => { const { t, ...seeded } = await seedRunningAttempt(); await t.mutation(api.workExecutionWorkflow.completeAttempt, { attemptId: seeded.attemptId, result: { ...successResult, baseRevision: "same", candidateRevision: "same", }, }); const attempt = await t.run((ctx) => ctx.db.get(seeded.attemptId as any)); expect(attempt?.classification).toBe("PermanentFailure"); }); }); describe("attempt re-entry", () => { test("markAttemptRunning re-claims an already-running attempt", async () => { const { t, ...seeded } = await seedRunningAttempt(); // First claim (already "running" from seed) must still succeed so a // workflow replay can re-enter the same live attempt. const first = await t.mutation( api.workExecutionWorkflow.markAttemptRunning, { attemptId: seeded.attemptId } ); expect(first).toBe(true); const attempt = await t.run((ctx) => ctx.db.get(seeded.attemptId as any)); expect(attempt?.status).toBe("running"); expect(attempt?.leaseExpiresAt).toBeGreaterThan(0); }); test("a terminal attempt is not re-runnable", async () => { const { t, ...seeded } = await seedRunningAttempt({ attemptStatus: "terminal", }); const result = await t.mutation( api.workExecutionWorkflow.markAttemptRunning, { attemptId: seeded.attemptId } ); expect(result).toBe(false); }); test("a cancelled run cannot re-enter a queued attempt", async () => { const { t, ...seeded } = await seedRunningAttempt({ attemptStatus: "queued", runStatus: "cancelled", }); const result = await t.mutation( api.workExecutionWorkflow.markAttemptRunning, { attemptId: seeded.attemptId } ); expect(result).toBe(false); const attempt = await t.run((ctx) => ctx.db.get(seeded.attemptId as any)); expect(attempt?.status).toBe("queued"); }); }); describe("forge credential validation", () => { const identity = { tokenIdentifier: "https://convex.test|forge-user" }; const seedForgableProject = async ( provider: "github" | "gitea", sourceHost: string ) => { const t = convexTest({ modules, schema }).withIdentity(identity); const ids = await t.run(async (ctx) => { const organizationId = await ctx.db.insert("organizations", { createdAt: 1, createdBy: identity.tokenIdentifier, kind: "personal", name: "Personal", }); await ctx.db.insert("organizationMembers", { createdAt: 1, organizationId, role: "owner", userId: identity.tokenIdentifier, }); const projectId = await ctx.db.insert("projects", { createdAt: 1, name: "Zopu", normalizedSourceUrl: `https://${sourceHost}/puter/zopu`, organizationId, repositoryPath: "puter/zopu", sourceHost, sourceUrl: `https://${sourceHost}/puter/zopu`, updatedAt: 1, }); const connectionId = await ctx.db.insert("gitConnections", { connectedAt: 1, credentialCiphertext: "x", credentialIv: "y", credentialKind: provider === "github" ? "oauth" : "token", organizationId, provider, serverUrl: `https://${sourceHost}`, updatedAt: 1, username: "zopu", }); return { connectionId, projectId }; }); return { t, ids }; }; test("rejects a git connection whose provider mismatches the project forge", async () => { // Gitea token attached to a GitHub-hosted project. const { t, ids } = await seedForgableProject("gitea", "github.com"); await expect( t.mutation(api.gitConnectionData.attachToProject, { connectionId: ids.connectionId, projectId: ids.projectId, }) ).rejects.toThrow(/does not match this project's forge/u); }); test("accepts a matching provider for the project forge", async () => { const { t, ids } = await seedForgableProject("github", "github.com"); const result = await t.mutation(api.gitConnectionData.attachToProject, { connectionId: ids.connectionId, projectId: ids.projectId, }); expect(result).toMatchObject({ attached: true }); }); });