From 438948530592ff9f3d3b3c9cc2109e3850e16b52 Mon Sep 17 00:00:00 2001 From: Diksha Dabhole Date: Tue, 23 Jun 2026 23:18:02 +0530 Subject: [PATCH 1/2] refactor(maintainer): use unwrapJoin to safely parse github_installations relations --- src/lib/maintainer/detect.ts | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/lib/maintainer/detect.ts b/src/lib/maintainer/detect.ts index 0f2c9240..f52dd1f9 100644 --- a/src/lib/maintainer/detect.ts +++ b/src/lib/maintainer/detect.ts @@ -1,5 +1,6 @@ import { getServiceSupabase } from '@/lib/supabase/service'; import { cacheGet, cacheSet } from '@/lib/cache'; +import { unwrapJoin } from '@/lib/supabase/inner-join'; /** * "Is this user a maintainer?" — true if they have at least one active @@ -30,8 +31,7 @@ export async function isUserMaintainer(userId: string): Promise { .limit(20); const has = (data ?? []).some((row) => { - const i = (row as unknown as { github_installations: { uninstalled_at: string | null } | null }) - .github_installations; + const i = unwrapJoin<{ uninstalled_at: string | null }>((row as any).github_installations); return i && i.uninstalled_at === null; }); @@ -64,15 +64,23 @@ export async function listMaintainerInstalls(userId: string): Promise row as unknown as Row) + .map((row) => { + const r = row as unknown as Row; + return { + ...r, + github_installations: unwrapJoin(r.github_installations), + }; + }) .filter((r) => r.github_installations && r.github_installations.uninstalled_at === null) .map((r) => ({ installationId: r.installation_id, From f70608d431138a87fb7b154d6d9c774ba125b67d Mon Sep 17 00:00:00 2001 From: Diksha Dabhole Date: Wed, 24 Jun 2026 08:43:44 +0530 Subject: [PATCH 2/2] test: add array-shape mock payloads and unify cast types --- src/lib/maintainer/detect.test.ts | 43 +++++++++++++++++++++++++++++++ src/lib/maintainer/detect.ts | 3 ++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/lib/maintainer/detect.test.ts b/src/lib/maintainer/detect.test.ts index 31c64a58..977f1e3c 100644 --- a/src/lib/maintainer/detect.test.ts +++ b/src/lib/maintainer/detect.test.ts @@ -50,6 +50,19 @@ describe('isUserMaintainer', () => { expect(cacheSet).toHaveBeenCalledWith('maint:status:user1', true, 3600); }); + it('returns true when github_installations is returned as an array (inner join ambiguity)', async () => { + vi.mocked(cacheGet).mockResolvedValue(null); + vi.mocked(getServiceSupabase).mockReturnValue( + mockSupabase({ + github_installation_users: [{ github_installations: [{ uninstalled_at: null }] }], + }) as unknown as ReturnType, + ); + + const result = await isUserMaintainer('user1'); + expect(result).toBe(true); + expect(cacheSet).toHaveBeenCalledWith('maint:status:user1', true, 3600); + }); + it('returns false when all installation rows have uninstalled_at set', async () => { vi.mocked(cacheGet).mockResolvedValue(null); vi.mocked(getServiceSupabase).mockReturnValue( @@ -143,6 +156,36 @@ describe('listMaintainerInstalls', () => { ]); }); + it('handles github_installations returned as an array (inner join ambiguity)', async () => { + vi.mocked(getServiceSupabase).mockReturnValue( + mockSupabase({ + github_installation_users: [ + { + installation_id: 1, + permission_level: 'org_admin', + github_installations: [ + { + account_login: 'org1', + account_type: 'Organization', + uninstalled_at: null, + }, + ], + }, + ], + }) as unknown as ReturnType, + ); + + const result = await listMaintainerInstalls('user1'); + expect(result).toEqual([ + { + installationId: 1, + accountLogin: 'org1', + accountType: 'Organization', + permissionLevel: 'org_admin', + }, + ]); + }); + it('filters out installs where uninstalled_at is not null', async () => { vi.mocked(getServiceSupabase).mockReturnValue( mockSupabase({ diff --git a/src/lib/maintainer/detect.ts b/src/lib/maintainer/detect.ts index f52dd1f9..eaa55884 100644 --- a/src/lib/maintainer/detect.ts +++ b/src/lib/maintainer/detect.ts @@ -31,7 +31,8 @@ export async function isUserMaintainer(userId: string): Promise { .limit(20); const has = (data ?? []).some((row) => { - const i = unwrapJoin<{ uninstalled_at: string | null }>((row as any).github_installations); + const r = row as unknown as { github_installations: unknown }; + const i = unwrapJoin<{ uninstalled_at: string | null }>(r.github_installations); return i && i.uninstalled_at === null; });