mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-12 22:42:13 +02:00
Backend Tests (`surfsense_backend`):
- `test_slack_history.py`:
- Tests for `SlackHistory` class methods including `get_all_channels`,
`get_conversation_history`, timestamp conversions, and message
formatting. Covers various API response scenarios, pagination,
and error handling.
- `test_connectors_indexing_tasks.py`:
- Unit tests for `index_slack_messages` task.
- Scenarios include initial indexing, periodic indexing, on-demand
re-indexing (targeted channels, force re-index, custom dates),
channel filtering, and error handling.
- Mocks `SlackHistory` and database interactions.
- `test_search_source_connectors_routes.py`:
- Tests for `/slack/{connector_id}/discover-channels` endpoint.
- Tests for `/slack/{connector_id}/reindex-channels` endpoint,
verifying parameter passing to the background task.
- Tests for the main `/search-source-connectors/{connector_id}/index`
endpoint for Slack connectors, ensuring the `force_full_reindex`
flag is handled.
Frontend Tests (`surfsense_web`):
- `components/editConnector/EditSlackConnectorConfigForm.test.tsx`:
- Tests rendering of all Slack configuration fields.
- Verifies `onConfigChange` callback functionality.
- Tests conditional rendering of periodic indexing settings.
- Checks disabled state of the form.
- `app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.test.tsx`:
- Tests for the "Channel Management" tab specific to Slack connectors.
- Verifies UI for discovering channels (mocking API calls).
- Tests selection of channels and updating the connector configuration.
- Tests UI for triggering on-demand re-indexing with various options.
- `hooks/useSearchSourceConnectors.test.ts`:
- Unit tests for the `discoverSlackChannels` function, mocking `fetch`.
- Unit tests for the `reindexSlackChannels` function, mocking `fetch`
and verifying correct request body construction.
- Includes testing of the `fetchWithAuth` helper function.
226 lines
11 KiB
TypeScript
226 lines
11 KiB
TypeScript
import React from 'react';
|
|
import { render, screen, fireEvent, within } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event'; // For more realistic interactions with Select
|
|
import EditSlackConnectorConfigForm from './EditSlackConnectorConfigForm';
|
|
import { SearchSourceConnector } from '@/hooks/useSearchSourceConnectors'; // Adjust path as needed
|
|
|
|
// Mock UI components that are not part of the core logic being tested, if necessary
|
|
// For ShadCN components, often direct interaction is fine, but sometimes they need setup.
|
|
// For this test, we'll assume direct interaction works.
|
|
// jest.mock("@/components/ui/input", () => (props: any) => <input {...props} data-testid={props.id || 'input-mock'} />);
|
|
// jest.mock("@/components/ui/checkbox", () => (props: any) => <input type="checkbox" {...props} data-testid={props.id || 'checkbox-mock'} />);
|
|
// jest.mock("@/components/ui/select", () => ({
|
|
// Select: (props: any) => <div data-testid={props.id || 'select-mock'}>{props.children}</div>,
|
|
// SelectTrigger: (props: any) => <button {...props}>{props.children}</button>,
|
|
// SelectValue: (props: any) => <div {...props}>{props.placeholder}</div>,
|
|
// SelectContent: (props: any) => <div {...props}>{props.children}</div>,
|
|
// SelectItem: (props: any) => <option value={props.value} {...props}>{props.children}</option>,
|
|
// }));
|
|
|
|
|
|
const initialMockConfig = {
|
|
SLACK_BOT_TOKEN: 'xoxb-initial-token',
|
|
slack_membership_filter_type: 'all_member_channels',
|
|
slack_selected_channel_ids: ['C123', 'C456'],
|
|
slack_initial_indexing_days: 30,
|
|
slack_initial_max_messages_per_channel: 1000,
|
|
slack_periodic_indexing_enabled: false,
|
|
slack_periodic_indexing_frequency: 'daily',
|
|
slack_max_messages_per_channel_periodic: 100,
|
|
};
|
|
|
|
const mockConnector: SearchSourceConnector = {
|
|
id: 1,
|
|
name: 'Slack Test Connector',
|
|
connector_type: 'SLACK_CONNECTOR',
|
|
is_indexable: true,
|
|
last_indexed_at: null,
|
|
config: { ...initialMockConfig },
|
|
user_id: 'user1',
|
|
created_at: new Date().toISOString(),
|
|
};
|
|
|
|
describe('EditSlackConnectorConfigForm', () => {
|
|
let mockOnConfigChange: jest.Mock;
|
|
|
|
beforeEach(() => {
|
|
mockOnConfigChange = jest.fn();
|
|
});
|
|
|
|
test('renders all form fields correctly with initial values', () => {
|
|
render(
|
|
<EditSlackConnectorConfigForm
|
|
connector={mockConnector}
|
|
onConfigChange={mockOnConfigChange}
|
|
disabled={false}
|
|
/>
|
|
);
|
|
|
|
// Authentication
|
|
expect(screen.getByLabelText(/Slack Bot Token/i)).toHaveValue(initialMockConfig.SLACK_BOT_TOKEN);
|
|
|
|
// Initial Indexing Settings
|
|
expect(screen.getByText('Index All Channels Where Bot is Member')).toBeInTheDocument(); // For Select value display
|
|
expect(screen.getByText(/Channel selection is managed in the 'Channels' tab/i)).toBeInTheDocument();
|
|
expect(screen.getByLabelText(/Initial Indexing Period \(days\)/i)).toHaveValue(initialMockConfig.slack_initial_indexing_days);
|
|
expect(screen.getByLabelText(/Max Messages Per Channel \(Initial Sync\)/i)).toHaveValue(initialMockConfig.slack_initial_max_messages_per_channel);
|
|
|
|
// Periodic Indexing Settings
|
|
const periodicCheckbox = screen.getByLabelText(/Enable Periodic Indexing/i) as HTMLInputElement;
|
|
expect(periodicCheckbox.checked).toBe(initialMockConfig.slack_periodic_indexing_enabled);
|
|
|
|
// Periodic fields should initially be hidden if checkbox is false
|
|
if (!initialMockConfig.slack_periodic_indexing_enabled) {
|
|
expect(screen.queryByLabelText(/Periodic Indexing Frequency/i)).not.toBeInTheDocument();
|
|
expect(screen.queryByLabelText(/Max Messages Per Channel \(Periodic Sync\)/i)).not.toBeInTheDocument();
|
|
}
|
|
});
|
|
|
|
test('calls onConfigChange with updated config for SLACK_BOT_TOKEN', () => {
|
|
render(
|
|
<EditSlackConnectorConfigForm
|
|
connector={mockConnector}
|
|
onConfigChange={mockOnConfigChange}
|
|
disabled={false}
|
|
/>
|
|
);
|
|
const tokenInput = screen.getByLabelText(/Slack Bot Token/i);
|
|
fireEvent.change(tokenInput, { target: { value: 'new-token' } });
|
|
expect(mockOnConfigChange).toHaveBeenCalledWith(
|
|
expect.objectContaining({ SLACK_BOT_TOKEN: 'new-token' })
|
|
);
|
|
});
|
|
|
|
test('calls onConfigChange for slack_initial_indexing_days', () => {
|
|
render(<EditSlackConnectorConfigForm connector={mockConnector} onConfigChange={mockOnConfigChange} disabled={false} />);
|
|
const input = screen.getByLabelText(/Initial Indexing Period \(days\)/i);
|
|
fireEvent.change(input, { target: { value: '10' } });
|
|
expect(mockOnConfigChange).toHaveBeenCalledWith(expect.objectContaining({ slack_initial_indexing_days: 10 }));
|
|
|
|
fireEvent.change(input, { target: { value: '-1' } });
|
|
expect(mockOnConfigChange).toHaveBeenCalledWith(expect.objectContaining({ slack_initial_indexing_days: -1 }));
|
|
|
|
fireEvent.change(input, { target: { value: '' } }); // Empty value
|
|
expect(mockOnConfigChange).toHaveBeenCalledWith(expect.objectContaining({ slack_initial_indexing_days: null }));
|
|
});
|
|
|
|
test('calls onConfigChange for slack_initial_max_messages_per_channel', () => {
|
|
render(<EditSlackConnectorConfigForm connector={mockConnector} onConfigChange={mockOnConfigChange} disabled={false} />);
|
|
const input = screen.getByLabelText(/Max Messages Per Channel \(Initial Sync\)/i);
|
|
fireEvent.change(input, { target: { value: '500' } });
|
|
expect(mockOnConfigChange).toHaveBeenCalledWith(expect.objectContaining({ slack_initial_max_messages_per_channel: 500 }));
|
|
});
|
|
|
|
test('calls onConfigChange for slack_membership_filter_type', async () => {
|
|
render(<EditSlackConnectorConfigForm connector={mockConnector} onConfigChange={mockOnConfigChange} disabled={false} />);
|
|
|
|
// For ShadCN Select, we need to click the trigger, then the item.
|
|
// `userEvent` is better for this, but `fireEvent` can also work.
|
|
const selectTrigger = screen.getByRole('combobox', { name: /Channel Indexing Behavior/i });
|
|
await userEvent.click(selectTrigger);
|
|
|
|
// Assuming SelectItem roles are 'option' or similar, and they are now in the document
|
|
const option = await screen.findByText('Index Only Selected Channels'); // Wait for option to appear
|
|
await userEvent.click(option);
|
|
|
|
expect(mockOnConfigChange).toHaveBeenCalledWith(
|
|
expect.objectContaining({ slack_membership_filter_type: 'selected_member_channels' })
|
|
);
|
|
});
|
|
|
|
test('shows/hides periodic indexing fields and calls onConfigChange', async () => {
|
|
render(<EditSlackConnectorConfigForm connector={mockConnector} onConfigChange={mockOnConfigChange} disabled={false} />);
|
|
const periodicCheckbox = screen.getByLabelText(/Enable Periodic Indexing/i);
|
|
|
|
// Initially periodic fields are not visible (as per initialMockConfig)
|
|
expect(screen.queryByLabelText(/Periodic Indexing Frequency/i)).not.toBeInTheDocument();
|
|
expect(screen.queryByLabelText(/Max Messages Per Channel \(Periodic Sync\)/i)).not.toBeInTheDocument();
|
|
|
|
// Check the checkbox
|
|
fireEvent.click(periodicCheckbox);
|
|
expect(mockOnConfigChange).toHaveBeenCalledWith(
|
|
expect.objectContaining({ slack_periodic_indexing_enabled: true })
|
|
);
|
|
|
|
// Now the fields should be visible
|
|
const frequencySelect = await screen.findByLabelText(/Periodic Indexing Frequency/i); // Wait for it
|
|
const maxMessagesInput = screen.getByLabelText(/Max Messages Per Channel \(Periodic Sync\)/i);
|
|
expect(frequencySelect).toBeInTheDocument();
|
|
expect(maxMessagesInput).toBeInTheDocument();
|
|
|
|
// Change frequency
|
|
const freqSelectTrigger = screen.getByRole('combobox', { name: /Periodic Indexing Frequency/i });
|
|
await userEvent.click(freqSelectTrigger);
|
|
const weeklyOption = await screen.findByText('Weekly');
|
|
await userEvent.click(weeklyOption);
|
|
expect(mockOnConfigChange).toHaveBeenCalledWith(
|
|
expect.objectContaining({ slack_periodic_indexing_frequency: 'weekly', slack_periodic_indexing_enabled: true })
|
|
);
|
|
|
|
// Change max messages periodic
|
|
fireEvent.change(maxMessagesInput, { target: { value: '75' } });
|
|
expect(mockOnConfigChange).toHaveBeenCalledWith(
|
|
expect.objectContaining({ slack_max_messages_per_channel_periodic: 75, slack_periodic_indexing_enabled: true })
|
|
);
|
|
|
|
// Uncheck the checkbox
|
|
fireEvent.click(periodicCheckbox);
|
|
expect(mockOnConfigChange).toHaveBeenCalledWith(
|
|
expect.objectContaining({ slack_periodic_indexing_enabled: false })
|
|
);
|
|
|
|
// Fields should be hidden again
|
|
expect(screen.queryByLabelText(/Periodic Indexing Frequency/i)).not.toBeInTheDocument();
|
|
expect(screen.queryByLabelText(/Max Messages Per Channel \(Periodic Sync\)/i)).not.toBeInTheDocument();
|
|
});
|
|
|
|
test('preserves other config values when one changes', () => {
|
|
render(<EditSlackConnectorConfigForm connector={mockConnector} onConfigChange={mockOnConfigChange} disabled={false} />);
|
|
const tokenInput = screen.getByLabelText(/Slack Bot Token/i);
|
|
fireEvent.change(tokenInput, { target: { value: 'new-xoxb-token' } });
|
|
|
|
expect(mockOnConfigChange).toHaveBeenCalledWith({
|
|
...initialMockConfig, // All original values
|
|
SLACK_BOT_TOKEN: 'new-xoxb-token', // Only this one changed
|
|
});
|
|
});
|
|
|
|
test('disables all form fields when disabled prop is true', () => {
|
|
render(<EditSlackConnectorConfigForm connector={mockConnector} onConfigChange={mockOnConfigChange} disabled={true} />);
|
|
|
|
expect(screen.getByLabelText(/Slack Bot Token/i)).toBeDisabled();
|
|
expect(screen.getByRole('combobox', { name: /Channel Indexing Behavior/i })).toBeDisabled();
|
|
expect(screen.getByLabelText(/Initial Indexing Period \(days\)/i)).toBeDisabled();
|
|
expect(screen.getByLabelText(/Max Messages Per Channel \(Initial Sync\)/i)).toBeDisabled();
|
|
expect(screen.getByLabelText(/Enable Periodic Indexing/i)).toBeDisabled();
|
|
|
|
// If periodic indexing was enabled by default in mock, check those too
|
|
const connectorWithPeriodicEnabled = {
|
|
...mockConnector,
|
|
config: { ...mockConnector.config, slack_periodic_indexing_enabled: true }
|
|
};
|
|
render(<EditSlackConnectorConfigForm connector={connectorWithPeriodicEnabled} onConfigChange={mockOnConfigChange} disabled={true} />);
|
|
expect(screen.getByRole('combobox', { name: /Periodic Indexing Frequency/i })).toBeDisabled();
|
|
expect(screen.getByLabelText(/Max Messages Per Channel \(Periodic Sync\)/i)).toBeDisabled();
|
|
});
|
|
|
|
test('handles empty string for number inputs correctly by converting to null', () => {
|
|
render(<EditSlackConnectorConfigForm connector={mockConnector} onConfigChange={mockOnConfigChange} disabled={false} />);
|
|
const initialDaysInput = screen.getByLabelText(/Initial Indexing Period \(days\)/i);
|
|
fireEvent.change(initialDaysInput, { target: { value: '' } });
|
|
expect(mockOnConfigChange).toHaveBeenCalledWith(expect.objectContaining({ slack_initial_indexing_days: null }));
|
|
|
|
const initialMaxMessagesInput = screen.getByLabelText(/Max Messages Per Channel \(Initial Sync\)/i);
|
|
fireEvent.change(initialMaxMessagesInput, { target: { value: '' } });
|
|
expect(mockOnConfigChange).toHaveBeenCalledWith(expect.objectContaining({ slack_initial_max_messages_per_channel: null }));
|
|
|
|
// Enable periodic to test that field
|
|
const periodicCheckbox = screen.getByLabelText(/Enable Periodic Indexing/i);
|
|
fireEvent.click(periodicCheckbox); // Enable
|
|
|
|
const periodicMaxMessagesInput = screen.getByLabelText(/Max Messages Per Channel \(Periodic Sync\)/i);
|
|
fireEvent.change(periodicMaxMessagesInput, { target: { value: '' } });
|
|
expect(mockOnConfigChange).toHaveBeenCalledWith(expect.objectContaining({ slack_max_messages_per_channel_periodic: null }));
|
|
});
|
|
|
|
});
|