diff --git a/.mocharc.yml b/.mocharc.yml index 93751be6..0902b793 100644 --- a/.mocharc.yml +++ b/.mocharc.yml @@ -6,4 +6,4 @@ spec: 'tests/**/*.test.ts' watch-extensions: ts -timeout: 10000 +timeout: 30000 diff --git a/src/controllers/admin/tournaments/updateTournament.ts b/src/controllers/admin/tournaments/updateTournament.ts index fd3c2f8e..44976370 100644 --- a/src/controllers/admin/tournaments/updateTournament.ts +++ b/src/controllers/admin/tournaments/updateTournament.ts @@ -2,7 +2,7 @@ import Joi from 'joi'; import { Request, Response, NextFunction } from 'express'; import { hasPermission } from '../../../middlewares/authentication'; import { validateBody } from '../../../middlewares/validation'; -import { notFound, success } from '../../../utils/responses'; +import { conflict, notFound, success } from '../../../utils/responses'; import { Error, Permission } from '../../../types'; import { fetchTournaments, updateTournament } from '../../../operations/tournament'; import { addCasterToTournament, removeAllCastersFromTournament } from '../../../operations/caster'; @@ -30,16 +30,27 @@ export default [ // Controller async (request: Request, response: Response, next: NextFunction) => { try { - if (!(await fetchTournaments()).some((tournament) => tournament.id === request.params.tournamentId)) { + const allTournaments = await fetchTournaments(); + if (!allTournaments.some((tournament) => tournament.id === request.params.tournamentId)) { return notFound(response, Error.NotFound); } + if ( + request.body.name && + allTournaments.some( + (tournament) => tournament.name === request.body.name && tournament.id !== request.params.tournamentId, + ) + ) { + return conflict(response, Error.TournamentNameAlreadyExists); + } + if (request.body.casters && request.body.casters.length > 0) { await removeAllCastersFromTournament(request.params.tournamentId as string); for (const casterName of request.body.casters) { await addCasterToTournament(request.params.tournamentId as string, casterName); } + request.body.casters = undefined; } const result = await updateTournament(request.params.tournamentId as string, request.body); diff --git a/src/controllers/teams/acceptRequest.ts b/src/controllers/teams/acceptRequest.ts index 075cc4cc..ae899ec2 100644 --- a/src/controllers/teams/acceptRequest.ts +++ b/src/controllers/teams/acceptRequest.ts @@ -34,6 +34,10 @@ export default [ return forbidden(response, Error.TeamFull); } + if (team.coaches.length >= Math.min(tournament.playersPerTeam, 2) && askingUser.type === UserType.coach) { + return forbidden(response, Error.TeamMaxCoachReached); + } + await joinTeam(team.id, askingUser, askingUser.type); const updatedTeam = await fetchTeam(team.id); diff --git a/src/controllers/teams/createTeamRequest.ts b/src/controllers/teams/createTeamRequest.ts index 467f6d65..2fb06141 100644 --- a/src/controllers/teams/createTeamRequest.ts +++ b/src/controllers/teams/createTeamRequest.ts @@ -47,9 +47,6 @@ export default [ return success(response, filterUser(updatedUser)); } catch (error) { - // This may happen when max coach amount is reached already - if (error.code === 'API_COACH_MAX_TEAM') return forbidden(response, ResponseError.TeamMaxCoachReached); - return next(error); } }, diff --git a/src/operations/team.ts b/src/operations/team.ts index 2af3892d..78a84333 100644 --- a/src/operations/team.ts +++ b/src/operations/team.ts @@ -11,12 +11,10 @@ import { PrimitiveTeamWithPartialTournament, } from '../types'; import nanoid from '../utils/nanoid'; -import { countCoaches, formatUser, userInclusions } from './user'; +import { formatUser, userInclusions } from './user'; import { setupDiscordTeam } from '../utils/discord'; import { fetchTournament } from './tournament'; -const teamMaxCoachCount = 2; - const teamInclusions = { users: { include: userInclusions, @@ -205,13 +203,6 @@ export const updateTeam = async (teamId: string, name: string): Promise => }; export const askJoinTeam = async (teamId: string, userId: string, userType: UserType) => { - // We check the amount of coaches at that point - const teamCoachCount = await countCoaches(teamId); - if (userType === UserType.coach && teamCoachCount >= teamMaxCoachCount) - throw Object.assign(new Error('Query cannot be executed: max count of coach reached already'), { - code: 'API_COACH_MAX_TEAM', - }); - // Then we create the join request when it is alright const updatedUser = await database.user.update({ data: { diff --git a/src/operations/tournament.ts b/src/operations/tournament.ts index 3ee3ff77..ddc89beb 100644 --- a/src/operations/tournament.ts +++ b/src/operations/tournament.ts @@ -71,7 +71,7 @@ export const updateTournament = ( ): PrismaPromise => database.tournament.update({ where: { id }, - data: { ...data, casters: undefined }, + data: { ...data }, }); export const updateTournamentsPosition = (tournaments: { id: string; position: number }[]) => diff --git a/src/types.ts b/src/types.ts index 254ca273..84b4c6e6 100755 --- a/src/types.ts +++ b/src/types.ts @@ -343,6 +343,7 @@ export const enum Error { TeamAlreadyExists = "Le nom de l'équipe existe déjà", PlaceAlreadyAttributed = 'Cette place est déjà attribuée', DiscordAccountAlreadyUsed = 'Ce compte discord est déjà lié à un compte', + TournamentNameAlreadyExists = 'Un tournoi a déjà ce nom', // 410 // indicates that access to the target resource is no longer available at the server. diff --git a/tests/admin/carts/refund.test.ts b/tests/admin/carts/refund.test.ts index 527eef39..13f49d2e 100644 --- a/tests/admin/carts/refund.test.ts +++ b/tests/admin/carts/refund.test.ts @@ -11,11 +11,7 @@ import * as userOperations from '../../../src/operations/user'; import { sandbox } from '../../setup'; import { generateToken } from '../../../src/utils/users'; -// eslint-disable-next-line func-names -describe('POST /admin/carts/:cartId/refund', function () { - // Setup is slow - this.timeout(30000); - +describe('POST /admin/carts/:cartId/refund', () => { let user: User; let admin: User; let adminToken: string; diff --git a/tests/admin/tournaments/updateTournament.test.ts b/tests/admin/tournaments/updateTournament.test.ts index 5c4a4bf8..878cd65e 100644 --- a/tests/admin/tournaments/updateTournament.test.ts +++ b/tests/admin/tournaments/updateTournament.test.ts @@ -15,7 +15,7 @@ describe('PATCH /admin/tournaments/{tournamentId}', () => { let tournament: Tournament; const validBody = { - name: 'test', + name: 'anothertestname', maxPlayers: 100, playersPerTeam: 5, cashprize: 100, @@ -39,7 +39,7 @@ describe('PATCH /admin/tournaments/{tournamentId}', () => { nonAdminUser = await createFakeUser(); adminToken = generateToken(admin); - tournament = await createFakeTournament({ id: 'test', name: 'Dragibus', maxTeams: 1, playersPerTeam: 1 }); + tournament = await createFakeTournament({ id: 'test', name: 'test', playersPerTeam: 1, maxTeams: 1 }); }); it('should error as the user is not authenticated', () => @@ -75,6 +75,14 @@ describe('PATCH /admin/tournaments/{tournamentId}', () => { .expect(404, { error: Error.NotFound }); }); + it('should fail as a tournament already has this name', async () => { + await request(app) + .patch(`/admin/tournaments/${tournament.id}`) + .send({ name: 'Pokémon' }) + .set('Authorization', `Bearer ${adminToken}`) + .expect(409, { error: Error.TournamentNameAlreadyExists }); + }); + it('should successfully update the tournament', async () => { await request(app) .patch(`/admin/tournaments/${tournament.id}`) diff --git a/tests/admin/users/forcePay.test.ts b/tests/admin/users/forcePay.test.ts index 27c32c7c..c2ebbfe1 100644 --- a/tests/admin/users/forcePay.test.ts +++ b/tests/admin/users/forcePay.test.ts @@ -12,11 +12,7 @@ import * as teamOperations from '../../../src/operations/team'; import * as userOperations from '../../../src/operations/user'; import * as tournamentOperations from '../../../src/operations/tournament'; -// eslint-disable-next-line func-names -describe('POST /admin/users/:userId/force-pay', function () { - // The setup is slow - this.timeout(30000); - +describe('POST /admin/users/:userId/force-pay', () => { let user: User; let admin: User; let adminToken: string; diff --git a/tests/etupay/callback.test.ts b/tests/etupay/callback.test.ts index dd21ec07..4a7be43f 100644 --- a/tests/etupay/callback.test.ts +++ b/tests/etupay/callback.test.ts @@ -77,11 +77,7 @@ const createCartAndPayload = async ( }; }; -// eslint-disable-next-line func-names -describe('POST /etupay/callback', function () { - // The setup is slow - this.timeout(30000); - +describe('POST /etupay/callback', () => { let cart: Cart; let paidPayload: string; let refusedPayload: string; diff --git a/tests/teams/acceptRequest.test.ts b/tests/teams/acceptRequest.test.ts index ef4c3306..928529a6 100644 --- a/tests/teams/acceptRequest.test.ts +++ b/tests/teams/acceptRequest.test.ts @@ -11,17 +11,20 @@ import { Error, Team, User, UserType } from '../../src/types'; import { createFakeUser, createFakeTeam } from '../utils'; import { generateToken } from '../../src/utils/users'; import { getCaptain } from '../../src/utils/teams'; +import { fetchUser } from '../../src/operations/user'; -// eslint-disable-next-line func-names -describe('POST /teams/current/join-requests/:userId', function () { - // Setup is slow - this.timeout(30000); - +describe('POST /teams/current/join-requests/:userId', () => { let user: User; let user2: User; let team: Team; let captain: User; let token: string; + let fullTeam: Team; + let fullCaptain: User; + let fullToken: string; + let onePlayerTeam: Team; + let onePlayerTeamCaptain: User; + let onePlayerTeamToken: string; before(async () => { const tournament = await tournamentOperations.fetchTournament('lol'); @@ -30,6 +33,9 @@ describe('POST /teams/current/join-requests/:userId', function () { user2 = await createFakeUser({ paid: true, type: UserType.player }); await teamOperations.askJoinTeam(team.id, user.id, UserType.player); await teamOperations.askJoinTeam(team.id, user2.id, UserType.player); + fullTeam = await createFakeTeam({ members: tournament.playersPerTeam, paid: true }); + fullCaptain = getCaptain(fullTeam); + fullToken = generateToken(fullCaptain); // Fill the tournament // Store the promises const promises = []; @@ -42,6 +48,10 @@ describe('POST /teams/current/join-requests/:userId', function () { captain = getCaptain(team); token = generateToken(captain); + + onePlayerTeam = await createFakeTeam({ tournament: 'pokemon' }); + onePlayerTeamCaptain = getCaptain(onePlayerTeam); + onePlayerTeamToken = generateToken(onePlayerTeamCaptain); }); after(async () => { @@ -101,11 +111,7 @@ describe('POST /teams/current/join-requests/:userId', function () { }); it('should fail as the team is full', async () => { - const fullTeam = await createFakeTeam({ members: 5 }); const otherUser = await createFakeUser(); - - const fullCaptain = getCaptain(fullTeam); - const fullToken = generateToken(fullCaptain); await teamOperations.askJoinTeam(fullTeam.id, otherUser.id, UserType.player); await request(app) @@ -115,17 +121,56 @@ describe('POST /teams/current/join-requests/:userId', function () { }); it('should succeed to join a full team as a coach', async () => { - const fullTeam = await createFakeTeam({ members: 5 }); const otherUser = await createFakeUser(); - const fullCaptain = getCaptain(fullTeam); - const fullToken = generateToken(fullCaptain); await teamOperations.askJoinTeam(fullTeam.id, otherUser.id, UserType.coach); await request(app) .post(`/teams/current/join-requests/${otherUser.id}`) .set('Authorization', `Bearer ${fullToken}`) .expect(200); + + const databaseUser = await fetchUser(otherUser.id); + expect(databaseUser.teamId).to.be.equal(fullTeam.id); + }); + + it('should fail to join the team as a coach because there are already 2 coaches', async () => { + // There is only one coach for the moment + const coach = await createFakeUser(); + await teamOperations.joinTeam(fullTeam.id, coach, UserType.coach); + const willNotJoinCoach = await createFakeUser(); + await teamOperations.askJoinTeam(fullTeam.id, willNotJoinCoach.id, UserType.coach); + await request(app) + .post(`/teams/current/join-requests/${willNotJoinCoach.id}`) + .set('Authorization', `Bearer ${fullToken}`) + .expect(403, { error: Error.TeamMaxCoachReached }); + + const databaseCoach = await fetchUser(willNotJoinCoach.id); + expect(databaseCoach.teamId).to.be.null; + }); + + it('should successfully join the 1-player team as a coach', async () => { + const coach = await createFakeUser(); + await teamOperations.askJoinTeam(onePlayerTeam.id, coach.id, UserType.coach); + await request(app) + .post(`/teams/current/join-requests/${coach.id}`) + .set('Authorization', `Bearer ${onePlayerTeamToken}`) + .expect(200); + + const databaseCoach = await fetchUser(coach.id); + expect(databaseCoach.teamId).to.be.equal(onePlayerTeam.id); + }); + + it('should fail to join the 1-player team as a coach because there is already a coach', async () => { + const coach = await createFakeUser(); + await teamOperations.askJoinTeam(onePlayerTeam.id, coach.id, UserType.coach); + await request(app) + .post(`/teams/current/join-requests/${coach.id}`) + .set('Authorization', `Bearer ${onePlayerTeamToken}`) + .expect(403, { error: Error.TeamMaxCoachReached }); + + const databaseCoach = await fetchUser(coach.id); + expect(databaseCoach.teamId).to.be.null; }); it('should successfully join the team and not lock the team as it is not full', async () => { diff --git a/tests/teams/createTeamRequest.test.ts b/tests/teams/createTeamRequest.test.ts index 7b5921b5..f9cafad9 100644 --- a/tests/teams/createTeamRequest.test.ts +++ b/tests/teams/createTeamRequest.test.ts @@ -54,39 +54,6 @@ describe('POST /teams/:teamId/join-requests', () => { .expect(403, { error: Error.AlreadyInTeam }); }); - it('should fail because coach limit is already reached (coach team members)', async () => { - const otherTeam = await createFakeTeam({ members: 2 }); - const [user1, user2] = otherTeam.players; - await updateAdminUser(user1.id, { type: UserType.coach }); - await updateAdminUser(user2.id, { type: UserType.coach }); - - const otherCoach = await createFakeUser(); - const otherToken = generateToken(otherCoach); - - return request(app) - .post(`/teams/${otherTeam.id}/join-requests`) - .send({ userType: UserType.coach }) - .set('Authorization', `Bearer ${otherToken}`) - .expect(403, { error: Error.TeamMaxCoachReached }); - }); - - it('should fail because coach limit is already reached (coach team member requests)', async () => { - const otherTeam = await createFakeTeam(); - const [user1] = otherTeam.players; - const user2 = await createFakeUser(); - await updateAdminUser(user1.id, { type: UserType.coach }); - await teamOperations.askJoinTeam(otherTeam.id, user2.id, UserType.coach); - - const otherCoach = await createFakeUser(); - const otherToken = generateToken(otherCoach); - - return request(app) - .post(`/teams/${otherTeam.id}/join-requests`) - .send({ userType: UserType.coach }) - .set('Authorization', `Bearer ${otherToken}`) - .expect(403, { error: Error.TeamMaxCoachReached }); - }); - it('should fail because the user is a spectator', async () => { const spectator = await createFakeUser({ type: UserType.spectator }); const spectatorToken = generateToken(spectator); diff --git a/tests/teams/deleteTeam.test.ts b/tests/teams/deleteTeam.test.ts index f4de9082..9773829f 100644 --- a/tests/teams/deleteTeam.test.ts +++ b/tests/teams/deleteTeam.test.ts @@ -11,11 +11,7 @@ import { createFakeTeam, createFakeUser } from '../utils'; import { generateToken } from '../../src/utils/users'; import { getCaptain } from '../../src/utils/teams'; -// eslint-disable-next-line func-names -describe('DELETE /teams/current', function () { - // Setup is slow - this.timeout(30000); - +describe('DELETE /teams/current', () => { let captain: User; let team: Team; let lockedTeam: Team;