mirror of https://github.com/buster-so/buster.git
fix linting
This commit is contained in:
parent
4ceb412e37
commit
c2d093291b
|
@ -0,0 +1,195 @@
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { HomePageController } from './HomePageController';
|
||||
|
||||
// Mock the dependencies
|
||||
vi.mock('@/api/buster_rest/users/useGetUserInfo', () => ({
|
||||
useGetUserBasicInfo: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('./useNewChatWarning', () => ({
|
||||
useNewChatWarning: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('./NewChatWarning', () => ({
|
||||
NewChatWarning: vi.fn(({ showWarning, hasDatasets, hasDatasources, isAdmin }) => (
|
||||
<div data-testid="new-chat-warning">
|
||||
Warning Component - showWarning: {showWarning.toString()}, hasDatasets:{' '}
|
||||
{hasDatasets.toString()}, hasDatasources: {hasDatasources.toString()}, isAdmin:{' '}
|
||||
{isAdmin?.toString() || 'undefined'}
|
||||
</div>
|
||||
)),
|
||||
}));
|
||||
|
||||
vi.mock('./NewChatInput', () => ({
|
||||
NewChatInput: vi.fn(({ initialValue, autoSubmit }) => (
|
||||
<div data-testid="new-chat-input">
|
||||
Chat Input - initialValue: {initialValue || 'none'}, autoSubmit:{' '}
|
||||
{autoSubmit?.toString() || 'false'}
|
||||
</div>
|
||||
)),
|
||||
}));
|
||||
|
||||
// Mock ClientOnly to render children directly in tests
|
||||
vi.mock('@tanstack/react-router', () => ({
|
||||
ClientOnly: vi.fn(({ children }) => <>{children}</>),
|
||||
}));
|
||||
|
||||
import { useGetUserBasicInfo } from '@/api/buster_rest/users/useGetUserInfo';
|
||||
import { useNewChatWarning } from './useNewChatWarning';
|
||||
|
||||
const mockUseGetUserBasicInfo = vi.mocked(useGetUserBasicInfo);
|
||||
const mockUseNewChatWarning = vi.mocked(useNewChatWarning);
|
||||
|
||||
describe('HomePageController', () => {
|
||||
beforeEach(() => {
|
||||
// Default user info
|
||||
mockUseGetUserBasicInfo.mockReturnValue({
|
||||
id: 'user-1',
|
||||
name: 'John Doe',
|
||||
email: 'john.doe@example.com',
|
||||
avatar_url: null,
|
||||
created_at: '2023-01-01T00:00:00Z',
|
||||
updated_at: '2023-01-01T00:00:00Z',
|
||||
attributes: {
|
||||
organization_id: 'org-1',
|
||||
organization_role: 'querier',
|
||||
user_email: 'john.doe@example.com',
|
||||
user_id: 'user-1',
|
||||
},
|
||||
favorites: [],
|
||||
});
|
||||
|
||||
// Mock time to ensure consistent greeting
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date('2023-01-01 10:00:00')); // 10 AM - morning
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should render NewChatWarning when showWarning is true', () => {
|
||||
// Mock the hook to return showWarning: true
|
||||
mockUseNewChatWarning.mockReturnValue({
|
||||
showWarning: true,
|
||||
hasDatasets: false,
|
||||
hasDatasources: false,
|
||||
isFetched: true,
|
||||
isAdmin: true,
|
||||
userRole: 'workspace_admin',
|
||||
});
|
||||
|
||||
render(<HomePageController initialValue="test" autoSubmit={true} />);
|
||||
|
||||
// Should show the warning component
|
||||
expect(screen.getByTestId('new-chat-warning')).toBeInTheDocument();
|
||||
expect(screen.getByText(/Warning Component.*showWarning: true/)).toBeInTheDocument();
|
||||
|
||||
// Should NOT show the main interface components
|
||||
expect(screen.queryByTestId('new-chat-input')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Good morning, John Doe')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('How can I help you today?')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render main interface when showWarning is false', () => {
|
||||
// Mock the hook to return showWarning: false
|
||||
mockUseNewChatWarning.mockReturnValue({
|
||||
showWarning: false,
|
||||
hasDatasets: true,
|
||||
hasDatasources: true,
|
||||
isFetched: true,
|
||||
isAdmin: false,
|
||||
userRole: 'querier',
|
||||
});
|
||||
|
||||
render(<HomePageController initialValue="hello world" autoSubmit={false} />);
|
||||
|
||||
// Should show the main interface components
|
||||
expect(screen.getByText('Good morning, John Doe')).toBeInTheDocument();
|
||||
expect(screen.getByText('How can I help you today?')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('new-chat-input')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(/Chat Input.*initialValue: hello world.*autoSubmit: false/)
|
||||
).toBeInTheDocument();
|
||||
|
||||
// Should NOT show the warning component
|
||||
expect(screen.queryByTestId('new-chat-warning')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should pass correct props to NewChatInput', () => {
|
||||
mockUseNewChatWarning.mockReturnValue({
|
||||
showWarning: false,
|
||||
hasDatasets: true,
|
||||
hasDatasources: true,
|
||||
isFetched: true,
|
||||
isAdmin: false,
|
||||
userRole: 'querier',
|
||||
});
|
||||
|
||||
render(<HomePageController initialValue="custom input" autoSubmit={true} />);
|
||||
|
||||
expect(
|
||||
screen.getByText(/Chat Input.*initialValue: custom input.*autoSubmit: true/)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should pass all newChatWarningProps to NewChatWarning', () => {
|
||||
const warningProps = {
|
||||
showWarning: true,
|
||||
hasDatasets: false,
|
||||
hasDatasources: true,
|
||||
isFetched: true,
|
||||
isAdmin: true,
|
||||
userRole: 'workspace_admin' as const,
|
||||
};
|
||||
|
||||
mockUseNewChatWarning.mockReturnValue(warningProps);
|
||||
|
||||
render(<HomePageController />);
|
||||
|
||||
expect(
|
||||
screen.getByText(
|
||||
/Warning Component.*showWarning: true.*hasDatasets: false.*hasDatasources: true.*isAdmin: true/
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('greeting logic', () => {
|
||||
beforeEach(() => {
|
||||
mockUseNewChatWarning.mockReturnValue({
|
||||
showWarning: false,
|
||||
hasDatasets: true,
|
||||
hasDatasources: true,
|
||||
isFetched: true,
|
||||
isAdmin: false,
|
||||
userRole: 'querier',
|
||||
});
|
||||
});
|
||||
|
||||
it('should show morning greeting at 10 AM', () => {
|
||||
vi.setSystemTime(new Date('2023-01-01 10:00:00'));
|
||||
render(<HomePageController />);
|
||||
expect(screen.getByText('Good morning, John Doe')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show afternoon greeting at 3 PM', () => {
|
||||
vi.setSystemTime(new Date('2023-01-01 15:00:00'));
|
||||
render(<HomePageController />);
|
||||
expect(screen.getByText('Good afternoon, John Doe')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show evening greeting at 8 PM', () => {
|
||||
vi.setSystemTime(new Date('2023-01-01 20:00:00'));
|
||||
render(<HomePageController />);
|
||||
expect(screen.getByText('Good evening, John Doe')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show night greeting at 2 AM', () => {
|
||||
vi.setSystemTime(new Date('2023-01-01 02:00:00'));
|
||||
render(<HomePageController />);
|
||||
expect(screen.getByText('Good night, John Doe')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,185 @@
|
|||
import { renderHook } from '@testing-library/react';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { useNewChatWarning } from './useNewChatWarning';
|
||||
|
||||
// Mock the API hooks
|
||||
vi.mock('@/api/buster_rest/data_source', () => ({
|
||||
useListDatasources: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@/api/buster_rest/datasets', () => ({
|
||||
useGetDatasets: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@/api/buster_rest/users/useGetUserInfo', () => ({
|
||||
useIsUserAdmin: vi.fn(),
|
||||
useGetUserRole: vi.fn(),
|
||||
}));
|
||||
|
||||
import { useListDatasources } from '@/api/buster_rest/data_source';
|
||||
import { useGetDatasets } from '@/api/buster_rest/datasets';
|
||||
import { useGetUserRole, useIsUserAdmin } from '@/api/buster_rest/users/useGetUserInfo';
|
||||
|
||||
const mockUseListDatasources = vi.mocked(useListDatasources);
|
||||
const mockUseGetDatasets = vi.mocked(useGetDatasets);
|
||||
const mockUseIsUserAdmin = vi.mocked(useIsUserAdmin);
|
||||
const mockUseGetUserRole = vi.mocked(useGetUserRole);
|
||||
|
||||
describe('useNewChatWarning', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should show warning when datasets are empty and data is fetched', () => {
|
||||
mockUseGetDatasets.mockReturnValue({
|
||||
data: [],
|
||||
isFetched: true,
|
||||
} as any);
|
||||
mockUseListDatasources.mockReturnValue({
|
||||
data: [{ id: '1', name: 'Test Datasource' }],
|
||||
isFetched: true,
|
||||
} as any);
|
||||
mockUseIsUserAdmin.mockReturnValue(true);
|
||||
mockUseGetUserRole.mockReturnValue('workspace_admin');
|
||||
|
||||
const { result } = renderHook(() => useNewChatWarning());
|
||||
|
||||
expect(result.current.showWarning).toBe(true);
|
||||
expect(result.current.hasDatasets).toBe(false);
|
||||
expect(result.current.hasDatasources).toBe(true);
|
||||
expect(result.current.isFetched).toBe(true);
|
||||
});
|
||||
|
||||
it('should show warning when datasources are empty and data is fetched', () => {
|
||||
mockUseGetDatasets.mockReturnValue({
|
||||
data: [{ id: '1', name: 'Test Dataset' }],
|
||||
isFetched: true,
|
||||
} as any);
|
||||
mockUseListDatasources.mockReturnValue({
|
||||
data: [],
|
||||
isFetched: true,
|
||||
} as any);
|
||||
mockUseIsUserAdmin.mockReturnValue(false);
|
||||
mockUseGetUserRole.mockReturnValue('querier');
|
||||
|
||||
const { result } = renderHook(() => useNewChatWarning());
|
||||
|
||||
expect(result.current.showWarning).toBe(true);
|
||||
expect(result.current.hasDatasets).toBe(true);
|
||||
expect(result.current.hasDatasources).toBe(false);
|
||||
expect(result.current.isFetched).toBe(true);
|
||||
});
|
||||
|
||||
it('should show warning when both datasets and datasources are empty', () => {
|
||||
mockUseGetDatasets.mockReturnValue({
|
||||
data: [],
|
||||
isFetched: true,
|
||||
} as any);
|
||||
mockUseListDatasources.mockReturnValue({
|
||||
data: [],
|
||||
isFetched: true,
|
||||
} as any);
|
||||
mockUseIsUserAdmin.mockReturnValue(true);
|
||||
mockUseGetUserRole.mockReturnValue('workspace_admin');
|
||||
|
||||
const { result } = renderHook(() => useNewChatWarning());
|
||||
|
||||
expect(result.current.showWarning).toBe(true);
|
||||
expect(result.current.hasDatasets).toBe(false);
|
||||
expect(result.current.hasDatasources).toBe(false);
|
||||
expect(result.current.isFetched).toBe(true);
|
||||
});
|
||||
|
||||
it('should not show warning when both datasets and datasources have data', () => {
|
||||
mockUseGetDatasets.mockReturnValue({
|
||||
data: [{ id: '1', name: 'Test Dataset' }],
|
||||
isFetched: true,
|
||||
} as any);
|
||||
mockUseListDatasources.mockReturnValue({
|
||||
data: [{ id: '1', name: 'Test Datasource' }],
|
||||
isFetched: true,
|
||||
} as any);
|
||||
mockUseIsUserAdmin.mockReturnValue(false);
|
||||
mockUseGetUserRole.mockReturnValue('viewer');
|
||||
|
||||
const { result } = renderHook(() => useNewChatWarning());
|
||||
|
||||
expect(result.current.showWarning).toBe(false);
|
||||
expect(result.current.hasDatasets).toBe(true);
|
||||
expect(result.current.hasDatasources).toBe(true);
|
||||
expect(result.current.isFetched).toBe(true);
|
||||
});
|
||||
|
||||
it('should not show warning when datasets are not yet fetched', () => {
|
||||
mockUseGetDatasets.mockReturnValue({
|
||||
data: undefined,
|
||||
isFetched: false,
|
||||
} as any);
|
||||
mockUseListDatasources.mockReturnValue({
|
||||
data: [],
|
||||
isFetched: true,
|
||||
} as any);
|
||||
mockUseIsUserAdmin.mockReturnValue(true);
|
||||
mockUseGetUserRole.mockReturnValue('workspace_admin');
|
||||
|
||||
const { result } = renderHook(() => useNewChatWarning());
|
||||
|
||||
expect(result.current.showWarning).toBe(false);
|
||||
expect(result.current.isFetched).toBe(false);
|
||||
});
|
||||
|
||||
it('should not show warning when datasources are not yet fetched', () => {
|
||||
mockUseGetDatasets.mockReturnValue({
|
||||
data: [],
|
||||
isFetched: true,
|
||||
} as any);
|
||||
mockUseListDatasources.mockReturnValue({
|
||||
data: undefined,
|
||||
isFetched: false,
|
||||
} as any);
|
||||
mockUseIsUserAdmin.mockReturnValue(false);
|
||||
mockUseGetUserRole.mockReturnValue('querier');
|
||||
|
||||
const { result } = renderHook(() => useNewChatWarning());
|
||||
|
||||
expect(result.current.showWarning).toBe(false);
|
||||
expect(result.current.isFetched).toBe(false);
|
||||
});
|
||||
|
||||
it('should not show warning when neither datasets nor datasources are fetched', () => {
|
||||
mockUseGetDatasets.mockReturnValue({
|
||||
data: undefined,
|
||||
isFetched: false,
|
||||
} as any);
|
||||
mockUseListDatasources.mockReturnValue({
|
||||
data: undefined,
|
||||
isFetched: false,
|
||||
} as any);
|
||||
mockUseIsUserAdmin.mockReturnValue(true);
|
||||
mockUseGetUserRole.mockReturnValue('workspace_admin');
|
||||
|
||||
const { result } = renderHook(() => useNewChatWarning());
|
||||
|
||||
expect(result.current.showWarning).toBe(false);
|
||||
expect(result.current.isFetched).toBe(false);
|
||||
});
|
||||
|
||||
it('should correctly return admin status and user role', () => {
|
||||
mockUseGetDatasets.mockReturnValue({
|
||||
data: [{ id: '1', name: 'Test Dataset' }],
|
||||
isFetched: true,
|
||||
} as any);
|
||||
mockUseListDatasources.mockReturnValue({
|
||||
data: [{ id: '1', name: 'Test Datasource' }],
|
||||
isFetched: true,
|
||||
} as any);
|
||||
mockUseIsUserAdmin.mockReturnValue(true);
|
||||
mockUseGetUserRole.mockReturnValue('data_admin');
|
||||
|
||||
const { result } = renderHook(() => useNewChatWarning());
|
||||
|
||||
expect(result.current.isAdmin).toBe(true);
|
||||
expect(result.current.userRole).toBe('data_admin');
|
||||
expect(result.current.showWarning).toBe(false);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
import { checkIfUserIsAdmin } from './user';
|
||||
|
||||
describe('checkIfUserIsAdmin', () => {
|
||||
it('should return false when userOrganization is undefined', () => {
|
||||
expect(checkIfUserIsAdmin(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when userOrganization is null', () => {
|
||||
expect(checkIfUserIsAdmin(null)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when user role is data_admin', () => {
|
||||
const userOrganization = { role: 'data_admin' as const };
|
||||
expect(checkIfUserIsAdmin(userOrganization)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true when user role is workspace_admin', () => {
|
||||
const userOrganization = { role: 'workspace_admin' as const };
|
||||
expect(checkIfUserIsAdmin(userOrganization)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when user role is neither data_admin nor workspace_admin', () => {
|
||||
const userOrganization = { role: 'restricted_querier' as const };
|
||||
expect(checkIfUserIsAdmin(userOrganization)).toBe(false);
|
||||
});
|
||||
});
|
|
@ -98,10 +98,10 @@ export function createAnalystAgent(analystAgentOptions: AnalystAgentOptions) {
|
|||
|
||||
const docsSystemMessage = docsContent
|
||||
? ({
|
||||
role: 'system',
|
||||
content: `<data_catalog_docs>\n${docsContent}\n</data_catalog_docs>`,
|
||||
providerOptions: DEFAULT_ANTHROPIC_OPTIONS,
|
||||
} as ModelMessage)
|
||||
role: 'system',
|
||||
content: `<data_catalog_docs>\n${docsContent}\n</data_catalog_docs>`,
|
||||
providerOptions: DEFAULT_ANTHROPIC_OPTIONS,
|
||||
} as ModelMessage)
|
||||
: null;
|
||||
|
||||
async function stream({ messages }: AnalystStreamOptions) {
|
||||
|
@ -134,19 +134,19 @@ export function createAnalystAgent(analystAgentOptions: AnalystAgentOptions) {
|
|||
// Create analyst instructions system message with proper escaping
|
||||
const analystInstructionsMessage = analystInstructions
|
||||
? ({
|
||||
role: 'system',
|
||||
content: `<organization_instructions>\n${analystInstructions}\n</organization_instructions>`,
|
||||
providerOptions: DEFAULT_ANTHROPIC_OPTIONS,
|
||||
} as ModelMessage)
|
||||
role: 'system',
|
||||
content: `<organization_instructions>\n${analystInstructions}\n</organization_instructions>`,
|
||||
providerOptions: DEFAULT_ANTHROPIC_OPTIONS,
|
||||
} as ModelMessage)
|
||||
: null;
|
||||
|
||||
// Create user personalization system message
|
||||
const userPersonalizationSystemMessage = userPersonalizationMessageContent
|
||||
? ({
|
||||
role: 'system',
|
||||
content: userPersonalizationMessageContent,
|
||||
providerOptions: DEFAULT_ANTHROPIC_OPTIONS,
|
||||
} as ModelMessage)
|
||||
role: 'system',
|
||||
content: userPersonalizationMessageContent,
|
||||
providerOptions: DEFAULT_ANTHROPIC_OPTIONS,
|
||||
} as ModelMessage)
|
||||
: null;
|
||||
|
||||
return wrapTraced(
|
||||
|
|
|
@ -83,10 +83,14 @@ describe('Analyst Agent Instructions', () => {
|
|||
expect(result).toContain('MANDATORY SQL NAMING CONVENTIONS');
|
||||
|
||||
// Ensure table references require full qualification
|
||||
expect(result).toContain('All Table References: MUST be fully qualified: `DATABASE_NAME.SCHEMA_NAME.TABLE_NAME`');
|
||||
expect(result).toContain(
|
||||
'All Table References: MUST be fully qualified: `DATABASE_NAME.SCHEMA_NAME.TABLE_NAME`'
|
||||
);
|
||||
|
||||
// Ensure column references use table aliases (not full qualifiers)
|
||||
expect(result).toContain('All Column References: MUST be qualified with their table alias (e.g., `c.customerid`)');
|
||||
expect(result).toContain(
|
||||
'All Column References: MUST be qualified with their table alias (e.g., `c.customerid`)'
|
||||
);
|
||||
|
||||
// Ensure examples show table alias usage without full qualification
|
||||
expect(result).toContain('c.customerid');
|
||||
|
|
|
@ -151,16 +151,23 @@ describe('Think and Prep Agent Instructions', () => {
|
|||
['investigation', 'investigation'],
|
||||
])('SQL naming conventions in %s mode', (modeName, mode) => {
|
||||
it(`should contain mandatory SQL naming conventions in ${modeName} mode`, () => {
|
||||
const result = getThinkAndPrepAgentSystemPrompt('Test guidance', mode as 'standard' | 'investigation');
|
||||
const result = getThinkAndPrepAgentSystemPrompt(
|
||||
'Test guidance',
|
||||
mode as 'standard' | 'investigation'
|
||||
);
|
||||
|
||||
// Check for MANDATORY SQL NAMING CONVENTIONS section
|
||||
expect(result).toContain('MANDATORY SQL NAMING CONVENTIONS');
|
||||
|
||||
// Ensure table references require full qualification
|
||||
expect(result).toContain('All Table References: MUST be fully qualified: `DATABASE_NAME.SCHEMA_NAME.TABLE_NAME`');
|
||||
expect(result).toContain(
|
||||
'All Table References: MUST be fully qualified: `DATABASE_NAME.SCHEMA_NAME.TABLE_NAME`'
|
||||
);
|
||||
|
||||
// Ensure column references use table aliases (not full qualifiers)
|
||||
expect(result).toContain('All Column References: MUST be qualified with their table alias (e.g., `c.customerid`)');
|
||||
expect(result).toContain(
|
||||
'All Column References: MUST be qualified with their table alias (e.g., `c.customerid`)'
|
||||
);
|
||||
|
||||
// Ensure examples show table alias usage without full qualification
|
||||
expect(result).toContain('c.customerid');
|
||||
|
@ -172,7 +179,10 @@ describe('Think and Prep Agent Instructions', () => {
|
|||
});
|
||||
|
||||
it(`should use column names qualified with table aliases in ${modeName} mode`, () => {
|
||||
const result = getThinkAndPrepAgentSystemPrompt('Test guidance', mode as 'standard' | 'investigation');
|
||||
const result = getThinkAndPrepAgentSystemPrompt(
|
||||
'Test guidance',
|
||||
mode as 'standard' | 'investigation'
|
||||
);
|
||||
|
||||
// Check for the updated description
|
||||
expect(result).toContain('Use column names qualified with table aliases');
|
||||
|
|
|
@ -14,7 +14,7 @@ export const DEFAULT_ANTHROPIC_OPTIONS = {
|
|||
additionalModelRequestFields: {
|
||||
anthropic_beta: ['fine-grained-tool-streaming-2025-05-14'],
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const DEFAULT_OPENAI_OPTIONS = {
|
||||
|
|
Loading…
Reference in New Issue