fix all of the bugs

This commit is contained in:
Nate Kelley 2025-07-12 22:01:25 -06:00
parent 4a1c4f73f0
commit 963bf6b2f2
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
36 changed files with 301 additions and 215 deletions

View File

@ -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" }
]
}

View File

@ -66,6 +66,8 @@ const mockMessage: Message = {
updatedAt: new Date().toISOString(),
deletedAt: null,
feedback: null,
postProcessingMessage: null,
triggerRunId: null,
};
describe('buildChatWithMessages', () => {

View File

@ -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();

View File

@ -51,6 +51,8 @@ const mockMessage: Message = {
updatedAt: new Date().toISOString(),
deletedAt: null,
feedback: null,
postProcessingMessage: null,
triggerRunId: null,
};
// Mock database functions

View File

@ -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';

View File

@ -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()
);
});

View File

@ -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<typeof organizations>;
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 () => {

View File

@ -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);

View File

@ -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<typeof organizations>;
describe('removeApprovedDomainsHandler (integration)', () => {
let testUser: User;
let testOrg: Organization;

View File

@ -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<typeof organizations>;
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 () => {

View File

@ -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<typeof service.formatWorkspaceSettingsResponse>[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<typeof service.formatWorkspaceSettingsResponse>[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<typeof service.formatWorkspaceSettingsResponse>[0];
const settingsFalse = {
restrictNewUserInvitations: false,
defaultRole: 'member',
};
defaultRole: 'member' as OrganizationRole,
} as Parameters<typeof service.formatWorkspaceSettingsResponse>[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<typeof service.formatWorkspaceSettingsResponse>[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<typeof service.buildUpdateData>[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<typeof service.buildUpdateData>[0];
const result = service.buildUpdateData(request);
expect(result).toHaveProperty('restrictNewUserInvitations', false);

View File

@ -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');
});

View File

@ -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');
});

View File

@ -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({

View File

@ -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();
});

View File

@ -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 () => {

View File

@ -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);

View File

@ -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" }
]
}

View File

@ -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" }
]
}

View File

@ -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" }]
}

View File

@ -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" }
]
}

View File

@ -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" }]
}

View File

@ -34,8 +34,6 @@ function validateEnvironment(): string {
}
return connectionString;
return connectionString;
}
// Initialize the database pool

View File

@ -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" }]
}

View File

@ -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" }]
}

View File

@ -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');
}
}
});

View File

@ -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: '$',

View File

@ -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),

View File

@ -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();
}
});

View File

@ -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" }]
}

View File

@ -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" }]
}

View File

@ -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" }]
}

View File

@ -1,4 +1,4 @@
{
"extends": "../typescript-config/base.json",
"include": ["volumes/**/*"]
"include": ["volumes*"]
}

View File

@ -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" }]
}

View File

@ -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",

View File

@ -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" }]
}