import { convexTest } from 'convex-test'; import { describe, expect, test } from 'vitest'; import { internal } from '../../convex/_generated/api.js'; import schema from '../../convex/schema'; const modules = import.meta.glob('../../convex/**/*.*s'); describe('migrateOpenAiDirectRuntime', () => { test('rewrites legacy openai_direct rows to opencode and leaves others', async () => { const t = convexTest(schema, modules); const now = Date.now(); const ids = await t.run(async (ctx) => { const ownerId = await ctx.db.insert('users', { email: 'owner@example.com', name: 'Owner', }); const spoonId = await ctx.db.insert('spoons', { ownerId, name: 'legacy-spoon', provider: 'gitea', upstreamOwner: 'upstream', upstreamRepo: 'legacy-spoon', upstreamDefaultBranch: 'main', upstreamUrl: 'https://example.com/upstream/legacy-spoon', forkOwner: 'owner', forkRepo: 'legacy-spoon', forkUrl: 'https://example.com/owner/legacy-spoon', visibility: 'private', maintenanceMode: 'watch', syncCadence: 'daily', productionRefStrategy: 'default_branch', status: 'active', createdAt: now, updatedAt: now, }); const agentRequestId = await ctx.db.insert('agentRequests', { spoonId, ownerId, prompt: 'legacy request', status: 'running', createdAt: now, updatedAt: now, }); const settings = (runtime: 'openai_direct' | 'codex') => ({ spoonId, ownerId, enabled: true, runtime, branchPrefix: 'spoon/', agentModel: 'gpt-5', reasoningEffort: 'medium' as const, maxJobDurationMs: 600000, maxOutputBytes: 1000000, createdAt: now, updatedAt: now, }); const job = (runtime: 'openai_direct' | 'codex') => ({ spoonId, ownerId, agentRequestId, status: 'running' as const, prompt: 'legacy job', runtime, baseBranch: 'main', workBranch: 'spoon/legacy', forkOwner: 'owner', forkRepo: 'legacy-spoon', forkUrl: 'https://example.com/owner/legacy-spoon', upstreamOwner: 'upstream', upstreamRepo: 'legacy-spoon', selectedSecretIds: [], model: 'gpt-5', reasoningEffort: 'medium' as const, createdAt: now, updatedAt: now, }); return { legacySettings: await ctx.db.insert( 'spoonAgentSettings', settings('openai_direct'), ), legacyJob: await ctx.db.insert('agentJobs', job('openai_direct')), codexSettings: await ctx.db.insert( 'spoonAgentSettings', settings('codex'), ), codexJob: await ctx.db.insert('agentJobs', job('codex')), }; }); const result = await t.mutation( internal.migrations.migrateOpenAiDirectRuntime, {}, ); expect(result.patched).toBe(2); const after = await t.run(async (ctx) => ({ legacySettings: await ctx.db.get(ids.legacySettings), legacyJob: await ctx.db.get(ids.legacyJob), codexSettings: await ctx.db.get(ids.codexSettings), codexJob: await ctx.db.get(ids.codexJob), })); expect(after.legacySettings?.runtime).toBe('opencode'); expect(after.legacyJob?.runtime).toBe('opencode'); expect(after.codexSettings?.runtime).toBe('codex'); expect(after.codexJob?.runtime).toBe('codex'); }); });