Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions src/api/calls/__tests__/saveCall.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { createCall, updateCall } from '../calls';

jest.mock('../../common/client', () => {
const post = jest.fn();
const put = jest.fn();
return {
createApiEndpoint: jest.fn(() => ({ get: jest.fn(), post, put, delete: jest.fn() })),
__mockPost: post,
__mockPut: put,
};
});

jest.mock('../../common/cached-client', () => ({
createCachedApiEndpoint: jest.fn(() => ({ get: jest.fn() })),
}));

jest.mock('@/lib/cache/cache-manager', () => ({
cacheManager: { remove: jest.fn() },
}));

jest.mock('@/lib/logging', () => ({
logger: { warn: jest.fn(), error: jest.fn(), info: jest.fn(), debug: jest.fn() },
}));

const { __mockPost: mockPost, __mockPut: mockPut } = jest.requireMock('../../common/client') as { __mockPost: jest.Mock; __mockPut: jest.Mock };

const base = { name: 'Structure fire', nature: 'Smoke showing', priority: 1, type: 'Fire' };

describe('call save payloads', () => {
beforeEach(() => {
jest.clearAllMocks();
mockPost.mockResolvedValue({ data: {} });
mockPut.mockResolvedValue({ data: {} });
});

describe('createCall', () => {
it('sends the reporter details and call identifiers', async () => {
await createCall({ ...base, contactName: 'Jo Caller', contactInfo: '555-0100', externalId: 'CAD-1', incidentId: 'INC-7', referenceId: 'R-9' });

expect(mockPost).toHaveBeenCalledWith(
expect.objectContaining({ ContactName: 'Jo Caller', ContactInfo: '555-0100', ExternalId: 'CAD-1', IncidentId: 'INC-7', ReferenceId: 'R-9' })
);
});

it('sends the scheduled dispatch time only when one is set', async () => {
await createCall({ ...base, dispatchOnUtc: '2026-10-09T14:30:00.000Z' });
expect(mockPost).toHaveBeenLastCalledWith(expect.objectContaining({ DispatchOnUtc: '2026-10-09T14:30:00.000Z' }));

await createCall({ ...base });
expect(mockPost.mock.calls[1][0]).not.toHaveProperty('DispatchOnUtc');

await createCall({ ...base, dispatchOnUtc: '' });
expect(mockPost.mock.calls[2][0]).not.toHaveProperty('DispatchOnUtc');
});
});

describe('updateCall', () => {
it('sends the call identifiers, blank meaning "keep what is stored"', async () => {
await updateCall({ ...base, callId: '42', externalId: 'CAD-1' });

expect(mockPut).toHaveBeenCalledWith(expect.objectContaining({ Id: '42', ExternalId: 'CAD-1', IncidentId: '', ReferenceId: '' }));
});

it('sends the scheduled dispatch time only when one is set, so leaving it out keeps the schedule', async () => {
await updateCall({ ...base, callId: '42', dispatchOnUtc: '2026-10-09T14:30:00.000Z' });
expect(mockPut).toHaveBeenLastCalledWith(expect.objectContaining({ DispatchOnUtc: '2026-10-09T14:30:00.000Z' }));

await updateCall({ ...base, callId: '42' });
expect(mockPut.mock.calls[1][0]).not.toHaveProperty('DispatchOnUtc');
});
});
});
26 changes: 26 additions & 0 deletions src/api/calls/calls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ export interface CreateCallRequest {
type?: string;
contactName?: string;
contactInfo?: string;
/** External (CAD) call id. On update, blank keeps the stored value. */
externalId?: string;
incidentId?: string;
referenceId?: string;
/**
* Scheduled dispatch time as an ISO 8601 UTC instant. Sent only when set: on create a past time dispatches at once
* (ignored for a pending call); on update, leaving it out keeps the schedule — the API has no way to clear it.
*/
dispatchOnUtc?: string;
/** Primary Contact (premises/customer record) to link; the contact must belong to the department. */
contactId?: string | null;
/** Additional Contacts to link. On update, supplying either list replaces the existing links; omitting both leaves them alone. */
Expand All @@ -75,6 +84,15 @@ export interface UpdateCallRequest {
type?: string;
contactName?: string;
contactInfo?: string;
/** External (CAD) call id. On update, blank keeps the stored value. */
externalId?: string;
incidentId?: string;
referenceId?: string;
/**
* Scheduled dispatch time as an ISO 8601 UTC instant. Sent only when set: on create a past time dispatches at once
* (ignored for a pending call); on update, leaving it out keeps the schedule — the API has no way to clear it.
*/
dispatchOnUtc?: string;
/** Primary Contact (premises/customer record) to link; the contact must belong to the department. */
contactId?: string | null;
/** Additional Contacts to link. On update, supplying either list replaces the existing links; omitting both leaves them alone. */
Expand Down Expand Up @@ -139,6 +157,10 @@ export const createCall = async (callData: CreateCallRequest) => {
Type: callData.type || '',
ContactName: callData.contactName || '',
ContactInfo: callData.contactInfo || '',
ExternalId: callData.externalId || '',
IncidentId: callData.incidentId || '',
ReferenceId: callData.referenceId || '',
...(callData.dispatchOnUtc ? { DispatchOnUtc: callData.dispatchOnUtc } : {}),
...(callData.contactId !== undefined ? { ContactId: callData.contactId || '' } : {}),
...(callData.additionalContactIds !== undefined ? { AdditionalContactIds: callData.additionalContactIds } : {}),
What3Words: callData.what3words || '',
Expand Down Expand Up @@ -174,6 +196,10 @@ export const updateCall = async (callData: UpdateCallRequest) => {
Type: callData.type || '',
ContactName: callData.contactName || '',
ContactInfo: callData.contactInfo || '',
ExternalId: callData.externalId || '',
IncidentId: callData.incidentId || '',
ReferenceId: callData.referenceId || '',
...(callData.dispatchOnUtc ? { DispatchOnUtc: callData.dispatchOnUtc } : {}),
...(callData.contactId !== undefined ? { ContactId: callData.contactId || '' } : {}),
...(callData.additionalContactIds !== undefined ? { AdditionalContactIds: callData.additionalContactIds } : {}),
What3Words: callData.what3words || '',
Expand Down
Loading
Loading