diff --git a/back/db/saveMessage-db.ts b/back/db/saveMessage-db.ts index 9b02290..d0be0af 100644 --- a/back/db/saveMessage-db.ts +++ b/back/db/saveMessage-db.ts @@ -73,19 +73,28 @@ export async function getThreadInfo(messageID: string): Promise<{ room_id: numbe return await execQueryAsync(query, values); } +export async function getThreadInfoOnId(threadId: number): Promise<{ room_id: number; root_id: number }[]> { + const query = ` + SELECT + app_room.room_id + app_thread.root_id + FROM app_room + WHERE room_id = ? + `; + const values = [threadId]; + return await execQueryAsync(query, values); +} + export async function registerThread(roomId: number, parentId: number, rootId: number) { const query = `INSERT IGNORE INTO app_thread (room_id, parent_id, root_id) VALUES (?, ?, ?)`; const values = [roomId, parentId, rootId]; return await execQueryAsync(query, values); } -export async function isRoomGroup(roomId: number): Promise { - return new Promise((resolve, reject) => { - const query = `SELECT isGroup FROM app_room WHERE room_id = ?`; - const values = [roomId]; - const results = execQueryAsync(query, values); - return results[0].isGroup; - }); +export async function getRoomType(roomId: number): Promise<{ room_type: number }[]> { + const query = `SELECT room_type FROM app_room WHERE room_id = ?`; + const values = [roomId]; + return await execQueryAsync(query, values); } export async function findRoomsFromMessage(messageID: string): Promise<{ room_id: number }[]> { diff --git a/back/mails/saveMessage.ts b/back/mails/saveMessage.ts index 86d43cd..6ed78ae 100644 --- a/back/mails/saveMessage.ts +++ b/back/mails/saveMessage.ts @@ -1,7 +1,7 @@ import { createRoom, registerMessageInRoom, - isRoomGroup, + getRoomType, findRoomsFromMessage, hasSameMembersAsParent, registerThread, @@ -9,6 +9,7 @@ import { getAllMembers, getThreadInfo, incrementNotSeenRoom, + getThreadInfoOnId, } from "../db/saveMessage-db"; import { findRoomByOwner, getAddresseId, getUserIdOfMailbox } from "../db/utils/mail"; @@ -111,7 +112,6 @@ export default class RegisterMessageInApp { // not a reply, add to the list of message if this sender await registerMessageInRoom(this.messageId, res[0].room_id, this.envelope.date); await this.incrementNotSeen(res[0].room_id); - } }); } @@ -121,27 +121,33 @@ export default class RegisterMessageInApp { async (threadId: number) => { // find parent room infos let roomId: number; + let root_id: number; await getThreadInfo(this.inReplyTo).then(async (room) => { // todo room not lenght, reply to transfer ? roomId = room[0].room_id; - let root_id = room[0].root_id; + root_id = room[0].root_id; if (root_id === undefined) root_id = roomId; await registerThread(threadId, roomId, root_id); }); // impl register previous message or go back await registerMessageInRoom(this.messageId, threadId, this.envelope.date); - await this.incrementNotSeen(roomId); + await this.incrementNotSeen(root_id); await this.incrementNotSeen(threadId); await this.registerMembers(threadId); }, ); } - async createOrRegisterOnMembers(roomId: number) { + async createOrRegisterOnMembers(roomId: number, isThread?: boolean) { const hasSameMembers = await hasSameMembersAsParent(this.messageId, this.inReplyTo); if (hasSameMembers) { await registerMessageInRoom(this.messageId, roomId, this.envelope.date); await this.incrementNotSeen(roomId); + if (isThread) { + await getThreadInfoOnId(roomId).then(async (res) => { + await this.incrementNotSeen(res[0].root_id); + }); + } } else { await this.initiateThread(); } @@ -178,18 +184,17 @@ export default class RegisterMessageInApp { } else if (rooms.length === 1) { // only one room so message is only in a room and not in a thread // as a thread is associated to a room - await isRoomGroup(rooms[0].room_id).then(async (isGroup: boolean) => { - if (isGroup) { - this.createOrRegisterOnMembers(rooms[0].room_id); - } else { - // reply from CHANNEL or DM or ROOM - await this.initiateThread(); - // todo - // if (sender == owner) { // correction from the original sender - // // leave in the same channel - // } - } - }); + const roomType = (await getRoomType(rooms[0].room_id))[0].room_type; + if (roomType == RoomType.GROUP || roomType == RoomType.THREAD) { + await this.createOrRegisterOnMembers(rooms[0].room_id, roomType == RoomType.THREAD); + } else { + // reply from CHANNEL or DM or ROOM + await this.initiateThread(); + // todo + // if (sender == owner) { // correction from the original sender + // // leave in the same channel + // } + } } else if (rooms.length > 1) { // get the lowest thread (order by room_id) const roomId = rooms[rooms.length - 1].room_id; diff --git a/back/test/mail/saveMessage-test.ts b/back/test/mail/saveMessage-test.ts index 4956c9b..b2e5c5c 100644 --- a/back/test/mail/saveMessage-test.ts +++ b/back/test/mail/saveMessage-test.ts @@ -6,7 +6,7 @@ mysql.createConnection.mockImplementation(() => { }); import saveMessageDatabase from "../test-utils/db/test-saveMessage"; -import { generateAttrs, generateUsers } from "../test-utils/test-attrsUtils"; +import { generateAttrs, generateUsers, randomInt } from "../test-utils/test-attrsUtils"; import { jest, describe, it, expect } from "@jest/globals"; import { mocked } from "jest-mock"; @@ -28,13 +28,14 @@ jest.mock("../../db/saveMessage-db", () => { return { createRoom: jest.fn(), registerMessageInRoom: jest.fn(), - isRoomGroup: jest.fn(), + getRoomType: jest.fn(), findRoomsFromMessage: jest.fn(), hasSameMembersAsParent: jest.fn(), registerThread: jest.fn(), registerMember: jest.fn(), getAllMembers: jest.fn(), getThreadInfo: jest.fn(), + getThreadInfoOnId: jest.fn(), incrementNotSeenRoom: jest.fn(), }; }); @@ -42,13 +43,14 @@ import { getAddresseId, getUserIdOfMailbox, findRoomByOwner } from "../../db/uti import { createRoom, registerMessageInRoom, - isRoomGroup, + getRoomType, findRoomsFromMessage, hasSameMembersAsParent, registerThread, registerMember, getAllMembers, getThreadInfo, + getThreadInfoOnId, incrementNotSeenRoom, } from "../../db/saveMessage-db"; import { AttrsWithEnvelope } from "../../interfaces/mail/attrs.interface"; @@ -75,32 +77,43 @@ beforeAll(async () => { mocked(createRoom).mockImplementation(db.createRoom); mocked(registerMessageInRoom).mockImplementation(db.registerMessageInRoom); - mocked(isRoomGroup).mockImplementation(db.isRoomGroup); + mocked(getRoomType).mockImplementation(db.getRoomType); mocked(findRoomsFromMessage).mockImplementation(db.findRoomsFromMessage); mocked(hasSameMembersAsParent).mockImplementation(db.hasSameMembersAsParent); mocked(registerThread).mockImplementation(db.registerThread); mocked(registerMember).mockImplementation(db.registerMember); mocked(getAllMembers).mockImplementation(db.getAllMembers); mocked(getThreadInfo).mockImplementation(db.getThreadInfo); + mocked(getThreadInfoOnId).mockImplementation(db.getThreadInfoOnId); mocked(incrementNotSeenRoom).mockImplementation(db.incrementNotSeenRoom); }); +let msgFromUs_1: { attrs: AttrsWithEnvelope; message_id: number }; +let replyTo1_2: { attrs: AttrsWithEnvelope; message_id: number }; -const msgFromUs_1: { attrs: AttrsWithEnvelope; message_id: number } = { - attrs: generateAttrs({ from: [ownUser.user], to: [db.users[1].user], messageId: "1" }), - message_id: 1, -}; -const replyTo1_2: { attrs: AttrsWithEnvelope; message_id: number } = { - attrs: generateAttrs({ from: [ownUser.user], to: [db.users[1].user], messageId: "2", inReplyTo: "1" }), - message_id: 2, -}; - -const replyTo2_3: { attrs: AttrsWithEnvelope; message_id: number } = { - attrs: generateAttrs({ from: [ownUser.user], to: [db.users[1].user, db.users[2].user], messageId: "3", inReplyTo: "2" }), - message_id: 3, -}; +let replyTo2_3: { attrs: AttrsWithEnvelope; message_id: number }; beforeEach(async () => { + msgFromUs_1 = { + attrs: generateAttrs({ from: [ownUser.user], to: [db.users[1].user], messageId: "1" }), + message_id: 1, + }; + + replyTo1_2 = { + attrs: generateAttrs({ from: [ownUser.user], to: [db.users[1].user], messageId: "2", inReplyTo: "1" }), + message_id: 2, + }; + + replyTo2_3 = { + attrs: generateAttrs({ + from: [ownUser.user], + to: [db.users[1].user, db.users[2].user], + messageId: "3", + inReplyTo: "2", + }), + message_id: 3, + }; + db.clear(); db.messages.push(msgFromUs_1); db.messages.push(replyTo1_2); @@ -108,7 +121,6 @@ beforeEach(async () => { }); describe("saveMessage", () => { - describe("functions", () => { it("isFromUs", async () => { const attrs = generateAttrs({ from: [ownUser.user], to: [db.users[1].user] }); @@ -179,21 +191,21 @@ describe("saveMessage", () => { await register.save(); register = new registerMessageInApp(replyTo1_2.message_id, replyTo1_2.attrs, boxId); await register.save(); - - const thread = db.rooms.find((room) => room.is_thread) + + const thread = db.rooms.find((room) => room.is_thread); expect(thread.room_type).toBe(RoomType.THREAD); expect(thread.root_id).toBe(0); expect(thread.parent_id).toBe(0); }); - - it("should create THREAD when reply in THREAD with different members", async() => { + + it("should create THREAD when reply in THREAD with different members", async () => { let register = new registerMessageInApp(msgFromUs_1.message_id, msgFromUs_1.attrs, boxId); await register.save(); register = new registerMessageInApp(replyTo1_2.message_id, replyTo1_2.attrs, boxId); await register.save(); register = new registerMessageInApp(replyTo2_3.message_id, replyTo2_3.attrs, boxId); await register.save(); - + const threads = db.rooms.filter((room) => room.is_thread); expect(threads).toHaveLength(2); const thread = threads[1]; @@ -203,16 +215,74 @@ describe("saveMessage", () => { expect(thread.members).toHaveLength(3); }); }); - describe("replies", () => { - it("", () => {}); + describe("joins room", () => { + it("should add message to THREAD when reply to a message in it with same members", async () => { + let register = new registerMessageInApp(msgFromUs_1.message_id, msgFromUs_1.attrs, boxId); + await register.save(); + register = new registerMessageInApp(replyTo1_2.message_id, replyTo1_2.attrs, boxId); + await register.save(); + // set same members to not create a new thread + let newReplyInThread = JSON.parse(JSON.stringify(replyTo1_2)); + newReplyInThread.attrs.envelope.inReplyTo = replyTo1_2.attrs.envelope.messageId; + newReplyInThread.message_id = randomInt(5); + newReplyInThread.attrs.envelope.messageId = newReplyInThread.message_id.toString(); + db.messages.push(newReplyInThread); + + register = new registerMessageInApp(newReplyInThread.message_id, newReplyInThread.attrs, boxId); + await register.save(); + + expect(db.rooms).toHaveLength(2); + expect(db.room_message.filter((message) => message.room_id === db.rooms[1].room_id)).toHaveLength(2); + }); }); describe("unseen behavior", () => { - it("should add unseen in room when a message creates a room", () => {}); + it("should add unseen in room when a message creates a room", async () => { + let register = new registerMessageInApp(msgFromUs_1.message_id, msgFromUs_1.attrs, boxId); + await register.save(); + expect(db.rooms[0].notSeen).toBe(1); + }); - it("should add unseen in room when a message joins a room", () => {}); + it("should add unseen in room when a message joins a room", async () => { + let register = new registerMessageInApp(msgFromUs_1.message_id, msgFromUs_1.attrs, boxId); + await register.save(); - it("should add unseen in root room and thread when new message creates a thread", () => {}); + register = new registerMessageInApp(msgFromUs_1.message_id, msgFromUs_1.attrs, boxId); + await register.save(); + expect(db.rooms).toHaveLength(1); + expect(db.rooms[0].notSeen).toBe(2); + }); - it("should add unseen in root room and thread when new message joins in thread", () => {}); + it("should add unseen in root room and THREAD when new message creates a THREAD", async () => { + let register = new registerMessageInApp(msgFromUs_1.message_id, msgFromUs_1.attrs, boxId); + await register.save(); + register = new registerMessageInApp(replyTo1_2.message_id, replyTo1_2.attrs, boxId); + await register.save(); + register = new registerMessageInApp(replyTo2_3.message_id, replyTo2_3.attrs, boxId); + await register.save(); + expect(db.rooms[0].notSeen).toBe(3); + expect(db.rooms[1].notSeen).toBe(1); + expect(db.rooms[2].notSeen).toBe(1); + }); + + it("should add unseen in root room and THREAD when new message joins in THREAD", async () => { + let register = new registerMessageInApp(msgFromUs_1.message_id, msgFromUs_1.attrs, boxId); + await register.save(); + register = new registerMessageInApp(replyTo1_2.message_id, replyTo1_2.attrs, boxId); + await register.save(); + + // set same members to not create a new thread + let newReplyInThread = JSON.parse(JSON.stringify(replyTo1_2)); + newReplyInThread.attrs.envelope.inReplyTo = replyTo1_2.attrs.envelope.messageId; + newReplyInThread.message_id = randomInt(5); + newReplyInThread.attrs.envelope.messageId = newReplyInThread.message_id.toString(); + db.messages.push(newReplyInThread); + + register = new registerMessageInApp(newReplyInThread.message_id, newReplyInThread.attrs, boxId); + await register.save(); + + expect(db.rooms).toHaveLength(2); + expect(db.rooms[0].notSeen).toBe(3); + expect(db.rooms[1].notSeen).toBe(2); + }); }); }); diff --git a/back/test/test-utils/db/test-saveMessage.ts b/back/test/test-utils/db/test-saveMessage.ts index 9ec47fa..5cb08b5 100644 --- a/back/test/test-utils/db/test-saveMessage.ts +++ b/back/test/test-utils/db/test-saveMessage.ts @@ -21,8 +21,8 @@ interface Room { export default class saveMessageDatabase { rooms: Room[]; roomId: number; - messages: {attrs: AttrsWithEnvelope, message_id: number}[]; - room_message: {room_id: number, message_id: number}[]; + messages: { attrs: AttrsWithEnvelope; message_id: number }[]; + room_message: { room_id: number; message_id: number }[]; users: UserTest[]; constructor(_users) { @@ -42,11 +42,11 @@ export default class saveMessageDatabase { _findRoomById = (roomId: number): Room => { return this.rooms.find((room) => room.room_id === roomId); - } + }; _findUserByMailbox = (mailbox: string): UserTest => { return this.users.find((user) => user.user.mailbox === mailbox); - } + }; createRoom = ( roomName: string | null | undefined, @@ -72,11 +72,11 @@ export default class saveMessageDatabase { return Promise.resolve(); }; - isRoomGroup = (roomId: number): Promise => { + getRoomType = (roomId: number): Promise<{ room_type: number }[]> => { return new Promise((resolve, reject) => { - resolve(this.rooms.find((room) => room.room_id == roomId).room_type === RoomType.GROUP); + resolve([{ room_type: this.rooms.find((room) => room.room_id == roomId).room_type }]); }); - } + }; findRoomsFromMessage = (messageID: string): Promise<{ room_id: number }[]> => { return new Promise((resolve, reject) => { @@ -90,8 +90,8 @@ export default class saveMessageDatabase { }; hasSameMembersAsParent = (messageId: number, messageID: string): Promise => { - const msg1 = this.messages.find((message) => message.attrs.envelope.messageId === messageID ); - const msg2 = this.messages.find((message) => message.message_id === messageId ); + const msg1 = this.messages.find((message) => message.attrs.envelope.messageId === messageID); + const msg2 = this.messages.find((message) => message.message_id === messageId); const members1 = getMembers(msg1.attrs.envelope); const members2 = getMembers(msg2.attrs.envelope); let ids1 = []; @@ -99,7 +99,7 @@ export default class saveMessageDatabase { members1.forEach((member) => ids1.push(this._findUserByMailbox(member.mailbox).id)); members2.forEach((member) => ids2.push(this._findUserByMailbox(member.mailbox).id)); return Promise.resolve(hasSameElements(ids1, ids2)); - } + }; registerThread = async (roomId: number, parentId: number, rootId: number) => { const room = this._findRoomById(roomId); @@ -113,7 +113,7 @@ export default class saveMessageDatabase { if (!room.members) room.members = []; room.members.push(this.users.find((user) => user.id == memberId)); return Promise.resolve(true); - } + }; getAllMembers = (messageId: number): Promise => { const message = this.messages.find((message) => message.message_id === messageId); @@ -121,15 +121,18 @@ export default class saveMessageDatabase { getMembers(message.attrs.envelope).forEach((member) => { res += this.users.find((user) => user.user.mailbox === member.mailbox).id + ","; }); - res = res.substring(0, res.length-1); - return Promise.resolve([{id: res}]); - } + res = res.substring(0, res.length - 1); + return Promise.resolve([{ id: res }]); + }; getThreadInfo = (messageID: string): Promise<{ room_id: number; root_id: number }[]> => { - return new Promise((resolve, reject) => { - const room = this.rooms.find((room) => room.message_id.toString() === messageID); - resolve([{ room_id: room.room_id, root_id: room.root_id }]); - }); + const room = this.rooms.find((room) => room.message_id.toString() === messageID); + return Promise.resolve([{ room_id: room.room_id, root_id: room.root_id }]); + }; + + getThreadInfoOnId = (threadId: number): Promise<{ room_id: number; root_id: number }[]> => { + const room = this._findRoomById(threadId); + return Promise.resolve([{ room_id: room.root_id, root_id: room.root_id }]); }; incrementNotSeenRoom = (roomId: number): Promise => { diff --git a/back/test/test-utils/test-attrsUtils.ts b/back/test/test-utils/test-attrsUtils.ts index f9e2998..8cb8c58 100644 --- a/back/test/test-utils/test-attrsUtils.ts +++ b/back/test/test-utils/test-attrsUtils.ts @@ -70,6 +70,6 @@ function randomString(length: number): string { return result; } -function randomInt(length: number): number { +export function randomInt(length: number): number { return parseInt((Math.random() * Math.pow(10, length)).toFixed()); } \ No newline at end of file