Add attachments to comments
- CommentAttachment link table (comment <-> file) + migration - fileIds in create/update comment; update replaces the whole set, synced in one transaction with the comment - CommentDto.attachments; empty for deleted comments - Soft delete drops attachments so GC can collect the files - Max 10 files per comment, ordered by upload time (uuid v7) - Registers comments as a file owner - Rename CommentAuthorExpanderService -> CommentExpanderService (expands authors and files) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
423afa658d
commit
60fb39f98f
28 files changed
+522
-69
No files matched your search
+29
-2
@@ -10,10 +10,13 @@ import { UpdateCommentCommand } from './update-comment.command.js';
|
||||
import {
|
||||
buildComment,
|
||||
buildIssue,
|
||||
mockAuthorExpander,
|
||||
mockCommentExpander,
|
||||
mockCommentRepository,
|
||||
mockFileAccess,
|
||||
mockIssueRepository,
|
||||
} from '../test-helpers.js';
|
||||
import { FileNotFoundException } from '../../../../file/domain/exceptions/file-not-found.exception.js';
|
||||
import type { FileAccessService } from '../../../../file/application/services/file-access.service.js';
|
||||
|
||||
describe('UpdateCommentUseCase', () => {
|
||||
let commentRepository: ICommentRepository;
|
||||
@@ -21,16 +24,19 @@ describe('UpdateCommentUseCase', () => {
|
||||
let commentEventsService: CommentEventsService;
|
||||
let useCase: UpdateCommentUseCase;
|
||||
let received: unknown[];
|
||||
let fileAccess: FileAccessService;
|
||||
|
||||
beforeEach(() => {
|
||||
commentRepository = mockCommentRepository();
|
||||
issueRepository = mockIssueRepository();
|
||||
commentEventsService = new CommentEventsService();
|
||||
fileAccess = mockFileAccess({ gone: new FileNotFoundException('gone') });
|
||||
useCase = new UpdateCommentUseCase(
|
||||
commentRepository,
|
||||
new CommentAccessService(commentRepository, issueRepository),
|
||||
mockAuthorExpander(),
|
||||
mockCommentExpander(),
|
||||
commentEventsService,
|
||||
fileAccess,
|
||||
);
|
||||
received = [];
|
||||
commentEventsService.onEvents().subscribe((event) => received.push(event));
|
||||
@@ -82,4 +88,25 @@ describe('UpdateCommentUseCase', () => {
|
||||
CommentNotFoundException,
|
||||
);
|
||||
});
|
||||
|
||||
it('replaces attachments, checking only newly added files', async () => {
|
||||
const comment = buildComment();
|
||||
comment.edit('Original', 'user-1', ['kept', 'removed']);
|
||||
vi.mocked(commentRepository.findById).mockResolvedValue(comment);
|
||||
|
||||
await useCase.execute(new UpdateCommentCommand('comment-1', 'user-1', 'Original', ['kept', 'new']));
|
||||
|
||||
expect(fileAccess.getAttachableFile).toHaveBeenCalledExactlyOnceWith('new', 'user-1');
|
||||
expect(vi.mocked(commentRepository.update).mock.calls[0]![0].fileIds).toEqual(['kept', 'new']);
|
||||
expect(received).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('does not save when an added file is gone', async () => {
|
||||
vi.mocked(commentRepository.findById).mockResolvedValue(buildComment());
|
||||
|
||||
await expect(
|
||||
useCase.execute(new UpdateCommentCommand('comment-1', 'user-1', 'Original', ['gone'])),
|
||||
).rejects.toThrow(FileNotFoundException);
|
||||
expect(commentRepository.update).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user