import { beforeEach, describe, expect, it, vi } from 'vitest'; import type { IIssueRepository } from '../../../domain/repositories/issue.repository.interface.js'; import { Issue } from '../../../domain/entities/issue.entity.js'; import { InvalidCalendarRangeException } from '../../../domain/exceptions/invalid-calendar-range.exception.js'; import { ListCalendarIssuesUseCase } from './list-calendar-issues.use-case.js'; import { ListCalendarIssuesQuery } from './list-calendar-issues.query.js'; import type { IssueUserExpanderService } from '../../services/issue-user-expander.service.js'; import type { IssueWithUsersDto } from '../../dto/issue-with-users.dto.js'; const FROM = new Date('2026-09-28T00:00:00+03:00'); const TO = new Date('2026-11-09T00:00:00+03:00'); const buildIssue = (id: number) => Issue.reconstitute({ id, key: `ISSUE-${id}`, title: `Issue ${id}`, content: null, status: 'OPEN', priority: 0, type: 'TASK', assignee: null, reporter: 'user-1', projectId: 'project-1', dueDate: new Date('2026-10-15T12:00:00Z'), createdAt: new Date(), updatedAt: new Date(), }); describe('ListCalendarIssuesUseCase', () => { let repository: IIssueRepository; let issueUserExpander: IssueUserExpanderService; let useCase: ListCalendarIssuesUseCase; beforeEach(() => { repository = { findById: vi.fn(), findByCKey: vi.fn(), findAll: vi.fn(), findByDueDateRange: vi.fn(), countByStatus: vi.fn(), create: vi.fn(), update: vi.fn(), delete: vi.fn(), }; issueUserExpander = { expandAll: vi.fn(async (issues: Issue[]) => issues.map((issue) => ({ id: issue.id }) as IssueWithUsersDto)), } as unknown as IssueUserExpanderService; useCase = new ListCalendarIssuesUseCase(repository, issueUserExpander); }); it('forwards the range and filters, asking for one issue more than the limit', async () => { vi.mocked(repository.findByDueDateRange).mockResolvedValue([buildIssue(1), buildIssue(2)]); const result = await useCase.execute( new ListCalendarIssuesQuery('user-1', FROM, TO, 'project-1', ['OPEN'], 'user-2'), ); expect(repository.findByDueDateRange).toHaveBeenCalledWith({ from: FROM, to: TO, limit: ListCalendarIssuesUseCase.MAX_ISSUES + 1, userId: 'user-1', projectId: 'project-1', status: ['OPEN'], assigneeId: 'user-2', }); expect(result).toEqual({ list: [{ id: 1 }, { id: 2 }], truncated: false }); }); it('cuts the list to the limit and marks it as truncated', async () => { const issues = Array.from({ length: ListCalendarIssuesUseCase.MAX_ISSUES + 1 }, (_, index) => buildIssue(index + 1), ); vi.mocked(repository.findByDueDateRange).mockResolvedValue(issues); const result = await useCase.execute(new ListCalendarIssuesQuery('user-1', FROM, TO)); expect(result.truncated).toBe(true); expect(result.list).toHaveLength(ListCalendarIssuesUseCase.MAX_ISSUES); }); it('rejects an invalid range without querying the repository', async () => { await expect(useCase.execute(new ListCalendarIssuesQuery('user-1', TO, FROM))).rejects.toThrow( InvalidCalendarRangeException, ); expect(repository.findByDueDateRange).not.toHaveBeenCalled(); }); });