From b28f6e1e35359e4d1fb1b66e8cddf16bf78f97ac Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Thu, 10 Jul 2025 10:14:16 -0600 Subject: [PATCH] additional types fixes --- .../security/get-approved-domains.int.test.ts | 2 +- .../get-workspace-settings.int.test.ts | 4 +- .../remove-approved-domains.int.test.ts | 4 +- .../api/v2/security/security-utils.test.ts | 18 ++++---- .../src/api/v2/security/test-db-utils.ts | 41 ++++++++++--------- .../update-workspace-settings.int.test.ts | 8 ++-- .../workspace-settings-service.test.ts | 9 +++- packages/supabase/biome.json | 16 ++++++++ packages/supabase/package.json | 5 ++- packages/supabase/tsconfig.json | 4 ++ 10 files changed, 70 insertions(+), 41 deletions(-) create mode 100644 packages/supabase/biome.json create mode 100644 packages/supabase/tsconfig.json diff --git a/apps/server/src/api/v2/security/get-approved-domains.int.test.ts b/apps/server/src/api/v2/security/get-approved-domains.int.test.ts index 7babf610a..1f40b8217 100644 --- a/apps/server/src/api/v2/security/get-approved-domains.int.test.ts +++ b/apps/server/src/api/v2/security/get-approved-domains.int.test.ts @@ -95,7 +95,7 @@ describe('getApprovedDomainsHandler (integration)', () => { let userWithoutOrg; try { userWithoutOrg = await createUserWithoutOrganization(); - + await expect(getApprovedDomainsHandler(userWithoutOrg)).rejects.toThrow(HTTPException); await expect(getApprovedDomainsHandler(userWithoutOrg)).rejects.toMatchObject({ status: 403, diff --git a/apps/server/src/api/v2/security/get-workspace-settings.int.test.ts b/apps/server/src/api/v2/security/get-workspace-settings.int.test.ts index 25ab81f09..760bb1ed1 100644 --- a/apps/server/src/api/v2/security/get-workspace-settings.int.test.ts +++ b/apps/server/src/api/v2/security/get-workspace-settings.int.test.ts @@ -111,12 +111,12 @@ describe('getWorkspaceSettingsHandler (integration)', () => { describe('Error Cases', () => { it('should return 403 for user without organization', async () => { const userWithoutOrg = await createUserWithoutOrganization(); - + await expect(getWorkspaceSettingsHandler(userWithoutOrg)).rejects.toMatchObject({ status: 403, message: 'User is not associated with an organization', }); - + await cleanupTestUser(userWithoutOrg.id); }); diff --git a/apps/server/src/api/v2/security/remove-approved-domains.int.test.ts b/apps/server/src/api/v2/security/remove-approved-domains.int.test.ts index f24ae80c4..e703dcaaf 100644 --- a/apps/server/src/api/v2/security/remove-approved-domains.int.test.ts +++ b/apps/server/src/api/v2/security/remove-approved-domains.int.test.ts @@ -137,7 +137,9 @@ describe('removeApprovedDomainsHandler (integration)', () => { const userWithoutOrg = await createUserWithoutOrganization(); const request = { domains: ['remove1.com'] }; - await expect(removeApprovedDomainsHandler(request, userWithoutOrg)).rejects.toThrow(HTTPException); + await expect(removeApprovedDomainsHandler(request, userWithoutOrg)).rejects.toThrow( + HTTPException + ); await expect(removeApprovedDomainsHandler(request, userWithoutOrg)).rejects.toMatchObject({ status: 403, message: 'User is not associated with an organization', diff --git a/apps/server/src/api/v2/security/security-utils.test.ts b/apps/server/src/api/v2/security/security-utils.test.ts index b372163c5..a73463df1 100644 --- a/apps/server/src/api/v2/security/security-utils.test.ts +++ b/apps/server/src/api/v2/security/security-utils.test.ts @@ -123,19 +123,15 @@ describe('security-utils', () => { it('should reject other roles', () => { const roles = ['member', 'viewer', 'user', 'guest']; - roles.forEach((role) => { + for (const role of roles) { expect(() => checkAdminPermissions(role)).toThrow(HTTPException); - expect(() => checkAdminPermissions(role)).toThrow( - 'Insufficient admin permissions' - ); - }); + expect(() => checkAdminPermissions(role)).toThrow('Insufficient admin permissions'); + } }); it('should reject null role', () => { expect(() => checkAdminPermissions(null)).toThrow(HTTPException); - expect(() => checkAdminPermissions(null)).toThrow( - 'Insufficient admin permissions' - ); + expect(() => checkAdminPermissions(null)).toThrow('Insufficient admin permissions'); }); it('should reject undefined role', () => { @@ -158,12 +154,12 @@ describe('security-utils', () => { it('should reject other roles', () => { const roles = ['admin', 'member', 'viewer', 'user']; - roles.forEach((role) => { + for (const role of roles) { expect(() => checkWorkspaceAdminPermission(role)).toThrow(HTTPException); expect(() => checkWorkspaceAdminPermission(role)).toThrow( 'Only workspace admins can update workspace settings' ); - }); + } }); it('should reject null role', () => { @@ -552,7 +548,7 @@ describe('security-utils', () => { // Should soft-delete all existing first, then batch upsert expect(mockTx.update).toHaveBeenCalledTimes(1); // 1 for soft-delete all expect(mockTx.insert).toHaveBeenCalledTimes(1); // 1 batch upsert for both datasets - + // Verify the insert was called with values expect(mockTx.insert).toHaveBeenCalled(); const insertMock = vi.mocked(mockTx.insert); diff --git a/apps/server/src/api/v2/security/test-db-utils.ts b/apps/server/src/api/v2/security/test-db-utils.ts index 09c382aa1..42fea605e 100644 --- a/apps/server/src/api/v2/security/test-db-utils.ts +++ b/apps/server/src/api/v2/security/test-db-utils.ts @@ -29,11 +29,13 @@ export async function createTestUserInDb(userData: Partial = {}): Promise< // Try to clean up any partial user data await db.delete(usersToOrganizations).where(eq(usersToOrganizations.userId, id)); // Re-throw to let the test handle it - throw new Error(`Failed to create test user: Database trigger tried to add user to non-existent organization. ${error.message}`); + throw new Error( + `Failed to create test user: Database trigger tried to add user to non-existent organization. ${error.message}` + ); } throw error; } - + return user as User; } @@ -68,13 +70,8 @@ export async function createTestOrgMemberInDb( const existing = await db .select() .from(usersToOrganizations) - .where( - and( - eq(usersToOrganizations.userId, userId), - isNull(usersToOrganizations.deletedAt) - ) - ); - + .where(and(eq(usersToOrganizations.userId, userId), isNull(usersToOrganizations.deletedAt))); + if (existing.length > 0) { // Delete existing memberships silently await db.delete(usersToOrganizations).where(eq(usersToOrganizations.userId, userId)); @@ -92,7 +89,7 @@ export async function createTestOrgMemberInDb( }; await db.insert(usersToOrganizations).values(member); - + // Verify the membership was created const verification = await db .select() @@ -105,11 +102,11 @@ export async function createTestOrgMemberInDb( ) ) .limit(1); - + if (!verification.length) { throw new Error('Failed to create test organization membership'); } - + if (verification[0].role !== role) { throw new Error(`Role mismatch: expected ${role}, got ${verification[0].role}`); } @@ -126,7 +123,7 @@ export async function cleanupTestUser(userId: string): Promise { export async function cleanupTestOrganization(orgId: string): Promise { // Import the necessary tables for cleanup const { permissionGroups, datasetsToPermissionGroups } = await import('@buster/database'); - + // Delete dataset associations for default permission group const defaultPermissionGroupName = `default:${orgId}`; const pgResult = await db @@ -134,15 +131,16 @@ export async function cleanupTestOrganization(orgId: string): Promise { .from(permissionGroups) .where(eq(permissionGroups.name, defaultPermissionGroupName)) .limit(1); - + if (pgResult[0]) { - await db.delete(datasetsToPermissionGroups) + await db + .delete(datasetsToPermissionGroups) .where(eq(datasetsToPermissionGroups.permissionGroupId, pgResult[0].id)); } - + // Delete permission groups await db.delete(permissionGroups).where(eq(permissionGroups.organizationId, orgId)); - + // Delete organization memberships await db.delete(usersToOrganizations).where(eq(usersToOrganizations.organizationId, orgId)); @@ -161,7 +159,10 @@ export async function getOrganizationFromDb(orgId: string): Promise { @@ -186,9 +187,9 @@ export async function verifyUserOrgMembership(userId: string, organizationId: st // Helper to create a user without any organization export async function createUserWithoutOrganization(): Promise { const user = await createTestUserInDb(); - + // Remove any auto-created organization memberships await db.delete(usersToOrganizations).where(eq(usersToOrganizations.userId, user.id)); - + return user; } diff --git a/apps/server/src/api/v2/security/update-workspace-settings.int.test.ts b/apps/server/src/api/v2/security/update-workspace-settings.int.test.ts index 20a8b740a..a25161b5d 100644 --- a/apps/server/src/api/v2/security/update-workspace-settings.int.test.ts +++ b/apps/server/src/api/v2/security/update-workspace-settings.int.test.ts @@ -37,7 +37,7 @@ describe('updateWorkspaceSettingsHandler (integration)', () => { describe('Happy Path', () => { it('should update all settings fields', async () => { await createTestOrgMemberInDb(testUser.id, testOrg.id, 'workspace_admin'); - + // Verify membership was created properly const membership = await verifyUserOrgMembership(testUser.id, testOrg.id); expect(membership).toBeTruthy(); @@ -130,7 +130,7 @@ describe('updateWorkspaceSettingsHandler (integration)', () => { restrictNewUserInvitations: false, defaultRole: 'querier', }); - + const roleUser = await createTestUserInDb(); await createTestOrgMemberInDb(roleUser.id, roleTestOrg.id, role); @@ -171,7 +171,7 @@ describe('updateWorkspaceSettingsHandler (integration)', () => { it('should return 403 for user without organization', async () => { const isolatedUser = await createUserWithoutOrganization(); - + const request = { restrict_new_user_invitations: true }; await expect(updateWorkspaceSettingsHandler(request, isolatedUser)).rejects.toThrow( @@ -181,7 +181,7 @@ describe('updateWorkspaceSettingsHandler (integration)', () => { status: 403, message: 'User is not associated with an organization', }); - + // Clean up await cleanupTestUser(isolatedUser.id); }); diff --git a/apps/server/src/api/v2/security/workspace-settings-service.test.ts b/apps/server/src/api/v2/security/workspace-settings-service.test.ts index 947cb17cd..81413846f 100644 --- a/apps/server/src/api/v2/security/workspace-settings-service.test.ts +++ b/apps/server/src/api/v2/security/workspace-settings-service.test.ts @@ -47,7 +47,14 @@ describe('WorkspaceSettingsService', () => { }); it('should handle all string values correctly', () => { - const roles = ['workspace_admin', 'data_admin', 'querier', 'restricted_querier', 'viewer', 'none']; + const roles = [ + 'workspace_admin', + 'data_admin', + 'querier', + 'restricted_querier', + 'viewer', + 'none', + ]; roles.forEach((role) => { const settings = { diff --git a/packages/supabase/biome.json b/packages/supabase/biome.json new file mode 100644 index 000000000..07aa74460 --- /dev/null +++ b/packages/supabase/biome.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "extends": ["../../biome.json"], + "overrides": [ + { + "include": ["**/*.ts"], + "linter": { + "rules": { + "suspicious": { + "noConsoleLog": "off" + } + } + } + } + ] +} diff --git a/packages/supabase/package.json b/packages/supabase/package.json index 5e791e0a1..b879b065c 100644 --- a/packages/supabase/package.json +++ b/packages/supabase/package.json @@ -2,5 +2,8 @@ "name": "@buster/supabase", "version": "0.0.1", "private": false, - "scripts": {} + "scripts": { + "lint": "biome check .", + "lint:fix": "biome check . --write" + } } diff --git a/packages/supabase/tsconfig.json b/packages/supabase/tsconfig.json new file mode 100644 index 000000000..01e85fe22 --- /dev/null +++ b/packages/supabase/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../typescript-config/base.json", + "include": ["volumes/**/*"] +}