diff --git a/apps/electric-server/tsconfig.json b/apps/electric-server/tsconfig.json index b104fe8b6..479a06577 100644 --- a/apps/electric-server/tsconfig.json +++ b/apps/electric-server/tsconfig.json @@ -1,6 +1,9 @@ { "extends": "@buster/typescript-config/base.json", "compilerOptions": {}, - "include": ["scripts/**/*"], - "exclude": ["node_modules"] -} + "include": ["scripts*"], + "exclude": ["node_modules"], + "references": [ + { "path": "../../packages/database" } + ] +} \ No newline at end of file diff --git a/apps/server/src/api/v2/chats/services/chat-helpers.test.ts b/apps/server/src/api/v2/chats/services/chat-helpers.test.ts index 93f6f040f..4d5416b1d 100644 --- a/apps/server/src/api/v2/chats/services/chat-helpers.test.ts +++ b/apps/server/src/api/v2/chats/services/chat-helpers.test.ts @@ -66,6 +66,8 @@ const mockMessage: Message = { updatedAt: new Date().toISOString(), deletedAt: null, feedback: null, + postProcessingMessage: null, + triggerRunId: null, }; describe('buildChatWithMessages', () => { diff --git a/apps/server/src/api/v2/chats/services/chat-redo.int.test.ts b/apps/server/src/api/v2/chats/services/chat-redo.int.test.ts index 80bdd204c..4174e252a 100644 --- a/apps/server/src/api/v2/chats/services/chat-redo.int.test.ts +++ b/apps/server/src/api/v2/chats/services/chat-redo.int.test.ts @@ -36,9 +36,9 @@ describe('Chat Message Redo Integration Tests', () => { await db.insert(organizations).values({ id: testOrgId, name: 'Test Organization for Redo', - slug: 'test-org-redo', - createdBy: testUserId, - updatedBy: testUserId, + restrictNewUserInvitations: false, + defaultRole: 'workspace_admin', + domains: [], }); // Create test user @@ -49,7 +49,6 @@ describe('Chat Message Redo Integration Tests', () => { email: 'test-redo@example.com', name: 'Test User Redo', avatarUrl: null, - metadata: {}, }) .returning(); diff --git a/apps/server/src/api/v2/chats/services/chat-service.test.ts b/apps/server/src/api/v2/chats/services/chat-service.test.ts index 2ff6d37e2..6bbc237e5 100644 --- a/apps/server/src/api/v2/chats/services/chat-service.test.ts +++ b/apps/server/src/api/v2/chats/services/chat-service.test.ts @@ -51,6 +51,8 @@ const mockMessage: Message = { updatedAt: new Date().toISOString(), deletedAt: null, feedback: null, + postProcessingMessage: null, + triggerRunId: null, }; // Mock database functions diff --git a/apps/server/src/api/v2/security/add-approved-domains.int.test.ts b/apps/server/src/api/v2/security/add-approved-domains.int.test.ts index f694477b5..40342083d 100644 --- a/apps/server/src/api/v2/security/add-approved-domains.int.test.ts +++ b/apps/server/src/api/v2/security/add-approved-domains.int.test.ts @@ -1,4 +1,5 @@ -import type { Organization, User } from '@buster/database'; +import type { User } from '@buster/database'; +import type { Organization, OrganizationRole } from '@buster/server-shared/organization'; import { HTTPException } from 'hono/http-exception'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { addApprovedDomainsHandler } from './add-approved-domains'; 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 987317fbd..f0d409f06 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 @@ -1,5 +1,6 @@ import { db, eq, organizations } from '@buster/database'; -import type { Organization, User } from '@buster/database'; +import type { User } from '@buster/database'; +import type { Organization, OrganizationRole } from '@buster/server-shared/organization'; import { HTTPException } from 'hono/http-exception'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { getApprovedDomainsHandler } from './get-approved-domains'; @@ -68,7 +69,7 @@ describe('getApprovedDomainsHandler (integration)', () => { }); it('should work for users with different roles', async () => { - const roles = ['querier', 'data_admin', 'workspace_admin']; + const roles: OrganizationRole[] = ['querier', 'data_admin', 'workspace_admin']; for (const role of roles) { let roleUser: User | undefined; @@ -153,7 +154,7 @@ describe('getApprovedDomainsHandler (integration)', () => { .limit(1); expect(orgAfter[0]?.domains).toEqual(originalOrg.domains); - expect(new Date(orgAfter[0]?.updatedAt).toISOString()).toEqual( + expect(new Date(orgAfter[0]!.updatedAt).toISOString()).toEqual( new Date(originalOrg.updatedAt).toISOString() ); }); 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 760bb1ed1..e38d85c35 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 @@ -1,5 +1,7 @@ import { db, eq, organizations } from '@buster/database'; -import type { Organization, User } from '@buster/database'; +import type { User } from '@buster/database'; +import type { OrganizationRole } from '@buster/server-shared/organization'; +import type { InferSelectModel } from 'drizzle-orm'; import { HTTPException } from 'hono/http-exception'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { getWorkspaceSettingsHandler } from './get-workspace-settings'; @@ -12,6 +14,8 @@ import { createUserWithoutOrganization, } from './test-db-utils'; +type Organization = InferSelectModel; + describe('getWorkspaceSettingsHandler (integration)', () => { let testUser: User; let testOrg: Organization; @@ -61,7 +65,12 @@ describe('getWorkspaceSettingsHandler (integration)', () => { }); it('should work for users with different roles', async () => { - const roles = ['querier', 'restricted_querier', 'data_admin', 'workspace_admin']; + const roles: OrganizationRole[] = [ + 'querier', + 'restricted_querier', + 'data_admin', + 'workspace_admin', + ]; for (const role of roles) { const roleUser = await createTestUserInDb(); @@ -169,7 +178,7 @@ describe('getWorkspaceSettingsHandler (integration)', () => { originalOrg.restrictNewUserInvitations ); expect(orgAfter[0]?.defaultRole).toEqual(originalOrg.defaultRole); - expect(new Date(orgAfter[0]?.updatedAt).toISOString()).toEqual(originalOrg.updatedAt); + expect(new Date(orgAfter[0]!.updatedAt).toISOString()).toEqual(originalOrg.updatedAt); }); it('should always return empty default_datasets array', async () => { diff --git a/apps/server/src/api/v2/security/get-workspace-settings.test.ts b/apps/server/src/api/v2/security/get-workspace-settings.test.ts index a0a6b50e8..ded98f057 100644 --- a/apps/server/src/api/v2/security/get-workspace-settings.test.ts +++ b/apps/server/src/api/v2/security/get-workspace-settings.test.ts @@ -67,7 +67,7 @@ describe('getWorkspaceSettingsHandler', () => { const orgWithDifferentSettings = { ...mockOrg, restrictNewUserInvitations: false, - defaultRole: 'data_admin', + defaultRole: 'data_admin' as const, }; vi.mocked(securityUtils.fetchOrganization).mockResolvedValue(orgWithDifferentSettings); 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 e703dcaaf..f4885816d 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 @@ -1,4 +1,5 @@ -import type { Organization, User } from '@buster/database'; +import type { User, organizations } from '@buster/database'; +import type { InferSelectModel } from 'drizzle-orm'; import { HTTPException } from 'hono/http-exception'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { removeApprovedDomainsHandler } from './remove-approved-domains'; @@ -12,6 +13,8 @@ import { getOrganizationFromDb, } from './test-db-utils'; +type Organization = InferSelectModel; + describe('removeApprovedDomainsHandler (integration)', () => { let testUser: User; let testOrg: Organization; 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 a25161b5d..d0c237da1 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 @@ -1,6 +1,6 @@ -import type { Organization, User } from '@buster/database'; -import { db, usersToOrganizations } from '@buster/database'; -import { eq } from 'drizzle-orm'; +import type { User, organizations } from '@buster/database'; +import type { OrganizationRole } from '@buster/server-shared/organization'; +import type { InferSelectModel } from 'drizzle-orm'; import { HTTPException } from 'hono/http-exception'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { @@ -15,6 +15,8 @@ import { } from './test-db-utils'; import { updateWorkspaceSettingsHandler } from './update-workspace-settings'; +type Organization = InferSelectModel; + describe('updateWorkspaceSettingsHandler (integration)', () => { let testUser: User; let testOrg: Organization; @@ -45,7 +47,7 @@ describe('updateWorkspaceSettingsHandler (integration)', () => { const request = { restrict_new_user_invitations: true, - default_role: 'data_admin', + default_role: 'data_admin' as const, }; const result = await updateWorkspaceSettingsHandler(request, testUser); @@ -72,7 +74,7 @@ describe('updateWorkspaceSettingsHandler (integration)', () => { expect(result1.default_role).toBe('querier'); // Should remain unchanged // Update only default_role - const request2 = { default_role: 'data_admin' }; + const request2 = { default_role: 'data_admin' as const }; const result2 = await updateWorkspaceSettingsHandler(request2, testUser); expect(result2.restrict_new_user_invitations).toBe(true); // Should keep previous update @@ -88,7 +90,7 @@ describe('updateWorkspaceSettingsHandler (integration)', () => { const request = { restrict_new_user_invitations: true, - default_role: 'viewer', + default_role: 'viewer' as const, }; const result = await updateWorkspaceSettingsHandler(request, testUser); @@ -122,7 +124,7 @@ describe('updateWorkspaceSettingsHandler (integration)', () => { describe('Error Cases', () => { it('should return 403 for non-workspace-admin users', async () => { - const roles = ['querier', 'restricted_querier', 'data_admin', 'viewer']; + const roles: OrganizationRole[] = ['querier', 'restricted_querier', 'data_admin', 'viewer']; for (const role of roles) { // Create a fresh organization for each role test to avoid conflicts @@ -193,7 +195,7 @@ describe('updateWorkspaceSettingsHandler (integration)', () => { const request = { restrict_new_user_invitations: true, - default_role: 'viewer', + default_role: 'viewer' as const, }; await updateWorkspaceSettingsHandler(request, testUser); @@ -209,13 +211,13 @@ describe('updateWorkspaceSettingsHandler (integration)', () => { const originalName = testOrg.name; const originalCreatedAt = testOrg.createdAt; - const request = { default_role: 'data_admin' }; + const request = { default_role: 'data_admin' as const }; await updateWorkspaceSettingsHandler(request, testUser); const updatedOrg = await getOrganizationFromDb(testOrg.id); expect(updatedOrg?.domains).toEqual(originalDomains); expect(updatedOrg?.name).toBe(originalName); - expect(new Date(updatedOrg?.createdAt).toISOString()).toBe(originalCreatedAt); + expect(new Date(updatedOrg!.createdAt).toISOString()).toBe(originalCreatedAt); }); it("should update organization's updatedAt timestamp", async () => { 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 16ca40e16..6ae1274ec 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 @@ -1,3 +1,4 @@ +import type { OrganizationRole } from '@buster/server-shared/organization'; import type { UpdateWorkspaceSettingsRequest } from '@buster/server-shared/security'; import { describe, expect, it } from 'vitest'; import { WorkspaceSettingsService } from './workspace-settings-service'; @@ -9,8 +10,8 @@ describe('WorkspaceSettingsService', () => { it('should map snake_case to camelCase fields', () => { const settings = { restrictNewUserInvitations: true, - defaultRole: 'member', - }; + defaultRole: 'workspace_admin', + } as Parameters[0]; const result = service.formatWorkspaceSettingsResponse(settings); expect(result).toEqual({ restrict_new_user_invitations: true, @@ -22,8 +23,8 @@ describe('WorkspaceSettingsService', () => { it('should include empty default_datasets array', () => { const settings = { restrictNewUserInvitations: false, - defaultRole: 'admin', - }; + defaultRole: 'admin' as OrganizationRole, + } as Parameters[0]; const result = service.formatWorkspaceSettingsResponse(settings); expect(result.default_datasets).toEqual([]); }); @@ -31,12 +32,12 @@ describe('WorkspaceSettingsService', () => { it('should handle all boolean values correctly', () => { const settingsTrue = { restrictNewUserInvitations: true, - defaultRole: 'member', - }; + defaultRole: 'member' as OrganizationRole, + } as Parameters[0]; const settingsFalse = { restrictNewUserInvitations: false, - defaultRole: 'member', - }; + defaultRole: 'member' as OrganizationRole, + } as Parameters[0]; expect( service.formatWorkspaceSettingsResponse(settingsTrue).restrict_new_user_invitations @@ -57,9 +58,9 @@ describe('WorkspaceSettingsService', () => { ]; for (const role of roles) { - const settings = { + const settings: Parameters[0] = { restrictNewUserInvitations: false, - defaultRole: role, + defaultRole: role as OrganizationRole, }; const result = service.formatWorkspaceSettingsResponse(settings); expect(result.default_role).toBe(role); @@ -88,8 +89,8 @@ describe('WorkspaceSettingsService', () => { it('should handle partial updates (only default_role)', () => { const request: UpdateWorkspaceSettingsRequest = { - default_role: 'admin', - }; + default_role: 'admin' as OrganizationRole, + } as Parameters[0]; const result = service.buildUpdateData(request); expect(result).toHaveProperty('defaultRole', 'admin'); @@ -99,8 +100,8 @@ describe('WorkspaceSettingsService', () => { it('should handle full updates', () => { const request: UpdateWorkspaceSettingsRequest = { restrict_new_user_invitations: false, - default_role: 'member', - }; + default_role: 'member' as OrganizationRole, + } as Parameters[0]; const result = service.buildUpdateData(request); expect(result).toHaveProperty('restrictNewUserInvitations', false); diff --git a/apps/server/src/api/v2/slack/channels.int.test.ts b/apps/server/src/api/v2/slack/channels.int.test.ts index 09db44a10..3128de0ed 100644 --- a/apps/server/src/api/v2/slack/channels.int.test.ts +++ b/apps/server/src/api/v2/slack/channels.int.test.ts @@ -1,4 +1,5 @@ import { db, slackIntegrations } from '@buster/database'; +import type { GetChannelsResponse, SlackErrorResponse } from '@buster/server-shared/slack'; import { eq } from 'drizzle-orm'; import { Hono } from 'hono'; import type { Context } from 'hono'; @@ -90,7 +91,12 @@ describe.skipIf(skipIfNoEnv)('Slack Channels Integration Tests', () => { app.use('*', async (c, next) => { const path = c.req.path; if (path.includes('/channels')) { - (c as Context).set('busterUser', { id: testUserId }); + (c as Context).set('busterUser', { + id: testUserId, + name: 'Test User', + email: 'test@example.com', + avatarUrl: 'https://example.com/avatar.png', + }); (c as Context).set('organizationId', testOrganizationId); } await next(); @@ -115,7 +121,7 @@ describe.skipIf(skipIfNoEnv)('Slack Channels Integration Tests', () => { }); expect(response.status).toBe(404); - const data = await response.json(); + const data = (await response.json()) as SlackErrorResponse; expect(data.error).toBe('No active Slack integration found'); expect(data.code).toBe('INTEGRATION_NOT_FOUND'); }); @@ -150,14 +156,14 @@ describe.skipIf(skipIfNoEnv)('Slack Channels Integration Tests', () => { }) .returning(); - createdIntegrationIds.push(integration.id); + createdIntegrationIds.push(integration!.id); const response = await app.request('/api/v2/slack/channels', { method: 'GET', }); expect(response.status).toBe(200); - const data = await response.json(); + const data = (await response.json()) as GetChannelsResponse; expect(data.channels).toBeDefined(); expect(Array.isArray(data.channels)).toBe(true); @@ -166,7 +172,7 @@ describe.skipIf(skipIfNoEnv)('Slack Channels Integration Tests', () => { expect(data.channels[0]).toHaveProperty('id'); expect(data.channels[0]).toHaveProperty('name'); // Should not have other properties (only id and name as requested) - expect(Object.keys(data.channels[0])).toEqual(['id', 'name']); + expect(Object.keys(data.channels[0]!)).toEqual(['id', 'name']); } // Clean up the secret if we created one @@ -200,7 +206,7 @@ describe.skipIf(skipIfNoEnv)('Slack Channels Integration Tests', () => { }) .returning(); - createdIntegrationIds.push(integration.id); + createdIntegrationIds.push(integration!.id); await app.request('/api/v2/slack/channels', { method: 'GET', @@ -210,11 +216,11 @@ describe.skipIf(skipIfNoEnv)('Slack Channels Integration Tests', () => { const [updated] = await db .select() .from(slackIntegrations) - .where(eq(slackIntegrations.id, integration.id)); + .where(eq(slackIntegrations.id, integration!.id)); - expect(updated.lastUsedAt).toBeTruthy(); - expect(new Date(updated.lastUsedAt!).getTime()).toBeGreaterThan( - new Date(integration.createdAt).getTime() + expect(updated!.lastUsedAt).toBeTruthy(); + expect(new Date(updated!.lastUsedAt!).getTime()).toBeGreaterThan( + new Date(integration!.createdAt).getTime() ); }); @@ -231,7 +237,12 @@ describe.skipIf(skipIfNoEnv)('Slack Channels Integration Tests', () => { const testApp = new Hono(); testApp.use('*', async (c, next) => { if (c.req.path.includes('/channels')) { - (c as Context).set('busterUser', { id: testUserId }); + (c as Context).set('busterUser', { + id: testUserId, + name: 'Test User', + email: 'test@example.com', + avatarUrl: 'https://example.com/avatar.png', + }); (c as Context).set('organizationId', testOrganizationId); } await next(); @@ -243,7 +254,7 @@ describe.skipIf(skipIfNoEnv)('Slack Channels Integration Tests', () => { }); expect(response.status).toBe(503); - const data = await response.json(); + const data = (await response.json()) as SlackErrorResponse; expect(data.error).toBe('Slack integration is not configured'); expect(data.code).toBe('INTEGRATION_NOT_CONFIGURED'); @@ -270,14 +281,14 @@ describe.skipIf(skipIfNoEnv)('Slack Channels Integration Tests', () => { }) .returning(); - createdIntegrationIds.push(integration.id); + createdIntegrationIds.push(integration!.id); const response = await app.request('/api/v2/slack/channels', { method: 'GET', }); expect(response.status).toBe(500); - const data = await response.json(); + const data = (await response.json()) as SlackErrorResponse; expect(data.error).toBe('Failed to retrieve authentication token'); expect(data.code).toBe('TOKEN_RETRIEVAL_ERROR'); }); diff --git a/apps/server/src/api/v2/slack/handler.int.test.ts b/apps/server/src/api/v2/slack/handler.int.test.ts index 424848f81..0a789f1c7 100644 --- a/apps/server/src/api/v2/slack/handler.int.test.ts +++ b/apps/server/src/api/v2/slack/handler.int.test.ts @@ -1,4 +1,10 @@ import { db, slackIntegrations } from '@buster/database'; +import type { + GetIntegrationResponse, + InitiateOAuthResponse, + RemoveIntegrationResponse, + SlackErrorResponse, +} from '@buster/server-shared/slack'; import { and, eq } from 'drizzle-orm'; import { Hono } from 'hono'; import type { Context } from 'hono'; @@ -110,7 +116,12 @@ describe.skipIf(skipIfNoEnv)('SlackHandler Integration Tests', () => { // Set auth context for protected routes const path = c.req.path; if (path.includes('/auth/init') || path.includes('/integration')) { - (c as Context).set('busterUser', { id: testUserId }); + (c as Context).set('busterUser', { + id: testUserId, + name: 'Test User', + email: 'test@example.com', + avatarUrl: 'https://example.com/avatar.png', + }); (c as Context).set('organizationId', testOrganizationId); } await next(); @@ -145,7 +156,12 @@ describe.skipIf(skipIfNoEnv)('SlackHandler Integration Tests', () => { const testApp = new Hono(); testApp.use('*', async (c, next) => { if (c.req.path.includes('/auth/init')) { - (c as Context).set('busterUser', { id: testUserId }); + (c as Context).set('busterUser', { + id: testUserId, + name: 'Test User', + email: 'test@example.com', + avatarUrl: 'https://example.com/avatar.png', + }); (c as Context).set('organizationId', testOrganizationId); } await next(); @@ -161,7 +177,7 @@ describe.skipIf(skipIfNoEnv)('SlackHandler Integration Tests', () => { }); expect(response.status).toBe(503); - const data = await response.json(); + const data = (await response.json()) as SlackErrorResponse; expect(data.error).toBe('Slack integration is not enabled'); expect(data.code).toBe('INTEGRATION_DISABLED'); @@ -221,8 +237,8 @@ describe.skipIf(skipIfNoEnv)('SlackHandler Integration Tests', () => { }); expect(response.status).toBe(200); - const data = await response.json(); - expect(data.authUrl).toContain('https://slack.com/oauth'); + const data = (await response.json()) as InitiateOAuthResponse; + expect(data.auth_url).toContain('https://slack.com/oauth'); expect(data.state).toBeTruthy(); // Verify pending integration was created in database @@ -232,12 +248,12 @@ describe.skipIf(skipIfNoEnv)('SlackHandler Integration Tests', () => { .where(eq(slackIntegrations.organizationId, testOrganizationId)); expect(pending).toBeTruthy(); - createdIntegrationIds.push(pending.id); + createdIntegrationIds.push(pending!.id); - expect(pending.status).toBe('pending'); - expect(pending.userId).toBe(testUserId); - expect(pending.oauthState).toBeTruthy(); - expect(pending.oauthMetadata).toMatchObject({ + expect(pending!.status).toBe('pending'); + expect(pending!.userId).toBe(testUserId); + expect(pending!.oauthState).toBeTruthy(); + expect(pending!.oauthMetadata).toMatchObject({ returnUrl: '/dashboard', source: 'settings', projectId: '550e8400-e29b-41d4-a716-446655440000', @@ -261,7 +277,7 @@ describe.skipIf(skipIfNoEnv)('SlackHandler Integration Tests', () => { }) .returning(); - createdIntegrationIds.push(existing.id); + createdIntegrationIds.push(existing!.id); const response = await app.request('/api/v2/slack/auth/init', { method: 'POST', @@ -272,7 +288,7 @@ describe.skipIf(skipIfNoEnv)('SlackHandler Integration Tests', () => { }); expect(response.status).toBe(409); - const data = await response.json(); + const data = (await response.json()) as SlackErrorResponse; expect(data.error).toBe('Organization already has an active Slack integration'); expect(data.code).toBe('INTEGRATION_EXISTS'); }); @@ -317,7 +333,7 @@ describe.skipIf(skipIfNoEnv)('SlackHandler Integration Tests', () => { }) .returning(); - createdIntegrationIds.push(pending.id); + createdIntegrationIds.push(pending!.id); const response = await app.request( `/api/v2/slack/auth/callback?code=test-code&state=${testState}`, @@ -333,14 +349,14 @@ describe.skipIf(skipIfNoEnv)('SlackHandler Integration Tests', () => { const [activated] = await db .select() .from(slackIntegrations) - .where(eq(slackIntegrations.id, pending.id)); + .where(eq(slackIntegrations.id, pending!.id)); - expect(activated.status).toBe('active'); - expect(activated.teamName).toBe('Test Workspace'); - expect(activated.tokenVaultKey).toBe(`slack-token-${pending.id}`); - expect(activated.installedAt).toBeTruthy(); - expect(activated.oauthState).toBeNull(); - expect(activated.oauthExpiresAt).toBeNull(); + expect(activated!.status).toBe('active'); + expect(activated!.teamName).toBe('Test Workspace'); + expect(activated!.tokenVaultKey).toBe(`slack-token-${pending!.id}`); + expect(activated!.installedAt).toBeTruthy(); + expect(activated!.oauthState).toBeNull(); + expect(activated!.oauthExpiresAt).toBeNull(); }); it('should handle invalid state', async () => { @@ -390,19 +406,19 @@ describe.skipIf(skipIfNoEnv)('SlackHandler Integration Tests', () => { }) .returning(); - createdIntegrationIds.push(integration.id); + createdIntegrationIds.push(integration!.id); const response = await app.request('/api/v2/slack/integration', { method: 'GET', }); expect(response.status).toBe(200); - const data = await response.json(); + const data = (await response.json()) as GetIntegrationResponse; expect(data.connected).toBe(true); expect(data.integration).toBeDefined(); - expect(data.integration.id).toBe(integration.id); - expect(data.integration.teamName).toBe('Status Test Workspace'); - expect(data.integration.teamDomain).toBe('status-test'); + expect(data.integration!.id).toBe(integration!.id); + expect(data.integration!.team_name).toBe('Status Test Workspace'); + expect(data.integration!.team_domain).toBe('status-test'); }); it('should return not connected when no integration exists', async () => { @@ -416,7 +432,7 @@ describe.skipIf(skipIfNoEnv)('SlackHandler Integration Tests', () => { }); expect(response.status).toBe(200); - const data = await response.json(); + const data = (await response.json()) as GetIntegrationResponse; expect(data.connected).toBe(false); expect(data.integration).toBeUndefined(); }); @@ -459,14 +475,14 @@ describe.skipIf(skipIfNoEnv)('SlackHandler Integration Tests', () => { }) .returning(); - createdIntegrationIds.push(integration.id); + createdIntegrationIds.push(integration!.id); const response = await app.request('/api/v2/slack/integration', { method: 'DELETE', }); expect(response.status).toBe(200); - const data = await response.json(); + const data = (await response.json()) as RemoveIntegrationResponse; expect(data.message).toBe('Slack integration removed successfully'); // Wait a moment for the database to update @@ -476,13 +492,13 @@ describe.skipIf(skipIfNoEnv)('SlackHandler Integration Tests', () => { const removedList = await db .select() .from(slackIntegrations) - .where(eq(slackIntegrations.id, integration.id)); + .where(eq(slackIntegrations.id, integration!.id)); expect(removedList.length).toBe(1); const removed = removedList[0]; expect(removed).toBeDefined(); - expect(removed.status).toBe('revoked'); - expect(removed.deletedAt).toBeTruthy(); + expect(removed!.status).toBe('revoked'); + expect(removed!.deletedAt).toBeTruthy(); }); it('should handle non-existent integration', async () => { @@ -499,7 +515,7 @@ describe.skipIf(skipIfNoEnv)('SlackHandler Integration Tests', () => { }); expect(response.status).toBe(404); - const data = await response.json(); + const data = (await response.json()) as SlackErrorResponse; expect(data.error).toBe('No active Slack integration found'); expect(data.code).toBe('INTEGRATION_NOT_FOUND'); }); diff --git a/apps/server/src/api/v2/slack/handler.test.ts b/apps/server/src/api/v2/slack/handler.test.ts index ea31cac92..4e719cde3 100644 --- a/apps/server/src/api/v2/slack/handler.test.ts +++ b/apps/server/src/api/v2/slack/handler.test.ts @@ -1,7 +1,8 @@ import { getUserOrganizationId } from '@buster/database'; +import { SlackError } from '@buster/server-shared/slack'; import { HTTPException } from 'hono/http-exception'; import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { SlackError, SlackHandler } from './handler'; +import { SlackHandler } from './handler'; // Mock dependencies const mockSlackOAuthService = { @@ -82,7 +83,7 @@ describe('SlackHandler', () => { // Mock getUserOrganizationId to return org info vi.mocked(getUserOrganizationId).mockResolvedValue({ organizationId: 'org-123', - userId: 'user-123', + role: 'owner', }); const mockResult = { @@ -115,7 +116,7 @@ describe('SlackHandler', () => { // Mock getUserOrganizationId to return org info vi.mocked(getUserOrganizationId).mockResolvedValue({ organizationId: 'org-123', - userId: 'user-123', + role: 'owner', }); vi.mocked(mockSlackOAuthService.initiateOAuth).mockRejectedValue( @@ -209,7 +210,7 @@ describe('SlackHandler', () => { // Mock getUserOrganizationId to return org info vi.mocked(getUserOrganizationId).mockResolvedValue({ organizationId: 'org-123', - userId: 'user-123', + role: 'owner', }); const mockStatus = { @@ -249,7 +250,7 @@ describe('SlackHandler', () => { // Mock getUserOrganizationId to return org info vi.mocked(getUserOrganizationId).mockResolvedValue({ organizationId: 'org-123', - userId: 'user-123', + role: 'owner', }); vi.mocked(mockSlackOAuthService.removeIntegration).mockResolvedValue({ @@ -273,7 +274,7 @@ describe('SlackHandler', () => { // Mock getUserOrganizationId to return org info vi.mocked(getUserOrganizationId).mockResolvedValue({ organizationId: 'org-123', - userId: 'user-123', + role: 'owner', }); vi.mocked(mockSlackOAuthService.removeIntegration).mockResolvedValue({ diff --git a/apps/server/src/api/v2/slack/services/slack-helpers.int.test.ts b/apps/server/src/api/v2/slack/services/slack-helpers.int.test.ts index f14dfa072..da0bffe7a 100644 --- a/apps/server/src/api/v2/slack/services/slack-helpers.int.test.ts +++ b/apps/server/src/api/v2/slack/services/slack-helpers.int.test.ts @@ -136,12 +136,12 @@ describe.skipIf(skipIfNoDatabase)('Slack Helpers Database Integration Tests', () }) .returning(); - createdIntegrationIds.push(integration.id); + createdIntegrationIds.push(integration!.id); const result = await slackHelpers.getActiveIntegration(testOrganizationId); expect(result).toBeTruthy(); - expect(result?.id).toBe(integration.id); + expect(result?.id).toBe(integration!.id); expect(result?.status).toBe('active'); expect(result?.teamName).toBe('Test Workspace'); expect(result?.organizationId).toBe(testOrganizationId); @@ -163,7 +163,7 @@ describe.skipIf(skipIfNoDatabase)('Slack Helpers Database Integration Tests', () }) .returning(); - createdIntegrationIds.push(integration.id); + createdIntegrationIds.push(integration!.id); const result = await slackHelpers.getActiveIntegration(testOrganizationId); expect(result).toBeNull(); @@ -192,14 +192,14 @@ describe.skipIf(skipIfNoDatabase)('Slack Helpers Database Integration Tests', () .where(eq(slackIntegrations.id, integrationId)); expect(created).toBeTruthy(); - expect(created.organizationId).toBe(testOrganizationId); - expect(created.userId).toBe(testUserId); - expect(created.status).toBe('pending'); - expect(created.oauthState).toBe(testIds.oauthState); - expect(created.oauthMetadata).toEqual(metadata); + expect(created!.organizationId).toBe(testOrganizationId); + expect(created!.userId).toBe(testUserId); + expect(created!.status).toBe('pending'); + expect(created!.oauthState).toBe(testIds.oauthState); + expect(created!.oauthMetadata).toEqual(metadata); // Check expiry is set to ~15 minutes - const expiryTime = new Date(created.oauthExpiresAt!).getTime(); + const expiryTime = new Date(created!.oauthExpiresAt!).getTime(); const expectedExpiry = Date.now() + 15 * 60 * 1000; expect(Math.abs(expiryTime - expectedExpiry)).toBeLessThan(30000); // Within 30 seconds }); @@ -219,7 +219,7 @@ describe.skipIf(skipIfNoDatabase)('Slack Helpers Database Integration Tests', () }) .returning(); - createdIntegrationIds.push(activeIntegration.id); + createdIntegrationIds.push(activeIntegration!.id); // Try to create a new pending integration await expect( @@ -248,17 +248,17 @@ describe.skipIf(skipIfNoDatabase)('Slack Helpers Database Integration Tests', () }) .returning(); - createdIntegrationIds.push(pending.id); + createdIntegrationIds.push(pending!.id); // Update it with OAuth response data - await slackHelpers.updateIntegrationAfterOAuth(pending.id, { + await slackHelpers.updateIntegrationAfterOAuth(pending!.id, { teamId: testIds.teamId, teamName: 'OAuth Workspace', teamDomain: 'oauth-workspace', enterpriseId: testIds.enterpriseId, botUserId: testIds.botUserId, scope: 'channels:read chat:write channels:join', - tokenVaultKey: `slack-token-${pending.id}`, + tokenVaultKey: `slack-token-${pending!.id}`, installedBySlackUserId: 'U11111', }); @@ -266,20 +266,20 @@ describe.skipIf(skipIfNoDatabase)('Slack Helpers Database Integration Tests', () const [updated] = await db .select() .from(slackIntegrations) - .where(eq(slackIntegrations.id, pending.id)); + .where(eq(slackIntegrations.id, pending!.id)); - expect(updated.status).toBe('active'); - expect(updated.teamId).toBe(testIds.teamId); - expect(updated.teamName).toBe('OAuth Workspace'); - expect(updated.teamDomain).toBe('oauth-workspace'); - expect(updated.enterpriseId).toBe(testIds.enterpriseId); - expect(updated.botUserId).toBe(testIds.botUserId); - expect(updated.scope).toBe('channels:read chat:write channels:join'); - expect(updated.tokenVaultKey).toBe(`slack-token-${pending.id}`); - expect(updated.installedBySlackUserId).toBe('U11111'); - expect(updated.installedAt).toBeTruthy(); - expect(updated.oauthState).toBeNull(); - expect(updated.oauthExpiresAt).toBeNull(); + expect(updated!.status).toBe('active'); + expect(updated!.teamId).toBe(testIds.teamId); + expect(updated!.teamName).toBe('OAuth Workspace'); + expect(updated!.teamDomain).toBe('oauth-workspace'); + expect(updated!.enterpriseId).toBe(testIds.enterpriseId); + expect(updated!.botUserId).toBe(testIds.botUserId); + expect(updated!.scope).toBe('channels:read chat:write channels:join'); + expect(updated!.tokenVaultKey).toBe(`slack-token-${pending!.id}`); + expect(updated!.installedBySlackUserId).toBe('U11111'); + expect(updated!.installedAt).toBeTruthy(); + expect(updated!.oauthState).toBeNull(); + expect(updated!.oauthExpiresAt).toBeNull(); }); }); @@ -302,7 +302,7 @@ describe.skipIf(skipIfNoDatabase)('Slack Helpers Database Integration Tests', () }) .returning(); - createdIntegrationIds.push(integration.id); + createdIntegrationIds.push(integration!.id); // Update default channel const defaultChannel = { @@ -310,17 +310,17 @@ describe.skipIf(skipIfNoDatabase)('Slack Helpers Database Integration Tests', () id: 'C12345', }; - await slackHelpers.updateDefaultChannel(integration.id, defaultChannel); + await slackHelpers.updateDefaultChannel(integration!.id, defaultChannel); // Verify the update const [updated] = await db .select() .from(slackIntegrations) - .where(eq(slackIntegrations.id, integration.id)); + .where(eq(slackIntegrations.id, integration!.id)); - expect(updated.defaultChannel).toEqual(defaultChannel); - expect(new Date(updated.updatedAt).getTime()).toBeGreaterThan( - new Date(updated.createdAt).getTime() + expect(updated!.defaultChannel).toEqual(defaultChannel); + expect(new Date(updated!.updatedAt).getTime()).toBeGreaterThan( + new Date(updated!.createdAt).getTime() ); }); }); @@ -344,21 +344,21 @@ describe.skipIf(skipIfNoDatabase)('Slack Helpers Database Integration Tests', () }) .returning(); - createdIntegrationIds.push(integration.id); + createdIntegrationIds.push(integration!.id); // Soft delete it - await slackHelpers.softDeleteIntegration(integration.id); + await slackHelpers.softDeleteIntegration(integration!.id); // Verify it was soft deleted const [deleted] = await db .select() .from(slackIntegrations) - .where(eq(slackIntegrations.id, integration.id)); + .where(eq(slackIntegrations.id, integration!.id)); - expect(deleted.status).toBe('revoked'); - expect(deleted.deletedAt).toBeTruthy(); - expect(new Date(deleted.updatedAt).getTime()).toBeGreaterThan( - new Date(deleted.createdAt).getTime() + expect(deleted!.status).toBe('revoked'); + expect(deleted!.deletedAt).toBeTruthy(); + expect(new Date(deleted!.updatedAt).getTime()).toBeGreaterThan( + new Date(deleted!.createdAt).getTime() ); }); }); @@ -382,23 +382,23 @@ describe.skipIf(skipIfNoDatabase)('Slack Helpers Database Integration Tests', () }) .returning(); - createdIntegrationIds.push(integration.id); + createdIntegrationIds.push(integration!.id); // Wait a bit to ensure timestamp difference await new Promise((resolve) => setTimeout(resolve, 100)); // Update last used - await slackHelpers.updateLastUsedAt(integration.id); + await slackHelpers.updateLastUsedAt(integration!.id); // Verify the update const [updated] = await db .select() .from(slackIntegrations) - .where(eq(slackIntegrations.id, integration.id)); + .where(eq(slackIntegrations.id, integration!.id)); - expect(updated.lastUsedAt).toBeTruthy(); - expect(new Date(updated.lastUsedAt!).getTime()).toBeGreaterThan( - new Date(updated.createdAt).getTime() + expect(updated!.lastUsedAt).toBeTruthy(); + expect(new Date(updated!.lastUsedAt!).getTime()).toBeGreaterThan( + new Date(updated!.createdAt).getTime() ); }); }); @@ -424,7 +424,7 @@ describe.skipIf(skipIfNoDatabase)('Slack Helpers Database Integration Tests', () }) .returning(); - createdIntegrationIds.push(integration.id); + createdIntegrationIds.push(integration!.id); const result = await slackHelpers.hasActiveIntegration(testOrganizationId); expect(result).toBe(true); @@ -446,12 +446,12 @@ describe.skipIf(skipIfNoDatabase)('Slack Helpers Database Integration Tests', () }) .returning(); - createdIntegrationIds.push(integration.id); + createdIntegrationIds.push(integration!.id); const result = await slackHelpers.getPendingIntegrationByState(testIds.oauthState); expect(result).toBeTruthy(); - expect(result?.id).toBe(integration.id); + expect(result?.id).toBe(integration!.id); expect(result?.oauthState).toBe(testIds.oauthState); }); @@ -469,7 +469,7 @@ describe.skipIf(skipIfNoDatabase)('Slack Helpers Database Integration Tests', () }) .returning(); - createdIntegrationIds.push(integration.id); + createdIntegrationIds.push(integration!.id); const result = await slackHelpers.getPendingIntegrationByState(testIds.oauthState); expect(result).toBeNull(); @@ -505,7 +505,7 @@ describe.skipIf(skipIfNoDatabase)('Slack Helpers Database Integration Tests', () }) .returning(); - createdIntegrationIds.push(valid.id); // Only track valid one since expired will be deleted + createdIntegrationIds.push(valid!.id); // Only track valid one since expired will be deleted // Run cleanup const deletedCount = await slackHelpers.cleanupExpiredPendingIntegrations(); @@ -516,7 +516,7 @@ describe.skipIf(skipIfNoDatabase)('Slack Helpers Database Integration Tests', () const [expiredCheck] = await db .select() .from(slackIntegrations) - .where(eq(slackIntegrations.id, expired.id)); + .where(eq(slackIntegrations.id, expired!.id)); expect(expiredCheck).toBeUndefined(); @@ -524,7 +524,7 @@ describe.skipIf(skipIfNoDatabase)('Slack Helpers Database Integration Tests', () const [validCheck] = await db .select() .from(slackIntegrations) - .where(eq(slackIntegrations.id, valid.id)); + .where(eq(slackIntegrations.id, valid!.id)); expect(validCheck).toBeTruthy(); }); diff --git a/apps/server/src/api/v2/slack/services/slack-oauth-service.int.test.ts b/apps/server/src/api/v2/slack/services/slack-oauth-service.int.test.ts index 488ae08cf..d33be6c6b 100644 --- a/apps/server/src/api/v2/slack/services/slack-oauth-service.int.test.ts +++ b/apps/server/src/api/v2/slack/services/slack-oauth-service.int.test.ts @@ -138,12 +138,12 @@ describe.skipIf(skipIfNoEnv)('SlackOAuthService Integration Tests', () => { .where(eq(slackIntegrations.organizationId, testOrganizationId)); expect(pending).toBeTruthy(); - createdIntegrationIds.push(pending.id); + createdIntegrationIds.push(pending!.id); - expect(pending.status).toBe('pending'); - expect(pending.userId).toBe(testUserId); - expect(pending.oauthState).toBeTruthy(); - expect(pending.oauthMetadata).toMatchObject({ + expect(pending!.status).toBe('pending'); + expect(pending!.userId).toBe(testUserId); + expect(pending!.oauthState).toBeTruthy(); + expect(pending!.oauthMetadata).toMatchObject({ returnUrl: '/dashboard', source: 'settings', projectId: '550e8400-e29b-41d4-a716-446655440000', @@ -168,7 +168,7 @@ describe.skipIf(skipIfNoEnv)('SlackOAuthService Integration Tests', () => { }) .returning(); - createdIntegrationIds.push(existing.id); + createdIntegrationIds.push(existing!.id); // Try to initiate OAuth again await expect( @@ -202,9 +202,9 @@ describe.skipIf(skipIfNoEnv)('SlackOAuthService Integration Tests', () => { source: 'onboarding', }, }) - .returning(); + .returning()!; - createdIntegrationIds.push(pending.id); + createdIntegrationIds.push(pending!.id); const testIds = generateTestIds(); // Mock the auth service to return specific state @@ -227,7 +227,7 @@ describe.skipIf(skipIfNoEnv)('SlackOAuthService Integration Tests', () => { }); expect(result.success).toBe(true); - expect(result.integrationId).toBe(pending.id); + expect(result.integrationId).toBe(pending!.id); expect(result.teamName).toBe('Callback Workspace'); expect(result.metadata).toMatchObject({ returnUrl: '/settings', @@ -241,22 +241,22 @@ describe.skipIf(skipIfNoEnv)('SlackOAuthService Integration Tests', () => { const activatedList = await db .select() .from(slackIntegrations) - .where(eq(slackIntegrations.id, pending.id)); + .where(eq(slackIntegrations.id, pending!.id)); expect(activatedList.length).toBe(1); const activated = activatedList[0]; expect(activated).toBeDefined(); - expect(activated.status).toBe('active'); - expect(activated.teamId).toBe(testIds.teamId); - expect(activated.teamName).toBe('Callback Workspace'); - expect(activated.teamDomain).toBe('callback-workspace'); - expect(activated.botUserId).toBe(testIds.botUserId); - expect(activated.scope).toBe('channels:read,chat:write,channels:join'); - expect(activated.tokenVaultKey).toBe(`slack-token-${pending.id}`); - expect(activated.installedBySlackUserId).toBe('U88888'); - expect(activated.installedAt).toBeTruthy(); - expect(activated.oauthState).toBeNull(); - expect(activated.oauthExpiresAt).toBeNull(); + expect(activated!.status).toBe('active'); + expect(activated!.teamId).toBe(testIds.teamId); + expect(activated!.teamName).toBe('Callback Workspace'); + expect(activated!.teamDomain).toBe('callback-workspace'); + expect(activated!.botUserId).toBe(testIds.botUserId); + expect(activated!.scope).toBe('channels:read,chat:write,channels:join'); + expect(activated!.tokenVaultKey).toBe(`slack-token-${pending!.id}`); + expect(activated!.installedBySlackUserId).toBe('U88888'); + expect(activated!.installedAt).toBeTruthy(); + expect(activated!.oauthState).toBeNull(); + expect(activated!.oauthExpiresAt).toBeNull(); }); it('should handle invalid state', async () => { @@ -299,13 +299,13 @@ describe.skipIf(skipIfNoEnv)('SlackOAuthService Integration Tests', () => { expect(result.success).toBe(false); expect(result.error).toBe('Failed to exchange code for token'); - expect(result.integrationId).toBe(pending.id); + expect(result.integrationId).toBe(pending!.id); // Verify the pending integration was deleted (to allow retry) const integrations = await db .select() .from(slackIntegrations) - .where(eq(slackIntegrations.id, pending.id)); + .where(eq(slackIntegrations.id, pending!.id)); expect(integrations.length).toBe(0); }); @@ -332,13 +332,13 @@ describe.skipIf(skipIfNoEnv)('SlackOAuthService Integration Tests', () => { }) .returning(); - createdIntegrationIds.push(integration.id); + createdIntegrationIds.push(integration!.id); const status = await service.getIntegrationStatus(testOrganizationId); expect(status.connected).toBe(true); expect(status.integration).toBeDefined(); - expect(status.integration!.id).toBe(integration.id); + expect(status.integration!.id).toBe(integration!.id); expect(status.integration!.teamName).toBe('Status Test Workspace'); expect(status.integration!.teamDomain).toBe('status-test'); expect(status.integration!.installedAt).toContain('2024-01-01'); @@ -380,7 +380,7 @@ describe.skipIf(skipIfNoEnv)('SlackOAuthService Integration Tests', () => { .returning(); // Store the ID immediately - const integrationId = integration.id; + const integrationId = integration!.id; createdIntegrationIds.push(integrationId); // Verify it was created @@ -390,7 +390,7 @@ describe.skipIf(skipIfNoEnv)('SlackOAuthService Integration Tests', () => { .where(eq(slackIntegrations.id, integrationId)); expect(created).toBeDefined(); - expect(created.status).toBe('active'); + expect(created!.status).toBe('active'); // The service will try to delete the token from vault // In integration tests, we let it run naturally @@ -410,8 +410,8 @@ describe.skipIf(skipIfNoEnv)('SlackOAuthService Integration Tests', () => { expect(removedIntegrations.length).toBe(1); const removed = removedIntegrations[0]; expect(removed).toBeDefined(); - expect(removed.status).toBe('revoked'); - expect(removed.deletedAt).toBeTruthy(); + expect(removed!.status).toBe('revoked'); + expect(removed!.deletedAt).toBeTruthy(); }); it('should handle non-existent integration', async () => { diff --git a/apps/server/src/api/v2/slack/services/token-storage.int.test.ts b/apps/server/src/api/v2/slack/services/token-storage.int.test.ts index 7c956ba7b..70cae11e5 100644 --- a/apps/server/src/api/v2/slack/services/token-storage.int.test.ts +++ b/apps/server/src/api/v2/slack/services/token-storage.int.test.ts @@ -136,7 +136,7 @@ describe.skipIf(skipIfNoEnv)('Token Storage Integration Tests', () => { }) .returning(); - createdIntegrationIds.push(integration.id); + createdIntegrationIds.push(integration!.id); // Retrieve state const stateData = await storage.getState(testState); @@ -167,7 +167,7 @@ describe.skipIf(skipIfNoEnv)('Token Storage Integration Tests', () => { }) .returning(); - createdIntegrationIds.push(integration.id); + createdIntegrationIds.push(integration!.id); // Should return null for expired state const stateData = await storage.getState(testState); diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json index 7cdc58abb..4b9604594 100644 --- a/apps/server/tsconfig.json +++ b/apps/server/tsconfig.json @@ -6,5 +6,14 @@ "tsBuildInfoFile": "dist/.cache/tsbuildinfo.json" }, "include": ["src"], - "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts"] + "exclude": ["node_modules", "dist", "**/*.test.tsx", "**/*.test.ts"], + "references": [ + { "path": "../../packages/access-controls" }, + { "path": "../../packages/ai" }, + { "path": "../../packages/database" }, + { "path": "../../packages/server-shared" }, + { "path": "../../packages/slack" }, + { "path": "../../packages/test-utils" }, + { "path": "../../packages/vitest-config" } + ] } diff --git a/apps/trigger/tsconfig.json b/apps/trigger/tsconfig.json index aae9d8a35..7b94df61c 100644 --- a/apps/trigger/tsconfig.json +++ b/apps/trigger/tsconfig.json @@ -7,5 +7,14 @@ "tsBuildInfoFile": "dist/.cache/tsbuildinfo.json" }, "include": ["src/**/*", "tests/**/*", "env.d.ts"], - "exclude": ["node_modules", "dist"] + "exclude": ["node_modules", "dist"], + "references": [ + { "path": "../../packages/access-controls" }, + { "path": "../../packages/ai" }, + { "path": "../../packages/database" }, + { "path": "../../packages/server-shared" }, + { "path": "../../packages/slack" }, + { "path": "../../packages/test-utils" }, + { "path": "../../packages/vitest-config" } + ] } diff --git a/packages/access-controls/tsconfig.json b/packages/access-controls/tsconfig.json index 0c5f6a44f..a20a3ef86 100644 --- a/packages/access-controls/tsconfig.json +++ b/packages/access-controls/tsconfig.json @@ -7,6 +7,7 @@ "module": "ESNext", "moduleResolution": "bundler" }, - "include": ["src/**/*", "env.d.ts"], - "exclude": ["node_modules", "dist", "tests", "**/*.test.ts", "**/*.spec.ts"] + "include": ["src*", "env.d.ts"], + "exclude": ["node_modules", "dist", "tests", "**/*.test.tsx", "**/*.test.ts", "**/*.spec.ts"], + "references": [{ "path": "../database" }, { "path": "../vitest-config" }] } diff --git a/packages/ai/tsconfig.json b/packages/ai/tsconfig.json index bbce4976e..9848187b8 100644 --- a/packages/ai/tsconfig.json +++ b/packages/ai/tsconfig.json @@ -5,6 +5,15 @@ "outDir": "dist", "rootDir": "src" }, - "include": ["src/**/*", "env.d.ts"], - "exclude": ["node_modules", "dist"] + "include": ["src*", "env.d.ts"], + "exclude": ["node_modules", "dist"], + "references": [ + { "path": "../access-controls" }, + { "path": "../data-source" }, + { "path": "../database" }, + { "path": "../server-shared" }, + { "path": "../stored-values" }, + { "path": "../test-utils" }, + { "path": "../vitest-config" } + ] } diff --git a/packages/data-source/tsconfig.json b/packages/data-source/tsconfig.json index 09c0bbdd1..f02bd88fc 100644 --- a/packages/data-source/tsconfig.json +++ b/packages/data-source/tsconfig.json @@ -13,5 +13,6 @@ } }, "include": ["src/**/*", "env.d.ts"], - "exclude": ["node_modules", "dist", "tests", "**/*.test.ts", "**/*.spec.ts"] + "exclude": ["node_modules", "dist", "tests", "**/*.test.ts", "**/*.spec.ts"], + "references": [{ "path": "../vitest-config" }] } diff --git a/packages/database/src/connection.ts b/packages/database/src/connection.ts index 74a3bf84f..f11c05d03 100644 --- a/packages/database/src/connection.ts +++ b/packages/database/src/connection.ts @@ -34,8 +34,6 @@ function validateEnvironment(): string { } return connectionString; - - return connectionString; } // Initialize the database pool diff --git a/packages/database/tsconfig.json b/packages/database/tsconfig.json index 98d2d738b..2bacfdd66 100644 --- a/packages/database/tsconfig.json +++ b/packages/database/tsconfig.json @@ -6,5 +6,6 @@ "rootDir": "src" }, "include": ["src/**/*", "env.d.ts"], - "exclude": ["node_modules", "dist", "tests", "**/*.test.ts", "**/*.spec.ts"] + "exclude": ["node_modules", "dist", "tests", "**/*.test.ts", "**/*.spec.ts"], + "references": [{ "path": "../vitest-config" }] } diff --git a/packages/rerank/tsconfig.json b/packages/rerank/tsconfig.json index c060df8c4..ebceee319 100644 --- a/packages/rerank/tsconfig.json +++ b/packages/rerank/tsconfig.json @@ -5,6 +5,7 @@ "outDir": "dist", "rootDir": "src" }, - "include": ["src/**/*", "env.d.ts"], - "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"] + "include": ["src*", "env.d.ts"], + "exclude": ["node_modules", "dist", "**/*.test.tsx", "**/*.test.ts", "**/*.spec.ts"], + "references": [{ "path": "../vitest-config" }] } diff --git a/packages/server-shared/src/chats/chat-message.types.test.ts b/packages/server-shared/src/chats/chat-message.types.test.ts index 0cde51046..c4cc7d487 100644 --- a/packages/server-shared/src/chats/chat-message.types.test.ts +++ b/packages/server-shared/src/chats/chat-message.types.test.ts @@ -207,12 +207,12 @@ describe('ReasoningMessageSchema', () => { expect(result.data.type).toBe('files'); if (result.data.type === 'files') { expect(result.data.file_ids).toHaveLength(2); - expect(result.data.files['file-1'].file_type).toBe('metric'); - expect(result.data.files['file-1'].file.modified).toEqual([ + expect(result.data.files['file-1']?.file_type).toBe('metric'); + expect(result.data.files['file-1']?.file?.modified).toEqual([ [0, 10], [20, 30], ]); - expect(result.data.files['file-2'].file.modified).toBeUndefined(); + expect(result.data.files['file-2']?.file?.modified).toBeUndefined(); } } }); @@ -258,9 +258,9 @@ describe('ReasoningMessageSchema', () => { expect(result.data.type).toBe('pills'); if (result.data.type === 'pills') { expect(result.data.pill_containers).toHaveLength(2); - expect(result.data.pill_containers[0].pills).toHaveLength(2); - expect(result.data.pill_containers[0].pills[0].type).toBe('metric'); - expect(result.data.pill_containers[1].pills[0].text).toBe('Sales Overview'); + expect(result.data.pill_containers?.[0]?.pills).toHaveLength(2); + expect(result.data.pill_containers?.[0]?.pills?.[0]?.type).toBe('metric'); + expect(result.data.pill_containers?.[1]?.pills?.[0]?.text).toBe('Sales Overview'); } } }); diff --git a/packages/server-shared/src/metrics/charts/chartConfigProps.test.ts b/packages/server-shared/src/metrics/charts/chartConfigProps.test.ts index f03813e7e..6c7b52bab 100644 --- a/packages/server-shared/src/metrics/charts/chartConfigProps.test.ts +++ b/packages/server-shared/src/metrics/charts/chartConfigProps.test.ts @@ -193,8 +193,8 @@ describe('chartConfigProps', () => { }); // Check that defaults are applied to column settings - expect(result.columnSettings.revenue.lineWidth).toBe(2); - expect(result.columnSettings.profit.columnVisualization).toBe('bar'); + expect(result.columnSettings.revenue?.lineWidth).toBe(2); + expect(result.columnSettings.profit?.columnVisualization).toBe('bar'); expect(result.columnLabelFormats.revenue).toMatchObject({ prefix: '$', diff --git a/packages/server-shared/src/metrics/defaultHelpers.test.ts b/packages/server-shared/src/metrics/defaultHelpers.test.ts index 83661cb32..5391495a4 100644 --- a/packages/server-shared/src/metrics/defaultHelpers.test.ts +++ b/packages/server-shared/src/metrics/defaultHelpers.test.ts @@ -312,7 +312,10 @@ describe('Helper function edge cases', () => { it('should handle null and undefined defaults correctly', () => { const NullDefaultsSchema = z.object({ nullable: z.string().nullable().default(null), - optional: z.string().optional().default(undefined), + optional: z + .string() + .optional() + .default(undefined as any), emptyString: z.string().default(''), zero: z.number().default(0), false: z.boolean().default(false), diff --git a/packages/server-shared/src/share/share-interfaces.test.ts b/packages/server-shared/src/share/share-interfaces.test.ts index 14c8b4df8..3e190b397 100644 --- a/packages/server-shared/src/share/share-interfaces.test.ts +++ b/packages/server-shared/src/share/share-interfaces.test.ts @@ -191,8 +191,8 @@ describe('ShareConfigSchema', () => { if (result.success) { expect(result.data.individual_permissions).toHaveLength(2); - expect(result.data.individual_permissions?.[0].email).toBe('user1@example.com'); - expect(result.data.individual_permissions?.[0].role).toBe('canEdit'); + expect(result.data.individual_permissions?.[0]?.email).toBe('user1@example.com'); + expect(result.data.individual_permissions?.[0]?.role).toBe('canEdit'); expect(result.data.publicly_accessible).toBe(true); expect(result.data.permission).toBe('owner'); } @@ -369,11 +369,11 @@ describe('ShareConfigSchema', () => { if (result.success) { expect(result.data.individual_permissions).toHaveLength(4); - expect(result.data.individual_permissions?.[0].role).toBe('owner'); - expect(result.data.individual_permissions?.[1].role).toBe('canEdit'); - expect(result.data.individual_permissions?.[2].role).toBe('canView'); - expect(result.data.individual_permissions?.[3].role).toBe('canFilter'); - expect(result.data.individual_permissions?.[2].name).toBeUndefined(); + expect(result.data.individual_permissions?.[0]?.role).toBe('owner'); + expect(result.data.individual_permissions?.[1]?.role).toBe('canEdit'); + expect(result.data.individual_permissions?.[2]?.role).toBe('canView'); + expect(result.data.individual_permissions?.[3]?.role).toBe('canFilter'); + expect(result.data.individual_permissions?.[2]?.name).toBeUndefined(); } }); diff --git a/packages/server-shared/tsconfig.json b/packages/server-shared/tsconfig.json index 02356e4a6..8f600ac89 100644 --- a/packages/server-shared/tsconfig.json +++ b/packages/server-shared/tsconfig.json @@ -6,8 +6,6 @@ "tsBuildInfoFile": "dist/.cache/tsbuildinfo.json" }, "include": ["src"], - "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"], - "references": [ - { "path": "../database" } // Reference other projects - ] + "exclude": ["node_modules", "dist", "**/*.test.tsx", "**/*.test.ts", "**/*.spec.ts"], + "references": [{ "path": "../database" }, { "path": "../vitest-config" }] } diff --git a/packages/slack/tsconfig.json b/packages/slack/tsconfig.json index 98d2d738b..2bacfdd66 100644 --- a/packages/slack/tsconfig.json +++ b/packages/slack/tsconfig.json @@ -6,5 +6,6 @@ "rootDir": "src" }, "include": ["src/**/*", "env.d.ts"], - "exclude": ["node_modules", "dist", "tests", "**/*.test.ts", "**/*.spec.ts"] + "exclude": ["node_modules", "dist", "tests", "**/*.test.ts", "**/*.spec.ts"], + "references": [{ "path": "../vitest-config" }] } diff --git a/packages/stored-values/tsconfig.json b/packages/stored-values/tsconfig.json index 98d2d738b..bd2a87d97 100644 --- a/packages/stored-values/tsconfig.json +++ b/packages/stored-values/tsconfig.json @@ -5,6 +5,7 @@ "outDir": "dist", "rootDir": "src" }, - "include": ["src/**/*", "env.d.ts"], - "exclude": ["node_modules", "dist", "tests", "**/*.test.ts", "**/*.spec.ts"] + "include": ["src*", "env.d.ts"], + "exclude": ["node_modules", "dist", "tests", "**/*.test.tsx", "**/*.test.ts", "**/*.spec.ts"], + "references": [{ "path": "../database" }, { "path": "../vitest-config" }] } diff --git a/packages/supabase/tsconfig.json b/packages/supabase/tsconfig.json index 01e85fe22..b23c954f0 100644 --- a/packages/supabase/tsconfig.json +++ b/packages/supabase/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../typescript-config/base.json", - "include": ["volumes/**/*"] + "include": ["volumes*"] } diff --git a/packages/test-utils/tsconfig.json b/packages/test-utils/tsconfig.json index b77ad9a03..24488515e 100644 --- a/packages/test-utils/tsconfig.json +++ b/packages/test-utils/tsconfig.json @@ -4,6 +4,7 @@ "tsBuildInfoFile": "dist/.cache/tsbuildinfo.json", "outDir": "dist" }, - "include": ["src/**/*", "env.d.ts"], - "exclude": ["node_modules", "dist"] + "include": ["src*", "env.d.ts"], + "exclude": ["node_modules", "dist"], + "references": [{ "path": "../database" }, { "path": "../vitest-config" }] } diff --git a/packages/vitest-config/tsconfig.json b/packages/vitest-config/tsconfig.json index ec4872de8..8fe6071e7 100644 --- a/packages/vitest-config/tsconfig.json +++ b/packages/vitest-config/tsconfig.json @@ -3,8 +3,9 @@ "declaration": true, "declarationMap": true, "esModuleInterop": true, - "incremental": false, + "incremental": true, "isolatedModules": true, + "composite": true, "lib": ["es2022", "DOM", "DOM.Iterable"], "module": "ESNext", "moduleDetection": "force", diff --git a/packages/web-tools/tsconfig.json b/packages/web-tools/tsconfig.json index a01a2531e..f15644f7c 100644 --- a/packages/web-tools/tsconfig.json +++ b/packages/web-tools/tsconfig.json @@ -10,5 +10,6 @@ } }, "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"] + "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"], + "references": [{ "path": "../vitest-config" }] }