Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(server): test message read, delete in models and server #163

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions packages/models/tests/messages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,42 @@ describe("Messages", () => {
).to.be.eq(0);
});
});

describe(nameof(messages.markAsDeleted), () => {
it("should mark message as deleted", async () => {
const tutor = await fixtures.tutor();
const student = await fixtures.student();
const room = await rooms.create([tutor.id, student.id]);

const message = await messages.create({
roomId: room,
text: "1",
userId: student.id,
});

expect(message.deleted).to.be.eq(false);
await messages.markAsDeleted(message.id);
const updatedMessage = await messages.findById(message.id);
expect(updatedMessage?.deleted).to.be.eq(true);
});
});

describe(nameof(messages.markAsRead), () => {
it("should mark message as read", async () => {
const tutor = await fixtures.tutor();
const student = await fixtures.student();
const room = await rooms.create([tutor.id, student.id]);

const message = await messages.create({
roomId: room,
text: "1",
userId: student.id,
});

expect(message.read).to.be.eq(false);
await messages.markAsRead(message.id);
const updatedMessage = await messages.findById(message.id);
expect(updatedMessage?.read).to.be.eq(true);
});
});
});
1 change: 1 addition & 0 deletions packages/types/src/wss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export type ClientEventsMap = {
type: ICall.Type;
}>;
[ClientEvent.LeaveCall]: EventCallback<{ callId: number }>;
[ClientEvent.MarkAsRead]: EventCallback<{ messageId: number }>;
};

/**
Expand Down
8 changes: 8 additions & 0 deletions services/server/fixtures/wss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ export class ClientSocket {
this.client.emit(Wss.ClientEvent.LeaveCall, { callId });
}

deleteMessage(messageId: number) {
this.client.emit(Wss.ClientEvent.DeleteMessage, { id: messageId });
}

markAsRead(messageId: number) {
this.client.emit(Wss.ClientEvent.DeleteMessage, { id: messageId });
}

/**
* Wait for event.
*
Expand Down
34 changes: 27 additions & 7 deletions services/server/src/wss/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ export class WssHandler {
Wss.ClientEvent.DeleteMessage,
this.deleteMessage.bind(this)
);
this.socket.on(
Wss.ClientEvent.UpdateMessage,
this.updateMessage.bind(this)
);
this.socket.on(
Wss.ClientEvent.MarkAsRead,
this.markMessageAsRead.bind(this)
);
this.socket.on(Wss.ClientEvent.PeerOpened, this.peerOpened.bind(this));
this.socket.on(Wss.ClientEvent.RegisterPeer, this.registerPeer.bind(this));
this.socket.on(Wss.ClientEvent.Disconnect, this.disconnect.bind(this));
Expand Down Expand Up @@ -210,10 +218,12 @@ export class WssHandler {
if (!isMember)
throw new Error(`User(${user.id}) isn't member of room Id: ${roomId}`);

this.socket.to(roomId.toString()).emit(Wss.ServerEvent.UserTyping, {
roomId,
userId: user.id,
});
this.socket
.to(this.asChatRoomId(roomId))
.emit(Wss.ServerEvent.UserTyping, {
roomId,
userId: user.id,
});
});

if (error instanceof Error) stdout.error(error.message);
Expand Down Expand Up @@ -357,15 +367,25 @@ export class WssHandler {
const message = await messages.findById(messageId);
if (!message) throw new Error("Message not found");

const userId = user.id;
if (userId !== message.userId) throw new Error("Unauthorized");
/**
* We will get all members then check if the user is in the room, then
* we check if the user is the other member of the room
*/
const roomMembers = await rooms.findRoomMembers({
roomIds: [message.roomId],
});
const otherRoomMember = roomMembers.find(
(member) => member.id !== user.id
);
if (user.id !== otherRoomMember?.id) throw new Error("Unauthorized");

if (message.read)
return console.log("Message is already marked as read".yellow);

await messages.markAsRead(messageId);

this.socket.broadcast
.to(message.roomId.toString())
.to(this.asChatRoomId(message.roomId))
.emit(Wss.ServerEvent.MessageRead, { messageId });
} catch (error) {
console.log(error);
Expand Down
134 changes: 134 additions & 0 deletions services/server/tests/wss/chat.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { Api } from "@fixtures/api";
import db, { flush } from "@fixtures/db";
import { ClientSocket } from "@fixtures/wss";
import { IMessage, IUser, Wss } from "@litespace/types";
import dayjs from "@/lib/dayjs";
import { IRule } from "@litespace/types";
import { Time } from "@litespace/sol/time";
import { faker } from "@faker-js/faker/locale/ar";
import { unpackRules } from "@litespace/sol/rule";
import { expect } from "chai";
import { messages } from "@litespace/models";

describe("Messages", () => {
let tutor: IUser.LoginApiResponse;
let student: IUser.LoginApiResponse;
let room: number;
let chatMessages: IMessage.Self[];
let tutorSocket: ClientSocket;
let studentSocket: ClientSocket;

beforeEach(async () => {
await flush();

const tutorApi = await Api.forTutor();
tutor = await tutorApi.findCurrentUser();

const studentApi = await Api.forStudent();
student = await studentApi.findCurrentUser();

room = await db.room([student.user.id, tutor.user.id]);

chatMessages = await Promise.all(
[
{
text: "1",
userId: student.user.id,
roomId: room,
},
{
text: "2",
userId: student.user.id,
roomId: room,
},
{
text: "3",
userId: student.user.id,
roomId: room,
},
].map(async (message) => {
return await messages.create(message);
})
);
tutorSocket = new ClientSocket(tutor.token);
studentSocket = new ClientSocket(student.token);
});

describe("deleteMessage", () => {
it("should delete a message and emits the results", async () => {
const result1 = tutorSocket.wait(Wss.ServerEvent.RoomMessageDeleted);
const result2 = studentSocket.wait(Wss.ServerEvent.RoomMessageDeleted);
studentSocket.deleteMessage(chatMessages[1].id);

const { messageId, roomId } = await result1;
expect(messageId).to.be.eq(chatMessages[1].id);
expect(roomId).to.be.eq(room);

const { messageId: id, roomId: id2 } = await result2;
expect(id).to.be.eq(chatMessages[1].id);
expect(id2).to.be.eq(room);

const message = await messages.findById(messageId);
expect(message?.deleted).to.be.eq(true);
});

it("should not delete a message that is already deleted or not found", async () => {
try {
studentSocket.deleteMessage(chatMessages[1].id);
} catch (error) {
if (error instanceof Error) {
expect(error.message).to.be.eq("Message not found");
}
}
});

it("should not delete a message if unauthorized user", async () => {
try {
tutorSocket.deleteMessage(chatMessages[1].id);
} catch (error) {
if (error instanceof Error) {
expect(error.message).to.be.eq("Forbidden");
}
}
});
});

describe("markAsRead", () => {
it("should mark a message as read and emits the results", async () => {
const result = studentSocket.wait(Wss.ServerEvent.MessageRead);
tutorSocket.markAsRead(chatMessages[1].id);

const { messageId } = await result;
expect(messageId).to.be.eq(chatMessages[1].id);

const message = await messages.findById(messageId);
expect(message?.read).to.be.eq(true);
});

it("should not mark a not found message as read", async () => {
try {
tutorSocket.markAsRead(chatMessages[1].id);
} catch (error) {
if (error instanceof Error) {
expect(error.message).to.be.eq("Message not found");
}
}
});

it("should not mark a message if unauthorized user", async () => {
try {
const tutorApi = await Api.forTutor();
const unAuthorizedtutor = await tutorApi.findCurrentUser();
const unAuthorizedtutorSocket = new ClientSocket(
unAuthorizedtutor.token
);

unAuthorizedtutorSocket.markAsRead(chatMessages[1].id);
} catch (error) {
if (error instanceof Error) {
expect(error.message).to.be.eq("Unauthorized");
}
}
});
});
});
2 changes: 1 addition & 1 deletion services/server/tests/wss/typing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe("Typing", () => {
studentSocket = new ClientSocket(student.token);
});

it("should emit an event", async () => {
it("should emit a typing event", async () => {
const result = tutorSocket.wait(Wss.ServerEvent.UserTyping);
studentSocket.userTyping(room);

Expand Down
Loading