forked from wangziqi/gongxue-base
fix: keep room capacity and beds consistent
This commit is contained in:
136
apps/server/src/rooms/rooms.service.spec.ts
Normal file
136
apps/server/src/rooms/rooms.service.spec.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import { DataSource, EntityManager, Repository } from 'typeorm';
|
||||
import { Bed } from '../entities/bed.entity';
|
||||
import { Locker } from '../entities/locker.entity';
|
||||
import { Occupancy } from '../entities/occupancy.entity';
|
||||
import { Room } from '../entities/room.entity';
|
||||
import { RoomExpense } from '../entities/room-expense.entity';
|
||||
import { RoomsService } from './rooms.service';
|
||||
|
||||
describe('RoomsService — capacity consistency', () => {
|
||||
const createService = (options?: {
|
||||
room?: Partial<Room>;
|
||||
beds?: Partial<Bed>[];
|
||||
activeOccupantCount?: number;
|
||||
}) => {
|
||||
const room = { id: 1, capacity: 2, status: 'full', ...options?.room } as Room;
|
||||
const beds = (options?.beds ?? [
|
||||
{ id: 1, roomId: 1, bedNumber: '1号床' },
|
||||
{ id: 2, roomId: 1, bedNumber: '2号床' },
|
||||
]) as Bed[];
|
||||
|
||||
const roomRepo = {
|
||||
findOne: jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce(room)
|
||||
.mockResolvedValue({ ...room, capacity: 4 }),
|
||||
update: jest.fn().mockResolvedValue(undefined),
|
||||
} as unknown as Repository<Room>;
|
||||
const bedRepo = {
|
||||
find: jest.fn().mockResolvedValue(beds),
|
||||
create: jest.fn((value) => value),
|
||||
save: jest.fn(async (value) => value),
|
||||
} as unknown as Repository<Bed>;
|
||||
const occupancyRepo = {
|
||||
count: jest.fn().mockResolvedValue(options?.activeOccupantCount ?? 2),
|
||||
} as unknown as Repository<Occupancy>;
|
||||
|
||||
const manager = {
|
||||
getRepository: jest.fn((entity) => {
|
||||
if (entity === Room) return roomRepo;
|
||||
if (entity === Bed) return bedRepo;
|
||||
if (entity === Occupancy) return occupancyRepo;
|
||||
throw new Error(`Unexpected repository: ${String(entity)}`);
|
||||
}),
|
||||
} as unknown as EntityManager;
|
||||
const dataSource = {
|
||||
transaction: jest.fn(async (callback) => callback(manager)),
|
||||
} as unknown as DataSource;
|
||||
|
||||
const service = new RoomsService(
|
||||
roomRepo,
|
||||
occupancyRepo,
|
||||
{} as Repository<RoomExpense>,
|
||||
bedRepo,
|
||||
{} as Repository<Locker>,
|
||||
dataSource,
|
||||
);
|
||||
|
||||
return { service, roomRepo, bedRepo, occupancyRepo };
|
||||
};
|
||||
|
||||
it('automatically creates missing beds when capacity increases', async () => {
|
||||
const { service, roomRepo, bedRepo } = createService();
|
||||
|
||||
await service.update(1, { capacity: 4 });
|
||||
|
||||
expect(bedRepo.create).toHaveBeenNthCalledWith(1, { roomId: 1, bedNumber: '3号床' });
|
||||
expect(bedRepo.create).toHaveBeenNthCalledWith(2, { roomId: 1, bedNumber: '4号床' });
|
||||
expect(bedRepo.save).toHaveBeenCalledWith([
|
||||
{ roomId: 1, bedNumber: '3号床' },
|
||||
{ roomId: 1, bedNumber: '4号床' },
|
||||
]);
|
||||
expect(roomRepo.update).toHaveBeenCalledWith(1, { capacity: 4, status: 'available' });
|
||||
});
|
||||
|
||||
it('rejects capacity lower than the active occupant count', async () => {
|
||||
const { service, roomRepo, bedRepo } = createService({
|
||||
room: { capacity: 4, status: 'available' },
|
||||
beds: [{ id: 1, roomId: 1, bedNumber: '1号床' }],
|
||||
activeOccupantCount: 3,
|
||||
});
|
||||
|
||||
await expect(service.update(1, { capacity: 2 })).rejects.toThrow(
|
||||
new BadRequestException('额定人数不能少于当前入住人数,当前有 3 人入住'),
|
||||
);
|
||||
expect(roomRepo.update).not.toHaveBeenCalled();
|
||||
expect(bedRepo.save).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects capacity lower than the existing bed count', async () => {
|
||||
const { service, roomRepo, bedRepo } = createService({
|
||||
room: { capacity: 4, status: 'available' },
|
||||
activeOccupantCount: 1,
|
||||
beds: [
|
||||
{ id: 1, roomId: 1, bedNumber: '1号床' },
|
||||
{ id: 2, roomId: 1, bedNumber: '2号床' },
|
||||
{ id: 3, roomId: 1, bedNumber: '3号床' },
|
||||
{ id: 4, roomId: 1, bedNumber: '4号床' },
|
||||
],
|
||||
});
|
||||
|
||||
await expect(service.update(1, { capacity: 3 })).rejects.toThrow(
|
||||
new BadRequestException(
|
||||
'额定人数不能少于现有床位数,当前有 4 张床位,请先删除多余的空闲床位',
|
||||
),
|
||||
);
|
||||
expect(roomRepo.update).not.toHaveBeenCalled();
|
||||
expect(bedRepo.save).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('marks the room full when a valid capacity reduction reaches the occupant count', async () => {
|
||||
const { service, roomRepo, bedRepo } = createService({
|
||||
room: { capacity: 4, status: 'available' },
|
||||
activeOccupantCount: 2,
|
||||
beds: [
|
||||
{ id: 1, roomId: 1, bedNumber: '1号床' },
|
||||
{ id: 2, roomId: 1, bedNumber: '2号床' },
|
||||
],
|
||||
});
|
||||
|
||||
await service.update(1, { capacity: 2 });
|
||||
|
||||
expect(roomRepo.update).toHaveBeenCalledWith(1, { capacity: 2, status: 'full' });
|
||||
expect(bedRepo.save).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('updates other room fields without changing beds', async () => {
|
||||
const { service, roomRepo, bedRepo, occupancyRepo } = createService();
|
||||
|
||||
await service.update(1, { building: '2号楼' });
|
||||
|
||||
expect(roomRepo.update).toHaveBeenCalledWith(1, { building: '2号楼' });
|
||||
expect(bedRepo.find).not.toHaveBeenCalled();
|
||||
expect(occupancyRepo.count).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user