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; beds?: Partial[]; 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; const bedRepo = { find: jest.fn().mockResolvedValue(beds), create: jest.fn((value) => value), save: jest.fn(async (value) => value), } as unknown as Repository; const occupancyRepo = { count: jest.fn().mockResolvedValue(options?.activeOccupantCount ?? 2), } as unknown as Repository; 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, bedRepo, {} as Repository, 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(); }); });