diff --git a/src/categories/index.js b/src/categories/index.js index cb18c6cb32..54d2a33bcd 100644 --- a/src/categories/index.js +++ b/src/categories/index.js @@ -77,7 +77,7 @@ Categories.getAllCategories = async function (uid) { }; Categories.getCidsByPrivilege = async function (set, uid, privilege) { - const cids = await Categories.getAllCidsFromSet('categories:cid'); + const cids = await Categories.getAllCidsFromSet(set); return await privileges.categories.filterCids(privilege, cids, uid); }; diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index c0f0fd56ae..46b1b3e67b 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -213,7 +213,7 @@ helpers.getCategories = async function (set, uid, privilege, selectedCid) { helpers.getCategoriesByStates = async function (uid, selectedCid, states) { let cids = await user.getCategoriesByStates(uid, states); - cids = await privileges.categories.filterCids('read', cids, uid); + cids = await privileges.categories.filterCids('topics:read', cids, uid); return await getCategoryData(cids, uid, selectedCid); }; diff --git a/src/controllers/recent.js b/src/controllers/recent.js index 7df255de4a..78e98b4d0e 100644 --- a/src/controllers/recent.js +++ b/src/controllers/recent.js @@ -48,7 +48,7 @@ recentController.getData = async function (req, url, sort) { const stop = start + settings.topicsPerPage - 1; const data = await topics.getSortedTopics({ - cids: cid, + cids: cid || categoryData.categories.map(c => c.cid), uid: req.uid, start: start, stop: stop, diff --git a/src/database/redis/sorted.js b/src/database/redis/sorted.js index 2ced68c2d0..cb167c876e 100644 --- a/src/database/redis/sorted.js +++ b/src/database/redis/sorted.js @@ -11,28 +11,28 @@ module.exports = function (module) { require('./sorted/intersect')(module); module.getSortedSetRange = async function (key, start, stop) { - return await sortedSetRange('zrange', key, start, stop, false); + return await sortedSetRange('zrange', key, start, stop, '-inf', '+inf', false); }; module.getSortedSetRevRange = async function (key, start, stop) { - return await sortedSetRange('zrevrange', key, start, stop, false); + return await sortedSetRange('zrevrange', key, start, stop, '-inf', '+inf', false); }; module.getSortedSetRangeWithScores = async function (key, start, stop) { - return await sortedSetRange('zrange', key, start, stop, true); + return await sortedSetRange('zrange', key, start, stop, '-inf', '+inf', true); }; module.getSortedSetRevRangeWithScores = async function (key, start, stop) { - return await sortedSetRange('zrevrange', key, start, stop, true); + return await sortedSetRange('zrevrange', key, start, stop, '-inf', '+inf', true); }; - async function sortedSetRange(method, key, start, stop, withScores) { + async function sortedSetRange(method, key, start, stop, min, max, withScores) { if (Array.isArray(key)) { if (!key.length) { return []; } const batch = module.client.batch(); - key.forEach(key => batch[method]([key, 0, stop, 'WITHSCORES'])); + key.forEach(key => batch[method](genParams(method, key, 0, stop, min, max, true))); const data = await helpers.execBatch(batch); const batchData = data.map(setData => helpers.zsetToObjectArray(setData)); @@ -48,11 +48,7 @@ module.exports = function (module) { return objects; } - var params = [key, start, stop]; - if (withScores) { - params.push('WITHSCORES'); - } - + const params = genParams(method, key, start, stop, min, max, withScores); const data = await module.client.async[method](params); if (!withScores) { return data; @@ -61,25 +57,46 @@ module.exports = function (module) { return objects; } + function genParams(method, key, start, stop, min, max, withScores) { + const params = { + zrevrange: [key, start, stop], + zrange: [key, start, stop], + zrangebyscore: [key, min, max], + zrevrangebyscore: [key, max, min], + }; + if (withScores) { + params[method].push('WITHSCORES'); + } + + if (method === 'zrangebyscore' || method === 'zrevrangebyscore') { + const count = stop !== -1 ? stop - start + 1 : stop; + params[method].push('LIMIT', start, count); + } + return params[method]; + } + module.getSortedSetRangeByScore = async function (key, start, count, min, max) { - return await module.client.async.zrangebyscore([key, min, max, 'LIMIT', start, count]); + return await sortedSetRangeByScore('zrangebyscore', key, start, count, min, max, false); }; module.getSortedSetRevRangeByScore = async function (key, start, count, max, min) { - return await module.client.async.zrevrangebyscore([key, max, min, 'LIMIT', start, count]); + return await sortedSetRangeByScore('zrevrangebyscore', key, start, count, min, max, false); }; module.getSortedSetRangeByScoreWithScores = async function (key, start, count, min, max) { - return await sortedSetRangeByScoreWithScores('zrangebyscore', key, start, count, min, max); + return await sortedSetRangeByScore('zrangebyscore', key, start, count, min, max, true); }; module.getSortedSetRevRangeByScoreWithScores = async function (key, start, count, max, min) { - return await sortedSetRangeByScoreWithScores('zrevrangebyscore', key, start, count, max, min); + return await sortedSetRangeByScore('zrevrangebyscore', key, start, count, min, max, true); }; - async function sortedSetRangeByScoreWithScores(method, key, start, count, min, max) { - const data = await module.client.async[method]([key, min, max, 'WITHSCORES', 'LIMIT', start, count]); - return helpers.zsetToObjectArray(data); + async function sortedSetRangeByScore(method, key, start, count, min, max, withScores) { + if (parseInt(count, 10) === 0) { + return []; + } + const stop = (parseInt(count, 10) === -1) ? -1 : (start + count - 1); + return await sortedSetRange(method, key, start, stop, min, max, withScores); } module.sortedSetCount = async function (key, min, max) { diff --git a/src/privileges/categories.js b/src/privileges/categories.js index b6917fa307..397796b4a4 100644 --- a/src/privileges/categories.js +++ b/src/privileges/categories.js @@ -103,9 +103,7 @@ module.exports = function (privileges) { cids = _.uniq(cids); const results = await privileges.categories.getBase(privilege, cids, uid); - return cids.filter(function (cid, index) { - return !!cid && !results.categories[index].disabled && (results.allowedTo[index] || results.isAdmin); - }); + return cids.filter((cid, index) => !!cid && !results.categories[index].disabled && (results.allowedTo[index] || results.isAdmin)); }; privileges.categories.getBase = async function (privilege, cids, uid) { diff --git a/src/privileges/topics.js b/src/privileges/topics.js index f13dbb1f4a..af9e57698d 100644 --- a/src/privileges/topics.js +++ b/src/privileges/topics.js @@ -71,10 +71,7 @@ module.exports = function (privileges) { let cids = _.uniq(topicsData.map(topic => topic.cid)); const results = await privileges.categories.getBase(privilege, cids, uid); - cids = cids.filter(function (cid, index) { - return !results.categories[index].disabled && - (results.allowedTo[index] || results.isAdmin); - }); + cids = cids.filter((cid, index) => !results.categories[index].disabled && (results.allowedTo[index] || results.isAdmin)); const cidsSet = new Set(cids); diff --git a/src/socket.io/topics/unread.js b/src/socket.io/topics/unread.js index 05a2cad951..e997e1608d 100644 --- a/src/socket.io/topics/unread.js +++ b/src/socket.io/topics/unread.js @@ -1,5 +1,6 @@ 'use strict'; +const db = require('../../database'); const user = require('../../user'); const topics = require('../../topics'); @@ -53,7 +54,7 @@ module.exports = function (SocketTopics) { throw new Error('[[error:no-privileges]]'); } const isAdmin = await user.isAdministrator(socket.uid); - + const now = Date.now(); await Promise.all(tids.map(async (tid) => { const topicData = await topics.getTopicFields(tid, ['tid', 'cid']); if (!topicData.tid) { @@ -64,7 +65,8 @@ module.exports = function (SocketTopics) { throw new Error('[[error:no-privileges]]'); } await topics.markAsUnreadForAll(tid); - await topics.updateRecent(tid, Date.now()); + await topics.updateRecent(tid, now); + await db.sortedSetAdd('cid:' + topicData.cid + ':tids:lastposttime', now, tid); })); topics.pushUnreadCount(socket.uid); }; diff --git a/src/topics/follow.js b/src/topics/follow.js index 796840dc42..872cb92ce9 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -134,8 +134,7 @@ module.exports = function (Topics) { return []; } const scores = await db.sortedSetScores('uid:' + uid + ':followed_tids', tids); - tids = tids.filter((tid, index) => tid && !!scores[index]); - return tids; + return tids.filter((tid, index) => tid && !!scores[index]); }; Topics.filterNotIgnoredTids = async function (tids, uid) { @@ -143,8 +142,7 @@ module.exports = function (Topics) { return tids; } const scores = await db.sortedSetScores('uid:' + uid + ':ignored_tids', tids); - tids = tids.filter((tid, index) => tid && !scores[index]); - return tids; + return tids.filter((tid, index) => tid && !scores[index]); }; Topics.notifyFollowers = async function (postData, exceptUid) { diff --git a/src/topics/sorted.js b/src/topics/sorted.js index 57903be8f5..4048187e8b 100644 --- a/src/topics/sorted.js +++ b/src/topics/sorted.js @@ -33,29 +33,32 @@ module.exports = function (Topics) { async function getTids(params) { let tids = []; - if (params.term === 'alltime') { - if (params.cids) { - tids = await getCidTids(params.cids, params.sort); - } else { - tids = await db.getSortedSetRevRange('topics:' + params.sort, 0, 199); + if (params.term !== 'alltime') { + tids = await Topics.getLatestTidsFromSet('topics:tid', 0, -1, params.term); + if (params.filter === 'watched') { + tids = await Topics.filterWatchedTids(tids, params.uid); } + } else if (params.filter === 'watched') { + tids = await db.getSortedSetRevRange('uid:' + params.uid + ':followed_tids', 0, -1); + } else if (params.cids) { + tids = await getCidTids(params); } else { - tids = await Topics.getLatestTidsFromSet('topics:tid', 0, -1, params.term); + tids = await db.getSortedSetRevRange('topics:' + params.sort, 0, 199); } - if (params.term !== 'alltime' || params.cids || params.floatPinned) { + if (params.term !== 'alltime' || params.cids || params.filter === 'watched' || params.floatPinned) { tids = await sortTids(tids, params); } - return await filterTids(tids, params); + return await filterTids(tids.slice(0, 200), params); } - async function getCidTids(cids, sort) { + async function getCidTids(params) { const sets = []; const pinnedSets = []; - cids.forEach(function (cid) { - if (sort === 'recent') { + params.cids.forEach(function (cid) { + if (params.sort === 'recent') { sets.push('cid:' + cid + ':tids'); } else { - sets.push('cid:' + cid + ':tids' + (sort ? ':' + sort : '')); + sets.push('cid:' + cid + ':tids' + (params.sort ? ':' + params.sort : '')); } pinnedSets.push('cid:' + cid + ':tids:pinned'); }); @@ -115,9 +118,7 @@ module.exports = function (Topics) { const filter = params.filter; const uid = params.uid; - if (filter === 'watched') { - tids = await Topics.filterWatchedTids(tids, uid); - } else if (filter === 'new') { + if (filter === 'new') { tids = await Topics.filterNewTids(tids, uid); } else if (filter === 'unreplied') { tids = await Topics.filterUnrepliedTids(tids); @@ -130,7 +131,7 @@ module.exports = function (Topics) { const topicCids = _.uniq(topicData.map(topic => topic.cid)).filter(Boolean); async function getIgnoredCids() { - if (filter === 'watched' || meta.config.disableRecentCategoryFilter) { + if (params.cids || filter === 'watched' || meta.config.disableRecentCategoryFilter) { return []; } return await categories.isIgnored(topicCids, uid); @@ -144,9 +145,7 @@ module.exports = function (Topics) { topicData = filtered; const cids = params.cids && params.cids.map(String); - tids = topicData.filter(function (topic) { - return topic && topic.cid && !isCidIgnored[topic.cid] && (!cids || (cids.length && cids.includes(topic.cid.toString()))); - }).map(topic => topic.tid); + tids = topicData.filter(t => t && t.cid && !isCidIgnored[t.cid] && (!cids || cids.includes(String(t.cid)))).map(t => t.tid); const result = await plugins.fireHook('filter:topics.filterSortedTids', { tids: tids, params: params }); return result.tids; diff --git a/src/topics/unread.js b/src/topics/unread.js index 9ce3c67737..24f74363a3 100644 --- a/src/topics/unread.js +++ b/src/topics/unread.js @@ -1,18 +1,18 @@ 'use strict'; -var async = require('async'); -var _ = require('lodash'); - -var db = require('../database'); -var user = require('../user'); -var posts = require('../posts'); -var notifications = require('../notifications'); -var categories = require('../categories'); -var privileges = require('../privileges'); -var meta = require('../meta'); -var utils = require('../utils'); -var plugins = require('../plugins'); +const async = require('async'); +const _ = require('lodash'); + +const db = require('../database'); +const user = require('../user'); +const posts = require('../posts'); +const notifications = require('../notifications'); +const categories = require('../categories'); +const privileges = require('../privileges'); +const meta = require('../meta'); +const utils = require('../utils'); +const plugins = require('../plugins'); module.exports = function (Topics) { Topics.getTotalUnread = async function (uid, filter) { @@ -22,7 +22,7 @@ module.exports = function (Topics) { }; Topics.getUnreadTopics = async function (params) { - var unreadTopics = { + const unreadTopics = { showSelect: true, nextStart: 0, topics: [], @@ -57,51 +57,18 @@ module.exports = function (Topics) { Topics.getUnreadData = async function (params) { const uid = parseInt(params.uid, 10); - const counts = { - '': 0, - new: 0, - watched: 0, - unreplied: 0, - }; - const noUnreadData = { - tids: [], - counts: counts, - tidsByFilter: { - '': [], - new: [], - watched: [], - unreplied: [], - }, - }; - - if (uid <= 0) { - return noUnreadData; - } params.filter = params.filter || ''; - var cutoff = params.cutoff || Topics.unreadCutoff(); - if (params.cid && !Array.isArray(params.cid)) { params.cid = [params.cid]; } - const [ignoredTids, recentTids, userScores, tids_unread] = await Promise.all([ - user.getIgnoredTids(uid, 0, -1), - db.getSortedSetRevRangeByScoreWithScores('topics:recent', 0, -1, '+inf', cutoff), - db.getSortedSetRevRangeByScoreWithScores('uid:' + uid + ':tids_read', 0, -1, '+inf', cutoff), - db.getSortedSetRevRangeWithScores('uid:' + uid + ':tids_unread', 0, -1), - ]); - if (recentTids && !recentTids.length && !tids_unread.length) { - return noUnreadData; + const data = await getTids(params); + if (!data.tids && !data.tids.length) { + return data; } - const data = await filterTopics(params, { - ignoredTids: ignoredTids, - recentTids: recentTids, - userScores: userScores, - tids_unread: tids_unread, - }); const result = await plugins.fireHook('filter:topics.getUnreadTids', { uid: uid, tids: data.tids, @@ -113,83 +80,69 @@ module.exports = function (Topics) { return result; }; - async function filterTopics(params, results) { - const counts = { - '': 0, - new: 0, - watched: 0, - unreplied: 0, - }; - - const tidsByFilter = { - '': [], - new: [], - watched: [], - unreplied: [], - }; + async function getTids(params) { + const counts = { '': 0, new: 0, watched: 0, unreplied: 0 }; + const tidsByFilter = { '': [], new: [], watched: [], unreplied: [] }; - var userRead = {}; - results.userScores.forEach(function (userItem) { - userRead[userItem.value] = userItem.score; - }); + if (params.uid <= 0) { + return { counts: counts, tids: [], tidsByFilter: tidsByFilter }; + } - results.recentTids = results.recentTids.concat(results.tids_unread); - results.recentTids.sort(function (a, b) { - return b.score - a.score; - }); + const cutoff = params.cutoff || Topics.unreadCutoff(); - var tids = results.recentTids.filter(function (recentTopic) { - if (results.ignoredTids.includes(String(recentTopic.value))) { - return false; - } - return !userRead[recentTopic.value] || recentTopic.score > userRead[recentTopic.value]; - }); + const [followedTids, ignoredTids, recentTids, userScores, tids_unread] = await Promise.all([ + getFollowedTids(params), + user.getIgnoredTids(params.uid, 0, -1), + getRecentTids(params), + db.getSortedSetRevRangeByScoreWithScores('uid:' + params.uid + ':tids_read', 0, -1, '+inf', cutoff), + db.getSortedSetRevRangeWithScores('uid:' + params.uid + ':tids_unread', 0, -1), + ]); - tids = _.uniq(tids.map(topic => topic.value)); + const userReadTime = _.mapValues(_.keyBy(userScores, 'value'), 'score'); + const isTopicsFollowed = _.mapValues(_.keyBy(followedTids, 'value'), 'score'); - var cid = params.cid; - var uid = params.uid; - var cids; - var topicData; + const unreadTopics = _.unionWith(recentTids, followedTids.concat(tids_unread), (a, b) => a.value === b.value) + .filter(t => !ignoredTids.includes(t.value) && (!userReadTime[t.value] || t.score > userReadTime[t.value])) + .sort((a, b) => b.score - a.score); - tids = tids.slice(0, 200); + let tids = _.uniq(unreadTopics.map(topic => topic.value)).slice(0, 200); if (!tids.length) { return { counts: counts, tids: tids, tidsByFilter: tidsByFilter }; } - const blockedUids = await user.blocks.list(uid); + + const blockedUids = await user.blocks.list(params.uid); tids = await filterTidsThatHaveBlockedPosts({ - uid: uid, + uid: params.uid, tids: tids, blockedUids: blockedUids, - recentTids: results.recentTids, + recentTids: recentTids, }); - topicData = await Topics.getTopicsFields(tids, ['tid', 'cid', 'uid', 'postcount']); - cids = _.uniq(topicData.map(topic => topic.cid)).filter(Boolean); + const topicData = await Topics.getTopicsFields(tids, ['tid', 'cid', 'uid', 'postcount']); + const topicCids = _.uniq(topicData.map(topic => topic.cid)).filter(Boolean); - const [isTopicsFollowed, categoryWatchState, readCids] = await Promise.all([ - db.sortedSetScores('uid:' + uid + ':followed_tids', tids), - categories.getWatchState(cids, uid), - privileges.categories.filterCids('read', cids, uid), + const [categoryWatchState, readCids] = await Promise.all([ + categories.getWatchState(topicCids, params.uid), + privileges.categories.filterCids('topics:read', topicCids, params.uid), ]); - cid = cid && cid.map(String); + + const filterCids = params.cid && params.cid.map(String); const readableCids = readCids.map(String); - const userCidState = _.zipObject(cids, categoryWatchState); + const userCidState = _.zipObject(topicCids, categoryWatchState); - topicData.forEach(function (topic, index) { - function cidMatch(topicCid) { - return (!cid || (cid.length && cid.includes(String(topicCid)))) && readableCids.includes(String(topicCid)); + topicData.forEach(function (topic) { + function cidMatch() { + return (!filterCids || (filterCids.length && filterCids.includes(String(topic.cid)))) && readableCids.includes(String(topic.cid)); } - if (topic && topic.cid && cidMatch(topic.cid) && !blockedUids.includes(parseInt(topic.uid, 10))) { - topic.tid = parseInt(topic.tid, 10); - if ((isTopicsFollowed[index] || userCidState[topic.cid] === categories.watchStates.watching)) { + if (topic && topic.cid && cidMatch() && !blockedUids.includes(topic.uid)) { + if (isTopicsFollowed[topic.tid] || userCidState[topic.cid] === categories.watchStates.watching) { tidsByFilter[''].push(topic.tid); } - if (isTopicsFollowed[index]) { + if (isTopicsFollowed[topic.tid]) { tidsByFilter.watched.push(topic.tid); } @@ -197,11 +150,12 @@ module.exports = function (Topics) { tidsByFilter.unreplied.push(topic.tid); } - if (!userRead[topic.tid]) { + if (!userReadTime[topic.tid]) { tidsByFilter.new.push(topic.tid); } } }); + counts[''] = tidsByFilter[''].length; counts.watched = tidsByFilter.watched.length; counts.unreplied = tidsByFilter.unreplied.length; @@ -214,6 +168,25 @@ module.exports = function (Topics) { }; } + async function getRecentTids(params) { + if (params.filter === 'watched') { + return []; + } + const cutoff = params.cutoff || Topics.unreadCutoff(); + const cids = params.cid || await user.getWatchedCategories(params.uid); + const keys = cids.map(cid => 'cid:' + cid + ':tids:lastposttime'); + return await db.getSortedSetRevRangeByScoreWithScores(keys, 0, -1, '+inf', cutoff); + } + + async function getFollowedTids(params) { + const tids = await db.getSortedSetRevRange('uid:' + params.uid + ':followed_tids', 0, -1); + const scores = await db.sortedSetScores('topics:recent', tids); + const cutoff = params.cutoff || Topics.unreadCutoff(); + + const data = tids.map((tid, index) => ({ value: tid, score: scores[index] })); + return data.filter(item => item.score > cutoff); + } + async function filterTidsThatHaveBlockedPosts(params) { if (!params.blockedUids.length) { return params.tids; @@ -234,14 +207,14 @@ module.exports = function (Topics) { } async function doesTidHaveUnblockedUnreadPosts(tid, params) { - var userLastReadTimestamp = params.userLastReadTimestamp; + const userLastReadTimestamp = params.userLastReadTimestamp; if (!userLastReadTimestamp) { return true; } - var start = 0; - var count = 3; - var done = false; - var hasUnblockedUnread = params.topicTimestamp > userLastReadTimestamp; + let start = 0; + const count = 3; + let done = false; + let hasUnblockedUnread = params.topicTimestamp > userLastReadTimestamp; if (!params.blockedUids.length) { return hasUnblockedUnread; } @@ -252,9 +225,7 @@ module.exports = function (Topics) { return hasUnblockedUnread; } let postData = await posts.getPostsFields(pidsSinceLastVisit, ['pid', 'uid']); - postData = postData.filter(function (post) { - return !params.blockedUids.includes(parseInt(post.uid, 10)); - }); + postData = postData.filter(post => !params.blockedUids.includes(parseInt(post.uid, 10))); done = postData.length > 0; hasUnblockedUnread = postData.length > 0; @@ -295,23 +266,21 @@ module.exports = function (Topics) { db.sortedSetScores('uid:' + uid + ':tids_read', tids), ]); - tids = tids.filter(function (tid, index) { - return topicScores[index] && (!userScores[index] || userScores[index] < topicScores[index]); - }); + tids = tids.filter((tid, index) => topicScores[index] && (!userScores[index] || userScores[index] < topicScores[index])); if (!tids.length) { return false; } - var now = Date.now(); - var scores = tids.map(() => now); + const now = Date.now(); + const scores = tids.map(() => now); const [topicData] = await Promise.all([ Topics.getTopicsFields(tids, ['cid']), db.sortedSetAdd('uid:' + uid + ':tids_read', scores, tids), db.sortedSetRemove('uid:' + uid + ':tids_unread', tids), ]); - var cids = _.uniq(topicData.map(t => t && t.cid).filter(Boolean)); + const cids = _.uniq(topicData.map(t => t && t.cid).filter(Boolean)); await categories.markAsRead(cids, uid); plugins.fireHook('action:topics.markAsRead', { uid: uid, tids: tids }); @@ -350,9 +319,9 @@ module.exports = function (Topics) { user.blocks.list(uid), ]); - var cutoff = Topics.unreadCutoff(); - var result = tids.map(function (tid, index) { - var read = !tids_unread[index] && + const cutoff = Topics.unreadCutoff(); + const result = tids.map(function (tid, index) { + const read = !tids_unread[index] && (topicScores[index] < cutoff || !!(userScores[index] && userScores[index] >= topicScores[index])); return { tid: tid, read: read, index: index }; diff --git a/test/database/sorted.js b/test/database/sorted.js index 52f0e88a50..73cc7714e2 100644 --- a/test/database/sorted.js +++ b/test/database/sorted.js @@ -227,22 +227,11 @@ describe('Sorted Set methods', function () { }); }); - it('should return duplicates if two sets have same elements', function (done) { - async.waterfall([ - function (next) { - db.sortedSetAdd('dupezset1', [1, 2], ['value 1', 'value 2'], next); - }, - function (next) { - db.sortedSetAdd('dupezset2', [2, 3], ['value 2', 'value 3'], next); - }, - function (next) { - db.getSortedSetRange(['dupezset1', 'dupezset2'], 0, -1, next); - }, - function (data, next) { - assert.deepStrictEqual(data, ['value 1', 'value 2', 'value 2', 'value 3']); - next(); - }, - ], done); + it('should return duplicates if two sets have same elements', async function () { + await db.sortedSetAdd('dupezset1', [1, 2], ['value 1', 'value 2']); + await db.sortedSetAdd('dupezset2', [2, 3], ['value 2', 'value 3']); + const data = await db.getSortedSetRange(['dupezset1', 'dupezset2'], 0, -1); + assert.deepStrictEqual(data, ['value 1', 'value 2', 'value 2', 'value 3']); }); it('should return correct number of elements', async function () { @@ -405,6 +394,15 @@ describe('Sorted Set methods', function () { done(); }); }); + + it('should work with an array of keys', async function () { + await db.sortedSetAddBulk([ + ['byScoreWithScoresKeys1', 1, 'value1'], + ['byScoreWithScoresKeys2', 2, 'value2'], + ]); + const data = await db.getSortedSetRevRangeByScoreWithScores(['byScoreWithScoresKeys1', 'byScoreWithScoresKeys2'], 0, -1, 5, -5); + assert.deepStrictEqual(data, [{ value: 'value2', score: 2 }, { value: 'value1', score: 1 }]); + }); }); describe('sortedSetCount()', function () { diff --git a/test/topics.js b/test/topics.js index 96825e1296..c139247ae1 100644 --- a/test/topics.js +++ b/test/topics.js @@ -1190,14 +1190,14 @@ describe('Topic\'s', function () { topic: function (next) { topics.post({ uid: topic.userId, title: 'unread topic', content: 'unread topic content', cid: topic.categoryId }, next); }, - user: function (next) { + joeUid: 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; + uid = results.joeUid; done(); }); }); @@ -1385,7 +1385,7 @@ describe('Topic\'s', function () { }, function (category, next) { privateCid = category.cid; - privileges.categories.rescind(['read'], category.cid, 'registered-users', next); + privileges.categories.rescind(['topics:read'], category.cid, 'registered-users', next); }, function (next) { topics.post({ uid: adminUid, title: 'topic in private category', content: 'registered-users cant see this', cid: privateCid }, next); @@ -1414,7 +1414,7 @@ describe('Topic\'s', function () { }, function (category, next) { ignoredCid = category.cid; - privileges.categories.rescind(['read'], category.cid, 'registered-users', next); + privileges.categories.rescind(['topics:read'], category.cid, 'registered-users', next); }, function (next) { topics.post({ uid: adminUid, title: 'topic in private category', content: 'registered-users cant see this', cid: ignoredCid }, next);