diff --git a/backend/src/chat/chat.gateway.ts b/backend/src/chat/chat.gateway.ts index 2c115283..b9bd7610 100644 --- a/backend/src/chat/chat.gateway.ts +++ b/backend/src/chat/chat.gateway.ts @@ -78,7 +78,7 @@ export class ChatGateway { @SubscribeMessage('request-match') async handleRequestMatch( - @MessageBody() data: { userId: number }, + @MessageBody() data: { requestedUserId: number }, @ConnectedSocket() client: Socket, ) { // Check if the requesting user is valid @@ -88,27 +88,31 @@ export class ChatGateway { return; } // Check if the requested user is connected - const requestedUserWsId = this.chatService.getWsFromUserId(data.userId)?.id; + const requestedUserWsId = this.chatService.getWsFromUserId( + data.requestedUserId, + )?.id; if (!requestedUserWsId) { this.logger.error('invalid requested user'); return; } // Check if the requesting user is blocked by the requested user - const blockings = await this.chatService.getUsersBlockedBy(data.userId); + const blockings = await this.chatService.getUsersBlockedBy( + data.requestedUserId, + ); if (blockings.some((user) => user.id === requestingUser.id)) return; // Check if the requested user is blocked by the requesting user const blocked = await this.chatService.getUsersBlockedBy(requestingUser.id); - if (blocked.some((user) => user.id === data.userId)) return; + if (blocked.some((user) => user.id === data.requestedUserId)) return; // Send the request this.server .to(requestedUserWsId) - .emit('request-match', { userId: requestingUser.id }); + .emit('request-match', { requestingUserId: requestingUser.id }); // Save the request - this.chatService.addMatchRequest(requestingUser.id, data.userId); + this.chatService.addMatchRequest(requestingUser.id, data.requestedUserId); } - @SubscribeMessage('cancel-request-match') - handleCancelRequestMatch(@ConnectedSocket() client: Socket) { + @SubscribeMessage('cancel-match-request') + handleCancelMatchRequest(@ConnectedSocket() client: Socket) { // Check if the requesting user is valid const requestingUser = this.chatService.getUser(client); if (!requestingUser) { @@ -119,7 +123,9 @@ export class ChatGateway { const requestedUser = this.chatService.getMatchRequest(requestingUser.id); if (!requestedUser) { this.logger.error('invalid requested user'); - this.server.to(client.id).emit('error-pong', 'No pending invite found.'); + this.server + .to(client.id) + .emit('invalid-request', 'No pending invite found.'); return; } // Cancel the request @@ -133,54 +139,58 @@ export class ChatGateway { // Send the cancel request this.server .to(requestedUserWsId) - .emit('cancel-request-match', requestingUser); + .emit('cancelled-match-request', requestingUser); } - @SubscribeMessage('approve-pong') - async handleApprovePong( - @MessageBody() data: { userId: number }, + @SubscribeMessage('approve-match-request') + async handleApproveMatchRequest( + @MessageBody() data: { approvedUserId: number }, @ConnectedSocket() client: Socket, ) { - const approvedUserWsId = this.chatService.getWsFromUserId(data.userId)?.id; + const approvedUserWsId = this.chatService.getWsFromUserId( + data.approvedUserId, + )?.id; if (!approvedUserWsId) { return; } else { if ( - this.chatService.getMatchRequest(data.userId) !== + this.chatService.getMatchRequest(data.approvedUserId) !== this.chatService.getUserId(client) ) { this.server .to(client.id) - .emit('error-pong', 'No pending invite found.'); + .emit('invalid-request', 'No pending invite found.'); return; } const emitData = { roomId: v4() }; - this.server.to(client.id).emit('match-pong', emitData); - this.server.to(approvedUserWsId).emit('match-pong', emitData); - this.chatService.removeMatchRequest(data.userId); + this.server.to(client.id).emit('approved-match-request', emitData); + this.server.to(approvedUserWsId).emit('approved-match-request', emitData); + this.chatService.removeMatchRequest(data.approvedUserId); } } - @SubscribeMessage('deny-pong') - handleDenyPong( - @MessageBody() data: { userId: number }, + @SubscribeMessage('deny-match-request') + handleDenyMatchRequest( + @MessageBody() data: { deniedUserId: number }, @ConnectedSocket() client: Socket, ) { - const deniedUserWsId = this.chatService.getWsFromUserId(data.userId)?.id; + const deniedUserWsId = this.chatService.getWsFromUserId( + data.deniedUserId, + )?.id; if (!deniedUserWsId) { return; } else { if ( - this.chatService.getMatchRequest(data.userId) !== + this.chatService.getMatchRequest(data.deniedUserId) !== this.chatService.getUserId(client) ) { this.server .to(client.id) - .emit('error-pong', 'No pending invite found.'); + .emit('invalid-request', 'No pending invite found.'); return; } - this.server.to(deniedUserWsId).emit('deny-pong'); - this.chatService.removeMatchRequest(data.userId); + this.server.to(deniedUserWsId).emit('denied-match-request'); + this.chatService.removeMatchRequest(data.deniedUserId); } } diff --git a/backend/test/chat-gateway.e2e-spec.ts b/backend/test/chat-gateway.e2e-spec.ts index 9f244bc0..b6edf66b 100644 --- a/backend/test/chat-gateway.e2e-spec.ts +++ b/backend/test/chat-gateway.e2e-spec.ts @@ -1398,7 +1398,7 @@ describe('ChatGateway and ChatController (e2e)', () => { describe('[joinDM]', () => { // TODO }); - describe('invite pong game', () => { + describe('Tests on requests to users of pong matches', () => { let userAndSockets: UserAndSocket[]; beforeAll(() => { @@ -1422,35 +1422,37 @@ describe('ChatGateway and ChatController (e2e)', () => { us.ws.connect(); }); }); - describe('invite a user', () => { + describe('Request match', () => { describe('success case', () => { - let invite: UserAndSocket; - let invited: UserAndSocket; - let notInvited: UserAndSocket; + let requestingUserAndSockets: UserAndSocket; + let requestedUserAndSockets: UserAndSocket; + let unrequestedUserAndSockets: UserAndSocket; let ctx1: Promise; const mockCallback = jest.fn(); beforeAll(() => { - invite = userAndSockets[0]; - invited = userAndSockets[1]; - notInvited = userAndSockets[2]; + requestingUserAndSockets = userAndSockets[0]; + requestedUserAndSockets = userAndSockets[1]; + unrequestedUserAndSockets = userAndSockets[2]; ctx1 = new Promise((resolve) => - invited.ws.on('request-match', (data) => resolve(data)), + requestedUserAndSockets.ws.on('request-match', (data) => + resolve(data), + ), ); - notInvited.ws.on('request-match', mockCallback); + unrequestedUserAndSockets.ws.on('request-match', mockCallback); - invite.ws.emit('request-match', { - userId: invited.user.id, + requestingUserAndSockets.ws.emit('request-match', { + requestedUserId: requestedUserAndSockets.user.id, }); ctx1.then((data) => { expect(data).toEqual({ - userId: invite.user.id, + requestingUserId: requestingUserAndSockets.user.id, }); }); }); - it('user who is invited should receive invite message', () => ctx1); - it("user who isn't invited should not receive invite message", () => + it('reqested user should receive match request', () => ctx1); + it('unrelated user should not receive match request', () => new Promise((resolve) => setTimeout(() => { expect(mockCallback).not.toBeCalled(); @@ -1458,38 +1460,38 @@ describe('ChatGateway and ChatController (e2e)', () => { }, waitTime), )); }); - // TODO: block してるuser から invite されるケース + // TODO: block してるuser から request されるケース describe('failure case', () => { - let invitee; + let requestedUserAndSockets: UserAndSocket; let blocked; let mockCallback: jest.Mock; beforeAll(async () => { mockCallback = jest.fn(); - invitee = userAndSockets[0]; + requestedUserAndSockets = userAndSockets[0]; blocked = userAndSockets[1]; await app .blockUser( - invitee.user.id, + requestedUserAndSockets.user.id, blocked.user.id, - invitee.user.accessToken, + requestedUserAndSockets.user.accessToken, ) .expect(200); - invitee.ws.on('request-match', mockCallback); + requestedUserAndSockets.ws.on('request-match', mockCallback); blocked.ws.emit('request-match', { - userId: invitee.user.id, + requestedUserId: requestedUserAndSockets.user.id, }); }); afterAll(async () => { await app .unblockUser( - invitee.user.id, + requestedUserAndSockets.user.id, blocked.user.id, - invitee.user.accessToken, + requestedUserAndSockets.user.accessToken, ) .expect(200); }); - it('user should not receive invite message from blocking user', () => + it('user should not receive match request message from blocking user', () => new Promise((resolve) => setTimeout(async () => { expect(mockCallback).not.toHaveBeenCalled(); @@ -1497,28 +1499,31 @@ describe('ChatGateway and ChatController (e2e)', () => { }, waitTime), )); }); - describe('invite -> cancel -> invite', () => { - let invitee; - let inviter; + describe('Request match -> Cancel match request -> Request match', () => { + let requestedUserAndSockets: UserAndSocket; + let requestingUserAndSockets: UserAndSocket; let mockCallback; beforeAll(() => { mockCallback = jest.fn(); - invitee = userAndSockets[0]; - inviter = userAndSockets[1]; + requestedUserAndSockets = userAndSockets[0]; + requestingUserAndSockets = userAndSockets[1]; - invitee.ws.on('request-match', mockCallback); - inviter.ws.emit('request-match', { - userId: invitee.user.id, + requestedUserAndSockets.ws.on('request-match', mockCallback); + // First request + requestingUserAndSockets.ws.emit('request-match', { + requestedUserId: requestedUserAndSockets.user.id, }); - inviter.ws.emit('cancel-request-match', { - userId: invitee.user.id, + // Cancel request + requestingUserAndSockets.ws.emit('cancel-match-request', { + requestedUserId: requestedUserAndSockets.user.id, }); - inviter.ws.emit('request-match', { - userId: invitee.user.id, + // Second request after cancel + requestingUserAndSockets.ws.emit('request-match', { + requestedUserId: requestedUserAndSockets.user.id, }); }); - it('user who is invited should receive invite message once per time', () => + it('user who is requested should receive match request message once per time', () => new Promise((resolve) => setTimeout(() => { expect(mockCallback).toHaveBeenCalledTimes(2); @@ -1527,47 +1532,59 @@ describe('ChatGateway and ChatController (e2e)', () => { )); }); }); - describe('approve invite', () => { + describe('approve match request', () => { describe('success case', () => { - let PromiseToMatchByInviter: Promise; - let PromiseToMatchByInvited: Promise; + let promiseToMatchByRequestingUser: Promise; + let promiseToMatchByRequestedUser: Promise; let roomId; const mockCallback1 = jest.fn(); beforeAll(() => { - const inviter = userAndSockets[0]; - const invitee = userAndSockets[1]; - const notInvited1 = userAndSockets[2]; - - const promiseToInvite = new Promise((resolve) => - invitee.ws.on('request-match', (data) => resolve(data)), + const requestingUserAndSockets = userAndSockets[0]; + const requestedUserAndSockets = userAndSockets[1]; + const unrequestedUserAndSockets = userAndSockets[2]; + + const promiseToRequest = new Promise((resolve) => + requestedUserAndSockets.ws.on('request-match', (data) => + resolve(data), + ), ); - PromiseToMatchByInviter = new Promise((resolve) => - inviter.ws.on('match-pong', (data) => resolve(data)), + promiseToMatchByRequestingUser = new Promise((resolve) => + requestingUserAndSockets.ws.on('approved-match-request', (data) => + resolve(data), + ), ); - PromiseToMatchByInvited = new Promise((resolve) => - invitee.ws.on('match-pong', (data) => resolve(data)), + promiseToMatchByRequestedUser = new Promise((resolve) => + requestedUserAndSockets.ws.on('approved-match-request', (data) => + resolve(data), + ), ); - notInvited1.ws.on('request-match', mockCallback1); - notInvited1.ws.on('approve-pong', mockCallback1); - notInvited1.ws.on('match-pong', mockCallback1); + unrequestedUserAndSockets.ws.on('request-match', mockCallback1); + unrequestedUserAndSockets.ws.on( + 'approve-match-request', + mockCallback1, + ); + unrequestedUserAndSockets.ws.on( + 'approved-match-request', + mockCallback1, + ); - inviter.ws.emit('request-match', { - userId: invitee.user.id, + requestingUserAndSockets.ws.emit('request-match', { + requestedUserId: requestedUserAndSockets.user.id, }); - return promiseToInvite.then((data) => { - invitee.ws.emit('approve-pong', { - userId: data.userId, + return promiseToRequest.then((data) => { + requestedUserAndSockets.ws.emit('approve-match-request', { + approvedUserId: data.requestingUserId, }); }); }); - it("invite user should receive room's id", () => - PromiseToMatchByInviter.then((data) => { + it("requesting user should receive room's id", () => + promiseToMatchByRequestingUser.then((data) => { expect(data).toHaveProperty('roomId'); roomId = data.roomId; })); it("approve user should receive room's id", () => - PromiseToMatchByInvited.then((data) => { + promiseToMatchByRequestedUser.then((data) => { expect(data).toHaveProperty('roomId'); expect(data.roomId).toEqual(roomId); })); @@ -1588,20 +1605,20 @@ describe('ChatGateway and ChatController (e2e)', () => { const emitter = userAndSockets[0]; const listener = userAndSockets[1]; - emitter.ws.on('match-pong', mockCallback1); + emitter.ws.on('approved-match-request', mockCallback1); errorCtx = new Promise((resolve) => - emitter.ws.on('error-pong', (data) => resolve(data)), + emitter.ws.on('invalid-request', (data) => resolve(data)), ); - listener.ws.on('match-pong', mockCallback2); + listener.ws.on('approved-match-request', mockCallback2); - emitter.ws.emit('approve-pong', { - userId: listener.user.id, + emitter.ws.emit('approve-match-request', { + approvedUserId: listener.user.id, }); }); - // TODO: 複数のuser から invite されるケース - it('should receive an error when approving without an existing invite', () => + // TODO: 複数のuser から request されるケース + it('should receive an error when approving without an existing request', () => errorCtx); - it('user should not receive approve message from not invite user', () => + it('user should not receive approve message from not requested user', () => new Promise((resolve) => setTimeout(() => { expect(mockCallback1).not.toHaveBeenCalled(); @@ -1610,7 +1627,7 @@ describe('ChatGateway and ChatController (e2e)', () => { }, waitTime), )); }); - describe('invite -> cancel -> approve: dose not match', () => { + describe('Request match -> Cancel match request -> approve: no match', () => { const mockToMatchByEmitter = jest.fn(); const mockToMatchByListener = jest.fn(); @@ -1618,28 +1635,30 @@ describe('ChatGateway and ChatController (e2e)', () => { const emitter = userAndSockets[0]; const listener = userAndSockets[1]; - emitter.ws.on('match-pong', mockToMatchByEmitter); + emitter.ws.on('approved-match-request', mockToMatchByEmitter); - const PromiseToInvite = new Promise((resolve) => + const promiseToRequest = new Promise((resolve) => listener.ws.on('request-match', (data) => resolve(data)), ); - listener.ws.on('match-pong', mockToMatchByListener); - + listener.ws.on('approved-match-request', mockToMatchByListener); + // Request match emitter.ws.emit('request-match', { - userId: listener.user.id, + requestedUserId: listener.user.id, }); - return PromiseToInvite.then((data) => { - emitter.ws.emit('cancel-request-match', { - userId: data.userId, + // Cancel request + return promiseToRequest.then((data) => { + emitter.ws.emit('cancel-match-request', { + requestedUserId: data.requestedUserId, }); + // Approve request after cancel setTimeout(() => { - listener.ws.emit('approve-pong', { - userId: data.userId, + listener.ws.emit('approve-match-request', { + approvedUserId: data.requestingUserId, }); }, waitTime); }); }); - it('user should not receive match message from canceled invite user', () => + it('user should not receive match message from canceled match request user', () => new Promise((resolve) => setTimeout(() => { expect(mockToMatchByEmitter).not.toHaveBeenCalled(); @@ -1649,36 +1668,43 @@ describe('ChatGateway and ChatController (e2e)', () => { )); }); }); - describe('deny invite', () => { + describe('deny match request', () => { describe('success case', () => { const mockCallback1 = jest.fn(); const mockCallback2 = jest.fn(); let ctxToDeny: Promise; beforeAll(() => { - const inviter = userAndSockets[0]; - const invitee = userAndSockets[1]; - const notInvited1 = userAndSockets[2]; - - notInvited1.ws.on('request-match', mockCallback1); - notInvited1.ws.on('deny-pong', mockCallback1); + const requestingUserAndSockets = userAndSockets[0]; + const requestedUserAndSockets = userAndSockets[1]; + const unrequestedUserAndSockets = userAndSockets[2]; + + unrequestedUserAndSockets.ws.on('request-match', mockCallback1); + unrequestedUserAndSockets.ws.on( + 'denied-match-request', + mockCallback1, + ); - const promiseToInvite = new Promise((resolve) => - invitee.ws.on('request-match', (data) => resolve(data)), + const promiseToRequest = new Promise((resolve) => + requestedUserAndSockets.ws.on('request-match', (data) => + resolve(data), + ), ); - inviter.ws.emit('request-match', { - userId: invitee.user.id, + requestingUserAndSockets.ws.emit('request-match', { + requestedUserId: requestedUserAndSockets.user.id, }); ctxToDeny = new Promise((resolve) => - inviter.ws.on('deny-pong', (data) => resolve(data)), + requestingUserAndSockets.ws.on('denied-match-request', (data) => + resolve(data), + ), ); - return promiseToInvite.then((data) => { - invitee.ws.emit('deny-pong', { - userId: data.userId, + return promiseToRequest.then((data) => { + requestedUserAndSockets.ws.emit('deny-match-request', { + deniedUserId: data.requestingUserId, }); }); }); - it('inviter should receive an deny message', () => ctxToDeny); + it('requesting user should receive an deny message', () => ctxToDeny); it('unrelated user should not receive any messages', () => new Promise((resolve) => setTimeout(() => { @@ -1697,17 +1723,17 @@ describe('ChatGateway and ChatController (e2e)', () => { const emitter = userAndSockets[0]; const listener = userAndSockets[1]; - listener.ws.on('error-pong', mockCallback2); - emitter.ws.emit('deny-pong', { - userId: listener.user.id, + listener.ws.on('invalid-request', mockCallback2); + emitter.ws.emit('deny-match-request', { + deniedUserId: listener.user.id, }); errorCtx = new Promise((resolve) => - emitter.ws.on('error-pong', (data) => resolve(data)), + emitter.ws.on('invalid-request', (data) => resolve(data)), ); }); - it('should receive an error when denying without an existing invite', () => + it('should receive an error when denying without an existing request', () => errorCtx); - it('user should not receive deny message from not invite user', () => + it('user should not receive deny message from not requesting user', () => new Promise((resolve) => setTimeout(() => { expect(mockCallback1).not.toHaveBeenCalled(); @@ -1723,27 +1749,34 @@ describe('ChatGateway and ChatController (e2e)', () => { let ctxToCancel: Promise; beforeAll(() => { - const inviter = userAndSockets[0]; - const invitee = userAndSockets[1]; - const notInvited1 = userAndSockets[2]; - - notInvited1.ws.on('request-match', mockCallback1); - notInvited1.ws.on('cancel-request-match', mockCallback1); + const requestingUserAndSockets = userAndSockets[0]; + const requestedUserAndSockets = userAndSockets[1]; + const unrequestedUserAndSockets = userAndSockets[2]; + + unrequestedUserAndSockets.ws.on('request-match', mockCallback1); + unrequestedUserAndSockets.ws.on( + 'cancelled-match-request', + mockCallback1, + ); - const promiseToInvite = new Promise((resolve) => - invitee.ws.on('request-match', (data) => resolve(data)), + const promiseToRequest = new Promise((resolve) => + requestedUserAndSockets.ws.on('request-match', (data) => + resolve(data), + ), ); - inviter.ws.emit('request-match', { - userId: invitee.user.id, + requestingUserAndSockets.ws.emit('request-match', { + requestedUserId: requestedUserAndSockets.user.id, }); ctxToCancel = new Promise((resolve) => - invitee.ws.on('cancel-request-match', (data) => resolve(data)), + requestedUserAndSockets.ws.on('cancelled-match-request', (data) => + resolve(data), + ), ); - return promiseToInvite.then(() => { - inviter.ws.emit('cancel-request-match'); + return promiseToRequest.then(() => { + requestingUserAndSockets.ws.emit('cancel-match-request'); }); }); - it('invitee should receive an request-cancel message', () => + it('requested user should receive an request-cancel message', () => ctxToCancel.then((data) => { expect(data).toHaveProperty('id'); expect(data).toHaveProperty('avatarURL'); @@ -1762,11 +1795,11 @@ describe('ChatGateway and ChatController (e2e)', () => { beforeAll(() => { const canceler = userAndSockets[0]; errorCtx = new Promise((resolve) => - canceler.ws.on('error-pong', (data) => resolve(data)), + canceler.ws.on('invalid-request', (data) => resolve(data)), ); - canceler.ws.emit('cancel-request-match'); + canceler.ws.emit('cancel-match-request'); }); - it('should receive an error when canceling without an existing invite', () => + it('should receive an error when canceling without an existing request', () => errorCtx); }); }); diff --git a/frontend/app/lib/client-socket-provider.tsx b/frontend/app/lib/client-socket-provider.tsx index 4c03fd2c..ef99b58e 100644 --- a/frontend/app/lib/client-socket-provider.tsx +++ b/frontend/app/lib/client-socket-provider.tsx @@ -7,11 +7,10 @@ import { usePathname, useRouter } from "next/navigation"; import { useEffect } from "react"; import { useAuthContext } from "./client-auth"; import { - DenyEvent, - InviteEvent, - MatchEvent, + ApprovedMatchRequestEvent, MessageEvent, PublicUserEntity, + RequestMatchEvent, } from "./dtos"; export default function SocketProvider() { @@ -27,21 +26,22 @@ export default function SocketProvider() { }); }; - const MatchPong = (data: MatchEvent) => { + const goToMatch = (data: ApprovedMatchRequestEvent) => { router.push(`/pong/${data.roomId}?mode=player`); }; - const showInvitePongToast = (message: InviteEvent) => { + const showMatchRequestToast = (message: RequestMatchEvent) => { + console.log("showMatchRequestToast", message); toast({ - title: `user id: ${message.userId}`, + title: `user id: ${message.requestingUserId}`, description: ` invited you to play pong!`, action: ( <>