Files
edr-platform/apps/edr-passenger-api/src/modules/seats/seats.service.spec.ts
2026-08-20 07:46:15 +03:00

169 lines
5.6 KiB
TypeScript

import { Test, TestingModule } from '@nestjs/testing';
import { SeatsService } from './seats.service';
import { PrismaService } from '../../common/prisma.service';
import { ConflictException } from '@nestjs/common';
import { SegmentsService } from '../segments/segments.service';
import { SystemConfigService } from '../system-config/system-config.service';
import { AuditService } from '../../common/audit.service';
import { SmsClientService } from '../notifications/sms-client.service';
import { JourneyDirection } from './seats.dto';
describe('SeatsService - Auto Assign', () => {
let service: SeatsService;
let prisma: PrismaService;
let segmentsService: SegmentsService;
const mockPrisma = {
seat: {
findMany: jest.fn(),
updateMany: jest.fn(),
},
tripStopTime: {
findMany: jest.fn(),
},
trainSchedule: {
findUnique: jest.fn(),
},
seatBlock: {
findMany: jest.fn(),
},
};
const mockSegmentsService = {
getSeatAvailabilityMap: jest.fn(),
};
const mockSystemConfigService = {
getNumber: jest.fn().mockResolvedValue(30),
};
const mockAuditService = {
log: jest.fn(),
};
const mockSmsClientService = {
send: jest.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
SeatsService,
{ provide: PrismaService, useValue: mockPrisma },
{ provide: SegmentsService, useValue: mockSegmentsService },
{ provide: SystemConfigService, useValue: mockSystemConfigService },
{ provide: AuditService, useValue: mockAuditService },
{ provide: SmsClientService, useValue: mockSmsClientService },
],
}).compile();
service = module.get<SeatsService>(SeatsService);
prisma = module.get<PrismaService>(PrismaService);
segmentsService = module.get<SegmentsService>(SegmentsService);
jest.clearAllMocks();
mockPrisma.trainSchedule.findUnique.mockResolvedValue({
originStationId: 'origin-station',
destinationStationId: 'destination-station',
});
mockPrisma.tripStopTime.findMany.mockResolvedValue([
{ stationId: 'origin-station', sequence: 0 },
{ stationId: 'destination-station', sequence: 1 },
]);
mockPrisma.seatBlock.findMany.mockResolvedValue([]);
mockSegmentsService.getSeatAvailabilityMap.mockResolvedValue(new Map());
});
describe('assertNoRouteSeatConflict', () => {
it('rejects overlapping origin-destination seat reuse on the same schedule', async () => {
mockPrisma.tripStopTime.findMany.mockResolvedValue([
{ stationId: 'seb', sequence: 1 },
{ stationId: 'ada', sequence: 2 },
{ stationId: 'dd', sequence: 3 },
]);
mockSegmentsService.getSeatAvailabilityMap.mockResolvedValue(new Map([['seat-1', 'BOOKED']]));
mockPrisma.seat.findMany.mockResolvedValue([
{ id: 'seat-1', seatNumber: '12', coach: { number: 'C1' } },
]);
await expect(service.assertNoRouteSeatConflict({
scheduleId: 'sched-1',
seatIds: ['seat-1'],
originStationId: 'seb',
destinationStationId: 'dd',
journeyDirection: JourneyDirection.ONE_WAY,
})).rejects.toThrow(ConflictException);
});
it('allows the same seat number to be reused on non-overlapping legs', async () => {
mockPrisma.tripStopTime.findMany.mockResolvedValue([
{ stationId: 'seb', sequence: 1 },
{ stationId: 'ada', sequence: 2 },
{ stationId: 'dd', sequence: 3 },
]);
mockSegmentsService.getSeatAvailabilityMap.mockResolvedValue(new Map());
await expect(service.assertNoRouteSeatConflict({
scheduleId: 'sched-1',
seatIds: ['seat-1'],
originStationId: 'seb',
destinationStationId: 'ada',
journeyDirection: JourneyDirection.ONE_WAY,
})).resolves.toBeUndefined();
});
});
describe('autoAssignSeats', () => {
it('should assign contiguous seats in same row', async () => {
const mockSeats = [
{ id: 'seat-1', coachId: 'coach-1', row: 1, col: 'A' },
{ id: 'seat-2', coachId: 'coach-1', row: 1, col: 'B' },
{ id: 'seat-3', coachId: 'coach-1', row: 1, col: 'C' },
{ id: 'seat-4', coachId: 'coach-1', row: 2, col: 'A' },
];
mockPrisma.seat.findMany.mockResolvedValue(mockSeats);
const result = await service.autoAssignSeats('trip-1', 2, 'ECONOMY_REGULAR');
expect(result).toHaveLength(2);
expect(result).toEqual(['seat-1', 'seat-2']);
});
it('should throw error if not enough seats available', async () => {
mockPrisma.seat.findMany.mockResolvedValue([
{ id: 'seat-1', coachId: 'coach-1', row: 1, col: 'A' },
]);
await expect(
service.autoAssignSeats('trip-1', 3, 'ECONOMY_REGULAR'),
).rejects.toThrow(ConflictException);
});
it('should respect eligibility filter', async () => {
const mockSeats = [
{ id: 'seat-1', coachId: 'coach-1', row: 1, col: 'A', eligibility: 'ACCESSIBLE' },
{ id: 'seat-2', coachId: 'coach-1', row: 1, col: 'B', eligibility: 'ACCESSIBLE' },
];
mockPrisma.seat.findMany.mockResolvedValue(mockSeats);
const result = await service.autoAssignSeats('trip-1', 2, 'ECONOMY_REGULAR');
expect(result).toHaveLength(2);
});
it('should assign single seat', async () => {
const mockSeats = [
{ id: 'seat-1', coachId: 'coach-1', row: 1, col: 'A' },
];
mockPrisma.seat.findMany.mockResolvedValue(mockSeats);
const result = await service.autoAssignSeats('trip-1', 1, 'ECONOMY_REGULAR');
expect(result).toEqual(['seat-1']);
});
});
});