update on the agents and registry side
This commit is contained in:
134
tests/lib/AgentOperationsBanner.test.jsx
Normal file
134
tests/lib/AgentOperationsBanner.test.jsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import React from 'react';
|
||||
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { MemoryRouter, Route, Routes, useLocation } from 'react-router-dom';
|
||||
import AgentOperationsBanner from '@/components/doormile/AgentOperationsBanner';
|
||||
import * as scanModule from '@/lib/assistant/scan';
|
||||
import { AgentFactory } from '@/lib/assistant/agent/AgentFactory';
|
||||
import { SkillRegistry } from '@/lib/assistant/skills/SkillRegistry';
|
||||
|
||||
jest.mock('lucide-react', () =>
|
||||
new Proxy({}, { get: (_t, prop) => (prop === '__esModule' ? true : (props) => <span data-testid={`icon-${String(prop)}`} {...props} />) })
|
||||
);
|
||||
jest.mock('@/lib/assistant/scan');
|
||||
jest.mock('@/lib/assistant/agent/AgentFactory');
|
||||
|
||||
const createTestQueryClient = () => new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
|
||||
function Where() {
|
||||
const loc = useLocation();
|
||||
return <div data-testid="where">{loc.pathname + loc.search}</div>;
|
||||
}
|
||||
|
||||
const renderBanner = (props = {}) =>
|
||||
render(
|
||||
<QueryClientProvider client={createTestQueryClient()}>
|
||||
<MemoryRouter initialEntries={['/doormile/exceptions']}>
|
||||
<Routes>
|
||||
<Route path="*" element={<><AgentOperationsBanner {...props} /><Where /></>} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
|
||||
const SLA_FINDING = {
|
||||
id: 'sla_breached',
|
||||
skillId: 'skill_sla_guardian',
|
||||
severity: 'critical',
|
||||
title: '1 parcel is past the promised delivery time',
|
||||
why: 'Estimated delivery time has passed.',
|
||||
count: 1,
|
||||
rows: [{ bookingid: 101, bookingno: 'BK-101' }],
|
||||
detail: ['BK-101 — 35 min overdue'],
|
||||
proposal: {
|
||||
label: 'Notify the assigned riders',
|
||||
tool: 'notifyRider',
|
||||
scope: [{ bookingid: 101 }],
|
||||
blastRadius: 'Sends a message per rider.',
|
||||
},
|
||||
};
|
||||
|
||||
describe('AgentOperationsBanner', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
SkillRegistry.clearRegistryConfig();
|
||||
});
|
||||
|
||||
it('says nothing is flagged, naming how many skills looked', async () => {
|
||||
scanModule.scanBookings.mockResolvedValue({ rows: [], truncated: false });
|
||||
AgentFactory.synthesizeDefaultAgent.mockReturnValue({ evaluateTelemetry: () => [] });
|
||||
renderBanner();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/Nothing flagged by the 5 enabled skills/i)).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByRole('button', { name: /Configure skills/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders remediation cards when findings are detected', async () => {
|
||||
scanModule.scanBookings.mockResolvedValue({ rows: [{ bookingid: 101 }], truncated: false });
|
||||
AgentFactory.synthesizeDefaultAgent.mockReturnValue({ evaluateTelemetry: () => [SLA_FINDING] });
|
||||
renderBanner({ title: 'Custom Title' });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/Custom Title/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/1 parcel is past the promised delivery time/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('filters findings by category', async () => {
|
||||
scanModule.scanBookings.mockResolvedValue({ rows: [{ bookingid: 101 }], truncated: false });
|
||||
AgentFactory.synthesizeDefaultAgent.mockReturnValue({
|
||||
evaluateTelemetry: () => [
|
||||
{ ...SLA_FINDING, proposal: undefined },
|
||||
{
|
||||
id: 'rider_critical_battery',
|
||||
skillId: 'skill_rider_battery_safety', // category: rider_safety
|
||||
severity: 'warning',
|
||||
title: 'Rider battery critical',
|
||||
why: 'Low battery',
|
||||
count: 1,
|
||||
rows: [{ bookingid: 102 }],
|
||||
},
|
||||
],
|
||||
});
|
||||
renderBanner({ category: 'rider_safety', title: 'Rider Safety Only' });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/Rider Safety Only/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Rider battery critical/i)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/1 parcel is past the promised delivery time/i)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
// The branch opened a localStorage-only settings modal here. Settings now
|
||||
// live in the agent registry, edited on one page.
|
||||
it('"Configure skills" goes to Settings → Skills & Tools', async () => {
|
||||
scanModule.scanBookings.mockResolvedValue({ rows: [], truncated: false });
|
||||
AgentFactory.synthesizeDefaultAgent.mockReturnValue({ evaluateTelemetry: () => [] });
|
||||
renderBanner();
|
||||
|
||||
fireEvent.click(await screen.findByRole('button', { name: /Configure skills/i }));
|
||||
expect(screen.getByTestId('where')).toHaveTextContent('/doormile/settings?tab=skills');
|
||||
});
|
||||
|
||||
it('says when it is running on default thresholds, and stops once the registry answers', async () => {
|
||||
scanModule.scanBookings.mockResolvedValue({ rows: [], truncated: false });
|
||||
AgentFactory.synthesizeDefaultAgent.mockReturnValue({ evaluateTelemetry: () => [] });
|
||||
renderBanner();
|
||||
|
||||
expect(await screen.findByText(/Running on default thresholds/i)).toBeInTheDocument();
|
||||
|
||||
SkillRegistry.applyRegistryConfig([{ skillid: 'skill_sla_guardian', enabled: true, thresholds: {} }]);
|
||||
await waitFor(() => expect(screen.queryByText(/Running on default thresholds/i)).toBeNull());
|
||||
});
|
||||
|
||||
it('never calls anything autonomous', async () => {
|
||||
scanModule.scanBookings.mockResolvedValue({ rows: [], truncated: false });
|
||||
AgentFactory.synthesizeDefaultAgent.mockReturnValue({ evaluateTelemetry: () => [SLA_FINDING] });
|
||||
renderBanner();
|
||||
await screen.findByText(/1 parcel is past the promised delivery time/i);
|
||||
expect(screen.queryByText(/autonomous/i)).toBeNull();
|
||||
expect(screen.queryByText(/self-healing/i)).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user