From 61eb7aa68bd9126bc241ff42a95c0e070f59be82 Mon Sep 17 00:00:00 2001
From: Baris Usakli <barisusakli@gmail.com>
Date: Fri, 26 May 2017 16:56:26 -0400
Subject: [PATCH] more tests

---
 src/categories.js    | 628 ++++++++++++++++++++++---------------------
 src/topics/recent.js |  23 +-
 test/image.js        |   2 -
 test/topics.js       |   8 +
 4 files changed, 341 insertions(+), 320 deletions(-)

diff --git a/src/categories.js b/src/categories.js
index e6f0f5aa57..217541081c 100644
--- a/src/categories.js
+++ b/src/categories.js
@@ -9,148 +9,160 @@ var Groups = require('./groups');
 var plugins = require('./plugins');
 var privileges = require('./privileges');
 
-(function (Categories) {
-	require('./categories/data')(Categories);
-	require('./categories/create')(Categories);
-	require('./categories/delete')(Categories);
-	require('./categories/topics')(Categories);
-	require('./categories/unread')(Categories);
-	require('./categories/activeusers')(Categories);
-	require('./categories/recentreplies')(Categories);
-	require('./categories/update')(Categories);
-
-	Categories.exists = function (cid, callback) {
-		db.isSortedSetMember('categories:cid', cid, callback);
-	};
-
-	Categories.getCategoryById = function (data, callback) {
-		var category;
-		async.waterfall([
-			function (next) {
-				Categories.getCategories([data.cid], data.uid, next);
-			},
-			function (categories, next) {
-				if (!Array.isArray(categories) || !categories[0]) {
-					return next(new Error('[[error:invalid-cid]]'));
-				}
-				category = categories[0];
-
-				async.parallel({
-					topics: function (next) {
-						Categories.getCategoryTopics(data, next);
-					},
-					topicCount: function (next) {
-						if (Array.isArray(data.set)) {
-							db.sortedSetIntersectCard(data.set, next);
-						} else {
-							next(null, category.topic_count);
-						}
-					},
-					isIgnored: function (next) {
-						Categories.isIgnored([data.cid], data.uid, next);
-					},
-				}, next);
-			},
-			function (results, next) {
-				category.topics = results.topics.topics;
-				category.nextStart = results.topics.nextStart;
-				category.isIgnored = results.isIgnored[0];
-				category.topic_count = results.topicCount;
-
-				plugins.fireHook('filter:category.get', { category: category, uid: data.uid }, next);
-			},
-			function (data, next) {
-				next(null, data.category);
-			},
-		], callback);
-	};
-
-	Categories.isIgnored = function (cids, uid, callback) {
-		db.isSortedSetMembers('uid:' + uid + ':ignored:cids', cids, callback);
-	};
-
-	Categories.getPageCount = function (cid, uid, callback) {
-		async.parallel({
-			topicCount: async.apply(Categories.getCategoryField, cid, 'topic_count'),
-			settings: async.apply(user.getSettings, uid),
-		}, function (err, results) {
-			if (err) {
-				return callback(err);
+var Categories = module.exports;
+
+require('./categories/data')(Categories);
+require('./categories/create')(Categories);
+require('./categories/delete')(Categories);
+require('./categories/topics')(Categories);
+require('./categories/unread')(Categories);
+require('./categories/activeusers')(Categories);
+require('./categories/recentreplies')(Categories);
+require('./categories/update')(Categories);
+
+Categories.exists = function (cid, callback) {
+	db.isSortedSetMember('categories:cid', cid, callback);
+};
+
+Categories.getCategoryById = function (data, callback) {
+	var category;
+	async.waterfall([
+		function (next) {
+			Categories.getCategories([data.cid], data.uid, next);
+		},
+		function (categories, next) {
+			if (!Array.isArray(categories) || !categories[0]) {
+				return next(new Error('[[error:invalid-cid]]'));
 			}
-
+			category = categories[0];
+
+			async.parallel({
+				topics: function (next) {
+					Categories.getCategoryTopics(data, next);
+				},
+				topicCount: function (next) {
+					if (Array.isArray(data.set)) {
+						db.sortedSetIntersectCard(data.set, next);
+					} else {
+						next(null, category.topic_count);
+					}
+				},
+				isIgnored: function (next) {
+					Categories.isIgnored([data.cid], data.uid, next);
+				},
+			}, next);
+		},
+		function (results, next) {
+			category.topics = results.topics.topics;
+			category.nextStart = results.topics.nextStart;
+			category.isIgnored = results.isIgnored[0];
+			category.topic_count = results.topicCount;
+
+			plugins.fireHook('filter:category.get', { category: category, uid: data.uid }, next);
+		},
+		function (data, next) {
+			next(null, data.category);
+		},
+	], callback);
+};
+
+Categories.isIgnored = function (cids, uid, callback) {
+	db.isSortedSetMembers('uid:' + uid + ':ignored:cids', cids, callback);
+};
+
+Categories.getPageCount = function (cid, uid, callback) {
+	async.waterfall([
+		function (next) {
+			async.parallel({
+				topicCount: async.apply(Categories.getCategoryField, cid, 'topic_count'),
+				settings: async.apply(user.getSettings, uid),
+			}, next);
+		},
+		function (results, next) {
 			if (!parseInt(results.topicCount, 10)) {
-				return callback(null, 1);
+				return next(null, 1);
 			}
 
-			callback(null, Math.ceil(parseInt(results.topicCount, 10) / results.settings.topicsPerPage));
-		});
-	};
-
-	Categories.getAllCategories = function (uid, callback) {
-		db.getSortedSetRange('categories:cid', 0, -1, function (err, cids) {
-			if (err || !Array.isArray(cids) || !cids.length) {
-				return callback(err, []);
+			next(null, Math.ceil(parseInt(results.topicCount, 10) / results.settings.topicsPerPage));
+		},
+	], callback);
+};
+
+Categories.getAllCategories = function (uid, callback) {
+	async.waterfall([
+		function (next) {
+			db.getSortedSetRange('categories:cid', 0, -1, next);
+		},
+		function (cids, next) {
+			if (!Array.isArray(cids) || !cids.length) {
+				return next(null, []);
 			}
 
-			Categories.getCategories(cids, uid, callback);
-		});
-	};
-
-	Categories.getCategoriesByPrivilege = function (set, uid, privilege, callback) {
-		async.waterfall([
-			function (next) {
-				db.getSortedSetRange(set, 0, -1, next);
-			},
-			function (cids, next) {
-				privileges.categories.filterCids(privilege, cids, uid, next);
-			},
-			function (cids, next) {
-				Categories.getCategories(cids, uid, next);
-			},
-		], callback);
-	};
-
-	Categories.getModerators = function (cid, callback) {
-		Groups.getMembers('cid:' + cid + ':privileges:mods', 0, -1, function (err, uids) {
-			if (err || !Array.isArray(uids) || !uids.length) {
-				return callback(err, []);
+			Categories.getCategories(cids, uid, next);
+		},
+	], callback);
+};
+
+Categories.getCategoriesByPrivilege = function (set, uid, privilege, callback) {
+	async.waterfall([
+		function (next) {
+			db.getSortedSetRange(set, 0, -1, next);
+		},
+		function (cids, next) {
+			privileges.categories.filterCids(privilege, cids, uid, next);
+		},
+		function (cids, next) {
+			Categories.getCategories(cids, uid, next);
+		},
+	], callback);
+};
+
+Categories.getModerators = function (cid, callback) {
+	async.waterfall([
+		function (next) {
+			Groups.getMembers('cid:' + cid + ':privileges:mods', 0, -1, next);
+		},
+		function (uids, next) {
+			if (!Array.isArray(uids) || !uids.length) {
+				return next(null, []);
 			}
 
-			user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture'], callback);
-		});
-	};
-
+			user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture'], next);
+		},
+	], callback);
+};
 
-	Categories.getCategories = function (cids, uid, callback) {
-		if (!Array.isArray(cids)) {
-			return callback(new Error('[[error:invalid-cid]]'));
-		}
 
-		if (!cids.length) {
-			return callback(null, []);
-		}
+Categories.getCategories = function (cids, uid, callback) {
+	if (!Array.isArray(cids)) {
+		return callback(new Error('[[error:invalid-cid]]'));
+	}
 
-		async.parallel({
-			categories: function (next) {
-				Categories.getCategoriesData(cids, next);
-			},
-			children: function (next) {
-				Categories.getChildren(cids, uid, next);
-			},
-			parents: function (next) {
-				Categories.getParents(cids, next);
-			},
-			tagWhitelist: function (next) {
-				Categories.getTagWhitelist(cids, next);
-			},
-			hasRead: function (next) {
-				Categories.hasReadCategories(cids, uid, next);
-			},
-		}, function (err, results) {
-			if (err) {
-				return callback(err);
-			}
+	if (!cids.length) {
+		return callback(null, []);
+	}
 
+	async.waterfall([
+		function (next) {
+			async.parallel({
+				categories: function (next) {
+					Categories.getCategoriesData(cids, next);
+				},
+				children: function (next) {
+					Categories.getChildren(cids, uid, next);
+				},
+				parents: function (next) {
+					Categories.getParents(cids, next);
+				},
+				tagWhitelist: function (next) {
+					Categories.getTagWhitelist(cids, next);
+				},
+				hasRead: function (next) {
+					Categories.hasReadCategories(cids, uid, next);
+				},
+			}, next);
+		},
+		function (results, next) {
 			uid = parseInt(uid, 10);
 			results.categories.forEach(function (category, i) {
 				if (category) {
@@ -162,202 +174,202 @@ var privileges = require('./privileges');
 				}
 			});
 
-			callback(null, results.categories);
-		});
-	};
-
-	Categories.getTagWhitelist = function (cids, callback) {
-		var keys = cids.map(function (cid) {
-			return 'cid:' + cid + ':tag:whitelist';
-		});
-		db.getSortedSetsMembers(keys, callback);
-	};
-
-	function calculateTopicPostCount(category) {
-		if (!category) {
-			return;
-		}
-
-		var postCount = parseInt(category.post_count, 10) || 0;
-		var topicCount = parseInt(category.topic_count, 10) || 0;
-		if (!Array.isArray(category.children) || !category.children.length) {
-			category.totalPostCount = postCount;
-			category.totalTopicCount = topicCount;
-			return;
-		}
-
-		category.children.forEach(function (child) {
-			calculateTopicPostCount(child);
-			postCount += parseInt(child.totalPostCount, 10) || 0;
-			topicCount += parseInt(child.totalTopicCount, 10) || 0;
-		});
+			next(null, results.categories);
+		},
+	], callback);
+};
+
+Categories.getTagWhitelist = function (cids, callback) {
+	var keys = cids.map(function (cid) {
+		return 'cid:' + cid + ':tag:whitelist';
+	});
+	db.getSortedSetsMembers(keys, callback);
+};
+
+function calculateTopicPostCount(category) {
+	if (!category) {
+		return;
+	}
 
+	var postCount = parseInt(category.post_count, 10) || 0;
+	var topicCount = parseInt(category.topic_count, 10) || 0;
+	if (!Array.isArray(category.children) || !category.children.length) {
 		category.totalPostCount = postCount;
 		category.totalTopicCount = topicCount;
+		return;
 	}
 
-	Categories.getParents = function (cids, callback) {
-		var categoriesData;
-		var parentCids;
-		async.waterfall([
-			function (next) {
-				Categories.getCategoriesFields(cids, ['parentCid'], next);
-			},
-			function (_categoriesData, next) {
-				categoriesData = _categoriesData;
-
-				parentCids = categoriesData.filter(function (category) {
-					return category && category.hasOwnProperty('parentCid') && parseInt(category.parentCid, 10);
-				}).map(function (category) {
-					return parseInt(category.parentCid, 10);
-				});
-
-				if (!parentCids.length) {
-					return callback(null, cids.map(function () { return null; }));
-				}
-
-				Categories.getCategoriesData(parentCids, next);
-			},
-			function (parentData, next) {
-				parentData = categoriesData.map(function (category) {
-					return parentData[parentCids.indexOf(parseInt(category.parentCid, 10))];
-				});
-				next(null, parentData);
-			},
-		], callback);
-	};
-
-	Categories.getChildren = function (cids, uid, callback) {
-		var categories = cids.map(function (cid) {
-			return { cid: cid };
-		});
-
-		async.each(categories, function (category, next) {
-			getChildrenRecursive(category, uid, next);
-		}, function (err) {
-			callback(err, categories.map(function (c) {
-				return c && c.children;
-			}));
-		});
-	};
-
-	function getChildrenRecursive(category, uid, callback) {
-		async.waterfall([
-			function (next) {
-				db.getSortedSetRange('cid:' + category.cid + ':children', 0, -1, next);
-			},
-			function (children, next) {
-				privileges.categories.filterCids('find', children, uid, next);
-			},
-			function (children, next) {
-				children = children.filter(function (cid) {
-					return parseInt(category.cid, 10) !== parseInt(cid, 10);
-				});
-				if (!children.length) {
-					category.children = [];
-					return callback();
-				}
-				Categories.getCategoriesData(children, next);
-			},
-			function (childrenData, next) {
-				childrenData = childrenData.filter(Boolean);
-				category.children = childrenData;
-				async.each(category.children, function (child, next) {
-					getChildrenRecursive(child, uid, next);
-				}, next);
-			},
-		], callback);
-	}
+	category.children.forEach(function (child) {
+		calculateTopicPostCount(child);
+		postCount += parseInt(child.totalPostCount, 10) || 0;
+		topicCount += parseInt(child.totalTopicCount, 10) || 0;
+	});
+
+	category.totalPostCount = postCount;
+	category.totalTopicCount = topicCount;
+}
+
+Categories.getParents = function (cids, callback) {
+	var categoriesData;
+	var parentCids;
+	async.waterfall([
+		function (next) {
+			Categories.getCategoriesFields(cids, ['parentCid'], next);
+		},
+		function (_categoriesData, next) {
+			categoriesData = _categoriesData;
+
+			parentCids = categoriesData.filter(function (category) {
+				return category && category.hasOwnProperty('parentCid') && parseInt(category.parentCid, 10);
+			}).map(function (category) {
+				return parseInt(category.parentCid, 10);
+			});
 
-	Categories.flattenCategories = function (allCategories, categoryData) {
-		categoryData.forEach(function (category) {
-			if (category) {
-				if (!category.parent) {
-					allCategories.push(category);
-				}
+			if (!parentCids.length) {
+				return callback(null, cids.map(function () { return null; }));
+			}
 
-				if (Array.isArray(category.children) && category.children.length) {
-					Categories.flattenCategories(allCategories, category.children);
-				}
+			Categories.getCategoriesData(parentCids, next);
+		},
+		function (parentData, next) {
+			parentData = categoriesData.map(function (category) {
+				return parentData[parentCids.indexOf(parseInt(category.parentCid, 10))];
+			});
+			next(null, parentData);
+		},
+	], callback);
+};
+
+Categories.getChildren = function (cids, uid, callback) {
+	var categories = cids.map(function (cid) {
+		return { cid: cid };
+	});
+
+	async.each(categories, function (category, next) {
+		getChildrenRecursive(category, uid, next);
+	}, function (err) {
+		callback(err, categories.map(function (c) {
+			return c && c.children;
+		}));
+	});
+};
+
+function getChildrenRecursive(category, uid, callback) {
+	async.waterfall([
+		function (next) {
+			db.getSortedSetRange('cid:' + category.cid + ':children', 0, -1, next);
+		},
+		function (children, next) {
+			privileges.categories.filterCids('find', children, uid, next);
+		},
+		function (children, next) {
+			children = children.filter(function (cid) {
+				return parseInt(category.cid, 10) !== parseInt(cid, 10);
+			});
+			if (!children.length) {
+				category.children = [];
+				return callback();
 			}
-		});
-	};
-
-	/**
-	 * Recursively build tree
-	 *
-	 * @param categories {array} flat list of categories
-	 * @param parentCid {number} start from 0 to build full tree
-	 */
-	Categories.getTree = function (categories, parentCid) {
-		var tree = [];
-		var i = 0;
-		var len = categories.length;
-		var category;
-
-		for (i; i < len; i += 1) {
-			category = categories[i];
-			if (!category.hasOwnProperty('parentCid') || category.parentCid === null) {
-				category.parentCid = 0;
+			Categories.getCategoriesData(children, next);
+		},
+		function (childrenData, next) {
+			childrenData = childrenData.filter(Boolean);
+			category.children = childrenData;
+			async.each(category.children, function (child, next) {
+				getChildrenRecursive(child, uid, next);
+			}, next);
+		},
+	], callback);
+}
+
+Categories.flattenCategories = function (allCategories, categoryData) {
+	categoryData.forEach(function (category) {
+		if (category) {
+			if (!category.parent) {
+				allCategories.push(category);
 			}
 
-			if (parseInt(category.parentCid, 10) === parseInt(parentCid, 10)) {
-				tree.push(category);
-				category.children = Categories.getTree(categories, category.cid);
+			if (Array.isArray(category.children) && category.children.length) {
+				Categories.flattenCategories(allCategories, category.children);
 			}
 		}
+	});
+};
+
+/**
+ * Recursively build tree
+ *
+ * @param categories {array} flat list of categories
+ * @param parentCid {number} start from 0 to build full tree
+ */
+Categories.getTree = function (categories, parentCid) {
+	var tree = [];
+	var i = 0;
+	var len = categories.length;
+	var category;
+
+	for (i; i < len; i += 1) {
+		category = categories[i];
+		if (!category.hasOwnProperty('parentCid') || category.parentCid === null) {
+			category.parentCid = 0;
+		}
 
-		return tree;
-	};
+		if (parseInt(category.parentCid, 10) === parseInt(parentCid, 10)) {
+			tree.push(category);
+			category.children = Categories.getTree(categories, category.cid);
+		}
+	}
 
-	Categories.buildForSelect = function (uid, callback) {
-		function recursive(category, categoriesData, level) {
-			if (category.link) {
-				return;
-			}
+	return tree;
+};
 
-			var bullet = level ? '&bull; ' : '';
-			category.value = category.cid;
-			category.text = level + bullet + category.name;
-			categoriesData.push(category);
+Categories.buildForSelect = function (uid, callback) {
+	function recursive(category, categoriesData, level) {
+		if (category.link) {
+			return;
+		}
 
-			category.children.forEach(function (child) {
-				recursive(child, categoriesData, '&nbsp;&nbsp;&nbsp;&nbsp;' + level);
-			});
+		var bullet = level ? '&bull; ' : '';
+		category.value = category.cid;
+		category.text = level + bullet + category.name;
+		categoriesData.push(category);
+
+		category.children.forEach(function (child) {
+			recursive(child, categoriesData, '&nbsp;&nbsp;&nbsp;&nbsp;' + level);
+		});
+	}
+	Categories.getCategoriesByPrivilege('cid:0:children', uid, 'read', function (err, categories) {
+		if (err) {
+			return callback(err);
 		}
-		Categories.getCategoriesByPrivilege('cid:0:children', uid, 'read', function (err, categories) {
-			if (err) {
-				return callback(err);
-			}
 
-			var categoriesData = [];
+		var categoriesData = [];
 
-			categories = categories.filter(function (category) {
-				return category && !category.link && !parseInt(category.parentCid, 10);
-			});
+		categories = categories.filter(function (category) {
+			return category && !category.link && !parseInt(category.parentCid, 10);
+		});
 
-			categories.forEach(function (category) {
-				recursive(category, categoriesData, '');
-			});
-			callback(null, categoriesData);
+		categories.forEach(function (category) {
+			recursive(category, categoriesData, '');
 		});
-	};
-
-	Categories.getIgnorers = function (cid, start, stop, callback) {
-		db.getSortedSetRevRange('cid:' + cid + ':ignorers', start, stop, callback);
-	};
-
-	Categories.filterIgnoringUids = function (cid, uids, callback) {
-		async.waterfall([
-			function (next) {
-				db.isSortedSetMembers('cid:' + cid + ':ignorers', uids, next);
-			},
-			function (isIgnoring, next) {
-				var readingUids = uids.filter(function (uid, index) {
-					return uid && !isIgnoring[index];
-				});
-				next(null, readingUids);
-			},
-		], callback);
-	};
-}(exports));
+		callback(null, categoriesData);
+	});
+};
+
+Categories.getIgnorers = function (cid, start, stop, callback) {
+	db.getSortedSetRevRange('cid:' + cid + ':ignorers', start, stop, callback);
+};
+
+Categories.filterIgnoringUids = function (cid, uids, callback) {
+	async.waterfall([
+		function (next) {
+			db.isSortedSetMembers('cid:' + cid + ':ignorers', uids, next);
+		},
+		function (isIgnoring, next) {
+			var readingUids = uids.filter(function (uid, index) {
+				return uid && !isIgnoring[index];
+			});
+			next(null, readingUids);
+		},
+	], callback);
+};
diff --git a/src/topics/recent.js b/src/topics/recent.js
index 6801c0095b..b2e0de6023 100644
--- a/src/topics/recent.js
+++ b/src/topics/recent.js
@@ -141,19 +141,22 @@ module.exports = function (Topics) {
 
 	Topics.updateRecent = function (tid, timestamp, callback) {
 		callback = callback || function () {};
-		if (plugins.hasListeners('filter:topics.updateRecent')) {
-			plugins.fireHook('filter:topics.updateRecent', { tid: tid, timestamp: timestamp }, function (err, data) {
-				if (err) {
-					return callback(err);
+
+		async.waterfall([
+			function (next) {
+				if (plugins.hasListeners('filter:topics.updateRecent')) {
+					plugins.fireHook('filter:topics.updateRecent', { tid: tid, timestamp: timestamp }, next);
+				} else {
+					next(null, { tid: tid, timestamp: timestamp });
 				}
+			},
+			function (data, next) {
 				if (data && data.tid && data.timestamp) {
-					db.sortedSetAdd('topics:recent', data.timestamp, data.tid, callback);
+					db.sortedSetAdd('topics:recent', data.timestamp, data.tid, next);
 				} else {
-					callback();
+					next();
 				}
-			});
-		} else {
-			db.sortedSetAdd('topics:recent', timestamp, tid, callback);
-		}
+			},
+		], callback);
 	};
 };
diff --git a/test/image.js b/test/image.js
index 3c7b0c6369..119a1592c6 100644
--- a/test/image.js
+++ b/test/image.js
@@ -8,7 +8,6 @@ var image = require('../src/image');
 var file = require('../src/file');
 
 describe('image', function () {
-
 	it('should normalise image', function (done) {
 		image.normalise(path.join(__dirname, 'files/normalise.jpg'), '.jpg', function (err) {
 			assert.ifError(err);
@@ -19,5 +18,4 @@ describe('image', function () {
 			});
 		});
 	});
-
 });
diff --git a/test/topics.js b/test/topics.js
index 4551595fa6..2d3d7a4cf6 100644
--- a/test/topics.js
+++ b/test/topics.js
@@ -110,6 +110,14 @@ describe('Topic\'s', function () {
 				done();
 			});
 		});
+
+		it('should return false for falsy uid', function (done) {
+			topics.isOwner(topic.tid, 0, function (err, isOwner) {
+				assert.ifError(err);
+				assert(!isOwner);
+				done();
+			});
+		});
 	});
 
 	describe('.reply', function () {