// The wiring nothing else covers. // // The executor is tested in isolation, and SlaRemediationCard is tested in // isolation. What was never tested is the seam between them: does the banner // actually hand the card an executor, does a click reach the real // executeProposal, does the API get called, and does the board re-evaluate // afterwards. // // That seam is where this feature would fail silently — a card rendered with no // onRemediate looks identical to one wired correctly until somebody clicks it. import React from 'react'; import { render, screen, waitFor, fireEvent } from '@testing-library/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { MemoryRouter } from 'react-router-dom'; import AgentOperationsBanner from '@/components/doormile/AgentOperationsBanner'; import * as scanModule from '@/lib/assistant/scan'; jest.mock('@/lib/assistant/scan'); jest.mock('lucide-react', () => new Proxy({}, { get: (_t, prop) => (prop === '__esModule' ? true : (props) => ) }) ); // Only the network is faked. AgentFactory, SkillRegistry, the skills, the // adapter, briefing and the executor are all the real implementations. jest.mock('@/api/doormile', () => { const actual = jest.requireActual('@/api/doormile/queries'); return { __esModule: true, getMilers: jest.fn(), notifyRider: jest.fn(), batchAssignBookings: jest.fn(), buildMilerLookup: actual.buildMilerLookup, mapBookingStatusToDeliveryStatus: actual.mapBookingStatusToDeliveryStatus }; }); jest.mock('@/api/doormile/notify', () => ({ OpenToast: jest.fn() })); const api = require('@/api/doormile'); const { OpenToast } = require('@/api/doormile/notify'); const client = () => new QueryClient({ defaultOptions: { queries: { retry: false } } }); // A rider stamped arrival well over the 20-minute stall threshold. const stalledScan = () => ({ truncated: false, rows: [ { bookingid: 2, bookingno: 'DM-BK-002', status: 'Pickup_Scheduled', createdat: '2026-09-01 10:00:00', reachedat: '2026-09-01 13:00:00', assignedmileruserid: 8, milername: 'Kumar', serviceoptions: [] } ] }); beforeEach(() => { jest.clearAllMocks(); jest.useFakeTimers({ doNotFake: ['setTimeout', 'clearTimeout', 'setInterval', 'clearInterval', 'queueMicrotask'] }); jest.setSystemTime(new Date('2026-09-01T14:00:00')); scanModule.scanBookings.mockResolvedValue(stalledScan()); api.getMilers.mockResolvedValue([{ userid: 8, milerprofileid: 501, displayname: 'Kumar' }]); api.notifyRider.mockResolvedValue({ success: true }); }); afterEach(() => { jest.useRealTimers(); }); const renderBanner = () => render( ); describe('click-through: banner to executor to API', () => { it('surfaces a real stall finding from raw bookings', async () => { renderBanner(); expect(await screen.findByText(/stalled at door/i)).toBeInTheDocument(); }); // The whole point. A card with no executor renders disabled and says // "Review only"; if the banner failed to pass onRemediate this would fail. it('renders the stall proposal as clickable, not review-only', async () => { renderBanner(); await screen.findByText(/stalled at door/i); expect(screen.queryByRole('button', { name: /review only/i })).not.toBeInTheDocument(); expect(await screen.findByRole('button', { name: /message waiting riders/i })).toBeEnabled(); }); it('a click reaches the real executor and pushes to the right rider', async () => { renderBanner(); await screen.findByText(/stalled at door/i); fireEvent.click(await screen.findByRole('button', { name: /message waiting riders/i })); await waitFor(() => { expect(api.notifyRider).toHaveBeenCalledTimes(1); }); // Through the REAL buildMilerLookup: mileruserid 8 becomes profile 501. expect(api.notifyRider.mock.calls[0][0]).toBe(501); expect(api.notifyRider.mock.calls[0][0]).not.toBe(8); }); it('re-scans after acting, which is what makes it a loop', async () => { renderBanner(); await screen.findByText(/stalled at door/i); const scansBefore = scanModule.scanBookings.mock.calls.length; fireEvent.click(await screen.findByRole('button', { name: /message waiting riders/i })); // The verify pass: the board is re-evaluated rather than assumed fixed. await waitFor(() => { expect(scanModule.scanBookings.mock.calls.length).toBeGreaterThan(scansBefore); }); }); it('reports success only after the push actually landed', async () => { renderBanner(); await screen.findByText(/stalled at door/i); fireEvent.click(await screen.findByRole('button', { name: /message waiting riders/i })); await waitFor(() => { expect(OpenToast).toHaveBeenCalledWith(expect.stringMatching(/Notified 1 rider/), 'success'); }); }); // The failure that matters: the push is refused, and the operator must not be // told a rider was messaged. it('reports failure, not success, when the push is refused', async () => { api.notifyRider.mockRejectedValue(new Error('device offline')); renderBanner(); await screen.findByText(/stalled at door/i); fireEvent.click(await screen.findByRole('button', { name: /message waiting riders/i })); await waitFor(() => { expect(OpenToast).toHaveBeenCalledWith(expect.any(String), 'error'); }); expect(OpenToast).not.toHaveBeenCalledWith(expect.anything(), 'success'); }); // A rider who has left the fleet cannot be pushed to. Nothing landed, so // nothing may be reported as landed. it('does not claim success when no rider could be reached', async () => { api.getMilers.mockResolvedValue([]); // rider 8 is gone renderBanner(); await screen.findByText(/stalled at door/i); fireEvent.click(await screen.findByRole('button', { name: /message waiting riders/i })); await waitFor(() => { expect(OpenToast).toHaveBeenCalledWith(expect.any(String), 'error'); }); expect(api.notifyRider).not.toHaveBeenCalled(); expect(OpenToast).not.toHaveBeenCalledWith(expect.anything(), 'success'); }); });