- 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>
41 lines
1.9 KiB
TypeScript
41 lines
1.9 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import type { ICommentRepository } from '../../domain/repositories/comment.repository.interface.js';
|
|
import type { IIssueRepository } from '../../../issue/domain/repositories/issue.repository.interface.js';
|
|
import { FileOwnerRegistry } from '../../../file/application/services/file-owner.registry.js';
|
|
import { CommentAttachmentFileOwnerService } from './comment-attachment-file-owner.service.js';
|
|
import { buildIssue, mockCommentRepository, mockIssueRepository } from '../use-cases/test-helpers.js';
|
|
|
|
describe('CommentAttachmentFileOwnerService', () => {
|
|
let commentRepository: ICommentRepository;
|
|
let issueRepository: IIssueRepository;
|
|
let registry: FileOwnerRegistry;
|
|
|
|
beforeEach(() => {
|
|
commentRepository = mockCommentRepository();
|
|
issueRepository = mockIssueRepository();
|
|
registry = new FileOwnerRegistry();
|
|
new CommentAttachmentFileOwnerService(commentRepository, issueRepository, registry).onModuleInit();
|
|
});
|
|
|
|
it('registers itself, so GC keeps files attached to comments', async () => {
|
|
vi.mocked(commentRepository.findReferencedFileIds).mockResolvedValue(['a']);
|
|
|
|
expect(await registry.findReferenced(['a', 'b'])).toEqual(new Set(['a']));
|
|
});
|
|
|
|
it('makes a file visible to users who see the issue of its comment', async () => {
|
|
vi.mocked(commentRepository.findIssueIdsByAttachedFile).mockResolvedValue([1]);
|
|
vi.mocked(issueRepository.findById).mockResolvedValue(buildIssue());
|
|
|
|
expect(await registry.isVisibleThroughAnyOwner('file-1', 'user-2')).toBe(true);
|
|
expect(issueRepository.findById).toHaveBeenCalledWith(1, 'user-2');
|
|
});
|
|
|
|
it('hides a file when the issue is not visible', async () => {
|
|
vi.mocked(commentRepository.findIssueIdsByAttachedFile).mockResolvedValue([1]);
|
|
vi.mocked(issueRepository.findById).mockResolvedValue(null);
|
|
|
|
expect(await registry.isVisibleThroughAnyOwner('file-1', 'user-3')).toBe(false);
|
|
});
|
|
});
|