68 lines
3.2 KiB
JavaScript
68 lines
3.2 KiB
JavaScript
import { CashExposureSkill } from '@/lib/assistant/skills/definitions/CashExposureSkill';
|
|
|
|
describe('CashExposureSkill', () => {
|
|
const NOW = '2026-08-31 14:00:00';
|
|
|
|
it('flags riders carrying total COD cash above max threshold', () => {
|
|
const rows = [
|
|
// Rider 1: 12000 total (Critical)
|
|
{ bookingid: 1, userid: 101, ridername: 'Rider A', orderstatus: 'picked', paymenttype: 'cod', cod_amount: '5000' },
|
|
{ bookingid: 2, userid: 101, ridername: 'Rider A', orderstatus: 'accepted', paymenttype: 'cod', cod_amount: '7000' },
|
|
|
|
// Rider 2: 8000 total (Warning)
|
|
{ bookingid: 3, userid: 102, ridername: 'Rider B', orderstatus: 'picked', paymenttype: 'cod', cod_amount: '8000' },
|
|
|
|
// Rider 3: 4000 total (Safe)
|
|
{ bookingid: 4, userid: 103, ridername: 'Rider C', orderstatus: 'picked', paymenttype: 'cod', cod_amount: '4000' },
|
|
|
|
// Non-COD order for Rider 1 (Ignored)
|
|
{ bookingid: 5, userid: 101, ridername: 'Rider A', orderstatus: 'picked', paymenttype: 'prepaid', cod_amount: '0' }
|
|
];
|
|
|
|
const findings = CashExposureSkill.evaluate(rows, NOW, { maxCashPerRider: 10000, warningCashPercent: 75 });
|
|
|
|
const critical = findings.find((f) => f.id === 'cash_exposure_critical');
|
|
const warning = findings.find((f) => f.id === 'cash_exposure_warning');
|
|
|
|
expect(critical).toBeDefined();
|
|
expect(critical.count).toBe(1); // Only Rider 1
|
|
expect(critical.rows.length).toBe(2); // Rider 1 has 2 COD orders
|
|
expect(critical.detail[0]).toContain('12,000');
|
|
|
|
expect(warning).toBeDefined();
|
|
expect(warning.count).toBe(1); // Only Rider 2
|
|
expect(warning.detail[0]).toContain('8,000');
|
|
});
|
|
|
|
it('returns no findings when all riders are under warning threshold', () => {
|
|
const rows = [
|
|
{ bookingid: 1, userid: 101, orderstatus: 'picked', paymenttype: 'cod', cod_amount: '5000' }
|
|
];
|
|
const findings = CashExposureSkill.evaluate(rows, NOW, { maxCashPerRider: 10000, warningCashPercent: 75 });
|
|
expect(findings.length).toBe(0);
|
|
});
|
|
|
|
it('executes enforce_cash_handoff tool handler', async () => {
|
|
const tool = CashExposureSkill.tools.find((t) => t.name === 'enforce_cash_handoff');
|
|
expect(tool).toBeDefined();
|
|
const result = await tool.handler({ milerId: 101, totalCash: 12000, hubId: 'HUB-1' });
|
|
expect(result.success).toBe(true);
|
|
expect(result.proposal.action).toBe('cash_handoff');
|
|
expect(result.proposal.milerId).toBe(101);
|
|
expect(result.proposal.totalCash).toBe(12000);
|
|
expect(result.proposal.hubId).toBe('HUB-1');
|
|
});
|
|
|
|
it('handles orders with non-numeric or missing amounts gracefully', () => {
|
|
const rows = [
|
|
{ bookingid: 1, userid: 101, orderstatus: 'picked', paymenttype: 'cod' }, // missing amount
|
|
{ bookingid: 2, userid: 101, orderstatus: 'picked', paymenttype: 'cod', cod_amount: 'invalid' }, // invalid amount
|
|
{ bookingid: 3, userid: 101, orderstatus: 'picked', paymenttype: 'cod', cod_amount: '12000' } // valid amount
|
|
];
|
|
const findings = CashExposureSkill.evaluate(rows, NOW, { maxCashPerRider: 10000, warningCashPercent: 75 });
|
|
const critical = findings.find((f) => f.id === 'cash_exposure_critical');
|
|
expect(critical).toBeDefined();
|
|
expect(critical.detail[0]).toContain('12,000');
|
|
});
|
|
});
|