mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 09:00:57 +00:00
100 lines
3.5 KiB
TypeScript
100 lines
3.5 KiB
TypeScript
import { MaintenanceService } from './maintenance.service';
|
|
import { MaintenanceStatus } from './entities/maintenance-schedule.entity';
|
|
|
|
/**
|
|
* KM-based auto-scheduling: completing a maintenance with an odometer reading
|
|
* creates the next SCHEDULED item at completedKm + intervalKm, matched on the
|
|
* schedule's (type, serviceItem) interval. Re-completing must not duplicate.
|
|
*/
|
|
function makeService(opts: {
|
|
before: Record<string, unknown> | null;
|
|
after: Record<string, unknown> | null;
|
|
interval: Record<string, unknown> | null;
|
|
}) {
|
|
const saved: Array<Record<string, unknown>> = [];
|
|
const service = Object.create(MaintenanceService.prototype) as Record<string, unknown>;
|
|
service.scheduleRepository = {
|
|
findOneBy: jest
|
|
.fn()
|
|
.mockResolvedValueOnce(opts.before)
|
|
.mockResolvedValueOnce(opts.after),
|
|
update: jest.fn(),
|
|
create: jest.fn((v: Record<string, unknown>) => v),
|
|
save: jest.fn(async (v: Record<string, unknown>) => {
|
|
saved.push(v);
|
|
return v;
|
|
}),
|
|
};
|
|
service.intervalRepository = {
|
|
getByVehicleAndType: jest.fn().mockResolvedValue(opts.interval),
|
|
};
|
|
service.dataSource = {
|
|
getRepository: jest.fn().mockReturnValue({ update: jest.fn() }),
|
|
};
|
|
service.logger = { error: jest.fn() };
|
|
return { service: service as unknown as MaintenanceService, saved };
|
|
}
|
|
|
|
const base = {
|
|
id: 's-1',
|
|
vehicleId: 'v-1',
|
|
maintenanceType: 'PREVENTIVE',
|
|
serviceItem: 'oil change',
|
|
description: 'Oil and filter',
|
|
};
|
|
|
|
describe('MaintenanceService auto-next scheduling', () => {
|
|
it('completing at 50,000 km with a 10,000 km interval schedules the next at 60,000', async () => {
|
|
const { service, saved } = makeService({
|
|
before: { ...base, status: MaintenanceStatus.SCHEDULED },
|
|
after: { ...base, status: MaintenanceStatus.COMPLETED, odometerReading: 50000 },
|
|
interval: { serviceItem: 'oil change', intervalKm: '10000.00', intervalDays: null, description: 'Oil and filter' },
|
|
});
|
|
|
|
await service.updateMaintenanceSchedule('s-1', {
|
|
status: MaintenanceStatus.COMPLETED,
|
|
odometerReading: 50000,
|
|
});
|
|
|
|
expect(saved).toHaveLength(1);
|
|
expect(saved[0]).toMatchObject({
|
|
vehicleId: 'v-1',
|
|
serviceItem: 'oil change',
|
|
nextDueKm: 60000,
|
|
status: MaintenanceStatus.SCHEDULED,
|
|
});
|
|
});
|
|
|
|
it('re-completing an already COMPLETED schedule does not duplicate the next one', async () => {
|
|
const { service, saved } = makeService({
|
|
before: { ...base, status: MaintenanceStatus.COMPLETED, odometerReading: 50000 },
|
|
after: { ...base, status: MaintenanceStatus.COMPLETED, odometerReading: 50000 },
|
|
interval: { serviceItem: 'oil change', intervalKm: '10000.00', intervalDays: null },
|
|
});
|
|
|
|
await service.updateMaintenanceSchedule('s-1', {
|
|
status: MaintenanceStatus.COMPLETED,
|
|
odometerReading: 50000,
|
|
});
|
|
|
|
expect(saved).toHaveLength(0);
|
|
});
|
|
|
|
it('a km + days interval produces ONE next schedule carrying both thresholds', async () => {
|
|
const { service, saved } = makeService({
|
|
before: { ...base, status: MaintenanceStatus.IN_PROGRESS },
|
|
after: { ...base, status: MaintenanceStatus.COMPLETED, odometerReading: 20000 },
|
|
interval: { serviceItem: 'oil change', intervalKm: '10000.00', intervalDays: 180 },
|
|
});
|
|
|
|
await service.updateMaintenanceSchedule('s-1', {
|
|
status: MaintenanceStatus.COMPLETED,
|
|
odometerReading: 20000,
|
|
});
|
|
|
|
expect(saved).toHaveLength(1);
|
|
expect(saved[0].nextDueKm).toBe(30000);
|
|
expect(saved[0].nextDueDate).toBeInstanceOf(Date);
|
|
});
|
|
});
|