From 49267d54b7cca615f0cc9264620f9e0cd3830e65 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 28 Nov 2016 14:15:20 +0300 Subject: [PATCH] topics unread tests --- src/categories/unread.js | 3 + src/notifications.js | 4 +- src/socket.io/posts.js | 2 +- src/socket.io/topics/unread.js | 141 ++++++++++++------------ src/topics/follow.js | 5 +- src/topics/unread.js | 30 ++--- test/mocks/databasemock.js | 6 + test/topics.js | 194 +++++++++++++++++++++++++++++++++ 8 files changed, 299 insertions(+), 86 deletions(-) diff --git a/src/categories/unread.js b/src/categories/unread.js index 3805e1e78f..3a1ba27277 100644 --- a/src/categories/unread.js +++ b/src/categories/unread.js @@ -33,6 +33,9 @@ module.exports = function (Categories) { }; Categories.markAsUnreadForAll = function (cid, callback) { + if (!parseInt(cid, 10)) { + return callback(); + } callback = callback || function () {}; db.delete('cid:' + cid + ':read_by_uid', callback); }; diff --git a/src/notifications.js b/src/notifications.js index ed2e98b7da..b99700be01 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -352,7 +352,9 @@ var utils = require('../public/src/utils'); function (next) { db.sortedSetAdd('uid:' + uid + ':notifications:read', datetimes, nids, next); } - ], callback); + ], function (err) { + callback(err); + }); }); }; diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index 7cc43178d6..fe729a5c11 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -44,7 +44,7 @@ SocketPosts.reply = function (socket, data, callback) { callback(null, postData); - socket.emit('event:new_post', result); + websockets.in('uid_' + socket.uid).emit('event:new_post', result); user.updateOnlineUsers(socket.uid); diff --git a/src/socket.io/topics/unread.js b/src/socket.io/topics/unread.js index 029a0c9e74..39c6485a26 100644 --- a/src/socket.io/topics/unread.js +++ b/src/socket.io/topics/unread.js @@ -11,61 +11,67 @@ module.exports = function (SocketTopics) { if (!Array.isArray(tids) || !socket.uid) { return callback(new Error('[[error:invalid-data]]')); } - - topics.markAsRead(tids, socket.uid, function (err) { - if (err) { - return callback(err); + async.waterfall([ + function (next) { + topics.markAsRead(tids, socket.uid, next); + }, + function (hasMarked, next) { + if (hasMarked) { + topics.pushUnreadCount(socket.uid); + + topics.markTopicNotificationsRead(tids, socket.uid); + } + next(); } - - topics.pushUnreadCount(socket.uid); - - topics.markTopicNotificationsRead(tids, socket.uid); - - callback(); - }); + ], callback); }; SocketTopics.markTopicNotificationsRead = function (socket, tids, callback) { if (!Array.isArray(tids) || !socket.uid) { return callback(new Error('[[error:invalid-data]]')); } - topics.markTopicNotificationsRead(tids, socket.uid); + topics.markTopicNotificationsRead(tids, socket.uid, callback); }; SocketTopics.markAllRead = function (socket, data, callback) { - topics.markAllRead(socket.uid, function (err) { - if (err) { - return callback(err); + if (!socket.uid) { + return callback(new Error('[[error:invalid-uid]]')); + } + async.waterfall([ + function (next) { + topics.markAllRead(socket.uid, next); + }, + function (next) { + topics.pushUnreadCount(socket.uid); + next(); } - - topics.pushUnreadCount(socket.uid); - - callback(); - }); + ], callback); }; SocketTopics.markCategoryTopicsRead = function (socket, cid, callback) { - topics.getUnreadTids(cid, socket.uid, '', function (err, tids) { - if (err) { - return callback(err); + async.waterfall([ + function (next) { + topics.getUnreadTids(cid, socket.uid, '', next); + }, + function (tids, next) { + SocketTopics.markAsRead(socket, tids, next); } - - SocketTopics.markAsRead(socket, tids, callback); - }); + ], callback); }; SocketTopics.markUnread = function (socket, tid, callback) { if (!tid || !socket.uid) { return callback(new Error('[[error:invalid-data]]')); } - topics.markUnread(tid, socket.uid, function (err) { - if (err) { - return callback(err); + async.waterfall([ + function (next) { + topics.markUnread(tid, socket.uid, next); + }, + function (next) { + topics.pushUnreadCount(socket.uid); + next(); } - - topics.pushUnreadCount(socket.uid); - callback(); - }); + ], callback); }; SocketTopics.markAsUnreadForAll = function (socket, tids, callback) { @@ -77,42 +83,41 @@ module.exports = function (SocketTopics) { return callback(new Error('[[error:no-privileges]]')); } - user.isAdministrator(socket.uid, function (err, isAdmin) { - if (err) { - return callback(err); - } - - async.each(tids, function (tid, next) { - async.waterfall([ - function (next) { - topics.exists(tid, next); - }, - function (exists, next) { - if (!exists) { - return next(new Error('[[error:invalid-tid]]')); - } - topics.getTopicField(tid, 'cid', next); - }, - function (cid, next) { - user.isModerator(socket.uid, cid, next); - }, - function (isMod, next) { - if (!isAdmin && !isMod) { - return next(new Error('[[error:no-privileges]]')); + async.waterfall([ + function (next) { + user.isAdministrator(socket.uid, next); + }, + function (isAdmin, next) { + async.each(tids, function (tid, next) { + async.waterfall([ + function (next) { + topics.exists(tid, next); + }, + function (exists, next) { + if (!exists) { + return next(new Error('[[error:no-topic]]')); + } + topics.getTopicField(tid, 'cid', next); + }, + function (cid, next) { + user.isModerator(socket.uid, cid, next); + }, + function (isMod, next) { + if (!isAdmin && !isMod) { + return next(new Error('[[error:no-privileges]]')); + } + topics.markAsUnreadForAll(tid, next); + }, + function (next) { + topics.updateRecent(tid, Date.now(), next); } - topics.markAsUnreadForAll(tid, next); - }, - function (next) { - topics.updateRecent(tid, Date.now(), next); - } - ], next); - }, function (err) { - if (err) { - return callback(err); - } + ], next); + }, next); + }, + function (next) { topics.pushUnreadCount(socket.uid); - callback(); - }); - }); + next(); + } + ], callback); }; }; \ No newline at end of file diff --git a/src/topics/follow.js b/src/topics/follow.js index 6993920fc0..0667218588 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -74,7 +74,10 @@ module.exports = function (Topics) { function (next) { method2(tid, uid, next); }, - async.apply(plugins.fireHook, hook, {uid: uid, tid: tid}) + function (next) { + plugins.fireHook(hook, {uid: uid, tid: tid}); + next(); + } ], callback); } diff --git a/src/topics/unread.js b/src/topics/unread.js index c7cabb2881..e61ca46d59 100644 --- a/src/topics/unread.js +++ b/src/topics/unread.js @@ -2,7 +2,6 @@ 'use strict'; var async = require('async'); -var winston = require('winston'); var db = require('../database'); var user = require('../user'); @@ -277,9 +276,10 @@ module.exports = function (Topics) { ], callback); }; - Topics.markTopicNotificationsRead = function (tids, uid) { + Topics.markTopicNotificationsRead = function (tids, uid, callback) { + callback = callback || function () {}; if (!Array.isArray(tids) || !tids.length) { - return; + return callback(); } async.waterfall([ @@ -288,23 +288,23 @@ module.exports = function (Topics) { }, function (nids, next) { notifications.markReadMultiple(nids, uid, next); + }, + function (next) { + user.notifications.pushCount(uid); + next(); } - ], function (err) { - if (err) { - return winston.error(err); - } - user.notifications.pushCount(uid); - }); + ], callback); }; Topics.markCategoryUnreadForAll = function (tid, callback) { - Topics.getTopicField(tid, 'cid', function (err, cid) { - if(err) { - return callback(err); + async.waterfall([ + function (next) { + Topics.getTopicField(tid, 'cid', next); + }, + function (cid, next) { + categories.markAsUnreadForAll(cid, next); } - - categories.markAsUnreadForAll(cid, callback); - }); + ], callback); }; Topics.hasReadTopics = function (tids, uid, callback) { diff --git a/test/mocks/databasemock.js b/test/mocks/databasemock.js index e42bee470e..f1fcb89c5a 100644 --- a/test/mocks/databasemock.js +++ b/test/mocks/databasemock.js @@ -110,6 +110,12 @@ enableDefaultPlugins(next); }, + function (next) { + meta.themes.set({ + type: 'local', + id: 'nodebb-theme-persona' + }, next); + }, function (next) { // nconf defaults, if not set in config if (!nconf.get('upload_path')) { diff --git a/test/topics.js b/test/topics.js index 4972257f5b..392296928a 100644 --- a/test/topics.js +++ b/test/topics.js @@ -683,6 +683,200 @@ describe('Topic\'s', function () { }); }); + describe('unread', function () { + var socketTopics = require('../src/socket.io/topics'); + var tid; + var mainPid; + var uid; + before(function (done) { + async.parallel({ + topic: function (next) { + topics.post({uid: topic.userId, title: 'unread topic', content: 'unread topic content', cid: topic.categoryId}, next); + }, + user: function (next) { + User.create({username: 'regularJoe'}, next); + } + }, function (err, results) { + assert.ifError(err); + tid = results.topic.topicData.tid; + mainPid = results.topic.postData.pid; + uid = results.user; + done(); + }); + }); + + it('should fail with invalid data', function (done) { + socketTopics.markUnread({uid: adminUid}, null, function (err) { + assert.equal(err.message, '[[error:invalid-data]]'); + done(); + }); + }); + + it('should fail if topic does not exist', function (done) { + socketTopics.markUnread({uid: adminUid}, 1231082, function (err) { + assert.equal(err.message, '[[error:no-topic]]'); + done(); + }); + }); + + it('should mark topic unread', function (done) { + socketTopics.markUnread({uid: adminUid}, tid, function (err) { + assert.ifError(err); + topics.hasReadTopic(tid, adminUid, function (err, hasRead) { + assert.ifError(err); + assert.equal(hasRead, false); + done(); + }); + }); + }); + + + it('should fail with invalid data', function (done) { + socketTopics.markAsRead({uid: 0}, null, function (err) { + assert.equal(err.message, '[[error:invalid-data]]'); + done(); + }); + }); + + + it('should mark topic read', function (done) { + socketTopics.markAsRead({uid: adminUid}, [tid], function (err) { + assert.ifError(err); + topics.hasReadTopic(tid, adminUid, function (err, hasRead) { + assert.ifError(err); + assert(hasRead); + done(); + }); + }); + }); + + it('should fail with invalid data', function (done) { + socketTopics.markTopicNotificationsRead({uid: 0}, null, function (err) { + assert.equal(err.message, '[[error:invalid-data]]'); + done(); + }); + }); + + it('should mark topic notifications read', function (done) { + var socketPosts = require('../src/socket.io/posts'); + + async.waterfall([ + function (next) { + socketTopics.follow({uid: adminUid}, tid, next); + }, + function (next) { + socketPosts.reply({uid: uid}, {content: 'some content', tid: tid}, next); + }, + function (data, next) { + setTimeout(next, 2500); + }, + function (next) { + User.notifications.getUnreadCount(adminUid, next); + }, + function (count, next) { + assert.equal(count, 1); + socketTopics.markTopicNotificationsRead({uid: adminUid}, [tid], next); + }, + function (next) { + User.notifications.getUnreadCount(adminUid, next); + }, + function (count, next) { + assert.equal(count, 0); + next(); + } + ], function (err) { + assert.ifError(err); + done(); + }); + }); + + it('should fail with invalid data', function (done) { + socketTopics.markAllRead({uid: 0}, null, function (err) { + assert.equal(err.message, '[[error:invalid-uid]]'); + done(); + }); + }); + + it('should mark all read', function (done) { + socketTopics.markUnread({uid: adminUid}, tid, function (err) { + assert.ifError(err); + socketTopics.markAllRead({uid: adminUid}, {}, function (err) { + assert.ifError(err); + topics.hasReadTopic(tid, adminUid, function (err, hasRead) { + assert.ifError(err); + assert(hasRead); + done(); + }); + }); + }); + }); + + it('should mark all read', function (done) { + socketTopics.markUnread({uid: adminUid}, tid, function (err) { + assert.ifError(err); + socketTopics.markCategoryTopicsRead({uid: adminUid}, topic.categoryId, function (err) { + assert.ifError(err); + topics.hasReadTopic(tid, adminUid, function (err, hasRead) { + assert.ifError(err); + assert(hasRead); + done(); + }); + }); + }); + }); + + + it('should fail with invalid data', function (done) { + socketTopics.markAsUnreadForAll({uid: adminUid}, null, function (err) { + assert.equal(err.message, '[[error:invalid-tid]]'); + done(); + }); + }); + + it('should fail with invalid data', function (done) { + socketTopics.markAsUnreadForAll({uid: 0}, [tid], function (err) { + assert.equal(err.message, '[[error:no-privileges]]'); + done(); + }); + }); + + it('should fail if user is not admin', function (done) { + socketTopics.markAsUnreadForAll({uid: uid}, [tid], function (err) { + assert.equal(err.message, '[[error:no-privileges]]'); + done(); + }); + }); + + it('should fail if topic does not exist', function (done) { + socketTopics.markAsUnreadForAll({uid: uid}, [12312313], function (err) { + assert.equal(err.message, '[[error:no-topic]]'); + done(); + }); + }); + + it('should mark topic unread for everyone', function (done) { + socketTopics.markAsUnreadForAll({uid: adminUid}, [tid], function (err) { + assert.ifError(err); + async.parallel({ + adminRead: function (next) { + topics.hasReadTopic(tid, adminUid, next); + }, + regularRead: function (next) { + topics.hasReadTopic(tid, uid, next); + } + }, function (err, results) { + assert.ifError(err); + assert.equal(results.adminRead, false); + assert.equal(results.regularRead, false); + done(); + }); + }); + }); + + + + + }); after(function (done) {