import { convexTest } from 'convex-test'; import { describe, expect, test } from 'vitest'; import type { Id } from '../../convex/_generated/dataModel.js'; import { internal } from '../../convex/_generated/api.js'; import schema from '../../convex/schema'; const modules = import.meta.glob('../../convex/**/*.*s'); const createUser = async (t: ReturnType, email: string) => await t.mutation(async (ctx) => { return await ctx.db.insert('users', { email, name: email }); }); type SeedSpoon = { ownerId: Id<'users'>; upstreamOwner: string; upstreamRepo: string; forkOwner?: string; forkRepo?: string; status?: 'draft' | 'active' | 'needs_connection' | 'paused' | 'archived'; }; const seedSpoon = async (t: ReturnType, spoon: SeedSpoon) => (await t.mutation(async (ctx) => { const now = Date.now(); return await ctx.db.insert('spoons', { ownerId: spoon.ownerId, name: `${spoon.upstreamOwner}/${spoon.upstreamRepo}`, provider: 'github', upstreamOwner: spoon.upstreamOwner, upstreamRepo: spoon.upstreamRepo, upstreamDefaultBranch: 'main', upstreamUrl: `https://github.com/${spoon.upstreamOwner}/${spoon.upstreamRepo}`, forkOwner: spoon.forkOwner, forkRepo: spoon.forkRepo, forkUrl: spoon.forkOwner ? `https://github.com/${spoon.forkOwner}/${spoon.forkRepo}` : undefined, visibility: 'public', maintenanceMode: 'watch', syncCadence: 'daily', productionRefStrategy: 'default_branch', status: spoon.status ?? 'active', createdAt: now, updatedAt: now, }); })) as Id<'spoons'>; describe('spoons.findForRepo', () => { test('matches on the upstream side', async () => { const t = convexTest(schema, modules); const ownerId = (await createUser(t, 'owner@example.com')) as Id<'users'>; const upstreamSpoon = await seedSpoon(t, { ownerId, upstreamOwner: 'up', upstreamRepo: 'x', forkOwner: 'me', forkRepo: 'x-fork', }); await seedSpoon(t, { ownerId, upstreamOwner: 'other', upstreamRepo: 'y', forkOwner: 'me', forkRepo: 'y-fork', }); const result = await t.query(internal.spoons.findForRepo, { owner: 'up', repo: 'x', }); expect(result).toEqual([{ spoonId: upstreamSpoon, ownerId }]); }); test('matches on the fork side', async () => { const t = convexTest(schema, modules); const ownerId = (await createUser(t, 'owner@example.com')) as Id<'users'>; await seedSpoon(t, { ownerId, upstreamOwner: 'up', upstreamRepo: 'x', forkOwner: 'me', forkRepo: 'x-fork', }); const forkSpoon = await seedSpoon(t, { ownerId, upstreamOwner: 'other', upstreamRepo: 'y', forkOwner: 'me', forkRepo: 'x-fork-2', }); const result = await t.query(internal.spoons.findForRepo, { owner: 'me', repo: 'x-fork-2', }); expect(result).toEqual([{ spoonId: forkSpoon, ownerId }]); }); test('returns empty for an unknown repo', async () => { const t = convexTest(schema, modules); const ownerId = (await createUser(t, 'owner@example.com')) as Id<'users'>; await seedSpoon(t, { ownerId, upstreamOwner: 'up', upstreamRepo: 'x', forkOwner: 'me', forkRepo: 'x-fork', }); const result = await t.query(internal.spoons.findForRepo, { owner: 'nobody', repo: 'nothing', }); expect(result).toEqual([]); }); test('dedups a spoon whose upstream and fork are the same repo', async () => { const t = convexTest(schema, modules); const ownerId = (await createUser(t, 'owner@example.com')) as Id<'users'>; const dupSpoon = await seedSpoon(t, { ownerId, upstreamOwner: 'dup', upstreamRepo: 'z', forkOwner: 'dup', forkRepo: 'z', }); const result = await t.query(internal.spoons.findForRepo, { owner: 'dup', repo: 'z', }); expect(result).toEqual([{ spoonId: dupSpoon, ownerId }]); }); test('excludes archived spoons', async () => { const t = convexTest(schema, modules); const ownerId = (await createUser(t, 'owner@example.com')) as Id<'users'>; await seedSpoon(t, { ownerId, upstreamOwner: 'up', upstreamRepo: 'x', forkOwner: 'me', forkRepo: 'x-fork', status: 'archived', }); const upstreamResult = await t.query(internal.spoons.findForRepo, { owner: 'up', repo: 'x', }); const forkResult = await t.query(internal.spoons.findForRepo, { owner: 'me', repo: 'x-fork', }); expect(upstreamResult).toEqual([]); expect(forkResult).toEqual([]); }); });