Files
edr-platform/apps/edr-freight-api/src/modules/wagons/purge-guard.spec.ts
Marshal 5d68a3f7b7 add permanent purge functionality for wagons and routes
- Implemented a  method in  to permanently delete wagons without history.
- Added corresponding permissions for hard delete actions in .
- Updated the UI components to include purge actions, ensuring they are only available to users with the appropriate permissions.
- Created modals for confirming permanent deletions in  and .
- Enhanced API services to handle purge requests for locomotives, wagons, and routes.
- Added tests for the purge functionality in both  and  services to ensure proper behavior and error handling.
2026-08-04 14:07:16 +00:00

62 lines
2.8 KiB
TypeScript

import { ConflictException, NotFoundException } from '@nestjs/common';
import { WagonsService } from './wagons.service';
// Minimal stubs: only what purge() touches.
const makeService = (wagon: any, counts: [number, number, number], pinned = false) => {
const wagonRepo = { findOne: jest.fn().mockResolvedValue(wagon), remove: jest.fn().mockResolvedValue(undefined) };
// Keyed off the SQL so the stub survives repeated purge() calls in one test.
const dataSource = {
query: jest.fn(async (sql: string) => {
if (sql.includes('train_schedule')) return pinned ? [{ x: 1 }] : [];
if (sql.includes('wagon_movements')) return [{ count: counts[0] }];
if (sql.includes('containers')) return [{ count: counts[1] }];
if (sql.includes('train_set_wagons')) return [{ count: counts[2] }];
return [];
}),
};
const svc = new WagonsService(wagonRepo as any, {} as any, dataSource as any);
return { svc, wagonRepo };
};
describe('WagonsService.purge', () => {
const clean = { id: 'w1', wagonNumber: 'W-0001', trainId: null };
it('purges a wagon with no references', async () => {
const { svc, wagonRepo } = makeService(clean, [0, 0, 0]);
await svc.purge('w1');
expect(wagonRepo.remove).toHaveBeenCalledWith(clean);
});
it('refuses when the wagon has movement history', async () => {
const { svc, wagonRepo } = makeService(clean, [12, 0, 0]);
await expect(svc.purge('w1')).rejects.toThrow(ConflictException);
await expect(svc.purge('w1')).rejects.toThrow(/12 movement record/);
expect(wagonRepo.remove).not.toHaveBeenCalled();
});
it('refuses when containers or train-set slots reference it', async () => {
const { svc, wagonRepo } = makeService(clean, [0, 3, 2]);
await expect(svc.purge('w1')).rejects.toThrow(/3 container\(s\), 2 train-set slot/);
expect(wagonRepo.remove).not.toHaveBeenCalled();
});
it('refuses a coupled wagon before any count query runs', async () => {
const { svc, wagonRepo } = makeService({ ...clean, trainId: 't1' }, [0, 0, 0]);
await expect(svc.purge('w1')).rejects.toThrow(/coupled to a train/);
expect(wagonRepo.remove).not.toHaveBeenCalled();
});
it('refuses a wagon pinned to a live schedule', async () => {
const { svc, wagonRepo } = makeService(clean, [0, 0, 0], true);
await expect(svc.purge('w1')).rejects.toThrow(/pinned to an active schedule/);
expect(wagonRepo.remove).not.toHaveBeenCalled();
});
it('404s an unknown wagon', async () => {
const wagonRepo = { findOne: jest.fn().mockResolvedValue(null), remove: jest.fn() };
const svc = new WagonsService(wagonRepo as any, {} as any, { query: jest.fn() } as any);
await expect(svc.purge('nope')).rejects.toThrow(NotFoundException);
expect(wagonRepo.remove).not.toHaveBeenCalled();
});
});