import { convexTest } from 'convex-test'; import { afterAll, beforeAll, describe, expect, test } from 'vitest'; import { api } from '../../convex/_generated/api.js'; import schema from '../../convex/schema'; const modules = import.meta.glob('../../convex/**/*.*s'); beforeAll(() => { process.env.SPOON_WORKER_TOKEN = 'test-worker-token'; }); afterAll(() => { delete process.env.SPOON_WORKER_TOKEN; }); describe('applyMaintenanceDecision', () => { test('resolves a low-risk sync and stops its maintenance job', async () => { const t = convexTest(schema, modules); const { jobId, threadId } = await t.mutation(async (ctx) => { const now = Date.now(); const ownerId = await ctx.db.insert('users', { email: 'maintenance@example.com', name: 'Maintenance Reviewer', }); const spoonId = await ctx.db.insert('spoons', { ownerId, name: 'Maintenance Spoon', provider: 'gitea', upstreamOwner: 'upstream', upstreamRepo: 'maintenance', upstreamDefaultBranch: 'main', upstreamUrl: 'https://git.example.com/upstream/maintenance', forkOwner: 'team', forkRepo: 'maintenance-spoon', forkUrl: 'https://git.example.com/team/maintenance-spoon', visibility: 'private', maintenanceMode: 'watch', syncCadence: 'daily', productionRefStrategy: 'default_branch', status: 'active', createdAt: now, updatedAt: now, }); const requestId = await ctx.db.insert('agentRequests', { spoonId, ownerId, prompt: 'Review upstream maintenance changes', requestType: 'upstream_review', status: 'running', createdAt: now, updatedAt: now, }); const threadId = await ctx.db.insert('threads', { ownerId, spoonId, title: 'Upstream maintenance review', source: 'upstream_update', status: 'running', priority: 'normal', relatedAgentRequestId: requestId, createdAt: now, updatedAt: now, }); const jobId = await ctx.db.insert('agentJobs', { spoonId, ownerId, agentRequestId: requestId, threadId, jobType: 'maintenance_review', status: 'running', workspaceStatus: 'active', prompt: 'Review upstream maintenance changes', runtime: 'opencode', baseBranch: 'main', workBranch: 'spoon/maintenance-review', forkOwner: 'team', forkRepo: 'maintenance-spoon', forkUrl: 'https://git.example.com/team/maintenance-spoon', upstreamOwner: 'upstream', upstreamRepo: 'maintenance', selectedSecretIds: [], model: '', reasoningEffort: 'medium', claimedBy: 'w1', createdAt: now, updatedAt: now, }); return { jobId, threadId }; }); await t.mutation(api.agentJobs.applyMaintenanceDecision, { workerToken: 'test-worker-token', workerId: 'w1', jobId, decision: 'sync', risk: 'low', summary: 'Safe to sync automatically.', ignoredCommitShas: [], recommendedAction: 'Sync the upstream changes.', requiresUserApproval: false, }); const { job, thread } = await t.run(async (ctx) => ({ job: await ctx.db.get(jobId), thread: await ctx.db.get(threadId), })); expect(thread?.status).toBe('resolved'); expect(job?.workspaceStatus).toBe('stopped'); expect(job?.completedAt).toEqual(expect.any(Number)); }); });