/** * Application Use Case Tests: CloseAdminVoteSessionUseCase * * Tests for closing admin vote sessions and generating rating events */ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { CloseAdminVoteSessionUseCase } from './CloseAdminVoteSessionUseCase'; import { RatingEventFactory } from '../../domain/services/RatingEventFactory'; import { RatingSnapshotCalculator } from '../../domain/services/RatingSnapshotCalculator'; // Mock repositories const createMockRepositories = () => ({ adminVoteSessionRepository: { save: vi.fn(), findById: vi.fn(), findActiveForAdmin: vi.fn(), findByAdminAndLeague: vi.fn(), findByLeague: vi.fn(), findClosedUnprocessed: vi.fn(), }, ratingEventRepository: { save: vi.fn(), findByUserId: vi.fn(), findByIds: vi.fn(), getAllByUserId: vi.fn(), findEventsPaginated: vi.fn(), }, userRatingRepository: { save: vi.fn(), }, }); // Mock services vi.mock('../../domain/services/RatingEventFactory', () => ({ RatingEventFactory: { createFromVote: vi.fn(), }, })); vi.mock('../../domain/services/RatingSnapshotCalculator', () => ({ RatingSnapshotCalculator: { calculate: vi.fn(), }, })); describe('CloseAdminVoteSessionUseCase', () => { let useCase: CloseAdminVoteSessionUseCase; let mockRepositories: ReturnType; beforeEach(() => { mockRepositories = createMockRepositories(); useCase = new CloseAdminVoteSessionUseCase( mockRepositories.adminVoteSessionRepository, mockRepositories.ratingEventRepository, mockRepositories.userRatingRepository ); vi.clearAllMocks(); // Default mock for RatingEventFactory.createFromVote to return an empty array // to avoid "events is not iterable" error in tests that don't explicitly mock it (RatingEventFactory.createFromVote as any).mockReturnValue([]); }); describe('Input validation', () => { it('should reject when voteSessionId is missing', async () => { const result = await useCase.execute({ voteSessionId: '', adminId: 'admin-123', }); expect(result.success).toBe(false); expect(result.errors).toContain('voteSessionId is required'); }); it('should reject when adminId is missing', async () => { const result = await useCase.execute({ voteSessionId: 'session-123', adminId: '', }); expect(result.success).toBe(false); expect(result.errors).toContain('adminId is required'); }); it('should accept valid input', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); const result = await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); console.log('Result:', JSON.stringify(result, null, 2)); console.log('Mock session closed:', mockSession.closed); console.log('Mock session _closed:', mockSession._closed); console.log('Mock session close called:', mockSession.close.mock.calls.length); expect(result.success).toBe(true); expect(result.errors).toBeUndefined(); }); }); describe('Session lookup', () => { it('should reject when vote session is not found', async () => { mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(null); const result = await useCase.execute({ voteSessionId: 'non-existent-session', adminId: 'admin-123', }); expect(result.success).toBe(false); expect(result.errors).toContain('Vote session not found'); }); it('should find session by ID when provided', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(mockRepositories.adminVoteSessionRepository.findById).toHaveBeenCalledWith('session-123'); }); }); describe('Admin ownership validation', () => { it('should reject when admin does not own the session', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'different-admin', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); const result = await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(result.success).toBe(false); expect(result.errors).toContain('Admin does not own this vote session'); }); it('should accept when admin owns the session', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); const result = await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(result.success).toBe(true); }); }); describe('Session closure validation', () => { it('should reject when session is already closed', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: true, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); const result = await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(result.success).toBe(false); expect(result.errors).toContain('Vote session is already closed'); }); it('should accept when session is not closed', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); const result = await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(result.success).toBe(true); }); }); describe('Voting window validation', () => { it('should reject when trying to close outside voting window', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); // Mock Date to be outside the window const originalDate = Date; global.Date = class extends originalDate { constructor() { super('2026-02-02'); } } as any; const result = await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(result.success).toBe(false); expect(result.errors).toContain('Cannot close session outside the voting window'); // Restore Date global.Date = originalDate; }); it('should accept when trying to close within voting window', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); // Mock Date to be within the window const originalDate = Date; global.Date = class extends originalDate { constructor() { super('2026-01-15T12:00:00'); } } as any; const result = await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(result.success).toBe(true); // Restore Date global.Date = originalDate; }); }); describe('Session closure', () => { it('should call close method on session', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(mockSession.close).toHaveBeenCalled(); }); it('should save closed session', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(mockRepositories.adminVoteSessionRepository.save).toHaveBeenCalledWith(mockSession); }); it('should return outcome in success response', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); const result = await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(result.success).toBe(true); expect(result.outcome).toBeDefined(); expect(result.outcome?.percentPositive).toBe(75); expect(result.outcome?.count).toEqual({ positive: 3, negative: 1, total: 4 }); expect(result.outcome?.eligibleVoterCount).toBe(4); expect(result.outcome?.participationRate).toBe(100); expect(result.outcome?.outcome).toBe('positive'); }); }); describe('Rating event creation', () => { it('should create rating events when outcome is positive', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); const mockEvent = { id: 'event-123' }; (RatingEventFactory.createFromVote as any).mockReturnValue([mockEvent]); await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(RatingEventFactory.createFromVote).toHaveBeenCalledWith({ userId: 'admin-123', voteSessionId: 'session-123', outcome: 'positive', voteCount: 4, eligibleVoterCount: 4, percentPositive: 75, }); }); it('should create rating events when outcome is negative', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 25, count: { positive: 1, negative: 3, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'negative', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); const mockEvent = { id: 'event-123' }; (RatingEventFactory.createFromVote as any).mockReturnValue([mockEvent]); await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(RatingEventFactory.createFromVote).toHaveBeenCalledWith({ userId: 'admin-123', voteSessionId: 'session-123', outcome: 'negative', voteCount: 4, eligibleVoterCount: 4, percentPositive: 25, }); }); it('should not create rating events when outcome is tie', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 50, count: { positive: 2, negative: 2, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'tie', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(RatingEventFactory.createFromVote).not.toHaveBeenCalled(); expect(mockRepositories.ratingEventRepository.save).not.toHaveBeenCalled(); }); it('should save created rating events', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); const mockEvent1 = { id: 'event-123' }; const mockEvent2 = { id: 'event-124' }; (RatingEventFactory.createFromVote as any).mockReturnValue([mockEvent1, mockEvent2]); await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(mockRepositories.ratingEventRepository.save).toHaveBeenCalledTimes(2); expect(mockRepositories.ratingEventRepository.save).toHaveBeenCalledWith(mockEvent1); expect(mockRepositories.ratingEventRepository.save).toHaveBeenCalledWith(mockEvent2); }); it('should return eventsCreated count', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); const mockEvent1 = { id: 'event-123' }; const mockEvent2 = { id: 'event-124' }; (RatingEventFactory.createFromVote as any).mockReturnValue([mockEvent1, mockEvent2]); const result = await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(result.eventsCreated).toBe(2); }); }); describe('Snapshot recalculation', () => { it('should recalculate snapshot when events are created', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); const mockEvent = { id: 'event-123' }; (RatingEventFactory.createFromVote as any).mockReturnValue([mockEvent]); const mockAllEvents = [{ id: 'event-1' }, { id: 'event-2' }]; mockRepositories.ratingEventRepository.getAllByUserId.mockResolvedValue(mockAllEvents); const mockSnapshot = { userId: 'admin-123', overallReputation: 75 }; (RatingSnapshotCalculator.calculate as any).mockReturnValue(mockSnapshot); await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(mockRepositories.ratingEventRepository.getAllByUserId).toHaveBeenCalledWith('admin-123'); expect(RatingSnapshotCalculator.calculate).toHaveBeenCalledWith('admin-123', mockAllEvents); expect(mockRepositories.userRatingRepository.save).toHaveBeenCalledWith(mockSnapshot); }); it('should not recalculate snapshot when no events are created (tie)', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 50, count: { positive: 2, negative: 2, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'tie', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(mockRepositories.ratingEventRepository.getAllByUserId).not.toHaveBeenCalled(); expect(RatingSnapshotCalculator.calculate).not.toHaveBeenCalled(); expect(mockRepositories.userRatingRepository.save).not.toHaveBeenCalled(); }); }); describe('Error handling', () => { it('should handle repository errors gracefully', async () => { mockRepositories.adminVoteSessionRepository.findById.mockRejectedValue(new Error('Database error')); const result = await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(result.success).toBe(false); expect(result.errors).toContain('Failed to close vote session: Database error'); }); it('should handle unexpected errors gracefully', async () => { mockRepositories.adminVoteSessionRepository.findById.mockRejectedValue('Unknown error'); const result = await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(result.success).toBe(false); expect(result.errors).toContain('Failed to close vote session: Unknown error'); }); it('should handle save errors gracefully', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); mockRepositories.adminVoteSessionRepository.save.mockRejectedValue(new Error('Save failed')); const result = await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(result.success).toBe(false); expect(result.errors).toContain('Failed to close vote session: Save failed'); }); }); describe('Return values', () => { it('should return voteSessionId in success response', async () => { const futureDate = new Date('2026-02-01'); const mockSession: any = { id: 'session-123', adminId: 'admin-123', startDate: new Date('2026-01-01'), endDate: futureDate, _closed: false, close: vi.fn().mockImplementation(function() { if (this._closed) { throw new Error('Session is already closed'); } const now = new Date(); if (now < this.startDate || now > this.endDate) { throw new Error('Cannot close session outside the voting window'); } this._closed = true; this._outcome = { percentPositive: 75, count: { positive: 3, negative: 1, total: 4 }, eligibleVoterCount: 4, participationRate: 100, outcome: 'positive', }; return this._outcome; }), get closed() { return this._closed; }, }; mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); const result = await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(result.voteSessionId).toBe('session-123'); }); it('should return voteSessionId in error response', async () => { mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(null); const result = await useCase.execute({ voteSessionId: 'session-123', adminId: 'admin-123', }); expect(result.voteSessionId).toBe('session-123'); }); }); });