view data tests
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Application Query Tests: GetUserRatingLedgerQuery
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { GetUserRatingLedgerQueryHandler } from './GetUserRatingLedgerQuery';
|
||||
import { RatingEventRepository } from '../../domain/repositories/RatingEventRepository';
|
||||
|
||||
// Mock repository
|
||||
const createMockRepository = () => ({
|
||||
save: vi.fn(),
|
||||
findByUserId: vi.fn(),
|
||||
findByIds: vi.fn(),
|
||||
getAllByUserId: vi.fn(),
|
||||
findEventsPaginated: vi.fn(),
|
||||
});
|
||||
|
||||
describe('GetUserRatingLedgerQueryHandler', () => {
|
||||
let handler: GetUserRatingLedgerQueryHandler;
|
||||
let mockRepository: ReturnType<typeof createMockRepository>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockRepository = createMockRepository();
|
||||
handler = new GetUserRatingLedgerQueryHandler(mockRepository as unknown as RatingEventRepository);
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should query repository with default pagination', async () => {
|
||||
mockRepository.findEventsPaginated.mockResolvedValue({
|
||||
items: [],
|
||||
total: 0,
|
||||
limit: 20,
|
||||
offset: 0,
|
||||
hasMore: false,
|
||||
});
|
||||
|
||||
await handler.execute({ userId: 'user-1' });
|
||||
|
||||
expect(mockRepository.findEventsPaginated).toHaveBeenCalledWith('user-1', {
|
||||
limit: 20,
|
||||
offset: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('should query repository with custom pagination', async () => {
|
||||
mockRepository.findEventsPaginated.mockResolvedValue({
|
||||
items: [],
|
||||
total: 0,
|
||||
limit: 50,
|
||||
offset: 100,
|
||||
hasMore: false,
|
||||
});
|
||||
|
||||
await handler.execute({
|
||||
userId: 'user-1',
|
||||
limit: 50,
|
||||
offset: 100,
|
||||
});
|
||||
|
||||
expect(mockRepository.findEventsPaginated).toHaveBeenCalledWith('user-1', {
|
||||
limit: 50,
|
||||
offset: 100,
|
||||
});
|
||||
});
|
||||
|
||||
it('should query repository with filters', async () => {
|
||||
mockRepository.findEventsPaginated.mockResolvedValue({
|
||||
items: [],
|
||||
total: 0,
|
||||
limit: 20,
|
||||
offset: 0,
|
||||
hasMore: false,
|
||||
});
|
||||
|
||||
const filter: any = {
|
||||
dimensions: ['trust'],
|
||||
sourceTypes: ['vote'],
|
||||
from: '2026-01-01T00:00:00Z',
|
||||
to: '2026-01-31T23:59:59Z',
|
||||
reasonCodes: ['VOTE_POSITIVE'],
|
||||
};
|
||||
|
||||
await handler.execute({
|
||||
userId: 'user-1',
|
||||
filter,
|
||||
});
|
||||
|
||||
expect(mockRepository.findEventsPaginated).toHaveBeenCalledWith('user-1', {
|
||||
limit: 20,
|
||||
offset: 0,
|
||||
filter: {
|
||||
dimensions: ['trust'],
|
||||
sourceTypes: ['vote'],
|
||||
from: new Date('2026-01-01T00:00:00Z'),
|
||||
to: new Date('2026-01-31T23:59:59Z'),
|
||||
reasonCodes: ['VOTE_POSITIVE'],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should map domain entities to DTOs', async () => {
|
||||
const mockEvent = {
|
||||
id: { value: 'event-1' },
|
||||
userId: 'user-1',
|
||||
dimension: { value: 'trust' },
|
||||
delta: { value: 5 },
|
||||
occurredAt: new Date('2026-01-15T12:00:00Z'),
|
||||
createdAt: new Date('2026-01-15T12:00:00Z'),
|
||||
source: 'admin_vote',
|
||||
reason: 'VOTE_POSITIVE',
|
||||
visibility: 'public',
|
||||
weight: 1.0,
|
||||
};
|
||||
|
||||
mockRepository.findEventsPaginated.mockResolvedValue({
|
||||
items: [mockEvent],
|
||||
total: 1,
|
||||
limit: 20,
|
||||
offset: 0,
|
||||
hasMore: false,
|
||||
});
|
||||
|
||||
const result = await handler.execute({ userId: 'user-1' });
|
||||
|
||||
expect(result.entries).toHaveLength(1);
|
||||
expect(result.entries[0]).toEqual({
|
||||
id: 'event-1',
|
||||
userId: 'user-1',
|
||||
dimension: 'trust',
|
||||
delta: 5,
|
||||
occurredAt: '2026-01-15T12:00:00.000Z',
|
||||
createdAt: '2026-01-15T12:00:00.000Z',
|
||||
source: 'admin_vote',
|
||||
reason: 'VOTE_POSITIVE',
|
||||
visibility: 'public',
|
||||
weight: 1.0,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle pagination metadata in result', async () => {
|
||||
mockRepository.findEventsPaginated.mockResolvedValue({
|
||||
items: [],
|
||||
total: 100,
|
||||
limit: 20,
|
||||
offset: 20,
|
||||
hasMore: true,
|
||||
nextOffset: 40,
|
||||
});
|
||||
|
||||
const result = await handler.execute({ userId: 'user-1', limit: 20, offset: 20 });
|
||||
|
||||
expect(result.pagination).toEqual({
|
||||
total: 100,
|
||||
limit: 20,
|
||||
offset: 20,
|
||||
hasMore: true,
|
||||
nextOffset: 40,
|
||||
});
|
||||
});
|
||||
});
|
||||
399
core/identity/application/use-cases/CastAdminVoteUseCase.test.ts
Normal file
399
core/identity/application/use-cases/CastAdminVoteUseCase.test.ts
Normal file
@@ -0,0 +1,399 @@
|
||||
/**
|
||||
* Application Use Case Tests: CastAdminVoteUseCase
|
||||
*
|
||||
* Tests for casting votes in admin vote sessions
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { CastAdminVoteUseCase } from './CastAdminVoteUseCase';
|
||||
import { AdminVoteSessionRepository } from '../../domain/repositories/AdminVoteSessionRepository';
|
||||
import { AdminVoteSession } from '../../domain/entities/AdminVoteSession';
|
||||
|
||||
// Mock repository
|
||||
const createMockRepository = () => ({
|
||||
save: vi.fn(),
|
||||
findById: vi.fn(),
|
||||
findActiveForAdmin: vi.fn(),
|
||||
findByAdminAndLeague: vi.fn(),
|
||||
findByLeague: vi.fn(),
|
||||
findClosedUnprocessed: vi.fn(),
|
||||
});
|
||||
|
||||
describe('CastAdminVoteUseCase', () => {
|
||||
let useCase: CastAdminVoteUseCase;
|
||||
let mockRepository: ReturnType<typeof createMockRepository>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockRepository = createMockRepository();
|
||||
useCase = new CastAdminVoteUseCase(mockRepository);
|
||||
});
|
||||
|
||||
describe('Input validation', () => {
|
||||
it('should reject when voteSessionId is missing', async () => {
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: '',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('voteSessionId is required');
|
||||
});
|
||||
|
||||
it('should reject when voterId is missing', async () => {
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: '',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('voterId is required');
|
||||
});
|
||||
|
||||
it('should reject when positive is not a boolean', async () => {
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: 'true' as any,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('positive must be a boolean value');
|
||||
});
|
||||
|
||||
it('should reject when votedAt is not a valid date', async () => {
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
votedAt: 'invalid-date',
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('votedAt must be a valid date if provided');
|
||||
});
|
||||
|
||||
it('should accept valid input with all fields', async () => {
|
||||
mockRepository.findById.mockResolvedValue({
|
||||
id: 'session-123',
|
||||
isVotingWindowOpen: vi.fn().mockReturnValue(true),
|
||||
castVote: vi.fn(),
|
||||
});
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
votedAt: '2024-01-01T00:00:00Z',
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.errors).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should accept valid input without optional votedAt', async () => {
|
||||
mockRepository.findById.mockResolvedValue({
|
||||
id: 'session-123',
|
||||
isVotingWindowOpen: vi.fn().mockReturnValue(true),
|
||||
castVote: vi.fn(),
|
||||
});
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.errors).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Session lookup', () => {
|
||||
it('should reject when vote session is not found', async () => {
|
||||
mockRepository.findById.mockResolvedValue(null);
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'non-existent-session',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('Vote session not found');
|
||||
});
|
||||
|
||||
it('should find session by ID when provided', async () => {
|
||||
const mockSession = {
|
||||
id: 'session-123',
|
||||
isVotingWindowOpen: vi.fn().mockReturnValue(true),
|
||||
castVote: vi.fn(),
|
||||
};
|
||||
mockRepository.findById.mockResolvedValue(mockSession);
|
||||
|
||||
await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(mockRepository.findById).toHaveBeenCalledWith('session-123');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Voting window validation', () => {
|
||||
it('should reject when voting window is not open', async () => {
|
||||
const mockSession = {
|
||||
id: 'session-123',
|
||||
isVotingWindowOpen: vi.fn().mockReturnValue(false),
|
||||
castVote: vi.fn(),
|
||||
};
|
||||
mockRepository.findById.mockResolvedValue(mockSession);
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('Vote session is not open for voting');
|
||||
expect(mockSession.isVotingWindowOpen).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should accept when voting window is open', async () => {
|
||||
const mockSession = {
|
||||
id: 'session-123',
|
||||
isVotingWindowOpen: vi.fn().mockReturnValue(true),
|
||||
castVote: vi.fn(),
|
||||
};
|
||||
mockRepository.findById.mockResolvedValue(mockSession);
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(mockSession.isVotingWindowOpen).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should use current time when votedAt is not provided', async () => {
|
||||
const mockSession = {
|
||||
id: 'session-123',
|
||||
isVotingWindowOpen: vi.fn().mockReturnValue(true),
|
||||
castVote: vi.fn(),
|
||||
};
|
||||
mockRepository.findById.mockResolvedValue(mockSession);
|
||||
|
||||
await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(mockSession.isVotingWindowOpen).toHaveBeenCalledWith(expect.any(Date));
|
||||
});
|
||||
|
||||
it('should use provided votedAt when available', async () => {
|
||||
const mockSession = {
|
||||
id: 'session-123',
|
||||
isVotingWindowOpen: vi.fn().mockReturnValue(true),
|
||||
castVote: vi.fn(),
|
||||
};
|
||||
mockRepository.findById.mockResolvedValue(mockSession);
|
||||
|
||||
const votedAt = new Date('2024-01-01T12:00:00Z');
|
||||
await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
votedAt: votedAt.toISOString(),
|
||||
});
|
||||
|
||||
expect(mockSession.isVotingWindowOpen).toHaveBeenCalledWith(votedAt);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Vote casting', () => {
|
||||
it('should cast positive vote when session is open', async () => {
|
||||
const mockSession = {
|
||||
id: 'session-123',
|
||||
isVotingWindowOpen: vi.fn().mockReturnValue(true),
|
||||
castVote: vi.fn(),
|
||||
};
|
||||
mockRepository.findById.mockResolvedValue(mockSession);
|
||||
|
||||
await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(mockSession.castVote).toHaveBeenCalledWith('voter-123', true, expect.any(Date));
|
||||
});
|
||||
|
||||
it('should cast negative vote when session is open', async () => {
|
||||
const mockSession = {
|
||||
id: 'session-123',
|
||||
isVotingWindowOpen: vi.fn().mockReturnValue(true),
|
||||
castVote: vi.fn(),
|
||||
};
|
||||
mockRepository.findById.mockResolvedValue(mockSession);
|
||||
|
||||
await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: false,
|
||||
});
|
||||
|
||||
expect(mockSession.castVote).toHaveBeenCalledWith('voter-123', false, expect.any(Date));
|
||||
});
|
||||
|
||||
it('should save updated session after casting vote', async () => {
|
||||
const mockSession = {
|
||||
id: 'session-123',
|
||||
isVotingWindowOpen: vi.fn().mockReturnValue(true),
|
||||
castVote: vi.fn(),
|
||||
};
|
||||
mockRepository.findById.mockResolvedValue(mockSession);
|
||||
|
||||
await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(mockRepository.save).toHaveBeenCalledWith(mockSession);
|
||||
});
|
||||
|
||||
it('should return success when vote is cast', async () => {
|
||||
const mockSession = {
|
||||
id: 'session-123',
|
||||
isVotingWindowOpen: vi.fn().mockReturnValue(true),
|
||||
castVote: vi.fn(),
|
||||
};
|
||||
mockRepository.findById.mockResolvedValue(mockSession);
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.voteSessionId).toBe('session-123');
|
||||
expect(result.voterId).toBe('voter-123');
|
||||
expect(result.errors).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error handling', () => {
|
||||
it('should handle repository errors gracefully', async () => {
|
||||
mockRepository.findById.mockRejectedValue(new Error('Database error'));
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('Failed to cast vote: Database error');
|
||||
});
|
||||
|
||||
it('should handle unexpected errors gracefully', async () => {
|
||||
mockRepository.findById.mockRejectedValue('Unknown error');
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('Failed to cast vote: Unknown error');
|
||||
});
|
||||
|
||||
it('should handle save errors gracefully', async () => {
|
||||
const mockSession = {
|
||||
id: 'session-123',
|
||||
isVotingWindowOpen: vi.fn().mockReturnValue(true),
|
||||
castVote: vi.fn(),
|
||||
};
|
||||
mockRepository.findById.mockResolvedValue(mockSession);
|
||||
mockRepository.save.mockRejectedValue(new Error('Save failed'));
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('Failed to cast vote: Save failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Return values', () => {
|
||||
it('should return voteSessionId in success response', async () => {
|
||||
const mockSession = {
|
||||
id: 'session-123',
|
||||
isVotingWindowOpen: vi.fn().mockReturnValue(true),
|
||||
castVote: vi.fn(),
|
||||
};
|
||||
mockRepository.findById.mockResolvedValue(mockSession);
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(result.voteSessionId).toBe('session-123');
|
||||
});
|
||||
|
||||
it('should return voterId in success response', async () => {
|
||||
const mockSession = {
|
||||
id: 'session-123',
|
||||
isVotingWindowOpen: vi.fn().mockReturnValue(true),
|
||||
castVote: vi.fn(),
|
||||
};
|
||||
mockRepository.findById.mockResolvedValue(mockSession);
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(result.voterId).toBe('voter-123');
|
||||
});
|
||||
|
||||
it('should return voteSessionId in error response', async () => {
|
||||
mockRepository.findById.mockResolvedValue(null);
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(result.voteSessionId).toBe('session-123');
|
||||
});
|
||||
|
||||
it('should return voterId in error response', async () => {
|
||||
mockRepository.findById.mockResolvedValue(null);
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-123',
|
||||
voterId: 'voter-123',
|
||||
positive: true,
|
||||
});
|
||||
|
||||
expect(result.voterId).toBe('voter-123');
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,251 @@
|
||||
/**
|
||||
* Application Use Case Tests: OpenAdminVoteSessionUseCase
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { OpenAdminVoteSessionUseCase } from './OpenAdminVoteSessionUseCase';
|
||||
import { AdminVoteSessionRepository } from '../../domain/repositories/AdminVoteSessionRepository';
|
||||
import { AdminVoteSession } from '../../domain/entities/AdminVoteSession';
|
||||
|
||||
// Mock repository
|
||||
const createMockRepository = () => ({
|
||||
save: vi.fn(),
|
||||
findById: vi.fn(),
|
||||
findActiveForAdmin: vi.fn(),
|
||||
findByAdminAndLeague: vi.fn(),
|
||||
findByLeague: vi.fn(),
|
||||
findClosedUnprocessed: vi.fn(),
|
||||
});
|
||||
|
||||
describe('OpenAdminVoteSessionUseCase', () => {
|
||||
let useCase: OpenAdminVoteSessionUseCase;
|
||||
let mockRepository: ReturnType<typeof createMockRepository>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockRepository = createMockRepository();
|
||||
useCase = new OpenAdminVoteSessionUseCase(mockRepository as unknown as AdminVoteSessionRepository);
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Input validation', () => {
|
||||
it('should reject when voteSessionId is missing', async () => {
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: '',
|
||||
leagueId: 'league-1',
|
||||
adminId: 'admin-1',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-07',
|
||||
eligibleVoters: ['voter-1'],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('voteSessionId is required');
|
||||
});
|
||||
|
||||
it('should reject when leagueId is missing', async () => {
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-1',
|
||||
leagueId: '',
|
||||
adminId: 'admin-1',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-07',
|
||||
eligibleVoters: ['voter-1'],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('leagueId is required');
|
||||
});
|
||||
|
||||
it('should reject when adminId is missing', async () => {
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-1',
|
||||
leagueId: 'league-1',
|
||||
adminId: '',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-07',
|
||||
eligibleVoters: ['voter-1'],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('adminId is required');
|
||||
});
|
||||
|
||||
it('should reject when startDate is missing', async () => {
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-1',
|
||||
leagueId: 'league-1',
|
||||
adminId: 'admin-1',
|
||||
startDate: '',
|
||||
endDate: '2026-01-07',
|
||||
eligibleVoters: ['voter-1'],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('startDate is required');
|
||||
});
|
||||
|
||||
it('should reject when endDate is missing', async () => {
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-1',
|
||||
leagueId: 'league-1',
|
||||
adminId: 'admin-1',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '',
|
||||
eligibleVoters: ['voter-1'],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('endDate is required');
|
||||
});
|
||||
|
||||
it('should reject when startDate is invalid', async () => {
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-1',
|
||||
leagueId: 'league-1',
|
||||
adminId: 'admin-1',
|
||||
startDate: 'invalid-date',
|
||||
endDate: '2026-01-07',
|
||||
eligibleVoters: ['voter-1'],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('startDate must be a valid date');
|
||||
});
|
||||
|
||||
it('should reject when endDate is invalid', async () => {
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-1',
|
||||
leagueId: 'league-1',
|
||||
adminId: 'admin-1',
|
||||
startDate: '2026-01-01',
|
||||
endDate: 'invalid-date',
|
||||
eligibleVoters: ['voter-1'],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('endDate must be a valid date');
|
||||
});
|
||||
|
||||
it('should reject when startDate is after endDate', async () => {
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-1',
|
||||
leagueId: 'league-1',
|
||||
adminId: 'admin-1',
|
||||
startDate: '2026-01-07',
|
||||
endDate: '2026-01-01',
|
||||
eligibleVoters: ['voter-1'],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('startDate must be before endDate');
|
||||
});
|
||||
|
||||
it('should reject when eligibleVoters is empty', async () => {
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-1',
|
||||
leagueId: 'league-1',
|
||||
adminId: 'admin-1',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-07',
|
||||
eligibleVoters: [],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('At least one eligible voter is required');
|
||||
});
|
||||
|
||||
it('should reject when eligibleVoters has duplicates', async () => {
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-1',
|
||||
leagueId: 'league-1',
|
||||
adminId: 'admin-1',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-07',
|
||||
eligibleVoters: ['voter-1', 'voter-1'],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('Duplicate eligible voters are not allowed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Business rules', () => {
|
||||
it('should reject when session ID already exists', async () => {
|
||||
mockRepository.findById.mockResolvedValue({ id: 'session-1' } as any);
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-1',
|
||||
leagueId: 'league-1',
|
||||
adminId: 'admin-1',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-07',
|
||||
eligibleVoters: ['voter-1'],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('Vote session with this ID already exists');
|
||||
});
|
||||
|
||||
it('should reject when there is an overlapping active session', async () => {
|
||||
mockRepository.findById.mockResolvedValue(null);
|
||||
mockRepository.findActiveForAdmin.mockResolvedValue([
|
||||
{
|
||||
startDate: new Date('2026-01-05'),
|
||||
endDate: new Date('2026-01-10'),
|
||||
}
|
||||
] as any);
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-1',
|
||||
leagueId: 'league-1',
|
||||
adminId: 'admin-1',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-07',
|
||||
eligibleVoters: ['voter-1'],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toContain('Active vote session already exists for this admin in this league with overlapping dates');
|
||||
});
|
||||
|
||||
it('should create and save a new session when valid', async () => {
|
||||
mockRepository.findById.mockResolvedValue(null);
|
||||
mockRepository.findActiveForAdmin.mockResolvedValue([]);
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-1',
|
||||
leagueId: 'league-1',
|
||||
adminId: 'admin-1',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-07',
|
||||
eligibleVoters: ['voter-1', 'voter-2'],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(mockRepository.save).toHaveBeenCalled();
|
||||
const savedSession = mockRepository.save.mock.calls[0][0];
|
||||
expect(savedSession).toBeInstanceOf(AdminVoteSession);
|
||||
expect(savedSession.id).toBe('session-1');
|
||||
expect(savedSession.leagueId).toBe('league-1');
|
||||
expect(savedSession.adminId).toBe('admin-1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error handling', () => {
|
||||
it('should handle repository errors gracefully', async () => {
|
||||
mockRepository.findById.mockRejectedValue(new Error('Database error'));
|
||||
|
||||
const result = await useCase.execute({
|
||||
voteSessionId: 'session-1',
|
||||
leagueId: 'league-1',
|
||||
adminId: 'admin-1',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-07',
|
||||
eligibleVoters: ['voter-1'],
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors?.[0]).toContain('Failed to open vote session: Database error');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user