Files
ftb-project-management/apps/server/src/modules/comment/comment.service.spec.ts

113 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { BadRequestException } from '@nestjs/common';
import { AuditService } from '../../common/audit/audit.service';
import { NotificationService } from '../notification/notification.service';
import { CommentService } from './comment.service';
describe('CommentService', () => {
const makeService = () => {
const prisma = {
comment: {
create: jest.fn(),
findUnique: jest.fn(),
update: jest.fn(),
},
projectMember: {
findMany: jest.fn(),
},
};
const notifications = {
createMany: jest.fn(),
} as unknown as NotificationService;
const audit = {
record: jest.fn(),
} as unknown as AuditService;
return {
prisma,
notifications,
audit,
service: new CommentService(prisma as any, notifications, audit),
};
};
it('creates a polymorphic comment, extracts @mentions, notifies mentioned members, and writes audit', async () => {
const { prisma, notifications, audit, service } = makeService();
prisma.projectMember.findMany.mockResolvedValue([
{ userId: 'm-alice', user: { id: 'm-alice', name: 'Alice' } },
{ userId: 'm-bob', user: { id: 'm-bob', name: 'Bob' } },
]);
prisma.comment.create.mockResolvedValue({
id: 'comment-1',
entityType: 'dev_task',
entityId: 'task-1',
mentionedMemberIds: ['m-alice', 'm-bob'],
});
const result = await service.create({
actorId: 'm-author',
entityType: 'dev_task',
entityId: 'task-1',
entityVersionId: 'ver-1',
projectId: 'project-1',
versionId: 'ver-1',
content: '请 @Alice 看一下接口Bob 也同步一下',
mentionMemberIds: ['m-bob'],
});
expect(result.id).toBe('comment-1');
expect(prisma.comment.create).toHaveBeenCalledWith({
data: expect.objectContaining({
entityType: 'dev_task',
entityId: 'task-1',
authorId: 'm-author',
mentionedMemberIds: ['m-alice', 'm-bob'],
}),
});
expect((notifications.createMany as jest.Mock)).toHaveBeenCalledWith([
expect.objectContaining({ recipientId: 'm-alice', type: 'mention', resourceType: 'comment', resourceId: 'comment-1' }),
expect.objectContaining({ recipientId: 'm-bob', type: 'mention', resourceType: 'comment', resourceId: 'comment-1' }),
]);
expect((audit.record as jest.Mock)).toHaveBeenCalledWith(expect.objectContaining({
actorId: 'm-author',
action: 'comment.created',
resourceType: 'comment',
resourceId: 'comment-1',
}));
});
it('soft deletes a comment and writes audit', async () => {
const { prisma, audit, service } = makeService();
prisma.comment.findUnique.mockResolvedValue({
id: 'comment-1',
authorId: 'm-author',
entityType: 'bug',
entityId: 'bug-1',
deletedAt: null,
});
prisma.comment.update.mockResolvedValue({ id: 'comment-1', deletedAt: new Date('2026-07-08T08:00:00.000Z') });
await service.remove('comment-1', 'm-author');
expect(prisma.comment.update).toHaveBeenCalledWith({
where: { id: 'comment-1' },
data: { deletedAt: expect.any(Date) },
});
expect((audit.record as jest.Mock)).toHaveBeenCalledWith(expect.objectContaining({
actorId: 'm-author',
action: 'comment.deleted',
resourceType: 'comment',
resourceId: 'comment-1',
}));
});
it('rejects unsupported comment entity types', async () => {
const { service } = makeService();
await expect(service.create({
actorId: 'm-author',
entityType: 'task',
entityId: 'task-1',
content: 'legacy task comment',
} as any)).rejects.toBeInstanceOf(BadRequestException);
});
});