From 5a71e104cde51dc340ba701c0acebcd7b66fe496 Mon Sep 17 00:00:00 2001 From: grimhilt Date: Tue, 4 Apr 2023 17:13:18 +0200 Subject: [PATCH] implement database as a class for tests --- back/db/saveMessage-db.ts | 26 ++--- back/db/utils/mail.ts | 1 + back/mails/saveMessage.ts | 67 ++++++----- back/test/mail/saveMessage-test.ts | 67 +++++------ back/test/test-utils/db/test-saveMessage.ts | 123 ++++++++++++++++++++ 5 files changed, 211 insertions(+), 73 deletions(-) diff --git a/back/db/saveMessage-db.ts b/back/db/saveMessage-db.ts index 8c10e9f..dc1d74c 100644 --- a/back/db/saveMessage-db.ts +++ b/back/db/saveMessage-db.ts @@ -35,21 +35,13 @@ export async function createRoom( } // todo date not good -export async function registerMessageInRoom( - messageId: number, - roomId: number, - isSeen: boolean, - idate: string | undefined | null, -) { +export async function registerMessageInRoom(messageId: number, roomId: number, idate: string | undefined | null) { if (!idate) idate = new Date().toString(); const query = `INSERT IGNORE INTO app_room_message (message_id, room_id) VALUES (?, ?)`; const values = [messageId, roomId]; await execQueryAsync(query, values); updateLastUpdateRoom(roomId, idate); - // if (!isSeen) { - // incrementNotSeenRoom(roomId); - // } } export function updateLastUpdateRoom(roomId: number, idate: string) { @@ -58,11 +50,13 @@ export function updateLastUpdateRoom(roomId: number, idate: string) { execQuery(query, values); } -export function incrementNotSeenRoom(roomId: number) { - // todo +export async function incrementNotSeenRoom(roomId: number) { + const query = `UPDATE app_room SET unseen = unseen + 1 WHERE room_id = ?`; + const values = [roomId]; + execQuery(query, values); } -export async function getRoomInfo(messageID: string): Promise<{ room_id: number; root_id: number }[]> { +export async function getThreadInfo(messageID: string): Promise<{ room_id: number; root_id: number }[]> { const query = ` SELECT app_room.room_id @@ -92,9 +86,13 @@ export async function isRoomGroup(roomId: number): Promise { }); } -export async function findRoomsFromMessage(messageID: string) { +export async function findRoomsFromMessage(messageID: string): Promise<{ room_id: number }[]> { // todo find message in room not started - const query = `SELECT room_id FROM app_room_message WHERE message_id = ? ORDER BY room_id`; + const query = ` + SELECT room_id FROM app_room_message + INNER JOIN message ON message.message_id = app_room_message.message_id + WHERE message.messageID = ? ORDER BY room_id + `; const values = [messageID]; return await execQueryAsync(query, values); } diff --git a/back/db/utils/mail.ts b/back/db/utils/mail.ts index 1d643a4..c8c960c 100644 --- a/back/db/utils/mail.ts +++ b/back/db/utils/mail.ts @@ -23,6 +23,7 @@ export async function findRoomByOwner(ownerId: number): Promise<{ room_id: numbe } export async function getUserIdOfMailbox(boxId: number): Promise<{ user_id: number }[]> { + console.log("fuckdsvreghiu") const query = ` SELECT app_account.user_id FROM mailbox diff --git a/back/mails/saveMessage.ts b/back/mails/saveMessage.ts index 36363d4..dc1f06c 100644 --- a/back/mails/saveMessage.ts +++ b/back/mails/saveMessage.ts @@ -7,7 +7,8 @@ import { registerThread, registerMember, getAllMembers, - getRoomInfo, + getThreadInfo, + incrementNotSeenRoom, } from "../db/saveMessage-db"; import { findRoomByOwner, getAddresseId, getUserIdOfMailbox } from "../db/utils/mail"; @@ -22,13 +23,13 @@ function createAddress(elt: User): string { return `${elt.mailbox}@${elt.host}`; } -export const roomType = { - ROOM: 0, - CHANNEL: 1, - GROUP: 2, - DM: 3, - THREAD: 4, -}; +export enum RoomType { + ROOM = 0, + CHANNEL = 1, + GROUP = 2, + DM = 3, + THREAD = 4, +} export default class RegisterMessageInApp { messageId: number; @@ -73,6 +74,12 @@ export default class RegisterMessageInApp { return this.ownerId == this.userId; } + async incrementNotSeen(roomId: number) { + if (!this.isSeen) { + await incrementNotSeenRoom(roomId); + } + } + async registerMembers(roomId: number) { getAllMembers(this.messageId).then((res) => { const data = res[0].id.split(","); @@ -82,42 +89,49 @@ export default class RegisterMessageInApp { }); } - async initiateRoom(owner: number, roomType: number) { + async initiateRoom(owner: number, roomType: RoomType) { try { const roomId = await createRoom(this.envelope.subject, owner, this.messageId, roomType); - await registerMessageInRoom(this.messageId, roomId, this.isSeen, this.envelope.date); - this.registerMembers(roomId); + await registerMessageInRoom(this.messageId, roomId, this.envelope.date); + await this.incrementNotSeen(roomId); + await this.registerMembers(roomId); return roomId; } catch (err) { logger.err(err); } } - async createOrRegisterOnExistence(owner: number, roomType: number) { + async createOrRegisterOnExistence(owner: number, roomType: RoomType) { await findRoomByOwner(owner).then(async (res) => { if (res.length == 0) { // first message with this sender await this.initiateRoom(owner, roomType); } else { // not a reply, add to the list of message if this sender - await registerMessageInRoom(this.messageId, res[0].room_id, this.isSeen, this.envelope.date); + await registerMessageInRoom(this.messageId, res[0].room_id, this.envelope.date); + await this.incrementNotSeen(res[0].room_id); + } }); } async initiateThread() { - await createRoom(this.envelope.subject, this.ownerId, this.messageId, roomType.THREAD).then( - async (roomId: number) => { + await createRoom(this.envelope.subject, this.ownerId, this.messageId, RoomType.THREAD).then( + async (threadId: number) => { // find parent room infos - await getRoomInfo(this.inReplyTo).then(async (room) => { + let roomId: 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; - if (!root_id) root_id = room[0].room_id; - await registerThread(roomId, room[0].room_id, root_id); + if (!root_id) root_id = roomId; + await registerThread(threadId, room[0].room_id, root_id); }); - // impl register previous message ? - await registerMessageInRoom(this.messageId, roomId, this.isSeen, this.envelope.date); - await this.registerMembers(roomId); + // impl register previous message or go back + await registerMessageInRoom(this.messageId, threadId, this.envelope.date); + await this.incrementNotSeen(roomId); + await this.incrementNotSeen(threadId); + await this.registerMembers(threadId); }, ); } @@ -125,7 +139,8 @@ export default class RegisterMessageInApp { async createOrRegisterOnMembers(roomId: number) { const hasSameMembers = await hasSameMembersAsParent(this.messageId, this.inReplyTo); if (hasSameMembers) { - await registerMessageInRoom(this.messageId, roomId, this.isSeen, this.envelope.date); + await registerMessageInRoom(this.messageId, roomId, this.envelope.date); + await this.incrementNotSeen(roomId); } else { await this.initiateThread(); } @@ -142,21 +157,21 @@ export default class RegisterMessageInApp { // create or add new message to DM if (!this.envelope.to) throw new Error("Who send a DM and put the recipient in cc ?"); const userTo = await getAddresseId(createAddress(this.envelope.to[0])); - await this.createOrRegisterOnExistence(userTo, roomType.DM); + await this.createOrRegisterOnExistence(userTo, RoomType.DM); } else { // it is not a reply and not a dm // so it is a channel, which can be possibly a group - this.initiateRoom(this.ownerId, roomType.ROOM); + this.initiateRoom(this.ownerId, RoomType.ROOM); } } else { - await this.createOrRegisterOnExistence(this.ownerId, roomType.ROOM); + await this.createOrRegisterOnExistence(this.ownerId, RoomType.ROOM); } } } async saveReply() { await findRoomsFromMessage(this.inReplyTo).then(async (rooms) => { - if (rooms.length == 0) { + if (rooms.length < 1) { // no rooms, so is a transfer // todo test if members of transferred message are included } else if (rooms.length === 1) { diff --git a/back/test/mail/saveMessage-test.ts b/back/test/mail/saveMessage-test.ts index 10603f9..7c3b72d 100644 --- a/back/test/mail/saveMessage-test.ts +++ b/back/test/mail/saveMessage-test.ts @@ -1,18 +1,31 @@ -import mysql from "mysql"; - jest.mock("mysql"); +import mysql from "mysql"; mysql.createConnection = jest.fn(); mysql.createConnection.mockImplementation(() => { return { connect: () => new Promise((resolve, rejects) => resolve(true)) }; }); -import { generateAttrs, generateUsers } from "../test-utils/test-attrsUtils"; -import registerMessageInApp, { roomType } from "../../mails/saveMessage"; -import { jest, describe, it, expect } from "@jest/globals"; +import saveMessageDatabase from "../test-utils/db/test-saveMessage"; +import { generateAttrs, generateUsers } from "../test-utils/test-attrsUtils"; +import registerMessageInApp, { RoomType } from "../../mails/saveMessage"; +import { jest, describe, it, expect } from "@jest/globals"; +import { mocked } from "jest-mock"; + +const db = new saveMessageDatabase(generateUsers(5)); + +const ownUser = db.users[0]; +const messageId = 1; +const boxId = 1; +jest.mock("../../db/utils/mail", () => { + return { + getAddresseId: jest.fn(), + getUserIdOfMailbox: jest.fn(), + }; +}); import { getAddresseId, getUserIdOfMailbox } from "../../db/utils/mail"; + // todo esbuild -// todo mock db // new message from us // to multiple people -> room // if response has same member => group @@ -27,36 +40,24 @@ import { getAddresseId, getUserIdOfMailbox } from "../../db/utils/mail"; // // make it better // if multiple members reply -> group // if only me reply -> channel -const users = generateUsers(5); -const ownUser = users[0]; -const messageId = 1; -const boxId = 1; - -jest.mock("../../db/utils/mail", () => ({ - getAddresseId: jest.fn().mockImplementation((email) => { - const match = users.find((user) => user.user.mailbox + "@" + user.user.host == email); - return new Promise((resolve, reject) => resolve(match?.id)); - }), - getUserIdOfMailbox: jest.fn().mockImplementation((boxId) => { - return new Promise((resolve, reject) => resolve([{ user_id: ownUser.id }])); - }), -})); beforeAll(async () => { - + db.clear(); + mocked(getAddresseId).mockImplementation(db.getAddresseId); + mocked(getUserIdOfMailbox).mockImplementation(db.getUserIdOfMailbox); }); describe("saveMessage", () => { describe("functions", () => { it("isFromUs", async () => { - const attrs = generateAttrs({ from: [ownUser.user], to: [users[1].user] }); + const attrs = generateAttrs({ from: [ownUser.user], to: [db.users[1].user] }); const register = new registerMessageInApp(messageId, attrs, boxId); await register.init(); const res = await register.isFromUs(); expect(res).toBe(true); - const attrs2 = generateAttrs({ from: [users[2].user], to: [users[1].user] }); + const attrs2 = generateAttrs({ from: [db.users[2].user], to: [db.users[1].user] }); const register2 = new registerMessageInApp(messageId, attrs2, boxId); await register2.init(); const res2 = await register2.isFromUs(); @@ -67,56 +68,56 @@ describe("saveMessage", () => { describe("implementation", () => { describe("new first message from us", () => { it("new first message from us to one recipient should create a DM", async () => { - const attrs = generateAttrs({ from: [ownUser.user], to: [users[1].user] }); + const attrs = generateAttrs({ from: [ownUser.user], to: [db.users[1].user] }); const register = new registerMessageInApp(messageId, attrs, boxId); const createOrRegisterOnExistence = jest .spyOn(register, "createOrRegisterOnExistence") .mockImplementation( - (owner: number, roomType: number) => new Promise((resolve, reject) => resolve()), + (owner: number, roomType: RoomType) => new Promise((resolve, reject) => resolve()), ); await register.save(); - expect(createOrRegisterOnExistence).toHaveBeenCalledWith(users[1].id, roomType.DM); + expect(createOrRegisterOnExistence).toHaveBeenCalledWith(db.users[1].id, RoomType.DM); }); it("new first message from us to multiple recipients should create a ROOM", async () => { - const attrs = generateAttrs({ from: [ownUser.user], to: [users[1].user, users[2].user] }); + const attrs = generateAttrs({ from: [ownUser.user], to: [db.users[1].user, db.users[2].user] }); const register = new registerMessageInApp(messageId, attrs, boxId); const initiateRoom = jest .spyOn(register, "initiateRoom") - .mockImplementation((owner: number, roomType: number) => Promise.resolve(1)); + .mockImplementation((owner: number, roomType: RoomType) => Promise.resolve(1)); await register.save(); - expect(initiateRoom).toHaveBeenCalledWith(ownUser.id, roomType.ROOM); + expect(initiateRoom).toHaveBeenCalledWith(ownUser.id, RoomType.ROOM); }); // it("response to new first message to multiple recipients with same members should change room type to GROUP", () => {}); // it("response to new first message to multiple recipients with different members should change room type to CHANNEL", () => {}); }); describe("new first message from other", () => { it("new first message from other to me only should create a room", async () => { - const attrs = generateAttrs({ from: [users[1].user], to: [ownUser.user] }); + const attrs = generateAttrs({ from: [db.users[1].user], to: [ownUser.user] }); const register = new registerMessageInApp(messageId, attrs, boxId); const createOrRegisterOnExistence = jest .spyOn(register, "createOrRegisterOnExistence") - .mockImplementation((owner: number, roomType: number) => { + .mockImplementation((owner: number, roomType: RoomType) => { return new Promise((resolve, reject) => resolve()); }); await register.save(); - expect(createOrRegisterOnExistence).toHaveBeenCalledWith(users[1].id, roomType.ROOM); + expect(createOrRegisterOnExistence).toHaveBeenCalledWith(db.users[1].id, RoomType.ROOM); }); }); // describe("replies", () => { // it("", () => {}); // }); - // describe("", () => {}); + describe("unseen behavior", () => {}); }); }); diff --git a/back/test/test-utils/db/test-saveMessage.ts b/back/test/test-utils/db/test-saveMessage.ts index e69de29..2d0ed4e 100644 --- a/back/test/test-utils/db/test-saveMessage.ts +++ b/back/test/test-utils/db/test-saveMessage.ts @@ -0,0 +1,123 @@ +import { RoomType } from "../../../mails/saveMessage"; +import { generateUsers } from "../test-attrsUtils"; + +interface Room { + room_id: number; + room_name: string; + owner_id: number; + message_id: number; + room_type: RoomType; + notSeen: number; + lastUpdate: string; + is_thread?: boolean; + parent_id?: number; + root_id?: number; +} + +export default class saveMessageDatabase { + rooms: Room[]; + roomId: number; + messages: { room_id: number; message_id: number }[]; + users: any[]; + + constructor(_users) { + this.rooms = []; + this.messages = []; + this.users = generateUsers(5); + this.roomId = 0; + } + + clear() { + this.rooms = []; + this.messages = []; + this.roomId = 0; + } + + _findRoomById(roomId: number): Room { + return this.rooms.find((room) => room.room_id === roomId); + } + + createRoom(roomName: string | null | undefined, ownerId: number, messageId: number, roomType: RoomType) { + this.rooms.push({ + room_id: this.roomId, + room_name: roomName, + owner_id: ownerId, + message_id: messageId, + room_type: roomType, + notSeen: 0, + lastUpdate: "0", + }); + this.roomId++; + } + + registerMessageInRoom = (messageId: number, roomId: number, idate: string | undefined | null) => { + this.messages.push({ message_id: messageId, room_id: roomId }); + }; + + isRoomGroup(roomId: number): Promise { + return new Promise((resolve, reject) => { + resolve(this.rooms.find((room) => room.room_id == roomId).room_type === RoomType.GROUP); + }); + } + + findRoomsFromMessage = (messageID: string): Promise<{ room_id: number }[]> => { + return new Promise((resolve, reject) => { + const rooms = this.rooms.filter((room) => room.message_id.toString() === messageID); + const res: { room_id: number }[] = []; + rooms.forEach((room) => { + res.push({ room_id: room.room_id }); + }); + resolve(res); + }); + }; + + hasSameMembersAsParent() { + // test_todo + } + + registerThread = async (roomId: number, parentId: number, rootId: number) => { + const room = this._findRoomById(roomId); + room.parent_id = parentId; + room.room_id = rootId; + }; + + registerMember() { + // test_todo + } + + getAllMembers() { + // test_todo + } + + 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 }]); + }); + }; + + incrementNotSeenRoom = (roomId: number) => { + const room = this._findRoomById(roomId); + room.notSeen++; + } + + findRoomByOwner = (ownerId: number): Promise<{ room_id: number }[]> => { + return new Promise((resolve, reject) => { + const rooms = this.rooms.filter((room) => room.owner_id === ownerId); + const res = []; + rooms.forEach((room) => { + res.push({ room_id: room.room_id }); + }); + resolve(res); + }); + }; + + getAddresseId = (email: string, name?: string): Promise => { + const match = this.users.find((user) => user.user.mailbox + "@" + user.user.host == email); + return new Promise((resolve, reject) => resolve(match?.id)); + }; + + getUserIdOfMailbox = (boxId: number): Promise<{ user_id: number }[]> => { + return new Promise((resolve, rejects) => resolve([{ user_id: this.users[0].id }])); + }; +}