diff --git a/public/language/en-GB/modules.json b/public/language/en-GB/modules.json index 44233692a2..c242b12119 100644 --- a/public/language/en-GB/modules.json +++ b/public/language/en-GB/modules.json @@ -35,6 +35,10 @@ "chat.show-ip": "Show IP", "chat.owner": "Room Owner", + "chat.system.user-join": "%1 has joined the room", + "chat.system.user-leave": "%1 has left the room", + "chat.system.room-rename": "%2 has renamed this room: %1", + "composer.compose": "Compose", "composer.show_preview": "Show Preview", "composer.hide_preview": "Hide Preview", diff --git a/public/src/client/chats/messages.js b/public/src/client/chats/messages.js index 454e038d2c..502ce0f6e9 100644 --- a/public/src/client/chats/messages.js +++ b/public/src/client/chats/messages.js @@ -94,11 +94,19 @@ define('forum/chats/messages', ['components', 'sounds', 'translator', 'benchpres messages.parseMessage = function (data, callback) { - Benchpress.parse('partials/chats/message' + (Array.isArray(data) ? 's' : ''), { - messages: data, - }, function (html) { + function done(html) { translator.translate(html, callback); - }); + } + + if (Array.isArray(data)) { + Benchpress.parse('partials/chats/message' + (Array.isArray(data) ? 's' : ''), { + messages: data, + }, done); + } else { + Benchpress.parse('partials/chats/' + (data.system ? 'system-message' : 'message'), { + messages: data, + }, done); + } }; diff --git a/src/messaging/create.js b/src/messaging/create.js index b7be32164d..59f1bc6fe3 100644 --- a/src/messaging/create.js +++ b/src/messaging/create.js @@ -53,6 +53,7 @@ module.exports = function (Messaging) { var mid; var message; var isNewSet; + const timestamp = data.timestamp || new Date().getTime(); async.waterfall([ function (next) { @@ -65,10 +66,11 @@ module.exports = function (Messaging) { mid = _mid; message = { content: String(data.content), - timestamp: data.timestamp, + timestamp: timestamp, fromuid: data.uid, roomId: data.roomId, deleted: 0, + system: data.system || 0, }; if (data.ip) { message.ip = data.ip; @@ -80,7 +82,7 @@ module.exports = function (Messaging) { db.setObject('message:' + mid, message, next); }, function (next) { - Messaging.isNewSet(data.uid, data.roomId, data.timestamp, next); + Messaging.isNewSet(data.uid, data.roomId, timestamp, next); }, function (_isNewSet, next) { isNewSet = _isNewSet; @@ -91,8 +93,8 @@ module.exports = function (Messaging) { }, function (uids, next) { async.parallel([ - async.apply(Messaging.addRoomToUsers, data.roomId, uids, data.timestamp), - async.apply(Messaging.addMessageToUsers, data.roomId, uids, mid, data.timestamp), + async.apply(Messaging.addRoomToUsers, data.roomId, uids, timestamp), + async.apply(Messaging.addMessageToUsers, data.roomId, uids, mid, timestamp), async.apply(Messaging.markUnread, uids, data.roomId), ], next); }, @@ -115,6 +117,16 @@ module.exports = function (Messaging) { ], callback); }; + Messaging.addSystemMessage = async (content, uid, roomId) => { + const message = await Messaging.addMessage({ + content: content, + uid: uid, + roomId: roomId, + system: 1, + }); + Messaging.notifyUsersInRoom(uid, roomId, message); + }; + Messaging.addRoomToUsers = function (roomId, uids, timestamp, callback) { if (!uids.length) { return callback(); diff --git a/src/messaging/data.js b/src/messaging/data.js index 7b4f72329d..6781e2f773 100644 --- a/src/messaging/data.js +++ b/src/messaging/data.js @@ -7,7 +7,7 @@ var user = require('../user'); var utils = require('../utils'); var plugins = require('../plugins'); -const intFields = ['timestamp', 'edited', 'fromuid', 'roomId', 'deleted']; +const intFields = ['timestamp', 'edited', 'fromuid', 'roomId', 'deleted', 'system']; module.exports = function (Messaging) { Messaging.newMessageCutoff = 1000 * 60 * 3; @@ -86,9 +86,14 @@ module.exports = function (Messaging) { message.newSet = false; message.roomId = String(message.roomId || roomId); message.deleted = !!message.deleted; + message.system = !!message.system; }); async.map(messages, function (message, next) { + if (message.system) { + return setImmediate(next, null, message); + } + Messaging.parse(message.content, message.fromuid, uid, roomId, isNew, function (err, result) { if (err) { return next(err); diff --git a/src/messaging/index.js b/src/messaging/index.js index 078d660ddc..bce2afe297 100644 --- a/src/messaging/index.js +++ b/src/messaging/index.js @@ -259,11 +259,11 @@ Messaging.getLatestUndeletedMessage = function (uid, roomId, callback) { done = true; return next(); } - Messaging.getMessageField(mids[0], 'deleted', _next); + Messaging.getMessageFields(mids[0], ['deleted', 'system'], _next); }, - function (deleted, _next) { - done = !deleted; - if (!deleted) { + function (states, _next) { + done = !states.deleted && !states.system; + if (done) { latestMid = mids[0]; } index += 1; diff --git a/src/messaging/rooms.js b/src/messaging/rooms.js index de44274169..ed5ae4ec6b 100644 --- a/src/messaging/rooms.js +++ b/src/messaging/rooms.js @@ -1,6 +1,5 @@ 'use strict'; -var async = require('async'); var validator = require('validator'); var db = require('../database'); @@ -10,34 +9,23 @@ var privileges = require('../privileges'); var meta = require('../meta'); module.exports = function (Messaging) { - Messaging.getRoomData = function (roomId, callback) { - async.waterfall([ - function (next) { - db.getObject('chat:room:' + roomId, next); - }, - function (data, next) { - if (!data) { - return callback(new Error('[[error:no-chat-room]]')); - } - modifyRoomData([data]); - next(null, data); - }, - ], callback); + Messaging.getRoomData = async (roomId) => { + const data = await db.getObject('chat:room:' + roomId); + if (!data) { + throw new Error('[[error:no-chat-room]]'); + } + + modifyRoomData([data]); + return data; }; - Messaging.getRoomsData = function (roomIds, callback) { - var keys = roomIds.map(function (roomId) { + Messaging.getRoomsData = async (roomIds) => { + const roomData = await db.getObjects(roomIds.map(function (roomId) { return 'chat:room:' + roomId; - }); - async.waterfall([ - function (next) { - db.getObjects(keys, next); - }, - function (roomData, next) { - modifyRoomData(roomData); - next(null, roomData); - }, - ], callback); + })); + + modifyRoomData(roomData); + return roomData; }; function modifyRoomData(rooms) { @@ -52,284 +40,204 @@ module.exports = function (Messaging) { }); } - Messaging.newRoom = function (uid, toUids, callback) { - var roomId; - var now = Date.now(); - async.waterfall([ - function (next) { - db.incrObjectField('global', 'nextChatRoomId', next); - }, - function (_roomId, next) { - roomId = _roomId; - var room = { - owner: uid, - roomId: roomId, - }; - db.setObject('chat:room:' + roomId, room, next); - }, - function (next) { - db.sortedSetAdd('chat:room:' + roomId + ':uids', now, uid, next); - }, - function (next) { - Messaging.addUsersToRoom(uid, toUids, roomId, next); - }, - function (next) { - Messaging.addRoomToUsers(roomId, [uid].concat(toUids), now, next); - }, - function (next) { - next(null, roomId); - }, - ], callback); - }; + Messaging.newRoom = async (uid, toUids) => { + const now = Date.now(); + const roomId = await db.incrObjectField('global', 'nextChatRoomId'); + const room = { + owner: uid, + roomId: roomId, + }; - Messaging.isUserInRoom = function (uid, roomId, callback) { - async.waterfall([ - function (next) { - db.isSortedSetMember('chat:room:' + roomId + ':uids', uid, next); - }, - function (inRoom, next) { - plugins.fireHook('filter:messaging.isUserInRoom', { uid: uid, roomId: roomId, inRoom: inRoom }, next); - }, - function (data, next) { - next(null, data.inRoom); - }, - ], callback); - }; + await Promise.all([ + db.setObject('chat:room:' + roomId, room), + db.sortedSetAdd('chat:room:' + roomId + ':uids', now, uid), + ]); + await Promise.all([ + Messaging.addUsersToRoom(uid, toUids, roomId), + Messaging.addRoomToUsers(roomId, [uid].concat(toUids), now), + ]); - Messaging.roomExists = function (roomId, callback) { - db.exists('chat:room:' + roomId + ':uids', callback); + return roomId; }; - Messaging.getUserCountInRoom = function (roomId, callback) { - db.sortedSetCard('chat:room:' + roomId + ':uids', callback); + Messaging.isUserInRoom = async (uid, roomId) => { + const inRoom = db.isSortedSetMember('chat:room:' + roomId + ':uids', uid); + const data = await plugins.fireHook('filter:messaging.isUserInRoom', { uid: uid, roomId: roomId, inRoom: inRoom }); + return data.inRoom; }; - Messaging.isRoomOwner = function (uid, roomId, callback) { - async.waterfall([ - function (next) { - db.getObjectField('chat:room:' + roomId, 'owner', next); - }, - function (owner, next) { - next(null, parseInt(uid, 10) === parseInt(owner, 10)); - }, - ], callback); + Messaging.roomExists = async roomId => db.exists('chat:room:' + roomId + ':uids'); + + Messaging.getUserCountInRoom = async roomId => db.sortedSetCard('chat:room:' + roomId + ':uids'); + + Messaging.isRoomOwner = async (uid, roomId) => { + const owner = await db.getObjectField('chat:room:' + roomId, 'owner'); + return parseInt(uid, 10) === parseInt(owner, 10); }; - Messaging.addUsersToRoom = function (uid, uids, roomId, callback) { - async.waterfall([ - function (next) { - Messaging.isUserInRoom(uid, roomId, next); - }, - function (inRoom, next) { - if (!inRoom) { - return next(new Error('[[error:cant-add-users-to-chat-room]]')); - } - const now = Date.now(); - const timestamps = uids.map(() => now); - db.sortedSetAdd('chat:room:' + roomId + ':uids', timestamps, uids, next); - }, - function (next) { - async.parallel({ - userCount: async.apply(db.sortedSetCard, 'chat:room:' + roomId + ':uids'), - roomData: async.apply(db.getObject, 'chat:room:' + roomId), - }, next); - }, - function (results, next) { - if (!results.roomData.hasOwnProperty('groupChat') && results.userCount > 2) { - return db.setObjectField('chat:room:' + roomId, 'groupChat', 1, next); - } - next(); - }, - ], callback); + Messaging.addUsersToRoom = async function (uid, uids, roomId) { + const now = Date.now(); + const timestamps = uids.map(() => now); + const inRoom = await Messaging.isUserInRoom(uid, roomId); + if (!inRoom) { + throw new Error('[[error:cant-add-users-to-chat-room]]'); + } + + await db.sortedSetAdd('chat:room:' + roomId + ':uids', timestamps, uids); + const [userCount, roomData] = await Promise.all([ + db.sortedSetCard('chat:room:' + roomId + ':uids'), + db.getObject('chat:room:' + roomId), + ]); + + if (!roomData.hasOwnProperty('groupChat') && userCount > 2) { + await db.setObjectField('chat:room:' + roomId, 'groupChat', 1); + } + + await Promise.all(uids.map(uid => Messaging.addSystemMessage('user-join', uid, roomId))); }; - Messaging.removeUsersFromRoom = function (uid, uids, roomId, callback) { - async.waterfall([ - function (next) { - async.parallel({ - isOwner: async.apply(Messaging.isRoomOwner, uid, roomId), - userCount: async.apply(Messaging.getUserCountInRoom, roomId), - }, next); - }, - function (results, next) { - if (!results.isOwner) { - return next(new Error('[[error:cant-remove-users-from-chat-room]]')); - } - if (results.userCount === 2) { - return next(new Error('[[error:cant-remove-last-user]]')); - } - Messaging.leaveRoom(uids, roomId, next); - }, - ], callback); + Messaging.removeUsersFromRoom = async (uid, uids, roomId) => { + const [isOwner, userCount] = await Promise.all([ + Messaging.isRoomOwner(uid, roomId), + Messaging.getUserCountInRoom(roomId), + ]); + + if (!isOwner) { + throw new Error('[[error:cant-remove-users-from-chat-room]]'); + } + if (userCount === 2) { + throw new Error('[[error:cant-remove-last-user]]'); + } + + await Messaging.leaveRoom(uids, roomId); }; - Messaging.leaveRoom = function (uids, roomId, callback) { - async.waterfall([ - function (next) { - db.sortedSetRemove('chat:room:' + roomId + ':uids', uids, next); - }, - function (next) { - var keys = uids.map(function (uid) { - return 'uid:' + uid + ':chat:rooms'; - }); - keys = keys.concat(uids.map(function (uid) { - return 'uid:' + uid + ':chat:rooms:unread'; - })); - db.sortedSetsRemove(keys, roomId, next); - }, - function (next) { - updateOwner(roomId, next); - }, - ], callback); + Messaging.leaveRoom = async (uids, roomId) => { + const keys = uids + .map(function (uid) { + return 'uid:' + uid + ':chat:rooms'; + }) + .concat(uids.map(function (uid) { + return 'uid:' + uid + ':chat:rooms:unread'; + })); + + await Promise.all([ + db.sortedSetRemove('chat:room:' + roomId + ':uids', uids), + db.sortedSetsRemove(keys, roomId), + ]); + + await Promise.all(uids.map(uid => Messaging.addSystemMessage('user-leave', uid, roomId))); + await updateOwner(roomId); }; - Messaging.leaveRooms = function (uid, roomIds, callback) { - async.waterfall([ - function (next) { - const roomKeys = roomIds.map(roomId => 'chat:room:' + roomId + ':uids'); - db.sortedSetsRemove(roomKeys, uid, next); - }, - function (next) { - db.sortedSetRemove([ - 'uid:' + uid + ':chat:rooms', - 'uid:' + uid + ':chat:rooms:unread', - ], roomIds, next); - }, - function (next) { - async.eachSeries(roomIds, updateOwner, next); - }, - ], callback); + Messaging.leaveRooms = async (uid, roomIds) => { + const roomKeys = roomIds.map(roomId => 'chat:room:' + roomId + ':uids'); + await Promise.all([ + db.sortedSetsRemove(roomKeys, uid), + db.sortedSetRemove([ + 'uid:' + uid + ':chat:rooms', + 'uid:' + uid + ':chat:rooms:unread', + ], roomIds), + ]); + + await Promise.all( + roomIds.map(roomId => updateOwner(roomId)) + .concat(roomIds.map(roomId => Messaging.addSystemMessage('user-leave', uid, roomId))) + ); }; - function updateOwner(roomId, callback) { - async.waterfall([ - function (next) { - db.getSortedSetRange('chat:room:' + roomId + ':uids', 0, 0, next); - }, - function (uids, next) { - var newOwner = uids[0] || 0; - db.setObjectField('chat:room:' + roomId, 'owner', newOwner, next); - }, - ], callback); + async function updateOwner(roomId) { + const uids = await db.getSortedSetRange('chat:room:' + roomId + ':uids', 0, 0); + const newOwner = uids[0] || 0; + await db.setObjectField('chat:room:' + roomId, 'owner', newOwner); } - Messaging.getUidsInRoom = function (roomId, start, stop, callback) { - db.getSortedSetRevRange('chat:room:' + roomId + ':uids', start, stop, callback); - }; + Messaging.getUidsInRoom = async (roomId, start, stop) => db.getSortedSetRevRange('chat:room:' + roomId + ':uids', start, stop); + + Messaging.getUsersInRoom = async (roomId, start, stop) => { + const uids = await Messaging.getUidsInRoom(roomId, start, stop); + const [users, ownerId] = await Promise.all([ + user.getUsersFields(uids, ['uid', 'username', 'picture', 'status']), + db.getObjectField('chat:room:' + roomId, 'owner'), + ]); - Messaging.getUsersInRoom = function (roomId, start, stop, callback) { - async.waterfall([ - function (next) { - Messaging.getUidsInRoom(roomId, start, stop, next); - }, - function (uids, next) { - user.getUsersFields(uids, ['uid', 'username', 'picture', 'status'], next); - }, - function (users, next) { - db.getObjectField('chat:room:' + roomId, 'owner', function (err, ownerId) { - next(err, users.map(function (user) { - user.isOwner = parseInt(user.uid, 10) === parseInt(ownerId, 10); - return user; - })); - }); - }, - ], callback); + return users.map(function (user) { + user.isOwner = parseInt(user.uid, 10) === parseInt(ownerId, 10); + return user; + }); }; - Messaging.renameRoom = function (uid, roomId, newName, callback) { + Messaging.renameRoom = async function (uid, roomId, newName) { if (!newName) { - return callback(new Error('[[error:invalid-name]]')); + throw new Error('[[error:invalid-name]]'); } newName = newName.trim(); if (newName.length > 75) { - return callback(new Error('[[error:chat-room-name-too-long]]')); + throw new Error('[[error:chat-room-name-too-long]]'); } - async.waterfall([ - function (next) { - plugins.fireHook('filter:chat.renameRoom', { - uid: uid, - roomId: roomId, - newName: newName, - }, next); - }, - function (result, next) { - Messaging.isRoomOwner(uid, roomId, next); - }, - function (isOwner, next) { - if (!isOwner) { - return next(new Error('[[error:no-privileges]]')); - } - db.setObjectField('chat:room:' + roomId, 'roomName', newName, next); - }, - async.apply(plugins.fireHook, 'action:chat.renameRoom', { - roomId: roomId, - newName: newName, - }), - ], callback); - }; - Messaging.canReply = function (roomId, uid, callback) { - async.waterfall([ - function (next) { - db.isSortedSetMember('chat:room:' + roomId + ':uids', uid, next); - }, - function (inRoom, next) { - plugins.fireHook('filter:messaging.canReply', { uid: uid, roomId: roomId, inRoom: inRoom, canReply: inRoom }, next); - }, - function (data, next) { - next(null, data.canReply); - }, - ], callback); + const payload = await plugins.fireHook('filter:chat.renameRoom', { + uid: uid, + roomId: roomId, + newName: newName, + }); + const isOwner = await Messaging.isRoomOwner(payload.uid, payload.roomId); + if (!isOwner) { + throw new Error('[[error:no-privileges]]'); + } + + await db.setObjectField('chat:room:' + payload.roomId, 'roomName', payload.newName); + await Messaging.addSystemMessage('room-rename, ' + payload.newName.replace(',', '%2C'), payload.uid, payload.roomId); + + plugins.fireHook('action:chat.renameRoom', { + roomId: payload.roomId, + newName: payload.newName, + }); }; - Messaging.loadRoom = function (uid, data, callback) { - async.waterfall([ - function (next) { - privileges.global.can('chat', uid, next); - }, - function (canChat, next) { - if (!canChat) { - return next(new Error('[[error:no-privileges]]')); - } + Messaging.canReply = async (roomId, uid) => { + const inRoom = db.isSortedSetMember('chat:room:' + roomId + ':uids', uid); + const data = await plugins.fireHook('filter:messaging.canReply', { uid: uid, roomId: roomId, inRoom: inRoom, canReply: inRoom }); + return data.canReply; + }; - Messaging.isUserInRoom(uid, data.roomId, next); - }, - function (inRoom, next) { - if (!inRoom) { - return callback(null, null); - } + Messaging.loadRoom = async (uid, data) => { + const canChat = await privileges.global.can('chat', uid); + if (!canChat) { + throw new Error('[[error:no-privileges]]'); + } + const inRoom = await Messaging.isUserInRoom(uid, data.roomId); + if (!inRoom) { + return null; + } - async.parallel({ - roomData: async.apply(Messaging.getRoomData, data.roomId), - canReply: async.apply(Messaging.canReply, data.roomId, uid), - users: async.apply(Messaging.getUsersInRoom, data.roomId, 0, -1), - messages: async.apply(Messaging.getMessages, { - callerUid: uid, - uid: data.uid || uid, - roomId: data.roomId, - isNew: false, - }), - isAdminOrGlobalMod: function (next) { - user.isAdminOrGlobalMod(uid, next); - }, - }, next); - }, - function (results, next) { - var room = results.roomData; - room.messages = results.messages; - room.isOwner = parseInt(room.owner, 10) === parseInt(uid, 10); - room.users = results.users.filter(function (user) { - return user && parseInt(user.uid, 10) && parseInt(user.uid, 10) !== uid; - }); - room.canReply = results.canReply; - room.groupChat = room.hasOwnProperty('groupChat') ? room.groupChat : results.users.length > 2; - room.usernames = Messaging.generateUsernames(results.users, uid); - room.maximumUsersInChatRoom = meta.config.maximumUsersInChatRoom; - room.maximumChatMessageLength = meta.config.maximumChatMessageLength; - room.showUserInput = !room.maximumUsersInChatRoom || room.maximumUsersInChatRoom > 2; - room.isAdminOrGlobalMod = results.isAdminOrGlobalMod; - next(null, room); - }, - ], callback); + const [roomData, canReply, users, messages, isAdminOrGlobalMod] = await Promise.all([ + Messaging.getRoomData(data.roomId), + Messaging.canReply(data.roomId, uid), + Messaging.getUsersInRoom(data.roomId, 0, -1), + Messaging.getMessages({ + callerUid: uid, + uid: data.uid || uid, + roomId: data.roomId, + isNew: false, + }), + user.isAdminOrGlobalMod(uid), + ]); + + var room = roomData; + room.messages = messages; + room.isOwner = parseInt(room.owner, 10) === parseInt(uid, 10); + room.users = users.filter(function (user) { + return user && parseInt(user.uid, 10) && parseInt(user.uid, 10) !== uid; + }); + room.canReply = canReply; + room.groupChat = room.hasOwnProperty('groupChat') ? room.groupChat : users.length > 2; + room.usernames = Messaging.generateUsernames(users, uid); + room.maximumUsersInChatRoom = meta.config.maximumUsersInChatRoom; + room.maximumChatMessageLength = meta.config.maximumChatMessageLength; + room.showUserInput = !room.maximumUsersInChatRoom || room.maximumUsersInChatRoom > 2; + room.isAdminOrGlobalMod = isAdminOrGlobalMod; + + return room; }; }; diff --git a/test/messaging.js b/test/messaging.js index 2dcf1bc833..4188e7e50b 100644 --- a/test/messaging.js +++ b/test/messaging.js @@ -113,6 +113,16 @@ describe('Messaging Library', function () { }); }); + it('should send a user-join system message when a chat room is created', (done) => { + socketModules.chats.getMessages({ uid: fooUid }, { uid: fooUid, roomId: roomId, start: 0 }, function (err, messages) { + assert.ifError(err); + assert.equal(messages.length, 1); + assert.strictEqual(messages[0].system, true); + assert.strictEqual(messages[0].content, 'user-join'); + done(); + }); + }); + it('should fail to add user to room with invalid data', function (done) { socketModules.chats.addUserToRoom({ uid: fooUid }, null, function (err) { assert.equal(err.message, '[[error:invalid-data]]'); @@ -185,6 +195,17 @@ describe('Messaging Library', function () { }); }); + it('should send a user-leave system message when a user leaves the chat room', (done) => { + socketModules.chats.getMessages({ uid: fooUid }, { uid: fooUid, roomId: roomId, start: 0 }, function (err, messages) { + assert.ifError(err); + assert.equal(messages.length, 3); + const message = messages.pop(); + assert.strictEqual(message.system, true); + assert.strictEqual(message.content, 'user-leave'); + done(); + }); + }); + it('should change owner when owner leaves room', function (done) { socketModules.chats.newRoom({ uid: herpUid }, { touid: fooUid }, function (err, roomId) { assert.ifError(err); @@ -330,7 +351,8 @@ describe('Messaging Library', function () { myRoomId = _roomId; assert.ifError(err); assert(myRoomId); - socketModules.chats.getRaw({ uid: bazUid }, { mid: 1 }, function (err) { + socketModules.chats.getRaw({ uid: bazUid }, { mid: 2 }, function (err) { + assert(err); assert.equal(err.message, '[[error:not-allowed]]'); socketModules.chats.send({ uid: bazUid }, { roomId: myRoomId, message: 'admin will see this' }, function (err, message) { assert.ifError(err); @@ -392,8 +414,8 @@ describe('Messaging Library', function () { }, function (err, messages) { assert.ifError(err); assert(Array.isArray(messages)); - assert.equal(messages[0].roomId, roomId); - assert.equal(messages[0].fromuid, fooUid); + assert.equal(messages[4].roomId, roomId); + assert.equal(messages[4].fromuid, fooUid); done(); }); }); @@ -449,6 +471,16 @@ describe('Messaging Library', function () { }); }); + it('should send a room-rename system message when a room is renamed', (done) => { + socketModules.chats.getMessages({ uid: fooUid }, { uid: fooUid, roomId: roomId, start: 0 }, function (err, messages) { + assert.ifError(err); + const message = messages.pop(); + assert.strictEqual(message.system, true); + assert.strictEqual(message.content, 'room-rename, new room name'); + done(); + }); + }); + it('should fail to load room with invalid-data', function (done) { socketModules.chats.loadRoom({ uid: fooUid }, null, function (err) { assert.equal(err.message, '[[error:invalid-data]]');