62 lines
2.3 KiB
TypeScript
62 lines
2.3 KiB
TypeScript
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
|
import { NotificationsService } from './notifications.service';
|
|
|
|
function createQueryBuilder() {
|
|
return {
|
|
where: jest.fn().mockReturnThis(),
|
|
orderBy: jest.fn().mockReturnThis(),
|
|
take: jest.fn().mockReturnThis(),
|
|
andWhere: jest.fn().mockReturnThis(),
|
|
getMany: jest.fn().mockResolvedValue([]),
|
|
};
|
|
}
|
|
|
|
describe('NotificationsService boundaries', () => {
|
|
it('rejects empty recipients and de-duplicates repeated recipients', async () => {
|
|
const repo = {
|
|
save: jest.fn().mockImplementation(async (rows) => rows),
|
|
};
|
|
const service = new NotificationsService(repo as never, { emit: jest.fn() } as never);
|
|
|
|
await expect(
|
|
service.create({ recipientIds: [], type: 'test', title: '标题' }),
|
|
).rejects.toBeInstanceOf(BadRequestException);
|
|
|
|
const saved = await service.create({ recipientIds: [1, 1, 2], type: 'test', title: '标题' });
|
|
expect(saved).toHaveLength(2);
|
|
expect(repo.save).toHaveBeenCalledWith([
|
|
expect.objectContaining({ recipientId: 1 }),
|
|
expect.objectContaining({ recipientId: 2 }),
|
|
]);
|
|
});
|
|
|
|
it('clamps service-level page size to protect callers outside the controller', async () => {
|
|
const qb = createQueryBuilder();
|
|
const service = new NotificationsService(
|
|
{ createQueryBuilder: jest.fn().mockReturnValue(qb) } as never,
|
|
new EventEmitter2(),
|
|
);
|
|
|
|
await service.findByUser(7, undefined, 1000);
|
|
expect(qb.take).toHaveBeenCalledWith(100);
|
|
});
|
|
|
|
it('does not allow marking another user notification as read', async () => {
|
|
const repo = { findOne: jest.fn().mockResolvedValue(null), update: jest.fn() };
|
|
const service = new NotificationsService(repo as never, new EventEmitter2());
|
|
await expect(service.markRead(3, 7)).rejects.toBeInstanceOf(NotFoundException);
|
|
expect(repo.update).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('treats marking an already-read notification as idempotent', async () => {
|
|
const repo = {
|
|
findOne: jest.fn().mockResolvedValue({ id: 3, recipientId: 7, isRead: true }),
|
|
update: jest.fn(),
|
|
};
|
|
const service = new NotificationsService(repo as never, new EventEmitter2());
|
|
await expect(service.markRead(3, 7)).resolves.toBeUndefined();
|
|
expect(repo.update).not.toHaveBeenCalled();
|
|
});
|
|
});
|