validate-ballot.unit.test.ts
1 import { describe, expect, it } from 'vitest'; 2 import type { InProgressBallot } from './types/ballot'; 3 import type { Round } from './types/round'; 4 import { ballotValidationErrors, prepareBallotForSubmission } from './validate-ballot'; 5 6 const roundConstraints = { 7 minVotesPerProjectPerVoter: 10, 8 maxVotesPerProjectPerVoter: 100, 9 } satisfies Pick<Round, 'minVotesPerProjectPerVoter' | 'maxVotesPerProjectPerVoter'>; 10 11 describe('prepareBallotForSubmission', () => { 12 it('returns a sanitized ballot when all entries meet constraints', () => { 13 const ballot: InProgressBallot = { 14 'app-1': 20, 15 'app-2': '15', 16 'app-3': null, 17 }; 18 19 expect(prepareBallotForSubmission(ballot, roundConstraints)).toEqual({ 20 'app-1': 20, 21 'app-2': 15, 22 }); 23 }); 24 25 it('throws when a vote falls below the round minimum', () => { 26 const ballot: InProgressBallot = { 27 'app-1': 5, 28 }; 29 30 expect(() => prepareBallotForSubmission(ballot, roundConstraints)).toThrow( 31 'Invalid ballot: votes per project must be at least 10.', 32 ); 33 }); 34 35 it('throws when a vote exceeds the round maximum', () => { 36 const ballot: InProgressBallot = { 37 'app-1': 150, 38 }; 39 40 expect(() => prepareBallotForSubmission(ballot, roundConstraints)).toThrow( 41 'Invalid ballot: votes per project must not exceed 100.', 42 ); 43 }); 44 45 it('throws when votes are non-positive integers', () => { 46 const ballotZero: InProgressBallot = { 47 'app-1': 0, 48 }; 49 50 expect(() => prepareBallotForSubmission(ballotZero, roundConstraints)).toThrow( 51 ballotValidationErrors.POSITIVE_INTEGER_ERROR, 52 ); 53 54 const ballotNegative: InProgressBallot = { 55 'app-1': -5, 56 }; 57 58 expect(() => prepareBallotForSubmission(ballotNegative, roundConstraints)).toThrow( 59 ballotValidationErrors.POSITIVE_INTEGER_ERROR, 60 ); 61 }); 62 63 it('allows submissions when no minimum is configured', () => { 64 const roundWithoutMin = { 65 ...roundConstraints, 66 minVotesPerProjectPerVoter: null, 67 } satisfies Pick<Round, 'minVotesPerProjectPerVoter' | 'maxVotesPerProjectPerVoter'>; 68 69 const ballot: InProgressBallot = { 70 'app-1': 1, 71 }; 72 73 expect(prepareBallotForSubmission(ballot, roundWithoutMin)).toEqual({ 74 'app-1': 1, 75 }); 76 }); 77 });