@@ -16,6 +16,7 @@ const mockDelete = vi.fn();
1616vi . mock ( '@query/db' , ( ) => {
1717 return {
1818 db : {
19+ transaction : vi . fn ( ) . mockImplementation ( ( callback ) => callback ( db ) ) ,
1920 query : {
2021 admins : {
2122 findFirst : ( ...args : any [ ] ) => mockFindFirst ( 'admins' , ...args ) ,
@@ -44,6 +45,18 @@ vi.mock('@query/db', () => {
4445 events : {
4546 findFirst : ( ...args : any [ ] ) => mockFindFirst ( 'events' , ...args ) ,
4647 findMany : ( ...args : any [ ] ) => mockFindMany ( 'events' , ...args ) ,
48+ } ,
49+ judges : {
50+ findFirst : ( ...args : any [ ] ) => mockFindFirst ( 'judges' , ...args ) ,
51+ findMany : ( ...args : any [ ] ) => mockFindMany ( 'judges' , ...args ) ,
52+ } ,
53+ judgeAssignments : {
54+ findFirst : ( ...args : any [ ] ) => mockFindFirst ( 'judgeAssignments' , ...args ) ,
55+ findMany : ( ...args : any [ ] ) => mockFindMany ( 'judgeAssignments' , ...args ) ,
56+ } ,
57+ hackathonProjects : {
58+ findFirst : ( ...args : any [ ] ) => mockFindFirst ( 'hackathonProjects' , ...args ) ,
59+ findMany : ( ...args : any [ ] ) => mockFindMany ( 'hackathonProjects' , ...args ) ,
4760 }
4861 } ,
4962 insert : ( ) => ( {
@@ -82,6 +95,7 @@ vi.mock('@query/db', () => {
8295 status : 'status' ,
8396 isPublic : 'is_public' ,
8497 startDate : 'start_date' ,
98+ endDate : 'end_date' ,
8599 } ,
86100 hackathonParticipants : {
87101 id : 'id' ,
@@ -103,6 +117,18 @@ vi.mock('@query/db', () => {
103117 qrCode : 'qr_code' ,
104118 checkInEnabled : 'check_in_enabled' ,
105119 eventDate : 'event_date' ,
120+ } ,
121+ judges : {
122+ id : 'id' ,
123+ userId : 'user_id' ,
124+ } ,
125+ judgeAssignments : {
126+ judgeId : 'judge_id' ,
127+ hackathonId : 'hackathon_id' ,
128+ } ,
129+ hackathonProjects : {
130+ id : 'id' ,
131+ hackathonId : 'hackathon_id' ,
106132 }
107133 } ;
108134} ) ;
@@ -401,4 +427,134 @@ describe('Router Integration and Access Control Verification Suite', () => {
401427 } ) ;
402428 } ) ;
403429
430+ describe ( '7. Hackathon Teams, Judge and Project Submission Restrictions' , ( ) => {
431+ it ( 'should reject team creation if maxMembers is greater than 4' , async ( ) => {
432+ const ctx = createMockCtx ( 'user_id' ) ;
433+ const caller = appRouter . createCaller ( ctx ) ;
434+ await expect (
435+ caller . team . createTeam ( {
436+ hackathonId : '00000000-0000-0000-0000-000000000000' ,
437+ name : 'Super Team' ,
438+ maxMembers : 5 ,
439+ } )
440+ ) . rejects . toThrow ( ) ;
441+ } ) ;
442+
443+ it ( 'should prevent registered participants from applying to be a judge' , async ( ) => {
444+ const ctx = createMockCtx ( 'participant_user_id' ) ;
445+ const hackathonId = '00000000-0000-0000-0000-000000000001' ;
446+ mockFindFirst . mockImplementation ( ( table ) => {
447+ if ( table === 'hackathonParticipants' ) {
448+ return { id : 'participant_1' , userId : 'participant_user_id' , hackathonId } ;
449+ }
450+ return null ;
451+ } ) ;
452+
453+ const caller = appRouter . createCaller ( ctx ) ;
454+ await expect (
455+ caller . judge . register ( {
456+ hackathonId,
457+ name : 'John Doe' ,
458+ email : 'john@example.com' ,
459+ } )
460+ ) . rejects . toThrowError ( 'You cannot apply to be a judge because you are registered as a participant for this hackathon.' ) ;
461+ } ) ;
462+
463+ it ( 'should prevent project submissions before 12 hours after the hacking begins' , async ( ) => {
464+ const ctx = createMockCtx ( 'captain_user_id' ) ;
465+ const hackathonId = '00000000-0000-0000-0000-000000000001' ;
466+ const teamId = '00000000-0000-0000-0000-000000000002' ;
467+
468+ const recentStartDate = new Date ( Date . now ( ) - 11 * 60 * 60 * 1000 ) ; // 11 hours ago
469+
470+ mockFindFirst . mockImplementation ( ( table ) => {
471+ if ( table === 'hackathonParticipants' ) {
472+ return { id : 'participant_1' , userId : 'captain_user_id' , hackathonId, teamId } ;
473+ }
474+ if ( table === 'hackathons' ) {
475+ return { id : hackathonId , startDate : recentStartDate , hackingStartTime : null } ;
476+ }
477+ if ( table === 'hackathonTeams' ) {
478+ return { id : teamId , captainId : 'captain_user_id' , hackathonId } ;
479+ }
480+ return null ;
481+ } ) ;
482+
483+ const caller = appRouter . createCaller ( ctx ) ;
484+ await expect (
485+ caller . team . submitProject ( {
486+ hackathonId,
487+ teamId,
488+ name : 'Awesome Project' ,
489+ description : 'This is a long description of the awesome project.' ,
490+ } )
491+ ) . rejects . toThrowError ( 'Project submission is not open yet. It starts 12 hours after the hacking begins.' ) ;
492+ } ) ;
493+
494+ it ( 'should prevent project edits (existing project) after 34 hours of starting hacking' , async ( ) => {
495+ const ctx = createMockCtx ( 'captain_user_id' ) ;
496+ const hackathonId = '00000000-0000-0000-0000-000000000001' ;
497+ const teamId = '00000000-0000-0000-0000-000000000002' ;
498+
499+ const startDate35hAgo = new Date ( Date . now ( ) - 35 * 60 * 60 * 1000 ) ; // 35 hours ago
500+
501+ mockFindFirst . mockImplementation ( ( table ) => {
502+ if ( table === 'hackathonParticipants' ) {
503+ return { id : 'participant_1' , userId : 'captain_user_id' , hackathonId, teamId } ;
504+ }
505+ if ( table === 'hackathons' ) {
506+ return { id : hackathonId , startDate : startDate35hAgo , hackingStartTime : null } ;
507+ }
508+ if ( table === 'hackathonTeams' ) {
509+ return { id : teamId , captainId : 'captain_user_id' , hackathonId } ;
510+ }
511+ if ( table === 'hackathonProjects' ) {
512+ return { id : 'project_1' , hackathonId, teamId, name : 'Old Name' , description : 'Old Description' } ;
513+ }
514+ return null ;
515+ } ) ;
516+
517+ const caller = appRouter . createCaller ( ctx ) ;
518+ await expect (
519+ caller . team . submitProject ( {
520+ hackathonId,
521+ teamId,
522+ name : 'Awesome Project' ,
523+ description : 'This is a long description of the awesome project.' ,
524+ } )
525+ ) . rejects . toThrowError ( 'Project edits are closed. Devposts must be final 34 hours after the hacking starts.' ) ;
526+ } ) ;
527+
528+ it ( 'should prevent project submissions more than 36 hours after hacking starts' , async ( ) => {
529+ const ctx = createMockCtx ( 'captain_user_id' ) ;
530+ const hackathonId = '00000000-0000-0000-0000-000000000001' ;
531+ const teamId = '00000000-0000-0000-0000-000000000002' ;
532+
533+ const pastStartDate = new Date ( Date . now ( ) - 37 * 60 * 60 * 1000 ) ; // 37 hours ago
534+
535+ mockFindFirst . mockImplementation ( ( table ) => {
536+ if ( table === 'hackathonParticipants' ) {
537+ return { id : 'participant_1' , userId : 'captain_user_id' , hackathonId, teamId } ;
538+ }
539+ if ( table === 'hackathons' ) {
540+ return { id : hackathonId , startDate : pastStartDate , hackingStartTime : null } ;
541+ }
542+ if ( table === 'hackathonTeams' ) {
543+ return { id : teamId , captainId : 'captain_user_id' , hackathonId } ;
544+ }
545+ return null ;
546+ } ) ;
547+
548+ const caller = appRouter . createCaller ( ctx ) ;
549+ await expect (
550+ caller . team . submitProject ( {
551+ hackathonId,
552+ teamId,
553+ name : 'Awesome Project' ,
554+ description : 'This is a long description of the awesome project.' ,
555+ } )
556+ ) . rejects . toThrowError ( 'Project submission closed. The submission window ended 36 hours after the hacking started.' ) ;
557+ } ) ;
558+ } ) ;
559+
404560} ) ;
0 commit comments