/** * Ceremony traffic classifier — these requests bypass the 100/min-per-IP tRPC * budget (whole venues share one IP). A single non-ceremony procedure in a * batch must fall back to the strict bucket. */ import { describe, it, expect } from 'vitest' import { isCeremonyTraffic } from '@/lib/rate-limit' describe('isCeremonyTraffic', () => { it('accepts single ceremony procedures', () => { expect(isCeremonyTraffic('/api/trpc/liveVoting.getCeremonyState')).toBe(true) expect(isCeremonyTraffic('/api/trpc/liveVoting.getAudienceWindow')).toBe(true) expect(isCeremonyTraffic('/api/trpc/liveVoting.castFavoriteVote')).toBe(true) expect(isCeremonyTraffic('/api/trpc/live.getCursor')).toBe(true) }) it('accepts batched requests of only ceremony procedures', () => { expect( isCeremonyTraffic('/api/trpc/live.getCursor,liveVoting.getSessionForVotingByRound') ).toBe(true) }) it('rejects any batch containing a non-ceremony procedure', () => { expect(isCeremonyTraffic('/api/trpc/live.getCursor,user.me')).toBe(false) expect(isCeremonyTraffic('/api/trpc/liveVoting.getResults')).toBe(false) expect(isCeremonyTraffic('/api/trpc/deliberation.submitVote')).toBe(false) }) it('rejects admin/state-changing live procedures', () => { expect(isCeremonyTraffic('/api/trpc/live.startPresentation')).toBe(false) expect(isCeremonyTraffic('/api/trpc/liveVoting.openAudienceWindow')).toBe(false) expect(isCeremonyTraffic('/api/trpc/liveVoting.revealNext')).toBe(false) }) it('rejects empty and malformed paths', () => { expect(isCeremonyTraffic('/api/trpc/')).toBe(false) expect(isCeremonyTraffic('/api/trpc')).toBe(false) }) })