Skip to content

Commit

Permalink
[backend] Fix ban user from room (#207)
Browse files Browse the repository at this point in the history
* Add test to ensure messages from banned users are not received
* Add test to ensure banned users don't receive messages
* Fix to leave from the socket room when the ban API is called and succeeds
  • Loading branch information
lim396 authored Jan 13, 2024
1 parent e655e2a commit 700edea
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 11 deletions.
3 changes: 2 additions & 1 deletion backend/src/room/ban/ban.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { EventEmitter2 } from '@nestjs/event-emitter';
import { Test, TestingModule } from '@nestjs/testing';
import { PrismaService } from 'src/prisma/prisma.service';
import { BanController } from './ban.controller';
Expand All @@ -9,7 +10,7 @@ describe('BanController', () => {
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [BanController],
providers: [BanService, PrismaService],
providers: [BanService, PrismaService, EventEmitter2],
}).compile();

controller = module.get<BanController>(BanController);
Expand Down
3 changes: 2 additions & 1 deletion backend/src/room/ban/ban.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { EventEmitter2 } from '@nestjs/event-emitter';
import { Test, TestingModule } from '@nestjs/testing';
import { PrismaService } from 'src/prisma/prisma.service';
import { BanService } from './ban.service';
Expand All @@ -7,7 +8,7 @@ describe('BanService', () => {

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [BanService, PrismaService],
providers: [BanService, PrismaService, EventEmitter2],
}).compile();

service = module.get<BanService>(BanService);
Expand Down
29 changes: 21 additions & 8 deletions backend/src/room/ban/ban.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@ import {
Injectable,
NotFoundException,
} from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { Role } from '@prisma/client';
import { RoomLeftEvent } from 'src/common/events/room-left.event';
import { PrismaService } from 'src/prisma/prisma.service';

@Injectable()
export class BanService {
constructor(private prisma: PrismaService) {}
constructor(
private prisma: PrismaService,
private eventEmitter: EventEmitter2,
) {}

async create(roomId: number, userId: number) {
await this.prisma.$transaction(async (prisma) => {
Expand All @@ -33,14 +38,22 @@ export class BanService {
if (user.role === Role.OWNER) {
throw new ForbiddenException('Cannot ban owner');
}
await prisma.userOnRoom.delete({
where: {
userId_roomId_unique: {
userId: userId,
roomId: roomId,
await prisma.userOnRoom
.delete({
where: {
userId_roomId_unique: {
userId: userId,
roomId: roomId,
},
},
},
});
})
.then(() => {
const event: RoomLeftEvent = {
roomId: roomId,
userId: userId,
};
this.eventEmitter.emit('room.leave', event);
});
}
await prisma.banUserOnRoom.create({
data: {
Expand Down
143 changes: 142 additions & 1 deletion backend/test/chat-gateway.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ describe('ChatGateway and ChatController (e2e)', () => {
let ws3: Socket; // Client socket 3
let ws4: Socket; // Client socket 4
let ws5: Socket; // Client socket 5
let user1, user2, blockedUser1, blockedUser2, kickedUser1;
let ws6: Socket; // Client socket 6
let user1, user2, blockedUser1, blockedUser2, kickedUser1, bannedUser1;

beforeAll(async () => {
//app = await initializeApp();
Expand Down Expand Up @@ -54,11 +55,17 @@ describe('ChatGateway and ChatController (e2e)', () => {
email: '[email protected]',
password: 'test-password',
};
const dto6 = {
name: 'banned-user1',
email: '[email protected]',
password: 'test-password',
};
user1 = await app.createAndLoginUser(dto1);
user2 = await app.createAndLoginUser(dto2);
blockedUser1 = await app.createAndLoginUser(dto3);
blockedUser2 = await app.createAndLoginUser(dto4);
kickedUser1 = await app.createAndLoginUser(dto5);
bannedUser1 = await app.createAndLoginUser(dto6);
await app
.blockUser(user1.id, blockedUser1.id, user1.accessToken)
.expect(200);
Expand All @@ -73,12 +80,14 @@ describe('ChatGateway and ChatController (e2e)', () => {
await app.deleteUser(blockedUser1.id, blockedUser1.accessToken).expect(204);
await app.deleteUser(blockedUser2.id, blockedUser2.accessToken).expect(204);
await app.deleteUser(kickedUser1.id, kickedUser1.accessToken).expect(204);
await app.deleteUser(bannedUser1.id, bannedUser1.accessToken).expect(204);
await app.close();
ws1.close();
ws2.close();
ws3.close();
ws4.close();
ws5.close();
ws6.close();
});

const connect = (ws: Socket) => {
Expand Down Expand Up @@ -134,18 +143,23 @@ describe('ChatGateway and ChatController (e2e)', () => {
ws5 = io('ws://localhost:3000/chat', {
extraHeaders: { cookie: 'token=' + kickedUser1.accessToken },
});
ws6 = io('ws://localhost:3000/chat', {
extraHeaders: { cookie: 'token=' + bannedUser1.accessToken },
});
expect(ws1).toBeDefined();
expect(ws2).toBeDefined();
expect(ws3).toBeDefined();
expect(ws4).toBeDefined();
expect(ws5).toBeDefined();
expect(ws6).toBeDefined();

// Wait for connection
await connect(ws1);
await connect(ws2);
await connect(ws3);
await connect(ws4);
await connect(ws5);
await connect(ws6);
});

// // Enter room by API call
Expand All @@ -161,6 +175,7 @@ describe('ChatGateway and ChatController (e2e)', () => {
await app.enterRoom(room.id, blockedUser1.accessToken).expect(201);
await app.enterRoom(room.id, blockedUser2.accessToken).expect(201);
await app.enterRoom(room.id, kickedUser1.accessToken).expect(201);
await app.enterRoom(room.id, bannedUser1.accessToken).expect(201);
});

// Setup promises to recv messages
Expand Down Expand Up @@ -589,6 +604,132 @@ describe('ChatGateway and ChatController (e2e)', () => {
done();
}, 3000);
});

it('user1 bans bannedUser1', async () => {
await app.banUser(room.id, bannedUser1.id, user1.accessToken).expect(200);
});

it('bannedUser1 sends message', () => {
const helloMessage = {
userId: bannedUser1.id,
roomId: room.id,
content: 'hello',
};
ws6.emit('message', helloMessage);
});

it('user1 and user2 should not receive message from bannedUser1', (done) => {
const mockMessage = jest.fn();
const mockMessage2 = jest.fn();
ws1.on('message', mockMessage);
ws2.on('message', mockMessage2);
setTimeout(() => {
expect(mockMessage).not.toBeCalled();
expect(mockMessage2).not.toBeCalled();
ws1.off('message');
ws2.off('message');
done();
}, 3000);
});

it('user1 should get all messages except from bannedUser1 in the room', async () => {
const res = await app
.getMessagesInRoom(room.id, user1.accessToken)
.expect(200);
const messages = res.body;
expect(messages).toHaveLength(7);
expect(messages).toEqual([
{
user: {
id: user1.id,
name: user1.name,
avatarURL: user1.avatarURL,
},
roomId: room.id,
content: 'hello',
createdAt: expect.any(String),
},
{
user: {
id: user2.id,
name: user2.name,
avatarURL: user2.avatarURL,
},
roomId: room.id,
content: 'ACK: hello',
createdAt: expect.any(String),
},
{
user: {
id: blockedUser1.id,
name: blockedUser1.name,
avatarURL: blockedUser1.avatarURL,
},
roomId: room.id,
content: 'hello',
createdAt: expect.any(String),
},
{
user: {
id: blockedUser2.id,
name: blockedUser2.name,
avatarURL: blockedUser2.avatarURL,
},
roomId: room.id,
content: 'hello',
createdAt: expect.any(String),
},
{
user: {
id: blockedUser1.id,
name: blockedUser1.name,
avatarURL: blockedUser1.avatarURL,
},
roomId: room.id,
content: 'hello',
createdAt: expect.any(String),
},
{
user: {
id: user1.id,
name: user1.name,
avatarURL: user1.avatarURL,
},
roomId: room.id,
content: 'ACK: hello',
createdAt: expect.any(String),
},
{
user: {
id: user1.id,
name: user1.name,
avatarURL: user1.avatarURL,
},
roomId: room.id,
content: 'hello',
createdAt: expect.any(String),
},
]);
});

it('user1 sends message', () => {
const helloMessage = {
userId: user1.id,
roomId: room.id,
content: 'hello',
};
ws1.emit('message', helloMessage);
});

it('bannedUser1 should not receive message from user1', (done) => {
const mockMessage = jest.fn();
ws6.on('message', mockMessage);
setTimeout(() => {
expect(mockMessage).not.toBeCalled();
ws6.off('message');
done();
}, 3000);
});
});

/*
Expand Down

0 comments on commit 700edea

Please sign in to comment.