/** * Comprehensive Unit Tests for Moderation Lock Helpers * * These tests ensure proper lock management and concurrency control */ import { describe, it, expect, beforeEach, vi } from 'vitest'; import { canClaimSubmission, isActiveLock, getLockStatus, formatLockExpiry, getLockUrgency, type LockStatus, type LockUrgency, } from '@/lib/moderation/lockHelpers'; describe('canClaimSubmission', () => { const currentUserId = 'user-123'; const otherUserId = 'user-456'; describe('Unclaimed Submissions', () => { it('should allow claiming unassigned submission', () => { const submission = { assigned_to: null, locked_until: null, }; expect(canClaimSubmission(submission, currentUserId)).toBe(true); }); it('should allow claiming submission with no lock time', () => { const submission = { assigned_to: otherUserId, locked_until: null, }; expect(canClaimSubmission(submission, currentUserId)).toBe(true); }); }); describe('Expired Locks', () => { it('should allow claiming submission with expired lock', () => { const pastDate = new Date(Date.now() - 1000).toISOString(); const submission = { assigned_to: otherUserId, locked_until: pastDate, }; expect(canClaimSubmission(submission, currentUserId)).toBe(true); }); it('should allow claiming submission with lock expired 1 minute ago', () => { const pastDate = new Date(Date.now() - 60000).toISOString(); const submission = { assigned_to: otherUserId, locked_until: pastDate, }; expect(canClaimSubmission(submission, currentUserId)).toBe(true); }); it('should allow claiming submission with lock expired 1 hour ago', () => { const pastDate = new Date(Date.now() - 3600000).toISOString(); const submission = { assigned_to: otherUserId, locked_until: pastDate, }; expect(canClaimSubmission(submission, currentUserId)).toBe(true); }); }); describe('Active Locks by Others', () => { it('should not allow claiming submission locked by another user', () => { const futureDate = new Date(Date.now() + 60000).toISOString(); const submission = { assigned_to: otherUserId, locked_until: futureDate, }; expect(canClaimSubmission(submission, currentUserId)).toBe(false); }); it('should not allow claiming submission locked by another user (15 min lock)', () => { const futureDate = new Date(Date.now() + 15 * 60000).toISOString(); const submission = { assigned_to: otherUserId, locked_until: futureDate, }; expect(canClaimSubmission(submission, currentUserId)).toBe(false); }); }); describe('Own Locks', () => { it('should not allow claiming own locked submission', () => { const futureDate = new Date(Date.now() + 60000).toISOString(); const submission = { assigned_to: currentUserId, locked_until: futureDate, }; expect(canClaimSubmission(submission, currentUserId)).toBe(false); }); it('should allow claiming own submission if lock expired', () => { const pastDate = new Date(Date.now() - 1000).toISOString(); const submission = { assigned_to: currentUserId, locked_until: pastDate, }; expect(canClaimSubmission(submission, currentUserId)).toBe(true); }); }); describe('Edge Cases', () => { it('should handle submission assigned but no lock time', () => { const submission = { assigned_to: currentUserId, locked_until: null, }; expect(canClaimSubmission(submission, currentUserId)).toBe(true); }); it('should handle lock time just expired (1ms ago)', () => { const pastDate = new Date(Date.now() - 1).toISOString(); const submission = { assigned_to: otherUserId, locked_until: pastDate, }; expect(canClaimSubmission(submission, currentUserId)).toBe(true); }); it('should handle lock time about to expire (1ms future)', () => { const futureDate = new Date(Date.now() + 1).toISOString(); const submission = { assigned_to: otherUserId, locked_until: futureDate, }; expect(canClaimSubmission(submission, currentUserId)).toBe(false); }); }); }); describe('isActiveLock', () => { const assignedTo = 'user-123'; describe('Active Locks', () => { it('should return true for active lock', () => { const futureDate = new Date(Date.now() + 60000).toISOString(); expect(isActiveLock(assignedTo, futureDate)).toBe(true); }); it('should return true for lock expiring in 1 second', () => { const futureDate = new Date(Date.now() + 1000).toISOString(); expect(isActiveLock(assignedTo, futureDate)).toBe(true); }); it('should return true for lock expiring in 15 minutes', () => { const futureDate = new Date(Date.now() + 15 * 60000).toISOString(); expect(isActiveLock(assignedTo, futureDate)).toBe(true); }); }); describe('Inactive Locks', () => { it('should return false for expired lock', () => { const pastDate = new Date(Date.now() - 1000).toISOString(); expect(isActiveLock(assignedTo, pastDate)).toBe(false); }); it('should return false when no assignee', () => { const futureDate = new Date(Date.now() + 60000).toISOString(); expect(isActiveLock(null, futureDate)).toBe(false); }); it('should return false when no lock time', () => { expect(isActiveLock(assignedTo, null)).toBe(false); }); it('should return false when both null', () => { expect(isActiveLock(null, null)).toBe(false); }); }); describe('Edge Cases', () => { it('should return false for lock expired 1ms ago', () => { const pastDate = new Date(Date.now() - 1).toISOString(); expect(isActiveLock(assignedTo, pastDate)).toBe(false); }); it('should return true for lock expiring in 1ms', () => { const futureDate = new Date(Date.now() + 1).toISOString(); expect(isActiveLock(assignedTo, futureDate)).toBe(true); }); }); }); describe('getLockStatus', () => { const currentUserId = 'user-123'; const otherUserId = 'user-456'; describe('Unlocked Status', () => { it('should return unlocked when no assignee', () => { const submission = { assigned_to: null, locked_until: null, }; expect(getLockStatus(submission, currentUserId)).toBe('unlocked'); }); it('should return unlocked when no lock time', () => { const submission = { assigned_to: null, locked_until: new Date(Date.now() + 60000).toISOString(), }; expect(getLockStatus(submission, currentUserId)).toBe('unlocked'); }); it('should return unlocked when assignee but no lock time', () => { const submission = { assigned_to: currentUserId, locked_until: null, }; expect(getLockStatus(submission, currentUserId)).toBe('unlocked'); }); }); describe('Expired Status', () => { it('should return expired for expired lock by current user', () => { const pastDate = new Date(Date.now() - 1000).toISOString(); const submission = { assigned_to: currentUserId, locked_until: pastDate, }; expect(getLockStatus(submission, currentUserId)).toBe('expired'); }); it('should return expired for expired lock by other user', () => { const pastDate = new Date(Date.now() - 1000).toISOString(); const submission = { assigned_to: otherUserId, locked_until: pastDate, }; expect(getLockStatus(submission, currentUserId)).toBe('expired'); }); }); describe('Locked by Me Status', () => { it('should return locked_by_me for active lock by current user', () => { const futureDate = new Date(Date.now() + 60000).toISOString(); const submission = { assigned_to: currentUserId, locked_until: futureDate, }; expect(getLockStatus(submission, currentUserId)).toBe('locked_by_me'); }); it('should return locked_by_me for lock expiring soon', () => { const futureDate = new Date(Date.now() + 1000).toISOString(); const submission = { assigned_to: currentUserId, locked_until: futureDate, }; expect(getLockStatus(submission, currentUserId)).toBe('locked_by_me'); }); }); describe('Locked by Other Status', () => { it('should return locked_by_other for active lock by other user', () => { const futureDate = new Date(Date.now() + 60000).toISOString(); const submission = { assigned_to: otherUserId, locked_until: futureDate, }; expect(getLockStatus(submission, currentUserId)).toBe('locked_by_other'); }); it('should return locked_by_other for lock expiring soon by other user', () => { const futureDate = new Date(Date.now() + 1000).toISOString(); const submission = { assigned_to: otherUserId, locked_until: futureDate, }; expect(getLockStatus(submission, currentUserId)).toBe('locked_by_other'); }); }); describe('Edge Cases', () => { it('should handle lock expiring in 1ms by current user', () => { const futureDate = new Date(Date.now() + 1).toISOString(); const submission = { assigned_to: currentUserId, locked_until: futureDate, }; expect(getLockStatus(submission, currentUserId)).toBe('locked_by_me'); }); it('should handle lock expired 1ms ago', () => { const pastDate = new Date(Date.now() - 1).toISOString(); const submission = { assigned_to: currentUserId, locked_until: pastDate, }; expect(getLockStatus(submission, currentUserId)).toBe('expired'); }); }); }); describe('formatLockExpiry', () => { describe('Active Locks', () => { it('should format 1 minute remaining', () => { const futureDate = new Date(Date.now() + 60000).toISOString(); const result = formatLockExpiry(futureDate); expect(result).toBe('1:00'); }); it('should format 5 minutes 30 seconds remaining', () => { const futureDate = new Date(Date.now() + 5.5 * 60000).toISOString(); const result = formatLockExpiry(futureDate); expect(result).toBe('5:30'); }); it('should format 10 seconds remaining', () => { const futureDate = new Date(Date.now() + 10000).toISOString(); const result = formatLockExpiry(futureDate); expect(result).toBe('0:10'); }); it('should format 59 seconds remaining', () => { const futureDate = new Date(Date.now() + 59000).toISOString(); const result = formatLockExpiry(futureDate); expect(result).toBe('0:59'); }); it('should format 15 minutes remaining', () => { const futureDate = new Date(Date.now() + 15 * 60000).toISOString(); const result = formatLockExpiry(futureDate); expect(result).toBe('15:00'); }); it('should pad single digit seconds with zero', () => { const futureDate = new Date(Date.now() + 65000).toISOString(); // 1:05 const result = formatLockExpiry(futureDate); expect(result).toBe('1:05'); }); }); describe('Expired Locks', () => { it('should return "Expired" for expired lock', () => { const pastDate = new Date(Date.now() - 1000).toISOString(); const result = formatLockExpiry(pastDate); expect(result).toBe('Expired'); }); it('should return "Expired" for lock expired 1 minute ago', () => { const pastDate = new Date(Date.now() - 60000).toISOString(); const result = formatLockExpiry(pastDate); expect(result).toBe('Expired'); }); it('should return "Expired" for lock expired 1 hour ago', () => { const pastDate = new Date(Date.now() - 3600000).toISOString(); const result = formatLockExpiry(pastDate); expect(result).toBe('Expired'); }); }); describe('Edge Cases', () => { it('should handle lock expiring in 1 second', () => { const futureDate = new Date(Date.now() + 1000).toISOString(); const result = formatLockExpiry(futureDate); expect(result).toBe('0:01'); }); it('should handle lock expiring right now', () => { const futureDate = new Date(Date.now()).toISOString(); const result = formatLockExpiry(futureDate); expect(result).toBe('Expired'); }); it('should handle lock expiring in less than 1 second', () => { const futureDate = new Date(Date.now() + 500).toISOString(); const result = formatLockExpiry(futureDate); expect(result).toBe('0:00'); }); }); }); describe('getLockUrgency', () => { describe('Critical Urgency', () => { it('should return critical for 1 minute remaining', () => { const timeLeftMs = 60000; expect(getLockUrgency(timeLeftMs)).toBe('critical'); }); it('should return critical for 30 seconds remaining', () => { const timeLeftMs = 30000; expect(getLockUrgency(timeLeftMs)).toBe('critical'); }); it('should return critical for 1 second remaining', () => { const timeLeftMs = 1000; expect(getLockUrgency(timeLeftMs)).toBe('critical'); }); it('should return critical for exactly 2 minutes - 1ms remaining', () => { const timeLeftMs = 2 * 60 * 1000 - 1; expect(getLockUrgency(timeLeftMs)).toBe('critical'); }); it('should return critical for 0 seconds remaining', () => { const timeLeftMs = 0; expect(getLockUrgency(timeLeftMs)).toBe('critical'); }); it('should return critical for negative time (expired)', () => { const timeLeftMs = -1000; expect(getLockUrgency(timeLeftMs)).toBe('critical'); }); }); describe('Warning Urgency', () => { it('should return warning for 4 minutes remaining', () => { const timeLeftMs = 4 * 60000; expect(getLockUrgency(timeLeftMs)).toBe('warning'); }); it('should return warning for 3 minutes remaining', () => { const timeLeftMs = 3 * 60000; expect(getLockUrgency(timeLeftMs)).toBe('warning'); }); it('should return warning for exactly 2 minutes remaining', () => { const timeLeftMs = 2 * 60 * 1000; expect(getLockUrgency(timeLeftMs)).toBe('warning'); }); it('should return warning for exactly 5 minutes - 1ms remaining', () => { const timeLeftMs = 5 * 60 * 1000 - 1; expect(getLockUrgency(timeLeftMs)).toBe('warning'); }); }); describe('Normal Urgency', () => { it('should return normal for 6 minutes remaining', () => { const timeLeftMs = 6 * 60000; expect(getLockUrgency(timeLeftMs)).toBe('normal'); }); it('should return normal for 10 minutes remaining', () => { const timeLeftMs = 10 * 60000; expect(getLockUrgency(timeLeftMs)).toBe('normal'); }); it('should return normal for 15 minutes remaining', () => { const timeLeftMs = 15 * 60000; expect(getLockUrgency(timeLeftMs)).toBe('normal'); }); it('should return normal for exactly 5 minutes remaining', () => { const timeLeftMs = 5 * 60 * 1000; expect(getLockUrgency(timeLeftMs)).toBe('normal'); }); it('should return normal for 1 hour remaining', () => { const timeLeftMs = 60 * 60000; expect(getLockUrgency(timeLeftMs)).toBe('normal'); }); }); describe('Edge Cases', () => { it('should handle very large time values', () => { const timeLeftMs = 999999999; expect(getLockUrgency(timeLeftMs)).toBe('normal'); }); it('should handle very negative time values', () => { const timeLeftMs = -999999999; expect(getLockUrgency(timeLeftMs)).toBe('critical'); }); it('should handle exactly at 2 minute boundary', () => { const timeLeftMs = 2 * 60 * 1000; expect(getLockUrgency(timeLeftMs)).toBe('warning'); }); it('should handle exactly at 5 minute boundary', () => { const timeLeftMs = 5 * 60 * 1000; expect(getLockUrgency(timeLeftMs)).toBe('normal'); }); }); }); describe('Type Safety', () => { it('should enforce LockStatus type', () => { const validStatuses: LockStatus[] = [ 'locked_by_me', 'locked_by_other', 'unlocked', 'expired', ]; validStatuses.forEach(status => { expect(['locked_by_me', 'locked_by_other', 'unlocked', 'expired']).toContain(status); }); }); it('should enforce LockUrgency type', () => { const validUrgencies: LockUrgency[] = [ 'critical', 'warning', 'normal', ]; validUrgencies.forEach(urgency => { expect(['critical', 'warning', 'normal']).toContain(urgency); }); }); });