168 lines
5.1 KiB
TypeScript
168 lines
5.1 KiB
TypeScript
import { convexTest } from 'convex-test';
|
|
import { makeFunctionReference } from 'convex/server';
|
|
import { describe, expect, test } from 'vitest';
|
|
|
|
import { internal } from '../../convex/_generated/api.js';
|
|
import schema from '../../convex/schema';
|
|
import {
|
|
shouldAutoSync,
|
|
shouldCreateReviewThread,
|
|
} from '../../convex/syncGating';
|
|
|
|
const modules = import.meta.glob('../../convex/**/*.*s');
|
|
const backfillAutoSyncDefaults = makeFunctionReference<
|
|
'mutation',
|
|
Record<string, never>,
|
|
{ updated: number }
|
|
>('migrations:backfillAutoSyncDefaults');
|
|
|
|
describe('syncGating', () => {
|
|
test('auto-sync requires the flag and a clean behind compare', () => {
|
|
expect(
|
|
shouldAutoSync(
|
|
{ autoSyncEnabled: false, requireCleanCompareForSync: true },
|
|
{ status: 'behind', forkAheadBy: 0 },
|
|
),
|
|
).toBe(false);
|
|
expect(
|
|
shouldAutoSync(
|
|
{ autoSyncEnabled: true, requireCleanCompareForSync: true },
|
|
{ status: 'behind', forkAheadBy: 0 },
|
|
),
|
|
).toBe(true);
|
|
expect(
|
|
shouldAutoSync(
|
|
{ autoSyncEnabled: true, requireCleanCompareForSync: true },
|
|
{ status: 'behind', forkAheadBy: 2 },
|
|
),
|
|
).toBe(false);
|
|
expect(
|
|
shouldAutoSync(
|
|
{ autoSyncEnabled: true, requireCleanCompareForSync: true },
|
|
{ status: 'diverged', forkAheadBy: 0 },
|
|
),
|
|
).toBe(false);
|
|
});
|
|
|
|
test('review thread requires autoReviewEnabled and diverged', () => {
|
|
expect(
|
|
shouldCreateReviewThread(
|
|
{ autoReviewEnabled: false },
|
|
{ status: 'diverged' },
|
|
),
|
|
).toBe(false);
|
|
expect(
|
|
shouldCreateReviewThread(
|
|
{ autoReviewEnabled: true },
|
|
{ status: 'diverged' },
|
|
),
|
|
).toBe(true);
|
|
expect(
|
|
shouldCreateReviewThread(
|
|
{ autoReviewEnabled: true },
|
|
{ status: 'behind' },
|
|
),
|
|
).toBe(false);
|
|
});
|
|
|
|
test('backfill enables auto-sync only for disabled GitHub spoons', async () => {
|
|
const t = convexTest(schema, modules);
|
|
const seeded = await t.mutation(async (ctx) => {
|
|
const now = Date.now();
|
|
const ownerId = await ctx.db.insert('users', {
|
|
email: 'owner@example.com',
|
|
name: 'Owner',
|
|
});
|
|
const createSpoon = async (
|
|
name: string,
|
|
provider: 'github' | 'gitea',
|
|
autoSyncEnabled: boolean,
|
|
) => {
|
|
const spoonId = await ctx.db.insert('spoons', {
|
|
ownerId,
|
|
name,
|
|
provider,
|
|
upstreamOwner: 'upstream',
|
|
upstreamRepo: name,
|
|
upstreamDefaultBranch: 'main',
|
|
upstreamUrl: `https://example.com/upstream/${name}`,
|
|
forkOwner: 'owner',
|
|
forkRepo: name,
|
|
forkUrl: `https://example.com/owner/${name}`,
|
|
visibility: 'private',
|
|
maintenanceMode: 'watch',
|
|
syncCadence: 'daily',
|
|
productionRefStrategy: 'default_branch',
|
|
status: 'active',
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
});
|
|
const settingsId = await ctx.db.insert('spoonSettings', {
|
|
spoonId,
|
|
ownerId,
|
|
autoRefreshEnabled: true,
|
|
autoReviewEnabled: true,
|
|
autoSyncEnabled,
|
|
requireAiLowRiskForSync: true,
|
|
requireCleanCompareForSync: true,
|
|
ignoredFilePatterns: [],
|
|
importantFilePatterns: [],
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
});
|
|
return settingsId;
|
|
};
|
|
|
|
return {
|
|
disabledGithub: await createSpoon('disabled-github', 'github', false),
|
|
enabledGithub: await createSpoon('enabled-github', 'github', true),
|
|
disabledGitea: await createSpoon('disabled-gitea', 'gitea', false),
|
|
};
|
|
});
|
|
|
|
const result = await t.mutation(backfillAutoSyncDefaults, {});
|
|
const settings = await t.run(async (ctx) => ({
|
|
disabledGithub: await ctx.db.get(seeded.disabledGithub),
|
|
enabledGithub: await ctx.db.get(seeded.enabledGithub),
|
|
disabledGitea: await ctx.db.get(seeded.disabledGitea),
|
|
}));
|
|
|
|
expect(result).toEqual({ updated: 1 });
|
|
expect(settings.disabledGithub?.autoSyncEnabled).toBe(true);
|
|
expect(settings.enabledGithub?.autoSyncEnabled).toBe(true);
|
|
expect(settings.disabledGitea?.autoSyncEnabled).toBe(false);
|
|
});
|
|
|
|
test('new GitHub spoons default auto-sync on', async () => {
|
|
const t = convexTest(schema, modules);
|
|
const ownerId = await t.mutation(async (ctx) => {
|
|
return await ctx.db.insert('users', {
|
|
email: 'new-owner@example.com',
|
|
name: 'New owner',
|
|
});
|
|
});
|
|
|
|
const spoonId = await t.mutation(internal.github.createForkSpoonRecord, {
|
|
ownerId,
|
|
name: 'github-spoon',
|
|
upstreamOwner: 'upstream',
|
|
upstreamRepo: 'repo',
|
|
upstreamDefaultBranch: 'main',
|
|
upstreamUrl: 'https://github.com/upstream/repo',
|
|
forkOwner: 'new-owner',
|
|
forkRepo: 'repo',
|
|
forkDefaultBranch: 'main',
|
|
forkUrl: 'https://github.com/new-owner/repo',
|
|
visibility: 'private',
|
|
});
|
|
const settings = await t.run(async (ctx) => {
|
|
return await ctx.db
|
|
.query('spoonSettings')
|
|
.withIndex('by_spoon', (q) => q.eq('spoonId', spoonId))
|
|
.unique();
|
|
});
|
|
|
|
expect(settings?.autoSyncEnabled).toBe(true);
|
|
});
|
|
});
|