test: harden business boundary conditions

This commit is contained in:
2026-07-15 00:03:55 +08:00
parent 17a5046ea0
commit b1f35f9d1a
65 changed files with 2311 additions and 293 deletions

View File

@@ -117,8 +117,9 @@ describe('OccupanciesService — manual check-in deposit', () => {
expect(depositRepo.save).toHaveBeenCalledTimes(1);
});
it('does not create another paid deposit when one already exists', async () => {
const { service, depositRepo } = createService({ id: 99 } as Deposit);
it('adds the collected amount to the existing student deposit', async () => {
const existing = { id: 99, amount: 200, status: 'refunded' } as Deposit;
const { service, depositRepo } = createService(existing);
await service.checkIn({
studentId: 3,
@@ -130,7 +131,15 @@ describe('OccupanciesService — manual check-in deposit', () => {
});
expect(depositRepo.create).not.toHaveBeenCalled();
expect(depositRepo.save).not.toHaveBeenCalled();
expect(depositRepo.save).toHaveBeenCalledWith(
expect.objectContaining({
id: 99,
amount: 1000,
status: 'paid',
paidDate: '2026-07-14',
notes: '入住登记自动收取',
}),
);
});
});
@@ -274,3 +283,66 @@ describe('OccupanciesService — import student matching', () => {
expect(result).toEqual(expect.objectContaining({ imported: 1, skipped: 0 }));
});
});
describe('OccupanciesService — stay lifecycle boundaries', () => {
it('rejects check-out before check-in without releasing resources', async () => {
const occupancy = {
id: 1,
roomId: 2,
bedId: 3,
lockerId: 4,
checkInDate: '2026-07-10',
billingStartDate: '2026-07-10',
checkOutDate: null,
} as Occupancy;
const occupancyRepo = {
findOne: jest.fn().mockResolvedValue(occupancy),
save: jest.fn(),
} as any as Repository<Occupancy>;
const roomRepo = { update: jest.fn() } as any as Repository<Room>;
const bedRepo = { update: jest.fn() } as any as Repository<Bed>;
const lockerRepo = { update: jest.fn() } as any as Repository<Locker>;
const service = new OccupanciesService(
occupancyRepo,
roomRepo,
{} as Repository<Student>,
{} as Repository<Deposit>,
bedRepo,
lockerRepo,
{} as Repository<any>,
{} as DataSource,
);
await expect(service.checkOut(1, { checkOutDate: '2026-07-09' })).rejects.toThrow(
'退宿日期不能早于入住日期',
);
expect(occupancyRepo.save).not.toHaveBeenCalled();
expect(bedRepo.update).not.toHaveBeenCalled();
expect(lockerRepo.update).not.toHaveBeenCalled();
expect(roomRepo.update).not.toHaveBeenCalled();
});
it('rejects check-in to a maintenance room', async () => {
const occupancyRepo = {
findOne: jest.fn().mockResolvedValue(null),
count: jest.fn(),
} as any as Repository<Occupancy>;
const service = new OccupanciesService(
occupancyRepo,
{
findOne: jest.fn().mockResolvedValue({ id: 2, capacity: 4, status: 'maintenance' }),
} as any,
{} as Repository<Student>,
{} as Repository<Deposit>,
{} as Repository<Bed>,
{} as Repository<Locker>,
{} as Repository<any>,
{} as DataSource,
);
await expect(
service.checkIn({ studentId: 1, roomId: 2, checkInDate: '2026-07-10', bedId: 3 }),
).rejects.toThrow('该宿舍当前不可入住');
expect(occupancyRepo.count).not.toHaveBeenCalled();
});
});