diff --git a/install/package.json b/install/package.json index 2dd2ad5e00..38c7e8cf0d 100644 --- a/install/package.json +++ b/install/package.json @@ -34,7 +34,7 @@ "@fortawesome/fontawesome-free": "6.5.1", "@isaacs/ttlcache": "1.4.1", "@popperjs/core": "2.11.8", - "ace-builds": "1.32.1", + "ace-builds": "1.32.2", "archiver": "6.0.1", "async": "3.2.5", "autoprefixer": "10.4.16", @@ -60,13 +60,14 @@ "cookie-parser": "1.4.6", "cron": "3.1.6", "cropperjs": "1.6.1", - "csrf-sync": "4.0.1", + "csrf-sync": "4.0.3", "daemon": "1.1.0", "diff": "5.1.0", "esbuild": "0.19.9", "express": "4.18.2", "express-session": "1.17.3", "express-useragent": "1.0.15", + "fetch-cookie": "2.1.0", "file-loader": "6.2.0", "fs-extra": "11.2.0", "graceful-fs": "4.2.11", @@ -102,7 +103,7 @@ "nodebb-plugin-ntfy": "1.7.3", "nodebb-plugin-spam-be-gone": "2.2.0", "nodebb-rewards-essentials": "1.0.0", - "nodebb-theme-harmony": "1.1.103", + "nodebb-theme-harmony": "1.1.104", "nodebb-theme-lavender": "7.1.5", "nodebb-theme-peace": "2.1.25", "nodebb-theme-persona": "13.2.49", @@ -119,8 +120,6 @@ "progress-webpack-plugin": "1.0.16", "prompt": "1.3.0", "ioredis": "5.3.2", - "request": "2.88.2", - "request-promise-native": "1.0.9", "rimraf": "5.0.5", "rss": "1.2.2", "rtlcss": "4.1.1", @@ -128,7 +127,7 @@ "sass": "1.69.5", "semver": "7.5.4", "serve-favicon": "2.5.0", - "sharp": "0.33.0", + "sharp": "0.33.1", "sitemap": "7.1.1", "socket.io": "4.7.2", "socket.io-client": "4.7.2", @@ -142,11 +141,12 @@ "timeago": "1.6.7", "tinycon": "0.6.8", "toobusy-js": "0.5.1", + "tough-cookie": "4.1.3", "validator": "13.11.0", "webpack": "5.89.0", "webpack-merge": "5.10.0", "winston": "3.11.0", - "workerpool": "8.0.0", + "workerpool": "9.0.1", "xml": "1.0.1", "xregexp": "5.1.1", "yargs": "17.7.2", @@ -157,9 +157,9 @@ "@commitlint/cli": "18.4.3", "@commitlint/config-angular": "18.4.3", "coveralls": "3.1.1", - "eslint": "8.55.0", + "eslint": "8.56.0", "eslint-config-nodebb": "0.2.1", - "eslint-plugin-import": "2.29.0", + "eslint-plugin-import": "2.29.1", "grunt": "1.6.1", "grunt-contrib-watch": "1.1.0", "husky": "8.0.3", @@ -181,7 +181,7 @@ "url": "https://github.com/NodeBB/NodeBB/issues" }, "engines": { - "node": ">=16" + "node": ">=18" }, "maintainers": [ { diff --git a/public/500.html b/public/500.html new file mode 100644 index 0000000000..eec0e49f7a --- /dev/null +++ b/public/500.html @@ -0,0 +1,41 @@ + + + Internal Server Error + + + + + + +
+
+

500

+

+ Internal server error. +

+

+ {message} +

+

+  Alright. You can stop clicking... it's not going to make the site come back sooner! +

+
+
+ + diff --git a/public/503.html b/public/503.html index 43d1e648d9..51d0e52d53 100644 --- a/public/503.html +++ b/public/503.html @@ -2,147 +2,12 @@ Excessive Load Warning - ', - }, - }, (err, response, body) => { - assert.ifError(err); - assert.equal(response.statusCode, 500); - done(); - }); + const { response } = await request.post(`${nconf.get('url')}/login`, { + body: { + username: 'regular', + password: 'regularpwd', + }, + jar: jar, + headers: { + 'x-csrf-token': csrf_token, + 'x-forwarded-for': '', + }, }); + assert.equal(response.status, 500); }); it('should fail to login if user does not exist', async () => { - const { res, body } = await helpers.loginUser('doesnotexist', 'nopassword'); - assert.equal(res.statusCode, 403); + const { response, body } = await helpers.loginUser('doesnotexist', 'nopassword'); + assert.equal(response.statusCode, 403); assert.equal(body, '[[error:invalid-login-credentials]]'); }); it('should fail to login if username is empty', async () => { - const { res, body } = await helpers.loginUser('', 'some password'); - assert.equal(res.statusCode, 403); + const { response, body } = await helpers.loginUser('', 'some password'); + assert.equal(response.statusCode, 403); assert.equal(body, '[[error:invalid-username-or-password]]'); }); it('should fail to login if password is empty', async () => { - const { res, body } = await helpers.loginUser('someuser', ''); - assert.equal(res.statusCode, 403); + const { response, body } = await helpers.loginUser('someuser', ''); + assert.equal(response.statusCode, 403); assert.equal(body, '[[error:invalid-username-or-password]]'); }); it('should fail to login if username and password are empty', async () => { - const { res, body } = await helpers.loginUser('', ''); - assert.equal(res.statusCode, 403); + const { response, body } = await helpers.loginUser('', ''); + assert.equal(response.statusCode, 403); assert.equal(body, '[[error:invalid-username-or-password]]'); }); it('should fail to login if user does not have password field in db', async () => { await user.create({ username: 'hasnopassword', email: 'no@pass.org' }); - const { res, body } = await helpers.loginUser('hasnopassword', 'doesntmatter'); - assert.equal(res.statusCode, 403); + const { response, body } = await helpers.loginUser('hasnopassword', 'doesntmatter'); + assert.equal(response.statusCode, 403); assert.equal(body, '[[error:invalid-login-credentials]]'); }); it('should fail to login if password is longer than 4096', async () => { - let longPassword; + let longPassword = ''; for (let i = 0; i < 5000; i++) { longPassword += 'a'; } - const { res, body } = await helpers.loginUser('someuser', longPassword); - assert.equal(res.statusCode, 403); + const { response, body } = await helpers.loginUser('someuser', longPassword); + assert.equal(response.statusCode, 403); assert.equal(body, '[[error:password-too-long]]'); }); it('should fail to login if local login is disabled', async () => { await privileges.global.rescind(['groups:local:login'], 'registered-users'); - const { res, body } = await helpers.loginUser('regular', 'regularpwd'); - assert.equal(res.statusCode, 403); + const { response, body } = await helpers.loginUser('regular', 'regularpwd'); + assert.equal(response.statusCode, 403); assert.equal(body, '[[error:local-login-disabled]]'); await privileges.global.give(['groups:local:login'], 'registered-users'); }); - it('should fail to register if registraton is disabled', (done) => { + it('should fail to register if registraton is disabled', async () => { meta.config.registrationType = 'disabled'; - helpers.registerUser({ + const { response, body } = await helpers.registerUser({ username: 'someuser', password: 'somepassword', - }, (err, jar, response, body) => { - assert.ifError(err); - assert.equal(response.statusCode, 403); - assert.equal(body, 'Forbidden'); - done(); }); + assert.equal(response.statusCode, 403); + assert.equal(body, 'Forbidden'); }); - it('should return error if invitation is not valid', (done) => { + it('should return error if invitation is not valid', async () => { meta.config.registrationType = 'invite-only'; - helpers.registerUser({ + const { response, body } = await helpers.registerUser({ username: 'someuser', password: 'somepassword', - }, (err, jar, response, body) => { - meta.config.registrationType = 'normal'; - assert.ifError(err); - assert.equal(response.statusCode, 400); - assert.equal(body, '[[register:invite.error-invite-only]]'); - done(); }); + meta.config.registrationType = 'normal'; + assert.equal(response.statusCode, 400); + assert.equal(body, '[[register:invite.error-invite-only]]'); }); - it('should fail to register if username is falsy or too short', (done) => { - helpers.registerUser({ - username: '', - password: 'somepassword', - }, (err, jar, response, body) => { - assert.ifError(err); + it('should fail to register if username is falsy or too short', async () => { + const userData = [ + { username: '', password: 'somepassword' }, + { username: 'a', password: 'somepassword' }, + ]; + for (const user of userData) { + // eslint-disable-next-line no-await-in-loop + const { response, body } = await helpers.registerUser(user); assert.equal(response.statusCode, 400); assert.equal(body, '[[error:username-too-short]]'); - helpers.registerUser({ - username: 'a', - password: 'somepassword', - }, (err, jar, response, body) => { - assert.ifError(err); - assert.equal(response.statusCode, 400); - assert.equal(body, '[[error:username-too-short]]'); - done(); - }); - }); + } }); - it('should fail to register if username is too long', (done) => { - helpers.registerUser({ + it('should fail to register if username is too long', async () => { + const { response, body } = await helpers.registerUser({ username: 'thisisareallylongusername', password: '123456', - }, (err, jar, response, body) => { - assert.ifError(err); - assert.equal(response.statusCode, 400); - assert.equal(body, '[[error:username-too-long]]'); - done(); }); + + assert.equal(response.statusCode, 400); + assert.equal(body, '[[error:username-too-long]]'); }); - it('should queue user if ip is used before', (done) => { + it('should queue user if ip is used before', async () => { meta.config.registrationApprovalType = 'admin-approval-ip'; - helpers.registerUser({ + const { response, body } = await helpers.registerUser({ email: 'another@user.com', username: 'anotheruser', password: 'anotherpwd', gdpr_consent: 1, - }, (err, jar, response, body) => { - meta.config.registrationApprovalType = 'normal'; - assert.ifError(err); - assert.equal(response.statusCode, 200); - assert.equal(body.message, '[[register:registration-added-to-queue]]'); - done(); }); + meta.config.registrationApprovalType = 'normal'; + assert.equal(response.statusCode, 200); + assert.equal(body.message, '[[register:registration-added-to-queue]]'); }); @@ -468,41 +396,32 @@ describe('authentication', () => { const uid = await user.create({ username: 'ginger', password: '123456', email }); await user.setUserField(uid, 'email', email); await user.email.confirmByUid(uid); - const { res } = await helpers.loginUser('ginger@nodebb.org', '123456'); - assert.equal(res.statusCode, 200); + const { response } = await helpers.loginUser('ginger@nodebb.org', '123456'); + assert.equal(response.statusCode, 200); }); it('should fail to login if login type is username and an email is sent', async () => { meta.config.allowLoginWith = 'username'; - const { res, body } = await helpers.loginUser('ginger@nodebb.org', '123456'); + const { response, body } = await helpers.loginUser('ginger@nodebb.org', '123456'); meta.config.allowLoginWith = 'username-email'; - assert.equal(res.statusCode, 400); + assert.equal(response.statusCode, 400); assert.equal(body, '[[error:wrong-login-type-username]]'); }); - it('should send 200 if not logged in', (done) => { + it('should send 200 if not logged in', async () => { const jar = request.jar(); - request({ - url: `${nconf.get('url')}/api/config`, - json: true, - jar: jar, - }, (err, response, body) => { - assert.ifError(err); + const csrf_token = await helpers.getCsrfToken(jar); - request.post(`${nconf.get('url')}/logout`, { - form: {}, - json: true, - jar: jar, - headers: { - 'x-csrf-token': body.csrf_token, - }, - }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(body, 'not-logged-in'); - done(); - }); + const { response, body } = await request.post(`${nconf.get('url')}/logout`, { + data: {}, + jar: jar, + headers: { + 'x-csrf-token': csrf_token, + }, }); + + assert.equal(response.statusCode, 200); + assert.equal(body, 'not-logged-in'); }); describe('banned user authentication', () => { @@ -518,7 +437,7 @@ describe('authentication', () => { it('should prevent banned user from logging in', async () => { await user.bans.ban(bannedUser.uid, 0, 'spammer'); - const { res: res1, body: body1 } = await helpers.loginUser(bannedUser.username, bannedUser.pw); + const { response: res1, body: body1 } = await helpers.loginUser(bannedUser.username, bannedUser.pw); assert.equal(res1.statusCode, 403); delete body1.timestamp; assert.deepStrictEqual(body1, { @@ -532,7 +451,7 @@ describe('authentication', () => { await user.bans.unban(bannedUser.uid); const expiry = Date.now() + 10000; await user.bans.ban(bannedUser.uid, expiry, ''); - const { res: res2, body: body2 } = await helpers.loginUser(bannedUser.username, bannedUser.pw); + const { response: res2, body: body2 } = await helpers.loginUser(bannedUser.username, bannedUser.pw); assert.equal(res2.statusCode, 403); assert(body2.banned_until); assert(body2.reason, '[[user:info.banned-no-reason]]'); @@ -540,15 +459,15 @@ describe('authentication', () => { it('should allow banned user to log in if the "banned-users" group has "local-login" privilege', async () => { await privileges.global.give(['groups:local:login'], 'banned-users'); - const { res } = await helpers.loginUser(bannedUser.username, bannedUser.pw); - assert.strictEqual(res.statusCode, 200); + const { response } = await helpers.loginUser(bannedUser.username, bannedUser.pw); + assert.strictEqual(response.statusCode, 200); }); it('should allow banned user to log in if the user herself has "local-login" privilege', async () => { await privileges.global.rescind(['groups:local:login'], 'banned-users'); await privileges.categories.give(['local:login'], 0, bannedUser.uid); - const { res } = await helpers.loginUser(bannedUser.username, bannedUser.pw); - assert.strictEqual(res.statusCode, 200); + const { response } = await helpers.loginUser(bannedUser.username, bannedUser.pw); + assert.strictEqual(response.statusCode, 200); }); }); @@ -561,10 +480,10 @@ describe('authentication', () => { let data = await helpers.loginUser('lockme', 'abcdef'); meta.config.loginAttempts = 5; - assert.equal(data.res.statusCode, 403); + assert.equal(data.response.statusCode, 403); assert.equal(data.body, '[[error:account-locked]]'); data = await helpers.loginUser('lockme', 'abcdef'); - assert.equal(data.res.statusCode, 403); + assert.equal(data.response.statusCode, 403); assert.equal(data.body, '[[error:account-locked]]'); const locked = await db.exists(`lockout:${uid}`); assert(locked); @@ -594,57 +513,46 @@ describe('authentication', () => { }); it('should fail with invalid token', async () => { - const { res, body } = await helpers.request('get', `/api/self`, { - form: { - _uid: newUid, - }, - json: true, + const { response, body } = await helpers.request('get', `/api/self?_uid${newUid}`, { jar: jar, headers: { Authorization: `Bearer sdfhaskfdja-jahfdaksdf`, }, }); - assert.strictEqual(res.statusCode, 401); + assert.strictEqual(response.statusCode, 401); assert.strictEqual(body, 'not-authorized'); }); it('should use a token tied to an uid', async () => { - const { res, body } = await helpers.request('get', `/api/self`, { - json: true, + const { response, body } = await helpers.request('get', `/api/self`, { headers: { Authorization: `Bearer ${userToken}`, }, }); - assert.strictEqual(res.statusCode, 200); + assert.strictEqual(response.statusCode, 200); assert.strictEqual(body.username, 'apiUserTarget'); }); it('should fail if _uid is not passed in with master token', async () => { - const { res, body } = await helpers.request('get', `/api/self`, { - form: {}, - json: true, + const { response, body } = await helpers.request('get', `/api/self`, { headers: { Authorization: `Bearer ${masterToken}`, }, }); - assert.strictEqual(res.statusCode, 500); + assert.strictEqual(response.statusCode, 500); assert.strictEqual(body.error, '[[error:api.master-token-no-uid]]'); }); it('should use master api token and _uid', async () => { - const { res, body } = await helpers.request('get', `/api/self`, { - form: { - _uid: newUid, - }, - json: true, + const { response, body } = await helpers.request('get', `/api/self?_uid=${newUid}`, { headers: { Authorization: `Bearer ${masterToken}`, }, }); - assert.strictEqual(res.statusCode, 200); + assert.strictEqual(response.statusCode, 200); assert.strictEqual(body.username, 'apiUserTarget'); }); }); diff --git a/test/categories.js b/test/categories.js index 7ce3fd41c4..c81323d9a2 100644 --- a/test/categories.js +++ b/test/categories.js @@ -2,8 +2,8 @@ const assert = require('assert'); const nconf = require('nconf'); -const request = require('request'); +const request = require('../src/request'); const db = require('./mocks/databasemock'); const Categories = require('../src/categories'); const Topics = require('../src/topics'); @@ -76,14 +76,11 @@ describe('Categories', () => { }); }); - it('should load a category route', (done) => { - request(`${nconf.get('url')}/api/category/${categoryObj.cid}/test-category`, { json: true }, (err, response, body) => { - assert.ifError(err); - assert.equal(response.statusCode, 200); - assert.equal(body.name, 'Test Category & NodeBB'); - assert(body); - done(); - }); + it('should load a category route', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/category/${categoryObj.cid}/test-category`); + assert.equal(response.statusCode, 200); + assert.equal(body.name, 'Test Category & NodeBB'); + assert(body); }); describe('Categories.getRecentTopicReplies', () => { diff --git a/test/controllers-admin.js b/test/controllers-admin.js index 8578f0c52a..7760bf128e 100644 --- a/test/controllers-admin.js +++ b/test/controllers-admin.js @@ -3,9 +3,8 @@ const async = require('async'); const assert = require('assert'); const nconf = require('nconf'); -const request = require('request'); -const requestAsync = require('request-promise-native'); +const request = require('../src/request'); const db = require('./mocks/databasemock'); const categories = require('../src/categories'); const topics = require('../src/topics'); @@ -68,600 +67,427 @@ describe('Admin Controllers', () => { it('should 403 if user is not admin', async () => { ({ jar } = await helpers.loginUser('admin', 'barbar')); - const { statusCode, body } = await requestAsync(`${nconf.get('url')}/admin`, { + const { response, body } = await request.get(`${nconf.get('url')}/admin`, { jar: jar, - simple: false, - resolveWithFullResponse: true, }); - assert.equal(statusCode, 403); + assert.equal(response.statusCode, 403); assert(body); }); - it('should load admin dashboard', (done) => { - groups.join('administrators', adminUid, (err) => { - assert.ifError(err); - const dashboards = [ - '/admin', '/admin/dashboard/logins', '/admin/dashboard/users', '/admin/dashboard/topics', '/admin/dashboard/searches', - ]; - async.each(dashboards, (url, next) => { - request(`${nconf.get('url')}${url}`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200, url); - assert(body); - - next(); - }); - }, done); - }); - }); - - it('should load admin analytics', (done) => { - request(`${nconf.get('url')}/api/admin/analytics?units=hours`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); + it('should load admin dashboard', async () => { + await groups.join('administrators', adminUid); + const dashboards = [ + '/admin', '/admin/dashboard/logins', '/admin/dashboard/users', '/admin/dashboard/topics', '/admin/dashboard/searches', + ]; + await async.each(dashboards, async (url) => { + const { response, body } = await request.get(`${nconf.get('url')}${url}`, { jar: jar }); + assert.equal(response.statusCode, 200, url); assert(body); - assert(body.query); - assert(body.result); - done(); }); }); - it('should load groups page', (done) => { - request(`${nconf.get('url')}/admin/manage/groups`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load admin analytics', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/analytics?units=hours`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); + assert(body.query); + assert(body.result); }); - it('should load groups detail page', (done) => { - request(`${nconf.get('url')}/admin/manage/groups/administrators`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load groups page', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/admin/manage/groups`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load global privileges page', (done) => { - request(`${nconf.get('url')}/admin/manage/privileges`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load groups detail page', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/admin/manage/groups/administrators`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load admin privileges page', (done) => { - request(`${nconf.get('url')}/admin/manage/privileges/admin`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load global privileges page', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/admin/manage/privileges`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load privileges page for category 1', (done) => { - request(`${nconf.get('url')}/admin/manage/privileges/1`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load admin privileges page', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/admin/manage/privileges/admin`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load manage digests', (done) => { - request(`${nconf.get('url')}/admin/manage/digest`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load privileges page for category 1', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/admin/manage/privileges/1`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load manage uploads', (done) => { - request(`${nconf.get('url')}/admin/manage/uploads`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load manage digests', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/admin/manage/digest`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load general settings page', (done) => { - request(`${nconf.get('url')}/admin/settings`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load manage uploads', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/admin/manage/uploads`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load email settings page', (done) => { - request(`${nconf.get('url')}/admin/settings/email`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load general settings page', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/admin/settings`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load user settings page', (done) => { - request(`${nconf.get('url')}/admin/settings/user`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load email settings page', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/admin/settings/email`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load info page for a user', (done) => { - request(`${nconf.get('url')}/api/user/regular/info`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body.history); - assert(Array.isArray(body.history.flags)); - assert(Array.isArray(body.history.bans)); - assert(Array.isArray(body.sessions)); - done(); - }); + it('should load user settings page', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/admin/settings/user`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should 404 for edit/email page if user does not exist', (done) => { - request(`${nconf.get('url')}/api/user/doesnotexist/edit/email`, { jar: jar, json: true }, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - done(); - }); + it('should load info page for a user', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/regular/info`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body.history); + assert(Array.isArray(body.history.flags)); + assert(Array.isArray(body.history.bans)); + assert(Array.isArray(body.sessions)); }); - it('should load /admin/settings/homepage', (done) => { - request(`${nconf.get('url')}/api/admin/settings/general`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body.routes); - done(); - }); + it('should 404 for edit/email page if user does not exist', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/user/doesnotexist/edit/email`, { jar }); + assert.equal(response.statusCode, 404); }); - it('should load /admin/advanced/database', (done) => { - request(`${nconf.get('url')}/api/admin/advanced/database`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - - if (nconf.get('redis')) { - assert(body.redis); - } else if (nconf.get('mongo')) { - assert(body.mongo); - } else if (nconf.get('postgres')) { - assert(body.postgres); - } - done(); - }); + it('should load /admin/settings/homepage', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/settings/general`, { jar: jar, json: true }); + assert.equal(response.statusCode, 200); + assert(body.routes); }); - it('should load /admin/extend/plugins', function (done) { + it('should load /admin/advanced/database', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/advanced/database`, { jar: jar, json: true }); + + assert.equal(response.statusCode, 200); + + if (nconf.get('redis')) { + assert(body.redis); + } else if (nconf.get('mongo')) { + assert(body.mongo); + } else if (nconf.get('postgres')) { + assert(body.postgres); + } + }); + + it('should load /admin/extend/plugins', async function () { this.timeout(50000); - request(`${nconf.get('url')}/api/admin/extend/plugins`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert(body.hasOwnProperty('installed')); - assert(body.hasOwnProperty('upgradeCount')); - assert(body.hasOwnProperty('download')); - assert(body.hasOwnProperty('incompatible')); - done(); - }); + const { body } = await request.get(`${nconf.get('url')}/api/admin/extend/plugins`, { jar: jar }); + + assert(body.hasOwnProperty('installed')); + assert(body.hasOwnProperty('upgradeCount')); + assert(body.hasOwnProperty('download')); + assert(body.hasOwnProperty('incompatible')); }); - it('should load /admin/manage/users', (done) => { - request(`${nconf.get('url')}/api/admin/manage/users`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 200); - assert(body); - assert(body.users.length > 0); - done(); - }); + it('should load /admin/manage/users', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/manage/users`, { jar: jar, json: true }); + assert.strictEqual(response.statusCode, 200); + assert(body); + assert(body.users.length > 0); }); - it('should load /admin/manage/users?filters=banned', (done) => { - request(`${nconf.get('url')}/api/admin/manage/users?filters=banned`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 200); - assert(body); - assert.strictEqual(body.users.length, 0); - done(); - }); + it('should load /admin/manage/users?filters=banned', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/manage/users?filters=banned`, { jar: jar, json: true }); + assert.strictEqual(response.statusCode, 200); + assert(body); + assert.strictEqual(body.users.length, 0); }); - it('should load /admin/manage/users?filters=banned&filters=verified', (done) => { - request(`${nconf.get('url')}/api/admin/manage/users?filters=banned&filters=verified`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 200); - assert(body); - assert.strictEqual(body.users.length, 0); - done(); - }); + it('should load /admin/manage/users?filters=banned&filters=verified', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/manage/users?filters=banned&filters=verified`, { jar: jar, json: true }); + assert.strictEqual(response.statusCode, 200); + assert(body); + assert.strictEqual(body.users.length, 0); }); - it('should load /admin/manage/users?query=admin', (done) => { - request(`${nconf.get('url')}/api/admin/manage/users?query=admin`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 200); - assert(body); - assert.strictEqual(body.users[0].username, 'admin'); - done(); - }); + it('should load /admin/manage/users?query=admin', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/manage/users?query=admin`, { jar: jar, json: true }); + assert.strictEqual(response.statusCode, 200); + assert(body); + assert.strictEqual(body.users[0].username, 'admin'); }); - it('should return empty results if query is too short', (done) => { - request(`${nconf.get('url')}/api/admin/manage/users?query=a`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 200); - assert(body); - assert.strictEqual(body.users.length, 0); - done(); - }); + it('should return empty results if query is too short', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/manage/users?query=a`, { jar: jar }); + assert.strictEqual(response.statusCode, 200); + assert(body); + assert.strictEqual(body.users.length, 0); }); - it('should load /admin/manage/registration', (done) => { - request(`${nconf.get('url')}/api/admin/manage/registration`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/manage/registration', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/manage/registration`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should 404 if users is not privileged', (done) => { - request(`${nconf.get('url')}/api/registration-queue`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - assert(body); - done(); - }); + it('should 404 if users is not privileged', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/registration-queue`); + assert.equal(response.statusCode, 404); + assert(body); }); - it('should load /api/registration-queue', (done) => { - request(`${nconf.get('url')}/api/registration-queue`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /api/registration-queue', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/registration-queue`, { jar: jar, json: true }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /admin/manage/admins-mods', (done) => { - request(`${nconf.get('url')}/api/admin/manage/admins-mods`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/manage/admins-mods', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/manage/admins-mods`, { jar: jar, json: true }); + assert.equal(response.statusCode, 200); + assert(body); }); it('should load /admin/users/csv', (done) => { const socketAdmin = require('../src/socket.io/admin'); socketAdmin.user.exportUsersCSV({ uid: adminUid }, {}, (err) => { assert.ifError(err); - setTimeout(() => { - request(`${nconf.get('url')}/api/admin/users/csv`, { + setTimeout(async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/users/csv`, { jar: jar, headers: { referer: `${nconf.get('url')}/admin/manage/users`, }, - }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); }); + assert.equal(response.statusCode, 200); + assert(body); + done(); }, 2000); }); }); - it('should return 403 if no referer', (done) => { - request(`${nconf.get('url')}/api/admin/groups/administrators/csv`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 403); - assert.equal(body, '[[error:invalid-origin]]'); - done(); - }); + it('should return 403 if no referer', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/groups/administrators/csv`, { jar }); + assert.equal(response.statusCode, 403); + assert.equal(body, '[[error:invalid-origin]]'); }); - it('should return 403 if referer is not /api/admin/groups/administrators/csv', (done) => { - request(`${nconf.get('url')}/api/admin/groups/administrators/csv`, { + it('should return 403 if referer is not /api/admin/groups/administrators/csv', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/groups/administrators/csv`, { jar: jar, headers: { referer: '/topic/1/test', }, - }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 403); - assert.equal(body, '[[error:invalid-origin]]'); - done(); }); + assert.equal(response.statusCode, 403); + assert.equal(body, '[[error:invalid-origin]]'); }); - it('should load /api/admin/groups/administrators/csv', (done) => { - request(`${nconf.get('url')}/api/admin/groups/administrators/csv`, { + it('should load /api/admin/groups/administrators/csv', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/groups/administrators/csv`, { jar: jar, headers: { referer: `${nconf.get('url')}/admin/manage/groups`, }, - }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /admin/advanced/hooks', (done) => { - request(`${nconf.get('url')}/api/admin/advanced/hooks`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/advanced/hooks', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/advanced/hooks`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /admin/advanced/cache', (done) => { - request(`${nconf.get('url')}/api/admin/advanced/cache`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/advanced/cache', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/advanced/cache`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /api/admin/advanced/cache/dump and 404 with no query param', (done) => { - request(`${nconf.get('url')}/api/admin/advanced/cache/dump`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - assert(body); - done(); - }); + it('should load /api/admin/advanced/cache/dump and 404 with no query param', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/advanced/cache/dump`, { jar }); + assert.equal(response.statusCode, 404); + assert(body); }); - it('should load /api/admin/advanced/cache/dump', (done) => { - request(`${nconf.get('url')}/api/admin/advanced/cache/dump?name=post`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /api/admin/advanced/cache/dump', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/advanced/cache/dump?name=post`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /admin/advanced/errors', (done) => { - request(`${nconf.get('url')}/api/admin/advanced/errors`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/advanced/errors', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/advanced/errors`, { jar: jar, json: true }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /admin/advanced/errors/export', (done) => { - meta.errors.clear((err) => { - assert.ifError(err); - request(`${nconf.get('url')}/api/admin/advanced/errors/export`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.strictEqual(body, ''); - done(); - }); - }); + it('should load /admin/advanced/errors/export', async () => { + await meta.errors.clear(); + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/advanced/errors/export`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert.strictEqual(body, ''); }); - it('should load /admin/advanced/logs', (done) => { + it('should load /admin/advanced/logs', async () => { const fs = require('fs'); - fs.appendFile(meta.logs.path, 'dummy log', (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/api/admin/advanced/logs`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); + await fs.promises.appendFile(meta.logs.path, 'dummy log'); + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/advanced/logs`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /admin/settings/navigation', (done) => { + it('should load /admin/settings/navigation', async () => { const navigation = require('../src/navigation/admin'); const data = require('../install/data/navigation.json'); + await navigation.save(data); - navigation.save(data, (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/api/admin/settings/navigation`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert(body); - assert(body.available); - assert(body.enabled); - done(); - }); - }); + const { body } = await request.get(`${nconf.get('url')}/api/admin/settings/navigation`, { jar }); + assert(body); + assert(body.available); + assert(body.enabled); }); - it('should load /admin/development/info', (done) => { - request(`${nconf.get('url')}/api/admin/development/info`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/development/info', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/development/info`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /admin/development/logger', (done) => { - request(`${nconf.get('url')}/api/admin/development/logger`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/development/logger', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/development/logger`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /admin/advanced/events', (done) => { - request(`${nconf.get('url')}/api/admin/advanced/events`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/advanced/events', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/advanced/events`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /admin/manage/categories', (done) => { - request(`${nconf.get('url')}/api/admin/manage/categories`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/manage/categories', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/manage/categories`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /admin/manage/categories/1', (done) => { - request(`${nconf.get('url')}/api/admin/manage/categories/1`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/manage/categories/1', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/manage/categories/1`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); it('should load /admin/manage/catgories?cid=', async () => { const { cid: rootCid } = await categories.create({ name: 'parent category' }); const { cid: childCid } = await categories.create({ name: 'child category', parentCid: rootCid }); - const { res, body } = await helpers.request('get', `/api/admin/manage/categories?cid=${rootCid}`, { + const { response, body } = await helpers.request('get', `/api/admin/manage/categories?cid=${rootCid}`, { jar: jar, json: true, }); - assert.strictEqual(res.statusCode, 200); + assert.strictEqual(response.statusCode, 200); assert.strictEqual(body.categoriesTree[0].cid, rootCid); assert.strictEqual(body.categoriesTree[0].children[0].cid, childCid); assert.strictEqual(body.breadcrumbs[0].text, '[[admin/manage/categories:top-level]]'); assert.strictEqual(body.breadcrumbs[1].text, 'parent category'); }); - it('should load /admin/manage/categories/1/analytics', (done) => { - request(`${nconf.get('url')}/api/admin/manage/categories/1/analytics`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/manage/categories/1/analytics', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/manage/categories/1/analytics`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /admin/extend/rewards', (done) => { - request(`${nconf.get('url')}/api/admin/extend/rewards`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/extend/rewards', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/extend/rewards`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /admin/extend/widgets', (done) => { - request(`${nconf.get('url')}/api/admin/extend/widgets`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/extend/widgets', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/extend/widgets`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /admin/settings/languages', (done) => { - request(`${nconf.get('url')}/api/admin/settings/languages`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/settings/languages', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/settings/languages`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /admin/settings/social', (done) => { - request(`${nconf.get('url')}/api/admin/settings/general`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert(body); - body = body.postSharing.map(network => network && network.id); - assert(body.includes('facebook')); - assert(body.includes('twitter')); - done(); - }); + it('should load /admin/settings/social', async () => { + const { body } = await request.get(`${nconf.get('url')}/api/admin/settings/general`, { jar }); + assert(body); + const sharing = body.postSharing.map(network => network && network.id); + assert(sharing.includes('facebook')); + assert(sharing.includes('twitter')); + assert(sharing.includes('linkedin')); + assert(sharing.includes('whatsapp')); + assert(sharing.includes('telegram')); }); - it('should load /admin/manage/tags', (done) => { - request(`${nconf.get('url')}/api/admin/manage/tags`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/manage/tags', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/manage/tags`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('/post-queue should 404 for regular user', (done) => { - request(`${nconf.get('url')}/api/post-queue`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert(body); - assert.equal(res.statusCode, 404); - done(); - }); + it('/post-queue should 404 for regular user', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/post-queue`); + assert(body); + assert.equal(response.statusCode, 404); }); - it('should load /post-queue', (done) => { - request(`${nconf.get('url')}/api/post-queue`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /post-queue', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/post-queue`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('/ip-blacklist should 404 for regular user', (done) => { - request(`${nconf.get('url')}/api/ip-blacklist`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert(body); - assert.equal(res.statusCode, 404); - done(); - }); + it('/ip-blacklist should 404 for regular user', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/ip-blacklist`); + assert(body); + assert.equal(response.statusCode, 404); }); - it('should load /ip-blacklist', (done) => { - request(`${nconf.get('url')}/api/ip-blacklist`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /ip-blacklist', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/ip-blacklist`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /admin/appearance/themes', (done) => { - request(`${nconf.get('url')}/api/admin/appearance/themes`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/appearance/themes', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/appearance/themes`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /admin/appearance/customise', (done) => { - request(`${nconf.get('url')}/api/admin/appearance/customise`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /admin/appearance/customise', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin/appearance/customise`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /recent in maintenance mode', (done) => { + it('should load /recent in maintenance mode', async () => { meta.config.maintenanceMode = 1; - request(`${nconf.get('url')}/api/recent`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - meta.config.maintenanceMode = 0; - done(); - }); + const { response, body } = await request.get(`${nconf.get('url')}/api/recent`, { jar }); + assert.equal(response.statusCode, 200); + meta.config.maintenanceMode = 0; + assert(body); }); describe('mods page', () => { @@ -673,56 +499,46 @@ describe('Admin Controllers', () => { await groups.join(`cid:${cid}:privileges:moderate`, moderatorUid); }); - it('should error with no privileges', (done) => { - request(`${nconf.get('url')}/api/flags`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.deepStrictEqual(body, { - status: { - code: 'not-authorised', - message: 'A valid login session was not found. Please log in and try again.', - }, - response: {}, - }); - done(); + it('should error with no privileges', async () => { + const { body } = await request.get(`${nconf.get('url')}/api/flags`); + + assert.deepStrictEqual(body, { + status: { + code: 'not-authorised', + message: 'A valid login session was not found. Please log in and try again.', + }, + response: {}, }); }); - it('should load flags page data', (done) => { - request(`${nconf.get('url')}/api/flags`, { jar: moderatorJar, json: true }, (err, res, body) => { - assert.ifError(err); - assert(body); - assert(body.flags); - assert(body.filters); - assert.equal(body.filters.cid.indexOf(cid), -1); - done(); - }); + it('should load flags page data', async () => { + const { body } = await request.get(`${nconf.get('url')}/api/flags`, { jar: moderatorJar }); + assert(body); + assert(body.flags); + assert(body.filters); + assert.equal(body.filters.cid.indexOf(cid), -1); }); - it('should return a 404 if flag does not exist', (done) => { - request(`${nconf.get('url')}/api/flags/123123123`, { + it('should return a 404 if flag does not exist', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/flags/123123123`, { jar: moderatorJar, - json: true, headers: { Accept: 'text/html, application/json', }, - }, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 404); - done(); }); + assert.strictEqual(response.statusCode, 404); }); it('should error when you attempt to flag a privileged user\'s post', async () => { - const { res, body } = await helpers.request('post', '/api/v3/flags', { - json: true, + const { response, body } = await helpers.request('post', '/api/v3/flags', { jar: regularJar, - form: { + body: { id: pid, type: 'post', reason: 'spam', }, }); - assert.strictEqual(res.statusCode, 400); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.code, 'bad-request'); assert.strictEqual(body.status.message, 'You are not allowed to flag the profiles or content of privileged users (moderators/global moderators/admins)'); }); @@ -730,16 +546,15 @@ describe('Admin Controllers', () => { it('should error with not enough reputation to flag', async () => { const oldValue = meta.config['min:rep:flag']; meta.config['min:rep:flag'] = 1000; - const { res, body } = await helpers.request('post', '/api/v3/flags', { - json: true, + const { response, body } = await helpers.request('post', '/api/v3/flags', { jar: regularJar, - form: { + body: { id: regularPid, type: 'post', reason: 'spam', }, }); - assert.strictEqual(res.statusCode, 400); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.code, 'bad-request'); assert.strictEqual(body.status.message, 'You need 1000 reputation to flag this post'); @@ -749,10 +564,9 @@ describe('Admin Controllers', () => { it('should return flag details', async () => { const oldValue = meta.config['min:rep:flag']; meta.config['min:rep:flag'] = 0; - const result = await helpers.request('post', '/api/v3/flags', { - json: true, + await helpers.request('post', '/api/v3/flags', { jar: regularJar, - form: { + body: { id: regularPid, type: 'post', reason: 'spam', @@ -770,7 +584,6 @@ describe('Admin Controllers', () => { const { flagId } = flagsResult.body.flags[0]; const { body } = await helpers.request('get', `/api/flags/${flagId}`, { - json: true, jar: moderatorJar, }); assert(body.reports); @@ -779,7 +592,7 @@ describe('Admin Controllers', () => { }); }); - it('should escape special characters in config', (done) => { + it('should escape special characters in config', async () => { const plugins = require('../src/plugins'); function onConfigGet(config, callback) { config.someValue = '"foo"'; @@ -788,46 +601,34 @@ describe('Admin Controllers', () => { callback(null, config); } plugins.hooks.register('somePlugin', { hook: 'filter:config.get', method: onConfigGet }); - request(`${nconf.get('url')}/admin`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - assert(body.includes('"someValue":"\\\\"foo\\\\""')); - assert(body.includes('"otherValue":"\\\'123\\\'"')); - assert(body.includes('"script":"<\\/script>"')); - request(nconf.get('url'), { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - assert(body.includes('"someValue":"\\\\"foo\\\\""')); - assert(body.includes('"otherValue":"\\\'123\\\'"')); - assert(body.includes('"script":"<\\/script>"')); - plugins.hooks.unregister('somePlugin', 'filter:config.get', onConfigGet); - done(); - }); - }); + const { response, body } = await request.get(`${nconf.get('url')}/admin`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); + assert(body.includes('"someValue":"\\\\"foo\\\\""')); + assert(body.includes('"otherValue":"\\\'123\\\'"')); + assert(body.includes('"script":"<\\/script>"')); + const { response: res2, body: body2 } = await request.get(nconf.get('url'), { jar }); + assert.equal(res2.statusCode, 200); + assert(body2); + assert(body2.includes('"someValue":"\\\\"foo\\\\""')); + assert(body2.includes('"otherValue":"\\\'123\\\'"')); + assert(body2.includes('"script":"<\\/script>"')); + plugins.hooks.unregister('somePlugin', 'filter:config.get', onConfigGet); }); describe('admin page privileges', () => { - let userJar; let uid; const privileges = require('../src/privileges'); + const requestOpts = {}; before(async () => { uid = await user.create({ username: 'regularjoe', password: 'barbar' }); - userJar = (await helpers.loginUser('regularjoe', 'barbar')).jar; + requestOpts.jar = (await helpers.loginUser('regularjoe', 'barbar')).jar; }); describe('routeMap parsing', () => { it('should allow normal user access to admin pages', async function () { this.timeout(50000); - function makeRequest(url) { - return new Promise((resolve, reject) => { - request(url, { jar: userJar, json: true }, (err, res, body) => { - if (err) reject(err); - else resolve(res); - }); - }); - } + const uploadRoutes = [ 'category/uploadpicture', 'uploadfavicon', @@ -842,11 +643,11 @@ describe('Admin Controllers', () => { for (const route of adminRoutes) { /* eslint-disable no-await-in-loop */ await privileges.admin.rescind([privileges.admin.routeMap[route]], uid); - let res = await makeRequest(`${nconf.get('url')}/api/admin/${route}`); + let { response: res } = await request.get(`${nconf.get('url')}/api/admin/${route}`, requestOpts); assert.strictEqual(res.statusCode, 403); await privileges.admin.give([privileges.admin.routeMap[route]], uid); - res = await makeRequest(`${nconf.get('url')}/api/admin/${route}`); + ({ response: res } = await request.get(`${nconf.get('url')}/api/admin/${route}`, requestOpts)); assert.strictEqual(res.statusCode, 200); await privileges.admin.rescind([privileges.admin.routeMap[route]], uid); @@ -855,11 +656,11 @@ describe('Admin Controllers', () => { for (const route of adminRoutes) { /* eslint-disable no-await-in-loop */ await privileges.admin.rescind([privileges.admin.routeMap[route]], uid); - let res = await makeRequest(`${nconf.get('url')}/api/admin`); + let { response: res } = await await request.get(`${nconf.get('url')}/api/admin`, requestOpts); assert.strictEqual(res.statusCode, 403); await privileges.admin.give([privileges.admin.routeMap[route]], uid); - res = await makeRequest(`${nconf.get('url')}/api/admin`); + ({ response: res } = await await request.get(`${nconf.get('url')}/api/admin`, requestOpts)); assert.strictEqual(res.statusCode, 200); await privileges.admin.rescind([privileges.admin.routeMap[route]], uid); @@ -869,23 +670,14 @@ describe('Admin Controllers', () => { describe('routePrefixMap parsing', () => { it('should allow normal user access to admin pages', async () => { - // this.timeout(50000); - function makeRequest(url) { - return new Promise((resolve, reject) => { - request(url, { jar: userJar, json: true }, (err, res, body) => { - if (err) reject(err); - else resolve(res); - }); - }); - } for (const route of Object.keys(privileges.admin.routePrefixMap)) { /* eslint-disable no-await-in-loop */ await privileges.admin.rescind([privileges.admin.routePrefixMap[route]], uid); - let res = await makeRequest(`${nconf.get('url')}/api/admin/${route}foobar/derp`); + let { response: res } = await request.get(`${nconf.get('url')}/api/admin/${route}foobar/derp`, requestOpts); assert.strictEqual(res.statusCode, 403); await privileges.admin.give([privileges.admin.routePrefixMap[route]], uid); - res = await makeRequest(`${nconf.get('url')}/api/admin/${route}foobar/derp`); + ({ response: res } = await request.get(`${nconf.get('url')}/api/admin/${route}foobar/derp`, requestOpts)); assert.strictEqual(res.statusCode, 404); await privileges.admin.rescind([privileges.admin.routePrefixMap[route]], uid); diff --git a/test/controllers.js b/test/controllers.js index 8a6ba3e204..7d49990423 100644 --- a/test/controllers.js +++ b/test/controllers.js @@ -1,14 +1,12 @@ 'use strict'; -const async = require('async'); const assert = require('assert'); const nconf = require('nconf'); -const request = require('request'); -const requestAsync = require('request-promise-native'); const fs = require('fs'); const path = require('path'); const util = require('util'); +const request = require('../src/request'); const db = require('./mocks/databasemock'); const api = require('../src/api'); const categories = require('../src/categories'); @@ -33,6 +31,7 @@ describe('Controllers', () => { let fooUid; let adminUid; let category; + let testRoutes = []; before(async () => { category = await categories.create({ @@ -55,36 +54,68 @@ describe('Controllers', () => { const result = await topics.post({ uid: fooUid, title: 'test topic title', content: 'test topic content', cid: cid }); tid = result.topicData.tid; + pid = result.postData.pid; + + testRoutes = [ + { it: 'should load /reset without code', url: '/reset' }, + { it: 'should load /reset with invalid code', url: '/reset/123123' }, + { it: 'should load /login', url: '/login' }, + { it: 'should load /register', url: '/register' }, + { it: 'should load /robots.txt', url: '/robots.txt' }, + { it: 'should load /manifest.webmanifest', url: '/manifest.webmanifest' }, + { it: 'should load /outgoing?url=', url: '/outgoing?url=http://youtube.com' }, + { it: 'should 404 on /outgoing with no url', url: '/outgoing', status: 404 }, + { it: 'should 404 on /outgoing with javascript: protocol', url: '/outgoing?url=javascript:alert(1);', status: 404 }, + { it: 'should 404 on /outgoing with invalid url', url: '/outgoing?url=derp', status: 404 }, + { it: 'should load /sping', url: '/sping', body: 'healthy' }, + { it: 'should load /ping', url: '/ping', body: '200' }, + { it: 'should handle 404', url: '/arouteinthevoid', status: 404 }, + { it: 'should load topic rss feed', url: `/topic/${tid}.rss` }, + { it: 'should load category rss feed', url: `/category/${cid}.rss` }, + { it: 'should load topics rss feed', url: `/topics.rss` }, + { it: 'should load recent rss feed', url: `/recent.rss` }, + { it: 'should load top rss feed', url: `/top.rss` }, + { it: 'should load popular rss feed', url: `/popular.rss` }, + { it: 'should load popular rss feed with term', url: `/popular/day.rss` }, + { it: 'should load recent posts rss feed', url: `/recentposts.rss` }, + { it: 'should load category recent posts rss feed', url: `/category/${cid}/recentposts.rss` }, + { it: 'should load user topics rss feed', url: `/user/foo/topics.rss` }, + { it: 'should load tag rss feed', url: `/tags/nodebb.rss` }, + { it: 'should load client.css', url: `/assets/client.css` }, + { it: 'should load admin.css', url: `/assets/admin.css` }, + { it: 'should load sitemap.xml', url: `/sitemap.xml` }, + { it: 'should load sitemap/pages.xml', url: `/sitemap/pages.xml` }, + { it: 'should load sitemap/categories.xml', url: `/sitemap/categories.xml` }, + { it: 'should load sitemap/topics.1.xml', url: `/sitemap/topics.1.xml` }, + { it: 'should load theme screenshot', url: `/css/previews/nodebb-theme-harmony` }, + { it: 'should load users page', url: `/users` }, + { it: 'should load users page section', url: `/users?section=online` }, + { it: 'should load groups page', url: `/groups` }, + { it: 'should get recent posts', url: `/api/recent/posts/month` }, + { it: 'should get post data', url: `/api/v3/posts/${pid}` }, + { it: 'should get topic data', url: `/api/v3/topics/${tid}` }, + { it: 'should get category data', url: `/api/v3/categories/${cid}` }, + { it: 'should return osd data', url: `/osd.xml` }, + ]; }); - it('should load /config with csrf_token', (done) => { - request({ - url: `${nconf.get('url')}/api/config`, - json: true, - }, (err, response, body) => { - assert.ifError(err); - assert.equal(response.statusCode, 200); - assert(body.csrf_token); - done(); - }); + it('should load /config with csrf_token', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/config`); + assert.equal(response.statusCode, 200); + assert(body.csrf_token); }); - it('should load /config with no csrf_token as spider', (done) => { - request({ - url: `${nconf.get('url')}/api/config`, - json: true, + it('should load /config with no csrf_token as spider', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/config`, { headers: { 'user-agent': 'yandex', }, - }, (err, response, body) => { - assert.ifError(err); - assert.equal(response.statusCode, 200); - assert.strictEqual(body.csrf_token, false); - assert.strictEqual(body.uid, -1); - assert.strictEqual(body.loggedIn, false); - done(); }); + assert.equal(response.statusCode, 200); + assert.strictEqual(body.csrf_token, false); + assert.strictEqual(body.uid, -1); + assert.strictEqual(body.loggedIn, false); }); describe('homepage', () => { @@ -111,145 +142,75 @@ describe('Controllers', () => { await meta.templates.compileTemplate(name, message); }); - it('should load default', (done) => { - request(nconf.get('url'), (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + async function assertHomeUrl() { + const { response, body } = await request.get(nconf.get('url')); + assert.equal(response.statusCode, 200); + assert(body); + } + + it('should load default', async () => { + await assertHomeUrl(); }); - it('should load unread', (done) => { - meta.configs.set('homePageRoute', 'unread', (err) => { - assert.ifError(err); - - request(nconf.get('url'), (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); + it('should load unread', async () => { + await meta.configs.set('homePageRoute', 'unread'); + await assertHomeUrl(); }); - it('should load recent', (done) => { - meta.configs.set('homePageRoute', 'recent', (err) => { - assert.ifError(err); - - request(nconf.get('url'), (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); + it('should load recent', async () => { + await meta.configs.set('homePageRoute', 'recent'); + await assertHomeUrl(); }); - it('should load top', (done) => { - meta.configs.set('homePageRoute', 'top', (err) => { - assert.ifError(err); - - request(nconf.get('url'), (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); + it('should load top', async () => { + await meta.configs.set('homePageRoute', 'top'); + await assertHomeUrl(); }); - it('should load popular', (done) => { - meta.configs.set('homePageRoute', 'popular', (err) => { - assert.ifError(err); - - request(nconf.get('url'), (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); + it('should load popular', async () => { + await meta.configs.set('homePageRoute', 'popular'); + await assertHomeUrl(); }); - it('should load category', (done) => { - meta.configs.set('homePageRoute', 'category/1/test-category', (err) => { - assert.ifError(err); - - request(nconf.get('url'), (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); + it('should load category', async () => { + await meta.configs.set('homePageRoute', 'category/1/test-category'); + await assertHomeUrl(); }); - it('should not load breadcrumbs on home page route', (done) => { - request(`${nconf.get('url')}/api`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - assert(!body.breadcrumbs); - done(); - }); + it('should not load breadcrumbs on home page route', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api`); + assert.equal(response.statusCode, 200); + assert(body); + assert(!body.breadcrumbs); }); - it('should redirect to custom', (done) => { - meta.configs.set('homePageRoute', 'groups', (err) => { - assert.ifError(err); - - request(nconf.get('url'), (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); + it('should redirect to custom', async () => { + await meta.configs.set('homePageRoute', 'groups'); + await assertHomeUrl(); }); - it('should 404 if custom does not exist', (done) => { - meta.configs.set('homePageRoute', 'this-route-does-not-exist', (err) => { - assert.ifError(err); - - request(nconf.get('url'), (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - assert(body); - done(); - }); - }); + it('should 404 if custom does not exist', async () => { + await meta.configs.set('homePageRoute', 'this-route-does-not-exist'); + const { response, body } = await request.get(nconf.get('url')); + assert.equal(response.statusCode, 404); + assert(body); }); - it('api should work with hook', (done) => { - meta.configs.set('homePageRoute', 'mycustompage', (err) => { - assert.ifError(err); - - request(`${nconf.get('url')}/api`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(body.works, true); - assert.equal(body.template.mycustompage, true); - - done(); - }); - }); + it('api should work with hook', async () => { + await meta.configs.set('homePageRoute', 'mycustompage'); + const { response, body } = await request.get(`${nconf.get('url')}/api`); + assert.equal(response.statusCode, 200); + assert.equal(body.works, true); + assert.equal(body.template.mycustompage, true); }); - it('should render with hook', (done) => { - meta.configs.set('homePageRoute', 'mycustompage', (err) => { - assert.ifError(err); - - request(nconf.get('url'), (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.ok(body); - assert.ok(body.indexOf('
{ + await meta.configs.set('homePageRoute', 'mycustompage'); + const { response, body } = await request.get(nconf.get('url')); + assert.equal(response.statusCode, 200); + assert.ok(body); + assert.ok(body.indexOf('
{ @@ -259,84 +220,49 @@ describe('Controllers', () => { }); }); - it('should load /reset without code', (done) => { - request(`${nconf.get('url')}/reset`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load /reset with invalid code', (done) => { - request(`${nconf.get('url')}/reset/123123`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load /login', (done) => { - request(`${nconf.get('url')}/login`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load /register', (done) => { - request(`${nconf.get('url')}/register`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load /register/complete', (done) => { - const data = { - username: 'interstitial', - password: '123456', - 'password-confirm': '123456', - email: 'test@me.com', - }; - - const jar = request.jar(); - request({ - url: `${nconf.get('url')}/api/config`, - json: true, - jar: jar, - }, (err, response, body) => { - assert.ifError(err); - - request.post(`${nconf.get('url')}/register`, { - form: data, - json: true, - jar: jar, - headers: { - 'x-csrf-token': body.csrf_token, - }, - }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.strictEqual(body.next, `${nconf.get('relative_path')}/register/complete`); - request(`${nconf.get('url')}/api/register/complete`, { - jar: jar, - json: true, - }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body.sections); - assert(body.errors); - assert(body.title); - done(); - }); + describe('routes that should 200/404 etc.', () => { + const baseUrl = nconf.get('url'); + testRoutes.forEach((route) => { + it(route.it, async () => { + const { response, body } = await request.get(`${baseUrl}/${route.url}`); + assert.equal(response.statusCode, route.status || 200); + if (route.body) { + assert.strictEqual(String(body), route.body); + } else { + assert(body); + } }); }); }); + it('should load /register/complete', async () => { + const jar = request.jar(); + const csrf_token = await helpers.getCsrfToken(jar); + const { response, body } = await request.post(`${nconf.get('url')}/register`, { + body: { + username: 'interstitial', + password: '123456', + 'password-confirm': '123456', + email: 'test@me.com', + }, + jar, + headers: { + 'x-csrf-token': csrf_token, + }, + }); + assert.equal(response.statusCode, 200); + assert.strictEqual(body.next, `${nconf.get('relative_path')}/register/complete`); + + const { response: res2, body: body2 } = await request.get(`${nconf.get('url')}/api/register/complete`, { + jar: jar, + json: true, + }); + assert.equal(res2.statusCode, 200); + assert(body2.sections); + assert(body2.errors); + assert(body2.title); + }); + describe('registration interstitials', () => { describe('email update', () => { let jar; @@ -350,10 +276,10 @@ describe('Controllers', () => { method: dummyEmailerHook, }); - jar = await helpers.registerUser({ + jar = (await helpers.registerUser({ username: utils.generateUUID().slice(0, 10), password: utils.generateUUID(), - }); + })).jar; token = await helpers.getCsrfToken(jar); meta.config.requireEmailAddress = 1; @@ -365,44 +291,37 @@ describe('Controllers', () => { }); it('email interstitial should still apply if empty email entered and requireEmailAddress is enabled', async () => { - let res = await requestAsync(`${nconf.get('url')}/register/complete`, { - method: 'post', + const { response: res } = await request.post(`${nconf.get('url')}/register/complete`, { jar, - json: true, - followRedirect: false, - simple: false, - resolveWithFullResponse: true, + maxRedirect: 0, + redirect: 'manual', headers: { 'x-csrf-token': token, }, - form: { + body: { email: '', }, }); assert.strictEqual(res.headers.location, `${nconf.get('relative_path')}/register/complete`); - res = await requestAsync(`${nconf.get('url')}/api/register/complete`, { + const { response, body } = await request.get(`${nconf.get('url')}/api/register/complete`, { jar, - json: true, - resolveWithFullResponse: true, }); - assert.strictEqual(res.statusCode, 200); - assert(res.body.errors.length); - assert(res.body.errors.includes('[[error:invalid-email]]')); + assert.strictEqual(response.statusCode, 200); + assert(body.errors.length); + assert(body.errors.includes('[[error:invalid-email]]')); }); it('gdpr interstitial should still apply if email requirement is disabled', async () => { meta.config.requireEmailAddress = 0; - const res = await requestAsync(`${nconf.get('url')}/api/register/complete`, { + const { body } = await request.get(`${nconf.get('url')}/api/register/complete`, { jar, - json: true, - resolveWithFullResponse: true, }); - assert(!res.body.errors.includes('[[error:invalid-email]]')); - assert(!res.body.errors.includes('[[error:gdpr-consent-denied]]')); + assert(!body.errors.includes('[[error:invalid-email]]')); + assert(!body.errors.includes('[[error:gdpr-consent-denied]]')); meta.config.requireEmailAddress = 1; }); @@ -575,18 +494,16 @@ describe('Controllers', () => { const username = utils.generateUUID().slice(0, 10); before(async () => { - jar = await helpers.registerUser({ + jar = (await helpers.registerUser({ username, password: utils.generateUUID(), - }); + })).jar; token = await helpers.getCsrfToken(jar); }); async function abortInterstitial() { - await requestAsync(`${nconf.get('url')}/register/abort`, { - method: 'post', + await request.post(`${nconf.get('url')}/register/abort`, { jar, - simple: false, headers: { 'x-csrf-token': token, }, @@ -596,38 +513,32 @@ describe('Controllers', () => { it('should not apply if requireEmailAddress is not enabled', async () => { meta.config.requireEmailAddress = 0; - const res = await requestAsync(`${nconf.get('url')}/register/complete`, { - method: 'post', + const { response } = await request.post(`${nconf.get('url')}/register/complete`, { jar, - json: true, - followRedirect: false, - simple: false, - resolveWithFullResponse: true, + maxRedirect: 0, + redirect: 'manual', headers: { 'x-csrf-token': token, }, - form: { + body: { email: `${utils.generateUUID().slice(0, 10)}@example.org`, gdpr_agree_data: 'on', gdpr_agree_email: 'on', }, }); - console.log(res.headers.location); - assert.strictEqual(res.headers.location, `${nconf.get('relative_path')}/`); + + assert.strictEqual(response.headers.location, `${nconf.get('relative_path')}/`); meta.config.requireEmailAddress = 1; }); it('should allow access to regular resources after an email is entered, even if unconfirmed', async () => { - const res = await requestAsync(`${nconf.get('url')}/recent`, { + const { response } = await request.get(`${nconf.get('url')}/recent`, { jar, - json: true, - resolveWithFullResponse: true, - followRedirect: false, - simple: false, + maxRedirect: 0, }); - assert.strictEqual(res.statusCode, 200); + assert.strictEqual(response.statusCode, 200); }); it('should redirect back to interstitial for categories requiring validated email', async () => { @@ -635,16 +546,14 @@ describe('Controllers', () => { const { cid } = await categories.create({ name }); await privileges.categories.rescind(['groups:read'], cid, ['registered-users']); await privileges.categories.give(['groups:read'], cid, ['verified-users']); - const res = await requestAsync(`${nconf.get('url')}/category/${cid}/${slugify(name)}`, { + const { response } = await request.get(`${nconf.get('url')}/category/${cid}/${slugify(name)}`, { jar, - json: true, - resolveWithFullResponse: true, - followRedirect: false, - simple: false, + maxRedirect: 0, + redirect: 'manual', }); - assert.strictEqual(res.statusCode, 307); - assert.strictEqual(res.headers.location, `${nconf.get('relative_path')}/register/complete`); + assert.strictEqual(response.statusCode, 307); + assert.strictEqual(response.headers.location, `${nconf.get('relative_path')}/register/complete`); await abortInterstitial(); }); @@ -653,25 +562,21 @@ describe('Controllers', () => { const { cid } = await categories.create({ name }); await privileges.categories.rescind(['groups:topics:read'], cid, 'registered-users'); await privileges.categories.give(['groups:topics:read'], cid, 'verified-users'); - const res = await requestAsync(`${nconf.get('url')}/category/${cid}/${slugify(name)}`, { + const { response } = await request.get(`${nconf.get('url')}/category/${cid}/${slugify(name)}`, { jar, - json: true, - resolveWithFullResponse: true, - followRedirect: false, - simple: false, + maxRedirect: 0, + redirect: 'manual', }); - assert.strictEqual(res.statusCode, 200); + assert.strictEqual(response.statusCode, 200); const title = utils.generateUUID(); const uid = await user.getUidByUsername(username); const { topicData } = await topics.post({ uid, cid, title, content: utils.generateUUID() }); - const res2 = await requestAsync(`${nconf.get('url')}/topic/${topicData.tid}/${slugify(title)}`, { + const { response: res2 } = await request.get(`${nconf.get('url')}/topic/${topicData.tid}/${slugify(title)}`, { jar, - json: true, - resolveWithFullResponse: true, - followRedirect: false, - simple: false, + maxRedirect: 0, + redirect: 'manual', }); assert.strictEqual(res2.statusCode, 307); assert.strictEqual(res2.headers.location, `${nconf.get('relative_path')}/register/complete`); @@ -686,32 +591,29 @@ describe('Controllers', () => { let token; before(async () => { - jar = await helpers.registerUser({ + jar = (await helpers.registerUser({ username: utils.generateUUID().slice(0, 10), password: utils.generateUUID(), - }); + })).jar; token = await helpers.getCsrfToken(jar); }); it('registration should succeed once gdpr prompts are agreed to', async () => { - const res = await requestAsync(`${nconf.get('url')}/register/complete`, { - method: 'post', + const { response } = await request.post(`${nconf.get('url')}/register/complete`, { jar, - json: true, - followRedirect: false, - simple: false, - resolveWithFullResponse: true, + maxRedirect: 0, + redirect: 'manual', headers: { 'x-csrf-token': token, }, - form: { + body: { gdpr_agree_data: 'on', gdpr_agree_email: 'on', }, }); - assert.strictEqual(res.statusCode, 302); - assert.strictEqual(res.headers.location, `${nconf.get('relative_path')}/`); + assert.strictEqual(response.statusCode, 302); + assert.strictEqual(response.headers.location, `${nconf.get('relative_path')}/`); }); }); @@ -720,480 +622,131 @@ describe('Controllers', () => { let token; beforeEach(async () => { - jar = await helpers.registerUser({ + jar = (await helpers.registerUser({ username: utils.generateUUID().slice(0, 10), password: utils.generateUUID(), - }); + })).jar; token = await helpers.getCsrfToken(jar); }); it('should terminate the session and send user back to index if interstitials remain', async () => { - const res = await requestAsync(`${nconf.get('url')}/register/abort`, { - method: 'post', + const { response } = await request.post(`${nconf.get('url')}/register/abort`, { jar, - json: true, - followRedirect: false, - simple: false, - resolveWithFullResponse: true, + maxRedirect: 0, + redirect: 'manual', headers: { 'x-csrf-token': token, }, }); - assert.strictEqual(res.statusCode, 302); - assert.strictEqual(res.headers['set-cookie'][0], `express.sid=; Path=${nconf.get('relative_path') || '/'}; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax`); - assert.strictEqual(res.headers.location, `${nconf.get('relative_path')}/`); + assert.strictEqual(response.statusCode, 302); + assert.strictEqual(response.headers['set-cookie'], `express.sid=; Path=${nconf.get('relative_path') || '/'}; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax`); + assert.strictEqual(response.headers.location, `${nconf.get('relative_path')}/`); }); it('should preserve the session and send user back to user profile if no interstitials remain (e.g. GDPR OK + email change cancellation)', async () => { // Submit GDPR consent - await requestAsync(`${nconf.get('url')}/register/complete`, { - method: 'post', + await request.post(`${nconf.get('url')}/register/complete`, { jar, - json: true, - followRedirect: false, - simple: false, - resolveWithFullResponse: true, + maxRedirect: 0, + redirect: 'manual', headers: { 'x-csrf-token': token, }, - form: { + body: { gdpr_agree_data: 'on', gdpr_agree_email: 'on', }, }); // Start email change flow - await requestAsync(`${nconf.get('url')}/me/edit/email`, { jar }); + await request.get(`${nconf.get('url')}/me/edit/email`, { jar }); - const res = await requestAsync(`${nconf.get('url')}/register/abort`, { - method: 'post', + const { response } = await request.post(`${nconf.get('url')}/register/abort`, { jar, - json: true, - followRedirect: false, - simple: false, - resolveWithFullResponse: true, + maxRedirect: 0, + redirect: 'manual', headers: { 'x-csrf-token': token, }, }); - assert.strictEqual(res.statusCode, 302); - assert(res.headers.location.match(/\/uid\/\d+$/)); + assert.strictEqual(response.statusCode, 302); + assert(response.headers.location.match(/\/uid\/\d+$/)); }); }); }); - it('should load /robots.txt', (done) => { - request(`${nconf.get('url')}/robots.txt`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - it('should load /manifest.webmanifest', (done) => { - request(`${nconf.get('url')}/manifest.webmanifest`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load /outgoing?url=', (done) => { - request(`${nconf.get('url')}/outgoing?url=http://youtube.com`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should 404 on /outgoing with no url', (done) => { - request(`${nconf.get('url')}/outgoing`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - assert(body); - done(); - }); - }); - - it('should 404 on /outgoing with javascript: protocol', (done) => { - request(`${nconf.get('url')}/outgoing?url=javascript:alert(1);`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - assert(body); - done(); - }); - }); - - it('should 404 on /outgoing with invalid url', (done) => { - request(`${nconf.get('url')}/outgoing?url=derp`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - assert(body); - done(); - }); - }); - - it('should load /tos', (done) => { + it('should load /tos', async () => { meta.config.termsOfUse = 'please accept our tos'; - request(`${nconf.get('url')}/tos`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + const { response, body } = await request.get(`${nconf.get('url')}/tos`); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load 404 if meta.config.termsOfUse is empty', (done) => { + it('should return 404 if meta.config.termsOfUse is empty', async () => { meta.config.termsOfUse = ''; - request(`${nconf.get('url')}/tos`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - assert(body); - done(); - }); + const { response, body } = await request.get(`${nconf.get('url')}/tos`); + assert.equal(response.statusCode, 404); + assert(body); }); - it('should load /sping', (done) => { - request(`${nconf.get('url')}/sping`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(body, 'healthy'); - done(); - }); + + it('should error if guests do not have search privilege', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/users?query=bar§ion=sort-posts`); + assert.equal(response.statusCode, 500); + assert(body); + assert.equal(body.error, '[[error:no-privileges]]'); }); - it('should load /ping', (done) => { - request(`${nconf.get('url')}/ping`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(body, '200'); - done(); - }); + it('should load users search page', async () => { + await privileges.global.give(['groups:search:users'], 'guests'); + const { response, body } = await request.get(`${nconf.get('url')}/users?query=bar§ion=sort-posts`); + assert.equal(response.statusCode, 200); + assert(body); + await privileges.global.rescind(['groups:search:users'], 'guests'); }); - it('should handle 404', (done) => { - request(`${nconf.get('url')}/arouteinthevoid`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - assert(body); - done(); - }); - }); - - it('should load topic rss feed', (done) => { - request(`${nconf.get('url')}/topic/${tid}.rss`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load category rss feed', (done) => { - request(`${nconf.get('url')}/category/${cid}.rss`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load topics rss feed', (done) => { - request(`${nconf.get('url')}/topics.rss`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load recent rss feed', (done) => { - request(`${nconf.get('url')}/recent.rss`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load top rss feed', (done) => { - request(`${nconf.get('url')}/top.rss`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load popular rss feed', (done) => { - request(`${nconf.get('url')}/popular.rss`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load popular rss feed with term', (done) => { - request(`${nconf.get('url')}/popular/day.rss`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load recent posts rss feed', (done) => { - request(`${nconf.get('url')}/recentposts.rss`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load category recent posts rss feed', (done) => { - request(`${nconf.get('url')}/category/${cid}/recentposts.rss`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load user topics rss feed', (done) => { - request(`${nconf.get('url')}/user/foo/topics.rss`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load tag rss feed', (done) => { - request(`${nconf.get('url')}/tags/nodebb.rss`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load client.css', (done) => { - request(`${nconf.get('url')}/assets/client.css`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load admin.css', (done) => { - request(`${nconf.get('url')}/assets/admin.css`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load sitemap.xml', (done) => { - request(`${nconf.get('url')}/sitemap.xml`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load sitemap/pages.xml', (done) => { - request(`${nconf.get('url')}/sitemap/pages.xml`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load sitemap/categories.xml', (done) => { - request(`${nconf.get('url')}/sitemap/categories.xml`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load sitemap/topics/1.xml', (done) => { - request(`${nconf.get('url')}/sitemap/topics.1.xml`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load robots.txt', (done) => { - request(`${nconf.get('url')}/robots.txt`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load theme screenshot', (done) => { - request(`${nconf.get('url')}/css/previews/nodebb-theme-persona`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load users page', (done) => { - request(`${nconf.get('url')}/users`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load users page', (done) => { - request(`${nconf.get('url')}/users?section=online`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should error if guests do not have search privilege', (done) => { - request(`${nconf.get('url')}/api/users?query=bar§ion=sort-posts`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 500); - assert(body); - assert.equal(body.error, '[[error:no-privileges]]'); - done(); - }); - }); - - it('should load users search page', (done) => { - privileges.global.give(['groups:search:users'], 'guests', (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/users?query=bar§ion=sort-posts`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - privileges.global.rescind(['groups:search:users'], 'guests', done); - }); - }); - }); - - it('should load groups page', (done) => { - request(`${nconf.get('url')}/groups`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should load group details page', (done) => { - groups.create({ + it('should load group details page', async () => { + await groups.create({ name: 'group-details', description: 'Foobar!', hidden: 0, - }, (err) => { - assert.ifError(err); - groups.join('group-details', fooUid, (err) => { - assert.ifError(err); - topics.post({ - uid: fooUid, - title: 'topic title', - content: 'test topic content', - cid: cid, - }, (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/api/groups/group-details`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - assert.equal(body.posts[0].content, 'test topic content'); - done(); - }); - }); - }); }); + await groups.join('group-details', fooUid); + + await topics.post({ + uid: fooUid, + title: 'topic title', + content: 'test topic content', + cid: cid, + }); + + const { response, body } = await request.get(`${nconf.get('url')}/api/groups/group-details`); + assert.equal(response.statusCode, 200); + assert(body); + assert.equal(body.posts[0].content, 'test topic content'); }); - it('should load group members page', (done) => { - request(`${nconf.get('url')}/groups/group-details/members`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load group members page', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/groups/group-details/members`); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should 404 when trying to load group members of hidden group', (done) => { + it('should 404 when trying to load group members of hidden group', async () => { const groups = require('../src/groups'); - groups.create({ + await groups.create({ name: 'hidden-group', description: 'Foobar!', hidden: 1, - }, (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/groups/hidden-group/members`, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - done(); - }); }); + const { response } = await request.get(`${nconf.get('url')}/groups/hidden-group/members`); + assert.equal(response.statusCode, 404); }); - it('should get recent posts', (done) => { - request(`${nconf.get('url')}/api/recent/posts/month`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should get post data', (done) => { - request(`${nconf.get('url')}/api/v3/posts/${pid}`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should get topic data', (done) => { - request(`${nconf.get('url')}/api/v3/topics/${tid}`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); - - it('should get category data', (done) => { - request(`${nconf.get('url')}/api/v3/categories/${cid}`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); describe('revoke session', () => { @@ -1208,63 +761,52 @@ describe('Controllers', () => { csrf_token = login.csrf_token; }); - it('should fail to revoke session with missing uuid', (done) => { - request.del(`${nconf.get('url')}/api/user/revokeme/session`, { + it('should fail to revoke session with missing uuid', async () => { + const { response } = await request.del(`${nconf.get('url')}/api/user/revokeme/session`, { jar: jar, headers: { 'x-csrf-token': csrf_token, }, - }, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - done(); }); + assert.equal(response.statusCode, 404); }); - it('should fail if user doesn\'t exist', (done) => { - request.del(`${nconf.get('url')}/api/v3/users/doesnotexist/sessions/1112233`, { + it('should fail if user doesn\'t exist', async () => { + const { response, body } = await request.del(`${nconf.get('url')}/api/v3/users/doesnotexist/sessions/1112233`, { jar: jar, headers: { 'x-csrf-token': csrf_token, }, - }, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 404); - const parsedResponse = JSON.parse(body); - assert.deepStrictEqual(parsedResponse.response, {}); - assert.deepStrictEqual(parsedResponse.status, { - code: 'not-found', - message: 'User does not exist', - }); - done(); + }); + + assert.strictEqual(response.statusCode, 404); + // const parsedResponse = JSON.parse(body); + assert.deepStrictEqual(body.response, {}); + assert.deepStrictEqual(body.status, { + code: 'not-found', + message: 'User does not exist', }); }); - it('should revoke user session', (done) => { - db.getSortedSetRange(`uid:${uid}:sessions`, 0, -1, (err, sids) => { - assert.ifError(err); - const sid = sids[0]; + it('should revoke user session', async () => { + const sids = await db.getSortedSetRange(`uid:${uid}:sessions`, 0, -1); + const sid = sids[0]; + const sessionObj = await db.sessionStoreGet(sid); - db.sessionStore.get(sid, (err, sessionObj) => { - assert.ifError(err); - request.del(`${nconf.get('url')}/api/v3/users/${uid}/sessions/${sessionObj.meta.uuid}`, { - jar: jar, - headers: { - 'x-csrf-token': csrf_token, - }, - }, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 200); - assert.deepStrictEqual(JSON.parse(body), { - status: { - code: 'ok', - message: 'OK', - }, - response: {}, - }); - done(); - }); - }); + const { response, body } = await request.del(`${nconf.get('url')}/api/v3/users/${uid}/sessions/${sessionObj.meta.uuid}`, { + jar: jar, + headers: { + 'x-csrf-token': csrf_token, + }, + }); + + assert.strictEqual(response.statusCode, 200); + assert.deepStrictEqual(body, { + status: { + code: 'ok', + message: 'OK', + }, + response: {}, }); }); }); @@ -1272,114 +814,83 @@ describe('Controllers', () => { describe('widgets', () => { const widgets = require('../src/widgets'); - before((done) => { - async.waterfall([ - function (next) { - widgets.reset(next); - }, - function (next) { - const data = { - template: 'categories.tpl', - location: 'sidebar', - widgets: [ - { - widget: 'html', - data: { - html: 'test', - title: '', - container: '', - }, - }, - ], - }; + before(async () => { + await widgets.reset(); + const data = { + template: 'categories.tpl', + location: 'sidebar', + widgets: [ + { + widget: 'html', + data: { + html: 'test', + title: '', + container: '', + }, + }, + ], + }; - widgets.setArea(data, next); - }, - ], done); + await widgets.setArea(data); }); - it('should return {} if there are no widgets', (done) => { - request(`${nconf.get('url')}/api/category/${cid}`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body.widgets); - assert.equal(Object.keys(body.widgets).length, 0); - done(); - }); + it('should return {} if there are no widgets', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/category/${cid}`); + assert.equal(response.statusCode, 200); + assert(body.widgets); + assert.equal(Object.keys(body.widgets).length, 0); }); - it('should render templates', (done) => { + it('should render templates', async () => { const url = `${nconf.get('url')}/api/categories`; - request(url, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body.widgets); - assert(body.widgets.sidebar); - assert.equal(body.widgets.sidebar[0].html, 'test'); - done(); - }); + const { response, body } = await request.get(url); + assert.equal(response.statusCode, 200); + assert(body.widgets); + assert(body.widgets.sidebar); + assert.equal(body.widgets.sidebar[0].html, 'test'); }); - it('should reset templates', (done) => { - widgets.resetTemplates(['categories', 'category'], (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/api/categories`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body.widgets); - assert.equal(Object.keys(body.widgets).length, 0); - done(); - }); - }); + it('should reset templates', async () => { + await widgets.resetTemplates(['categories', 'category']); + const { response, body } = await request.get(`${nconf.get('url')}/api/categories`); + assert.equal(response.statusCode, 200); + assert(body.widgets); + assert.equal(Object.keys(body.widgets).length, 0); }); }); describe('tags', () => { - let tid; - before((done) => { - topics.post({ + before(async () => { + await topics.post({ uid: fooUid, title: 'topic title', content: 'test topic content', cid: cid, tags: ['nodebb', 'bug', 'test'], - }, (err, result) => { - assert.ifError(err); - tid = result.topicData.tid; - done(); }); }); - it('should render tags page', (done) => { - request(`${nconf.get('url')}/api/tags`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - assert(Array.isArray(body.tags)); - done(); - }); + it('should render tags page', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/tags`); + assert.equal(response.statusCode, 200); + assert(body); + assert(Array.isArray(body.tags)); }); - it('should render tag page with no topics', (done) => { - request(`${nconf.get('url')}/api/tags/notag`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - assert(Array.isArray(body.topics)); - assert.equal(body.topics.length, 0); - done(); - }); + it('should render tag page with no topics', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/tags/notag`); + assert.equal(response.statusCode, 200); + assert(body); + assert(Array.isArray(body.topics)); + assert.equal(body.topics.length, 0); }); - it('should render tag page with 1 topic', (done) => { - request(`${nconf.get('url')}/api/tags/nodebb`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - assert(Array.isArray(body.topics)); - assert.equal(body.topics.length, 1); - done(); - }); + it('should render tag page with 1 topic', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/tags/nodebb`); + assert.equal(response.statusCode, 200); + assert(body); + assert(Array.isArray(body.topics)); + assert.equal(body.topics.length, 1); }); }); @@ -1394,42 +905,30 @@ describe('Controllers', () => { done(); }); - it('should return 503 in maintenance mode', (done) => { - request(`${nconf.get('url')}/recent`, { json: true }, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 503); - done(); - }); + it('should return 503 in maintenance mode', async () => { + const { response } = await request.get(`${nconf.get('url')}/recent`); + assert.equal(response.statusCode, 503); }); - it('should return 503 in maintenance mode', (done) => { - request(`${nconf.get('url')}/api/recent`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 503); - assert(body); - done(); - }); + it('should return 503 in maintenance mode', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/recent`); + assert.equal(response.statusCode, 503); + assert(body); }); - it('should return 200 in maintenance mode', (done) => { - request(`${nconf.get('url')}/api/login`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should return 200 in maintenance mode', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/login`); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should return 200 if guests are allowed', (done) => { + it('should return 200 if guests are allowed', async () => { const oldValue = meta.config.groupsExemptFromMaintenanceMode; meta.config.groupsExemptFromMaintenanceMode.push('guests'); - request(`${nconf.get('url')}/api/recent`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 200); - assert(body); - meta.config.groupsExemptFromMaintenanceMode = oldValue; - done(); - }); + const { response, body } = await request.get(`${nconf.get('url')}/api/recent`); + assert.strictEqual(response.statusCode, 200); + assert(body); + meta.config.groupsExemptFromMaintenanceMode = oldValue; }); }); @@ -1441,239 +940,170 @@ describe('Controllers', () => { ({ jar, csrf_token } = await helpers.loginUser('foo', 'barbar')); }); - it('should redirect to account page with logged in user', (done) => { - request(`${nconf.get('url')}/api/login`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(res.headers['x-redirect'], '/user/foo'); - assert.equal(body, '/user/foo'); - done(); - }); + it('should redirect to account page with logged in user', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/login`, { jar }); + assert.equal(response.statusCode, 200); + assert.equal(response.headers['x-redirect'], '/user/foo'); + assert.equal(body, '/user/foo'); }); - it('should 404 if uid is not a number', (done) => { - request(`${nconf.get('url')}/api/uid/test`, { json: true }, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - done(); - }); + it('should 404 if uid is not a number', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/uid/test`, { jar }); + assert.equal(response.statusCode, 404); }); - it('should redirect to userslug', (done) => { - request(`${nconf.get('url')}/api/uid/${fooUid}`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(res.headers['x-redirect'], '/user/foo'); - assert.equal(body, '/user/foo'); - done(); - }); + it('should redirect to userslug', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/uid/${fooUid}`); + assert.equal(response.statusCode, 200); + assert.equal(response.headers['x-redirect'], '/user/foo'); + assert.equal(body, '/user/foo'); }); - it('should redirect to userslug and keep query params', (done) => { - request(`${nconf.get('url')}/api/uid/${fooUid}/topics?foo=bar`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(res.headers['x-redirect'], '/user/foo/topics?foo=bar'); - assert.equal(body, '/user/foo/topics?foo=bar'); - done(); - }); + it('should redirect to userslug and keep query params', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/uid/${fooUid}/topics?foo=bar`); + assert.equal(response.statusCode, 200); + assert.equal(response.headers['x-redirect'], '/user/foo/topics?foo=bar'); + assert.equal(body, '/user/foo/topics?foo=bar'); }); - it('should 404 if user does not exist', (done) => { - request(`${nconf.get('url')}/api/uid/123123`, { json: true }, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - done(); - }); + it('should 404 if user does not exist', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/uid/123123`); + assert.equal(response.statusCode, 404); }); describe('/me/*', () => { - it('should redirect to user profile', (done) => { - request(`${nconf.get('url')}/me`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body.includes('"template":{"name":"account/profile","account/profile":true}')); - assert(body.includes('"username":"foo"')); - done(); - }); + it('should redirect to user profile', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/me`, { jar }); + assert.equal(response.statusCode, 200); + assert(body.includes('"template":{"name":"account/profile","account/profile":true}')); + assert(body.includes('"username":"foo"')); }); - it('api should redirect to /user/[userslug]/bookmarks', (done) => { - request(`${nconf.get('url')}/api/me/bookmarks`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(res.headers['x-redirect'], '/user/foo/bookmarks'); - assert.equal(body, '/user/foo/bookmarks'); - done(); - }); + + it('api should redirect to /user/[userslug]/bookmarks', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/me/bookmarks`, { jar }); + assert.equal(response.statusCode, 200); + assert.equal(response.headers['x-redirect'], '/user/foo/bookmarks'); + assert.equal(body, '/user/foo/bookmarks'); }); - it('api should redirect to /user/[userslug]/edit/username', (done) => { - request(`${nconf.get('url')}/api/me/edit/username`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(res.headers['x-redirect'], '/user/foo/edit/username'); - assert.equal(body, '/user/foo/edit/username'); - done(); - }); + + it('api should redirect to /user/[userslug]/edit/username', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/me/edit/username`, { jar }); + assert.equal(response.statusCode, 200); + assert.equal(response.headers['x-redirect'], '/user/foo/edit/username'); + assert.equal(body, '/user/foo/edit/username'); }); - it('should redirect to login if user is not logged in', (done) => { - request(`${nconf.get('url')}/me/bookmarks`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body.includes('Login to your account'), body.slice(0, 500)); - done(); - }); + + it('should redirect to login if user is not logged in', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/me/bookmarks`); + assert.equal(response.statusCode, 200); + assert(body.includes('Login to your account'), body.slice(0, 500)); }); }); - it('should 401 if user is not logged in', (done) => { - request(`${nconf.get('url')}/api/admin`, { json: true }, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 401); - done(); - }); + it('should 401 if user is not logged in', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/admin`); + assert.equal(response.statusCode, 401); }); - it('should 403 if user is not admin', (done) => { - request(`${nconf.get('url')}/api/admin`, { jar: jar, json: true }, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 403); - done(); - }); + it('should 403 if user is not admin', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/admin`, { jar }); + assert.equal(response.statusCode, 403); }); - it('should load /user/foo/posts', (done) => { - request(`${nconf.get('url')}/api/user/foo/posts`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /user/foo/posts', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/posts`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should 401 if not logged in', (done) => { - request(`${nconf.get('url')}/api/user/foo/bookmarks`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 401); - assert(body); - done(); - }); + it('should 401 if not logged in', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/admin`); + assert.equal(response.statusCode, 401); + assert(body); }); - it('should load /user/foo/bookmarks', (done) => { - request(`${nconf.get('url')}/api/user/foo/bookmarks`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /user/foo/bookmarks', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/bookmarks`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /user/foo/upvoted', (done) => { - request(`${nconf.get('url')}/api/user/foo/upvoted`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /user/foo/upvoted', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/upvoted`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /user/foo/downvoted', (done) => { - request(`${nconf.get('url')}/api/user/foo/downvoted`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /user/foo/downvoted', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/downvoted`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /user/foo/best', (done) => { - request(`${nconf.get('url')}/api/user/foo/best`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /user/foo/best', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/best`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /user/foo/controversial', (done) => { - request(`${nconf.get('url')}/api/user/foo/controversial`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /user/foo/controversial', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/controversial`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /user/foo/watched', (done) => { - request(`${nconf.get('url')}/api/user/foo/watched`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /user/foo/watched', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/watched`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /user/foo/ignored', (done) => { - request(`${nconf.get('url')}/api/user/foo/ignored`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /user/foo/ignored', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/ignored`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /user/foo/topics', (done) => { - request(`${nconf.get('url')}/api/user/foo/topics`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /user/foo/topics', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/topics`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /user/foo/blocks', (done) => { - request(`${nconf.get('url')}/api/user/foo/blocks`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /user/foo/blocks', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/blocks`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /user/foo/consent', (done) => { - request(`${nconf.get('url')}/api/user/foo/consent`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /user/foo/consent', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/consent`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /user/foo/sessions', (done) => { - request(`${nconf.get('url')}/api/user/foo/sessions`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /user/foo/sessions', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/sessions`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /user/foo/categories', (done) => { - request(`${nconf.get('url')}/api/user/foo/categories`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /user/foo/categories', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/categories`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load /user/foo/uploads', (done) => { - request(`${nconf.get('url')}/api/user/foo/uploads`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load /user/foo/tags', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/tags`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); + }); + + it('should load /user/foo/uploads', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/uploads`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); describe('user data export routes', () => { @@ -1685,35 +1115,26 @@ describe('Controllers', () => { await sleep(10000); }); - it('should export users posts', (done) => { - request(`${nconf.get('url')}/api/v3/users/${fooUid}/exports/posts`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should export users posts', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/v3/users/${fooUid}/exports/posts`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should export users uploads', (done) => { - request(`${nconf.get('url')}/api/v3/users/${fooUid}/exports/uploads`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should export users uploads', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/v3/users/${fooUid}/exports/uploads`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should export users profile', (done) => { - request(`${nconf.get('url')}/api/v3/users/${fooUid}/exports/profile`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should export users profile', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/v3/users/${fooUid}/exports/profile`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); }); }); - it('should load notifications page', (done) => { + it('should load notifications page', async () => { const notifications = require('../src/notifications'); const notifData = { bodyShort: '[[notifications:user-posted-to, test1, test2]]', @@ -1726,294 +1147,192 @@ describe('Controllers', () => { mergeId: `notifications:user-posted-to|${1}`, topicTitle: 'topic title', }; - async.waterfall([ - function (next) { - notifications.create(notifData, next); - }, - function (notification, next) { - notifications.push(notification, fooUid, next); - }, - function (next) { - setTimeout(next, 2500); - }, - function (next) { - request(`${nconf.get('url')}/api/notifications`, { jar: jar, json: true }, next); - }, - function (res, body, next) { - assert.equal(res.statusCode, 200); - assert(body); - const notif = body.notifications[0]; - assert.equal(notif.bodyShort, 'test1 has posted a reply to: test2'); - assert.equal(notif.bodyLong, notifData.bodyLong); - assert.equal(notif.pid, notifData.pid); - assert.equal(notif.path, nconf.get('relative_path') + notifData.path); - assert.equal(notif.nid, notifData.nid); - next(); - }, - ], done); + const notification = await notifications.create(notifData); + await notifications.push(notification, fooUid); + await sleep(2500); + const { response, body } = await request.get(`${nconf.get('url')}/api/notifications`, { + jar, + }); + assert.equal(response.statusCode, 200); + assert(body); + const notif = body.notifications[0]; + assert.equal(notif.bodyShort, 'test1 has posted a reply to: test2'); + assert.equal(notif.bodyLong, notifData.bodyLong); + assert.equal(notif.pid, notifData.pid); + assert.equal(notif.path, nconf.get('relative_path') + notifData.path); + assert.equal(notif.nid, notifData.nid); }); - it('should 404 if user does not exist', (done) => { - request(`${nconf.get('url')}/api/user/email/doesnotexist`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - assert(body); - done(); - }); + it('should 404 if user does not exist', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/email/doesnotexist`); + assert.equal(response.statusCode, 404); + assert(body); }); - it('should load user by uid', (done) => { - request(`${nconf.get('url')}/api/user/uid/${fooUid}`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load user by uid', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/uid/${fooUid}`); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load user by username', (done) => { - request(`${nconf.get('url')}/api/user/username/foo`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load user by username', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/username/foo`); + assert.equal(response.statusCode, 200); + assert(body); }); it('should NOT load user by email (by default)', async () => { - const res = await requestAsync(`${nconf.get('url')}/api/user/email/foo@test.com`, { - resolveWithFullResponse: true, - simple: false, - }); + const { response } = await request.get(`${nconf.get('url')}/api/user/email/foo@test.com`); - assert.strictEqual(res.statusCode, 404); + assert.strictEqual(response.statusCode, 404); }); it('should load user by email if user has elected to show their email', async () => { await user.setSetting(fooUid, 'showemail', 1); - const res = await requestAsync(`${nconf.get('url')}/api/user/email/foo@test.com`, { - resolveWithFullResponse: true, - }); - assert.strictEqual(res.statusCode, 200); - assert(res.body); + const { response, body } = await request.get(`${nconf.get('url')}/api/user/email/foo@test.com`); + assert.strictEqual(response.statusCode, 200); + assert(body); await user.setSetting(fooUid, 'showemail', 0); }); - it('should return 401 if user does not have view:users privilege', (done) => { - privileges.global.rescind(['groups:view:users'], 'guests', (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/api/user/foo`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 401); - assert.deepEqual(body, { - response: {}, - status: { - code: 'not-authorised', - message: 'A valid login session was not found. Please log in and try again.', - }, - }); - privileges.global.give(['groups:view:users'], 'guests', done); - }); + it('should return 401 if user does not have view:users privilege', async () => { + await privileges.global.rescind(['groups:view:users'], 'guests'); + + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo`); + assert.equal(response.statusCode, 401); + assert.deepEqual(body, { + response: {}, + status: { + code: 'not-authorised', + message: 'A valid login session was not found. Please log in and try again.', + }, }); + await privileges.global.give(['groups:view:users'], 'guests'); }); it('should return false if user can not edit user', async () => { await user.create({ username: 'regularJoe', password: 'barbar' }); const { jar } = await helpers.loginUser('regularJoe', 'barbar'); - let { statusCode } = await requestAsync(`${nconf.get('url')}/api/user/foo/info`, { jar: jar, json: true, simple: false, resolveWithFullResponse: true }); - assert.equal(statusCode, 403); - ({ statusCode } = await requestAsync(`${nconf.get('url')}/api/user/foo/edit`, { jar: jar, json: true, simple: false, resolveWithFullResponse: true })); - assert.equal(statusCode, 403); + let { response } = await request.get(`${nconf.get('url')}/api/user/foo/info`, { jar }); + assert.equal(response.statusCode, 403); + ({ response } = await request.get(`${nconf.get('url')}/api/user/foo/edit`, { jar })); + assert.equal(response.statusCode, 403); }); - it('should load correct user', (done) => { - request(`${nconf.get('url')}/api/user/FOO`, { jar: jar, json: true }, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - done(); - }); + it('should load correct user', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/user/FOO`, { jar: jar }); + assert.equal(response.statusCode, 200); }); - it('should redirect', (done) => { - request(`${nconf.get('url')}/user/FOO`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should redirect', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/user/FOO`, { jar: jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should 404 if user does not exist', (done) => { - request(`${nconf.get('url')}/api/user/doesnotexist`, { jar: jar }, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - done(); - }); + it('should 404 if user does not exist', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/doesnotexist`, { jar }); + assert.equal(response.statusCode, 404); }); - it('should not increase profile view if you visit your own profile', (done) => { - request(`${nconf.get('url')}/api/user/foo`, { jar: jar }, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - setTimeout(() => { - user.getUserField(fooUid, 'profileviews', (err, viewcount) => { - assert.ifError(err); - assert(viewcount === 0); - done(); - }); - }, 500); - }); + it('should not increase profile view if you visit your own profile', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/user/foo`, { jar }); + assert.equal(response.statusCode, 200); + await sleep(500); + const viewcount = await user.getUserField(fooUid, 'profileviews'); + assert(viewcount === 0); }); - it('should not increase profile view if a guest visits a profile', (done) => { - request(`${nconf.get('url')}/api/user/foo`, {}, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - setTimeout(() => { - user.getUserField(fooUid, 'profileviews', (err, viewcount) => { - assert.ifError(err); - assert(viewcount === 0); - done(); - }); - }, 500); - }); + it('should not increase profile view if a guest visits a profile', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/user/foo`, {}); + assert.equal(response.statusCode, 200); + await sleep(500); + const viewcount = await user.getUserField(fooUid, 'profileviews'); + assert(viewcount === 0); }); it('should increase profile view', async () => { const { jar } = await helpers.loginUser('regularJoe', 'barbar'); - const { statusCode } = await requestAsync(`${nconf.get('url')}/api/user/foo`, { - jar: jar, - simple: false, - resolveWithFullResponse: true, + const { response } = await request.get(`${nconf.get('url')}/api/user/foo`, { + jar, }); - assert.equal(statusCode, 200); - + assert.equal(response.statusCode, 200); await sleep(500); const viewcount = await user.getUserField(fooUid, 'profileviews'); assert(viewcount > 0); }); - it('should parse about me', (done) => { - user.setUserFields(fooUid, { picture: '/path/to/picture', aboutme: 'hi i am a bot' }, (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/api/user/foo`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(body.aboutme, 'hi i am a bot'); - assert.equal(body.picture, '/path/to/picture'); - done(); - }); - }); + it('should parse about me', async () => { + await user.setUserFields(fooUid, { picture: '/path/to/picture', aboutme: 'hi i am a bot' }); + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo`); + assert.equal(response.statusCode, 200); + assert.equal(body.aboutme, 'hi i am a bot'); + assert.equal(body.picture, '/path/to/picture'); }); - it('should not return reputation if reputation is disabled', (done) => { + it('should not return reputation if reputation is disabled', async () => { meta.config['reputation:disabled'] = 1; - request(`${nconf.get('url')}/api/user/foo`, { json: true }, (err, res, body) => { - meta.config['reputation:disabled'] = 0; - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(!body.hasOwnProperty('reputation')); - done(); - }); + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo`); + meta.config['reputation:disabled'] = 0; + assert.equal(response.statusCode, 200); + assert(!body.hasOwnProperty('reputation')); }); - it('should only return posts that are not deleted', (done) => { - let topicData; - let pidToDelete; - async.waterfall([ - function (next) { - topics.post({ uid: fooUid, title: 'visible', content: 'some content', cid: cid }, next); - }, - function (data, next) { - topicData = data.topicData; - topics.reply({ uid: fooUid, content: '1st reply', tid: topicData.tid }, next); - }, - function (postData, next) { - pidToDelete = postData.pid; - topics.reply({ uid: fooUid, content: '2nd reply', tid: topicData.tid }, next); - }, - function (postData, next) { - posts.delete(pidToDelete, fooUid, next); - }, - function (next) { - request(`${nconf.get('url')}/api/user/foo`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - const contents = body.posts.map(p => p.content); - assert(!contents.includes('1st reply')); - done(); - }); - }, - ], done); + it('should only return posts that are not deleted', async () => { + const { topicData } = await topics.post({ uid: fooUid, title: 'visible', content: 'some content', cid: cid }); + const { pid: pidToDelete } = await topics.reply({ uid: fooUid, content: '1st reply', tid: topicData.tid }); + await topics.reply({ uid: fooUid, content: '2nd reply', tid: topicData.tid }); + await posts.delete(pidToDelete, fooUid); + + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo`); + assert.equal(response.statusCode, 200); + const contents = body.posts.map(p => p.content); + assert(!contents.includes('1st reply')); }); - it('should return selected group title', (done) => { - groups.create({ + it('should return selected group title', async () => { + await groups.create({ name: 'selectedGroup', - }, (err) => { - assert.ifError(err); - user.create({ username: 'groupie' }, (err, uid) => { - assert.ifError(err); - groups.join('selectedGroup', uid, (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/api/user/groupie`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(Array.isArray(body.selectedGroup)); - assert.equal(body.selectedGroup[0].name, 'selectedGroup'); - done(); - }); - }); - }); }); + const uid = await user.create({ username: 'groupie' }); + await groups.join('selectedGroup', uid); + + const { response, body } = await request.get(`${nconf.get('url')}/api/user/groupie`); + assert.equal(response.statusCode, 200); + assert(Array.isArray(body.selectedGroup)); + assert.equal(body.selectedGroup[0].name, 'selectedGroup'); }); - it('should 404 if user does not exist', (done) => { - groups.join('administrators', fooUid, (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/api/user/doesnotexist/edit`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - groups.leave('administrators', fooUid, done); - }); - }); + it('should 404 if user does not exist', async () => { + await groups.join('administrators', fooUid); + + const { response } = await request.get(`${nconf.get('url')}/api/user/doesnotexist/edit`, { jar }); + assert.equal(response.statusCode, 404); + await groups.leave('administrators', fooUid); }); - it('should render edit/password', (done) => { - request(`${nconf.get('url')}/api/user/foo/edit/password`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - done(); - }); + it('should render edit/password', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/user/foo/edit/password`, { jar }); + assert.equal(response.statusCode, 200); }); it('should render edit/email', async () => { - const res = await requestAsync(`${nconf.get('url')}/api/user/foo/edit/email`, { - jar, - json: true, - resolveWithFullResponse: true, - }); + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/edit/email`, { jar }); - assert.strictEqual(res.statusCode, 200); - assert.strictEqual(res.body, '/register/complete'); + assert.strictEqual(response.statusCode, 200); + assert.strictEqual(body, '/register/complete'); - await requestAsync({ - uri: `${nconf.get('url')}/register/abort`, - method: 'post', + await request.post(`${nconf.get('url')}/register/abort`, { jar, - simple: false, headers: { 'x-csrf-token': csrf_token, }, }); }); - it('should render edit/username', (done) => { - request(`${nconf.get('url')}/api/user/foo/edit/username`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - done(); - }); + it('should render edit/username', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/user/foo/edit/username`, { jar }); + assert.equal(response.statusCode, 200); }); }); @@ -2028,28 +1347,22 @@ describe('Controllers', () => { assert(isFollowing); }); - it('should get followers page', (done) => { - request(`${nconf.get('url')}/api/user/foo/followers`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(body.users[0].username, 'follower'); - done(); - }); + it('should get followers page', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/followers`); + assert.equal(response.statusCode, 200); + assert.equal(body.users[0].username, 'follower'); }); - it('should get following page', (done) => { - request(`${nconf.get('url')}/api/user/follower/following`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(body.users[0].username, 'foo'); - done(); - }); + it('should get following page', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/follower/following`); + assert.equal(response.statusCode, 200); + assert.equal(body.users[0].username, 'foo'); }); it('should return empty after unfollow', async () => { await apiUser.unfollow({ uid: uid }, { uid: fooUid }); - const { res, body } = await helpers.request('get', `/api/user/foo/followers`, { json: true }); - assert.equal(res.statusCode, 200); + const { response, body } = await request.get(`${nconf.get('url')}/api/user/foo/followers`); + assert.equal(response.statusCode, 200); assert.equal(body.users.length, 0); }); }); @@ -2060,86 +1373,41 @@ describe('Controllers', () => { ({ jar } = await helpers.loginUser('foo', 'barbar')); }); - it('should 404 for invalid pid', (done) => { - request(`${nconf.get('url')}/api/post/fail`, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - done(); - }); + it('should 404 for invalid pid', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/post/fail`); + assert.equal(response.statusCode, 404); }); - it('should 403 if user does not have read privilege', (done) => { - privileges.categories.rescind(['groups:topics:read'], category.cid, 'registered-users', (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/api/post/${pid}`, { jar: jar }, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 403); - privileges.categories.give(['groups:topics:read'], category.cid, 'registered-users', done); - }); - }); + it('should 403 if user does not have read privilege', async () => { + await privileges.categories.rescind(['groups:topics:read'], category.cid, 'registered-users'); + const { response } = await request.get(`${nconf.get('url')}/api/post/${pid}`, { jar }); + assert.equal(response.statusCode, 403); + await privileges.categories.give(['groups:topics:read'], category.cid, 'registered-users'); }); - it('should return correct post path', (done) => { - request(`${nconf.get('url')}/api/post/${pid}`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(res.headers['x-redirect'], '/topic/1/test-topic-title'); - assert.equal(body, '/topic/1/test-topic-title'); - done(); - }); + it('should return correct post path', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/post/${pid}`); + assert.equal(response.statusCode, 200); + assert.equal(response.headers['x-redirect'], '/topic/1/test-topic-title'); + assert.equal(body, '/topic/1/test-topic-title'); }); }); describe('cookie consent', () => { - it('should return relevant data in configs API route', (done) => { - request(`${nconf.get('url')}/api/config`, (err, res, body) => { - let parsed; - assert.ifError(err); - assert.equal(res.statusCode, 200); - - try { - parsed = JSON.parse(body); - } catch (e) { - assert.ifError(e); - } - - assert.ok(parsed.cookies); - assert.equal(translator.escape('[[global:cookies.message]]'), parsed.cookies.message); - assert.equal(translator.escape('[[global:cookies.accept]]'), parsed.cookies.dismiss); - assert.equal(translator.escape('[[global:cookies.learn-more]]'), parsed.cookies.link); - - done(); - }); + it('should return relevant data in configs API route', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/config`); + assert.equal(response.statusCode, 200); + assert.ok(body.cookies); + assert.equal(translator.escape('[[global:cookies.message]]'), body.cookies.message); + assert.equal(translator.escape('[[global:cookies.accept]]'), body.cookies.dismiss); + assert.equal(translator.escape('[[global:cookies.learn-more]]'), body.cookies.link); }); - it('response should be parseable when entries have apostrophes', (done) => { - meta.configs.set('cookieConsentMessage', 'Julian\'s Message', (err) => { - assert.ifError(err); - - request(`${nconf.get('url')}/api/config`, (err, res, body) => { - let parsed; - assert.ifError(err); - assert.equal(res.statusCode, 200); - - try { - parsed = JSON.parse(body); - } catch (e) { - assert.ifError(e); - } - - assert.equal('Julian's Message', parsed.cookies.message); - done(); - }); - }); - }); - }); - - it('should return osd data', (done) => { - request(`${nconf.get('url')}/osd.xml`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); + it('response should be parseable when entries have apostrophes', async () => { + await meta.configs.set('cookieConsentMessage', 'Julian\'s Message'); + const { response, body } = await request.get(`${nconf.get('url')}/api/config`); + assert.equal(response.statusCode, 200); + assert.equal('Julian's Message', body.cookies.message); }); }); @@ -2150,43 +1418,31 @@ describe('Controllers', () => { done(); }); - it('should handle topic malformed uri', (done) => { - request(`${nconf.get('url')}/topic/1/a%AFc`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should handle topic malformed uri', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/topic/1/a%AFc`); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should handle category malformed uri', (done) => { - request(`${nconf.get('url')}/category/1/a%AFc`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should handle category malformed uri', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/category/1/a%AFc`); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should handle malformed uri ', (done) => { - request(`${nconf.get('url')}/user/a%AFc`, (err, res, body) => { - assert.ifError(err); - assert(body); - assert.equal(res.statusCode, 400); - done(); - }); + it('should handle malformed uri ', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/user/a%AFc`); + assert(body); + assert.equal(response.statusCode, 400); }); - it('should handle malformed uri in api', (done) => { - request(`${nconf.get('url')}/api/user/a%AFc`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 400); - assert.equal(body.error, '[[global:400.title]]'); - done(); - }); + it('should handle malformed uri in api', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/a%AFc`); + assert.equal(response.statusCode, 400); + assert.equal(body.error, '[[global:400.title]]'); }); - it('should handle CSRF error', (done) => { + it('should handle CSRF error', async () => { plugins.loadedHooks['filter:router.page'] = plugins.loadedHooks['filter:router.page'] || []; plugins.loadedHooks['filter:router.page'].push({ method: function (req, res, next) { @@ -2196,15 +1452,12 @@ describe('Controllers', () => { }, }); - request(`${nconf.get('url')}/users`, {}, (err, res) => { - plugins.loadedHooks['filter:router.page'] = []; - assert.ifError(err); - assert.equal(res.statusCode, 403); - done(); - }); + const { response } = await request.get(`${nconf.get('url')}/users`); + plugins.loadedHooks['filter:router.page'] = []; + assert.equal(response.statusCode, 403); }); - it('should handle black-list error', (done) => { + it('should handle black-list error', async () => { plugins.loadedHooks['filter:router.page'] = plugins.loadedHooks['filter:router.page'] || []; plugins.loadedHooks['filter:router.page'].push({ method: function (req, res, next) { @@ -2213,17 +1466,13 @@ describe('Controllers', () => { next(err); }, }); - - request(`${nconf.get('url')}/users`, {}, (err, res, body) => { - plugins.loadedHooks['filter:router.page'] = []; - assert.ifError(err); - assert.equal(res.statusCode, 403); - assert.equal(body, 'blacklist error message'); - done(); - }); + const { response, body } = await request.get(`${nconf.get('url')}/users`); + plugins.loadedHooks['filter:router.page'] = []; + assert.equal(response.statusCode, 403); + assert.equal(body, 'blacklist error message'); }); - it('should handle page redirect through error', (done) => { + it('should handle page redirect through error', async () => { plugins.loadedHooks['filter:router.page'] = plugins.loadedHooks['filter:router.page'] || []; plugins.loadedHooks['filter:router.page'].push({ method: function (req, res, next) { @@ -2234,16 +1483,12 @@ describe('Controllers', () => { next(err); }, }); - - request(`${nconf.get('url')}/users`, {}, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + const { response, body } = await request.get(`${nconf.get('url')}/users`); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should handle api page redirect through error', (done) => { + it('should handle api page redirect through error', async () => { plugins.loadedHooks['filter:router.page'] = plugins.loadedHooks['filter:router.page'] || []; plugins.loadedHooks['filter:router.page'].push({ method: function (req, res, next) { @@ -2254,17 +1499,13 @@ describe('Controllers', () => { next(err); }, }); - - request(`${nconf.get('url')}/api/users`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(res.headers['x-redirect'], '/api/popular'); - assert(body, '/api/popular'); - done(); - }); + const { response, body } = await request.get(`${nconf.get('url')}/api/users`); + assert.equal(response.statusCode, 200); + assert.equal(response.headers['x-redirect'], '/api/popular'); + assert(body, '/api/popular'); }); - it('should handle error page', (done) => { + it('should handle error page', async () => { plugins.loadedHooks['filter:router.page'] = plugins.loadedHooks['filter:router.page'] || []; plugins.loadedHooks['filter:router.page'].push({ method: function (req, res, next) { @@ -2272,14 +1513,10 @@ describe('Controllers', () => { next(err); }, }); - - request(`${nconf.get('url')}/users`, (err, res, body) => { - plugins.loadedHooks['filter:router.page'] = []; - assert.ifError(err); - assert.equal(res.statusCode, 500); - assert(body); - done(); - }); + const { response, body } = await request.get(`${nconf.get('url')}/users`); + plugins.loadedHooks['filter:router.page'] = []; + assert.equal(response.statusCode, 500); + assert(body); }); }); @@ -2289,265 +1526,115 @@ describe('Controllers', () => { ({ jar } = await helpers.loginUser('foo', 'barbar')); }); - it('should return 404 if cid is not a number', (done) => { - request(`${nconf.get('url')}/api/category/fail`, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - done(); - }); + it('should return 404 if cid is not a number', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/category/fail`); + assert.equal(response.statusCode, 404); }); - it('should return 404 if topic index is not a number', (done) => { - request(`${nconf.get('url')}/api/category/${category.slug}/invalidtopicindex`, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - done(); - }); + it('should return 404 if topic index is not a number', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/category/${category.slug}/invalidtopicindex`); + assert.equal(response.statusCode, 404); }); - it('should 404 if category does not exist', (done) => { - request(`${nconf.get('url')}/api/category/123123`, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - done(); - }); + it('should 404 if category does not exist', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/category/123123`); + assert.equal(response.statusCode, 404); }); - it('should 404 if category is disabled', (done) => { - categories.create({ name: 'disabled' }, (err, category) => { - assert.ifError(err); - categories.setCategoryField(category.cid, 'disabled', 1, (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/api/category/${category.slug}`, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - done(); - }); - }); - }); + it('should 404 if category is disabled', async () => { + const category = await categories.create({ name: 'disabled' }); + await categories.setCategoryField(category.cid, 'disabled', 1); + const { response } = await request.get(`${nconf.get('url')}/api/category/${category.slug}`); + assert.equal(response.statusCode, 404); }); - it('should return 401 if not allowed to read', (done) => { - categories.create({ name: 'hidden' }, (err, category) => { - assert.ifError(err); - privileges.categories.rescind(['groups:read'], category.cid, 'guests', (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/api/category/${category.slug}`, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 401); - done(); - }); - }); - }); + it('should return 401 if not allowed to read', async () => { + const category = await categories.create({ name: 'hidden' }); + await privileges.categories.rescind(['groups:read'], category.cid, 'guests'); + const { response } = await request.get(`${nconf.get('url')}/api/category/${category.slug}`); + assert.equal(response.statusCode, 401); }); - it('should redirect if topic index is negative', (done) => { - request(`${nconf.get('url')}/api/category/${category.slug}/-10`, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.ok(res.headers['x-redirect']); - done(); - }); + it('should redirect if topic index is negative', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/category/${category.slug}/-10`); + assert.equal(response.statusCode, 200); + assert.ok(response.headers['x-redirect']); }); - it('should 404 if page is not found', (done) => { - user.setSetting(fooUid, 'usePagination', 1, (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/api/category/${category.slug}?page=100`, { jar: jar, json: true }, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - done(); - }); - }); + it('should 404 if page is not found', async () => { + await user.setSetting(fooUid, 'usePagination', 1); + const { response } = await request.get(`${nconf.get('url')}/api/category/${category.slug}?page=100`, { jar }); + assert.equal(response.statusCode, 404); }); - it('should load page 1 if req.query.page is not sent', (done) => { - request(`${nconf.get('url')}/api/category/${category.slug}`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(body.pagination.currentPage, 1); - done(); - }); + it('should load page 1 if req.query.page is not sent', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/category/${category.slug}`, { jar }); + assert.equal(response.statusCode, 200); + assert.equal(body.pagination.currentPage, 1); }); - it('should sort topics by most posts', (done) => { - async.waterfall([ - function (next) { - categories.create({ name: 'most-posts-category' }, next); - }, - function (category, next) { - async.waterfall([ - function (next) { - topics.post({ uid: fooUid, cid: category.cid, title: 'topic 1', content: 'topic 1 OP' }, next); - }, - function (data, next) { - topics.post({ uid: fooUid, cid: category.cid, title: 'topic 2', content: 'topic 2 OP' }, next); - }, - function (data, next) { - topics.reply({ uid: fooUid, content: 'topic 2 reply', tid: data.topicData.tid }, next); - }, - function (postData, next) { - request(`${nconf.get('url')}/api/category/${category.slug}?sort=most_posts`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(body.topics[0].title, 'topic 2'); - assert.equal(body.topics[0].postcount, 2); - assert.equal(body.topics[1].postcount, 1); - next(); - }); - }, - ], (err) => { - next(err); - }); - }, - ], done); + it('should sort topics by most posts', async () => { + const category = await categories.create({ name: 'most-posts-category' }); + await topics.post({ uid: fooUid, cid: category.cid, title: 'topic 1', content: 'topic 1 OP' }); + const t2 = await topics.post({ uid: fooUid, cid: category.cid, title: 'topic 2', content: 'topic 2 OP' }); + await topics.reply({ uid: fooUid, content: 'topic 2 reply', tid: t2.topicData.tid }); + + const { response, body } = await request.get(`${nconf.get('url')}/api/category/${category.slug}?sort=most_posts`, { jar }); + assert.equal(response.statusCode, 200); + assert.equal(body.topics[0].title, 'topic 2'); + assert.equal(body.topics[0].postcount, 2); + assert.equal(body.topics[1].postcount, 1); }); - it('should load a specific users topics from a category with tags', (done) => { - async.waterfall([ - function (next) { - categories.create({ name: 'filtered-category' }, next); - }, - function (category, next) { - async.waterfall([ - function (next) { - topics.post({ uid: fooUid, cid: category.cid, title: 'topic 1', content: 'topic 1 OP', tags: ['java', 'cpp'] }, next); - }, - function (data, next) { - topics.post({ uid: fooUid, cid: category.cid, title: 'topic 2', content: 'topic 2 OP', tags: ['node', 'javascript'] }, next); - }, - function (data, next) { - topics.post({ uid: fooUid, cid: category.cid, title: 'topic 3', content: 'topic 3 OP', tags: ['java', 'cpp', 'best'] }, next); - }, - function (data, next) { - request(`${nconf.get('url')}/api/category/${category.slug}?tag=node&author=foo`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(body.topics[0].title, 'topic 2'); - next(); - }); - }, - function (next) { - request(`${nconf.get('url')}/api/category/${category.slug}?tag[]=java&tag[]=cpp`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(body.topics[0].title, 'topic 3'); - assert.equal(body.topics[1].title, 'topic 1'); - next(); - }); - }, - ], (err) => { - next(err); - }); - }, - ], done); + it('should load a specific users topics from a category with tags', async () => { + const category = await categories.create({ name: 'filtered-category' }); + await topics.post({ uid: fooUid, cid: category.cid, title: 'topic 1', content: 'topic 1 OP', tags: ['java', 'cpp'] }); + await topics.post({ uid: fooUid, cid: category.cid, title: 'topic 2', content: 'topic 2 OP', tags: ['node', 'javascript'] }); + await topics.post({ uid: fooUid, cid: category.cid, title: 'topic 3', content: 'topic 3 OP', tags: ['java', 'cpp', 'best'] }); + + let { body } = await request.get(`${nconf.get('url')}/api/category/${category.slug}?tag=node&author=foo`, { jar }); + assert.equal(body.topics[0].title, 'topic 2'); + + ({ body } = await request.get(`${nconf.get('url')}/api/category/${category.slug}?tag[]=java&tag[]=cpp`, { jar })); + assert.equal(body.topics[0].title, 'topic 3'); + assert.equal(body.topics[1].title, 'topic 1'); }); - it('should redirect if category is a link', (done) => { - let cid; - let category; - async.waterfall([ - function (next) { - categories.create({ name: 'redirect', link: 'https://nodebb.org' }, next); - }, - function (_category, next) { - category = _category; - cid = category.cid; - request(`${nconf.get('url')}/api/category/${category.slug}`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(res.headers['x-redirect'], 'https://nodebb.org'); - assert.equal(body, 'https://nodebb.org'); - next(); - }); - }, - function (next) { - categories.setCategoryField(cid, 'link', '/recent', next); - }, - function (next) { - request(`${nconf.get('url')}/api/category/${category.slug}`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(res.headers['x-redirect'], '/recent'); - assert.equal(body, '/recent'); - next(); - }); - }, - ], done); + it('should redirect if category is a link', async () => { + const category = await categories.create({ name: 'redirect', link: 'https://nodebb.org' }); + const { cid } = category; + + let result = await request.get(`${nconf.get('url')}/api/category/${category.slug}`, { jar }); + assert.equal(result.response.headers['x-redirect'], 'https://nodebb.org'); + assert.equal(result.body, 'https://nodebb.org'); + await categories.setCategoryField(cid, 'link', '/recent'); + + result = await request.get(`${nconf.get('url')}/api/category/${category.slug}`, { jar }); + assert.equal(result.response.headers['x-redirect'], '/recent'); + assert.equal(result.body, '/recent'); }); - it('should get recent topic replies from children categories', (done) => { - let parentCategory; - let childCategory1; - let childCategory2; + it('should get recent topic replies from children categories', async () => { + const parentCategory = await categories.create({ name: 'parent category', backgroundImage: 'path/to/some/image' }); + const childCategory1 = await categories.create({ name: 'child category 1', parentCid: category.cid }); + const childCategory2 = await categories.create({ name: 'child category 2', parentCid: parentCategory.cid }); + await topics.post({ uid: fooUid, cid: childCategory2.cid, title: 'topic 1', content: 'topic 1 OP' }); - async.waterfall([ - function (next) { - categories.create({ name: 'parent category', backgroundImage: 'path/to/some/image' }, next); - }, - function (category, next) { - parentCategory = category; - async.waterfall([ - function (next) { - categories.create({ name: 'child category 1', parentCid: category.cid }, next); - }, - function (category, next) { - childCategory1 = category; - categories.create({ name: 'child category 2', parentCid: parentCategory.cid }, next); - }, - function (category, next) { - childCategory2 = category; - topics.post({ uid: fooUid, cid: childCategory2.cid, title: 'topic 1', content: 'topic 1 OP' }, next); - }, - function (data, next) { - request(`${nconf.get('url')}/api/category/${parentCategory.slug}`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(body.children[0].posts[0].content, 'topic 1 OP'); - next(); - }); - }, - ], (err) => { - next(err); - }); - }, - ], done); + const { body } = await request.get(`${nconf.get('url')}/api/category/${parentCategory.slug}`, { jar }); + assert.equal(body.children[0].posts[0].content, 'topic 1 OP'); }); - it('should create 2 pages of topics', (done) => { - async.waterfall([ - function (next) { - categories.create({ name: 'category with 2 pages' }, next); - }, - function (category, next) { - const titles = []; - for (let i = 0; i < 30; i++) { - titles.push(`topic title ${i}`); - } + it('should create 2 pages of topics', async () => { + const category = await categories.create({ name: 'category with 2 pages' }); + for (let i = 0; i < 30; i++) { + // eslint-disable-next-line no-await-in-loop + await topics.post({ uid: fooUid, cid: category.cid, title: `topic title ${i}`, content: 'does not really matter' }); + } + const userSettings = await user.getSettings(fooUid); - async.waterfall([ - function (next) { - async.eachSeries(titles, (title, next) => { - topics.post({ uid: fooUid, cid: category.cid, title: title, content: 'does not really matter' }, next); - }, next); - }, - function (next) { - user.getSettings(fooUid, next); - }, - function (settings, next) { - request(`${nconf.get('url')}/api/category/${category.slug}`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(body.topics.length, settings.topicsPerPage); - assert.equal(body.pagination.pageCount, 2); - next(); - }); - }, - ], (err) => { - next(err); - }); - }, - ], done); + const { body } = await request.get(`${nconf.get('url')}/api/category/${category.slug}`, { jar }); + assert.equal(body.topics.length, userSettings.topicsPerPage); + assert.equal(body.pagination.pageCount, 2); }); it('should load categories', async () => { @@ -2580,58 +1667,40 @@ describe('Controllers', () => { ({ jar } = await helpers.loginUser('foo', 'barbar')); }); - it('should load unread page', (done) => { - request(`${nconf.get('url')}/api/unread`, { jar: jar }, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - done(); - }); + it('should load unread page', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/unread`, { jar }); + assert.equal(response.statusCode, 200); }); - it('should 404 if filter is invalid', (done) => { - request(`${nconf.get('url')}/api/unread/doesnotexist`, { jar: jar }, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - done(); - }); + it('should 404 if filter is invalid', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/unread/doesnotexist`, { jar }); + assert.equal(response.statusCode, 404); }); - it('should return total unread count', (done) => { - request(`${nconf.get('url')}/api/unread/total?filter=new`, { jar: jar }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(body, 0); - done(); - }); + it('should return total unread count', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/unread/total?filter=new`, { jar }); + assert.equal(response.statusCode, 200); + assert.equal(body, 0); }); - it('should redirect if page is out of bounds', (done) => { - request(`${nconf.get('url')}/api/unread?page=-1`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(res.headers['x-redirect'], '/unread?page=1'); - assert.equal(body, '/unread?page=1'); - done(); - }); + it('should redirect if page is out of bounds', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/unread?page=-1`, { jar }); + assert.equal(response.statusCode, 200); + assert.equal(response.headers['x-redirect'], '/unread?page=1'); + assert.equal(body, '/unread?page=1'); }); }); describe('admin middlewares', () => { - it('should redirect to login', (done) => { - request(`${nconf.get('url')}//api/admin/advanced/database`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 401); - done(); - }); + it('should redirect to login', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/admin/advanced/database`); + assert.equal(response.statusCode, 401); }); - it('should redirect to login', (done) => { - request(`${nconf.get('url')}//admin/advanced/database`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body.includes('Login to your account')); - done(); - }); + it('should redirect to login', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/admin/advanced/database`); + assert.equal(response.statusCode, 200); + assert(body.includes('Login to your account')); }); }); @@ -2645,18 +1714,15 @@ describe('Controllers', () => { csrf_token = login.csrf_token; }); - it('should load the composer route', (done) => { - request(`${nconf.get('url')}/api/compose?cid=1`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body.title); - assert(body.template); - assert.equal(body.url, `${nconf.get('relative_path')}/compose`); - done(); - }); + it('should load the composer route', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/compose?cid=1`); + assert.equal(response.statusCode, 200); + assert(body.title); + assert(body.template); + assert.equal(body.url, `${nconf.get('relative_path')}/compose`); }); - it('should load the composer route if disabled by plugin', (done) => { + it('should load the composer route if disabled by plugin', async () => { function hookMethod(hookData, callback) { hookData.templateData.disabled = true; callback(null, hookData); @@ -2667,151 +1733,134 @@ describe('Controllers', () => { method: hookMethod, }); - request(`${nconf.get('url')}/api/compose?cid=1`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body.title); - assert.strictEqual(body.template.name, ''); - assert.strictEqual(body.url, `${nconf.get('relative_path')}/compose`); + const { response, body } = await request.get(`${nconf.get('url')}/api/compose?cid=1`); + assert.equal(response.statusCode, 200); + assert(body.title); + assert.strictEqual(body.template.name, ''); + assert.strictEqual(body.url, `${nconf.get('relative_path')}/compose`); - plugins.hooks.unregister('myTestPlugin', 'filter:composer.build', hookMethod); - done(); - }); + plugins.hooks.unregister('myTestPlugin', 'filter:composer.build', hookMethod); }); - it('should error with invalid data', (done) => { - request.post(`${nconf.get('url')}/compose`, { - form: { + it('should error with invalid data', async () => { + let result = await request.post(`${nconf.get('url')}/compose`, { + data: { content: 'a new reply', }, jar: jar, headers: { 'x-csrf-token': csrf_token, }, - }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 400); - request.post(`${nconf.get('url')}/compose`, { - form: { - tid: tid, - }, - jar: jar, - headers: { - 'x-csrf-token': csrf_token, - }, - }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 400); - done(); - }); }); - }); - it('should create a new topic and reply by composer route', (done) => { - const data = { - cid: cid, - title: 'no js is good', - content: 'a topic with noscript', - }; - request.post(`${nconf.get('url')}/compose`, { - form: data, + assert.equal(result.response.statusCode, 400); + result = await request.post(`${nconf.get('url')}/compose`, { + body: { + tid: tid, + }, jar: jar, headers: { 'x-csrf-token': csrf_token, }, - }, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 302); - request.post(`${nconf.get('url')}/compose`, { - form: { - tid: tid, - content: 'a new reply', - }, - jar: jar, - headers: { - 'x-csrf-token': csrf_token, - }, - }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 302); - done(); - }); }); + assert.equal(result.response.statusCode, 400); + }); + + it('should create a new topic and reply by composer route', async () => { + let result = await request.post(`${nconf.get('url')}/compose`, { + body: { + cid: cid, + title: 'no js is good', + content: 'a topic with noscript', + }, + jar: jar, + maxRedirect: 0, + redirect: 'manual', + headers: { + 'x-csrf-token': csrf_token, + }, + }); + + assert.equal(result.response.statusCode, 302); + result = await request.post(`${nconf.get('url')}/compose`, { + body: { + tid: tid, + content: 'a new reply', + }, + jar: jar, + maxRedirect: 0, + redirect: 'manual', + headers: { + 'x-csrf-token': csrf_token, + }, + }); + assert.equal(result.response.statusCode, 302); }); it('should create a new topic and reply by composer route as a guest', async () => { const jar = request.jar(); const csrf_token = await helpers.getCsrfToken(jar); - const data = { - cid: cid, - title: 'no js is good', - content: 'a topic with noscript', - handle: 'guest1', - }; await privileges.categories.give(['groups:topics:create', 'groups:topics:reply'], cid, 'guests'); const result = await helpers.request('post', `/compose`, { - form: data, + body: { + cid: cid, + title: 'no js is good', + content: 'a topic with noscript', + handle: 'guest1', + }, jar, + maxRedirect: 0, + redirect: 'manual', headers: { 'x-csrf-token': csrf_token, }, }); - assert.strictEqual(result.res.statusCode, 302); + assert.strictEqual(result.response.statusCode, 302); const replyResult = await helpers.request('post', `/compose`, { - form: { + body: { tid: tid, content: 'a new reply', handle: 'guest2', }, jar, + maxRedirect: 0, + redirect: 'manual', headers: { 'x-csrf-token': csrf_token, }, }); - assert.equal(replyResult.res.statusCode, 302); + assert.equal(replyResult.response.statusCode, 302); await privileges.categories.rescind(['groups:topics:post', 'groups:topics:reply'], cid, 'guests'); }); }); describe('test routes', () => { if (process.env.NODE_ENV === 'development') { - it('should load debug route', (done) => { - request(`${nconf.get('url')}/debug/test`, {}, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - assert(body); - done(); - }); + it('should load debug route', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/debug/test`); + assert.equal(response.statusCode, 404); + assert(body); }); - it('should load redoc read route', (done) => { - request(`${nconf.get('url')}/debug/spec/read`, {}, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load redoc read route', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/debug/spec/read`); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load redoc write route', (done) => { - request(`${nconf.get('url')}/debug/spec/write`, {}, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load redoc write route', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/debug/spec/write`); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load 404 for invalid type', (done) => { - request(`${nconf.get('url')}/debug/spec/doesnotexist`, {}, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - assert(body); - done(); - }); + it('should load 404 for invalid type', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/debug/spec/doesnotexist`); + assert.equal(response.statusCode, 404); + assert(body); }); } }); diff --git a/test/feeds.js b/test/feeds.js index 6f1c9d0b08..a54b485981 100644 --- a/test/feeds.js +++ b/test/feeds.js @@ -1,14 +1,12 @@ 'use strict'; const assert = require('assert'); -const async = require('async'); -const request = require('request'); const nconf = require('nconf'); const db = require('./mocks/databasemock'); +const request = require('../src/request'); const topics = require('../src/topics'); const categories = require('../src/categories'); -const groups = require('../src/groups'); const user = require('../src/user'); const meta = require('../src/meta'); const privileges = require('../src/privileges'); @@ -16,38 +14,27 @@ const helpers = require('./helpers'); describe('feeds', () => { let tid; - let pid; let fooUid; let cid; - before((done) => { + before(async () => { meta.config['feeds:disableRSS'] = 1; - async.series({ - category: function (next) { - categories.create({ - name: 'Test Category', - description: 'Test category created by testing script', - }, next); - }, - user: function (next) { - user.create({ username: 'foo', password: 'barbar', email: 'foo@test.com' }, next); - }, - }, (err, results) => { - if (err) { - return done(err); - } - cid = results.category.cid; - fooUid = results.user; - - topics.post({ uid: results.user, title: 'test topic title', content: 'test topic content', cid: results.category.cid }, (err, result) => { - tid = result.topicData.tid; - pid = result.postData.pid; - done(err); - }); + const category = await categories.create({ + name: 'Test Category', + description: 'Test category created by testing script', }); + cid = category.cid; + fooUid = await user.create({ username: 'foo', password: 'barbar', email: 'foo@test.com' }); + + const result = await topics.post({ + cid: cid, + uid: fooUid, + title: 'test topic title', + content: 'test topic content', + }); + tid = result.topicData.tid; }); - - it('should 404', (done) => { + it('should 404', async () => { const feedUrls = [ `${nconf.get('url')}/topic/${tid}.rss`, `${nconf.get('url')}/category/${cid}.rss`, @@ -61,67 +48,45 @@ describe('feeds', () => { `${nconf.get('url')}/user/foo/topics.rss`, `${nconf.get('url')}/tags/nodebb.rss`, ]; - async.eachSeries(feedUrls, (url, next) => { - request(url, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - next(); - }); - }, (err) => { - assert.ifError(err); - meta.config['feeds:disableRSS'] = 0; - done(); - }); + for (const url of feedUrls) { + // eslint-disable-next-line no-await-in-loop + const { response } = await request.get(url); + assert.equal(response.statusCode, 404); + } + meta.config['feeds:disableRSS'] = 0; }); - it('should 404 if topic does not exist', (done) => { - request(`${nconf.get('url')}/topic/${1000}.rss`, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - done(); - }); + it('should 404 if topic does not exist', async () => { + const { response } = await request.get(`${nconf.get('url')}/topic/${1000}.rss`); + assert.equal(response.statusCode, 404); }); - it('should 404 if category id is not a number', (done) => { - request(`${nconf.get('url')}/category/invalid.rss`, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - done(); - }); + it('should 404 if category id is not a number', async () => { + const { response } = await request.get(`${nconf.get('url')}/category/invalid.rss`); + assert.equal(response.statusCode, 404); }); - it('should redirect if we do not have read privilege', (done) => { - privileges.categories.rescind(['groups:topics:read'], cid, 'guests', (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/topic/${tid}.rss`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - assert(body.includes('Login to your account')); - privileges.categories.give(['groups:topics:read'], cid, 'guests', done); - }); - }); + it('should redirect if we do not have read privilege', async () => { + await privileges.categories.rescind(['groups:topics:read'], cid, 'guests'); + const { response, body } = await request.get(`${nconf.get('url')}/topic/${tid}.rss`); + assert.equal(response.statusCode, 200); + assert(body); + assert(body.includes('Login to your account')); + await privileges.categories.give(['groups:topics:read'], cid, 'guests'); }); - it('should 404 if user is not found', (done) => { - request(`${nconf.get('url')}/user/doesnotexist/topics.rss`, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - done(); - }); + it('should 404 if user is not found', async () => { + const { response } = await request.get(`${nconf.get('url')}/user/doesnotexist/topics.rss`); + assert.equal(response.statusCode, 404); }); - it('should redirect if we do not have read privilege', (done) => { - privileges.categories.rescind(['groups:read'], cid, 'guests', (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/category/${cid}.rss`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - assert(body.includes('Login to your account')); - privileges.categories.give(['groups:read'], cid, 'guests', done); - }); - }); + it('should redirect if we do not have read privilege', async () => { + await privileges.categories.rescind(['groups:read'], cid, 'guests'); + const { response, body } = await request.get(`${nconf.get('url')}/category/${cid}.rss`); + assert.equal(response.statusCode, 200); + assert(body); + assert(body.includes('Login to your account')); + await privileges.categories.give(['groups:read'], cid, 'guests'); }); describe('private feeds and tokens', () => { @@ -131,69 +96,45 @@ describe('feeds', () => { ({ jar } = await helpers.loginUser('foo', 'barbar')); }); - it('should load feed if its not private', (done) => { - request(`${nconf.get('url')}/category/${cid}.rss`, { }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load feed if its not private', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/category/${cid}.rss`); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should not allow access if uid or token is missing', (done) => { - privileges.categories.rescind(['groups:read'], cid, 'guests', (err) => { - assert.ifError(err); - async.parallel({ - test1: function (next) { - request(`${nconf.get('url')}/category/${cid}.rss?uid=${fooUid}`, { }, next); - }, - test2: function (next) { - request(`${nconf.get('url')}/category/${cid}.rss?token=sometoken`, { }, next); - }, - }, (err, results) => { - assert.ifError(err); - assert.equal(results.test1[0].statusCode, 200); - assert.equal(results.test2[0].statusCode, 200); - assert(results.test1[0].body.includes('Login to your account')); - assert(results.test2[0].body.includes('Login to your account')); - done(); - }); - }); + it('should not allow access if uid or token is missing', async () => { + await privileges.categories.rescind(['groups:read'], cid, 'guests'); + const [test1, test2] = await Promise.all([ + request.get(`${nconf.get('url')}/category/${cid}.rss?uid=${fooUid}`, { }), + request.get(`${nconf.get('url')}/category/${cid}.rss?token=sometoken`, { }), + ]); + + assert.equal(test1.response.statusCode, 200); + assert.equal(test2.response.statusCode, 200); + assert(test1.body.includes('Login to your account')); + assert(test2.body.includes('Login to your account')); }); - it('should not allow access if token is wrong', (done) => { - request(`${nconf.get('url')}/category/${cid}.rss?uid=${fooUid}&token=sometoken`, { }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body.includes('Login to your account')); - done(); - }); + it('should not allow access if token is wrong', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/category/${cid}.rss?uid=${fooUid}&token=sometoken`); + assert.equal(response.statusCode, 200); + assert(body.includes('Login to your account')); }); - it('should allow access if token is correct', (done) => { - request(`${nconf.get('url')}/api/category/${cid}`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - rssToken = body.rssFeedUrl.split('token')[1].slice(1); - request(`${nconf.get('url')}/category/${cid}.rss?uid=${fooUid}&token=${rssToken}`, { }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body.startsWith(' { + const { body: body1 } = await request.get(`${nconf.get('url')}/api/category/${cid}`, { jar }); + rssToken = body1.rssFeedUrl.split('token')[1].slice(1); + const { response, body: body2 } = await request.get(`${nconf.get('url')}/category/${cid}.rss?uid=${fooUid}&token=${rssToken}`); + assert.equal(response.statusCode, 200); + assert(body2.startsWith(' { - privileges.categories.rescind(['groups:read'], cid, 'registered-users', (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/category/${cid}.rss?uid=${fooUid}&token=${rssToken}`, { }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body.includes('Login to your account')); - done(); - }); - }); + it('should not allow access if token is correct but has no privilege', async () => { + await privileges.categories.rescind(['groups:read'], cid, 'registered-users'); + const { response, body } = await request.get(`${nconf.get('url')}/category/${cid}.rss?uid=${fooUid}&token=${rssToken}`); + assert.equal(response.statusCode, 200); + assert(body.includes('Login to your account')); }); }); }); diff --git a/test/flags.js b/test/flags.js index 65dd23ef66..ee150a10c4 100644 --- a/test/flags.js +++ b/test/flags.js @@ -2,15 +2,13 @@ const assert = require('assert'); const nconf = require('nconf'); -const async = require('async'); -const request = require('request-promise-native'); const util = require('util'); const sleep = util.promisify(setTimeout); const db = require('./mocks/databasemock'); const helpers = require('./helpers'); - +const request = require('../src/request'); const Flags = require('../src/flags'); const Categories = require('../src/categories'); const Topics = require('../src/topics'); @@ -243,13 +241,11 @@ describe('Flags', () => { it('should show user history for admins', async () => { await Groups.join('administrators', moderatorUid); - const flagData = await request({ - uri: `${nconf.get('url')}/api/flags/1`, + const { body: flagData } = await request.get(`${nconf.get('url')}/api/flags/1`, { jar, headers: { 'x-csrf-token': csrfToken, }, - json: true, }); assert(flagData.history); @@ -260,13 +256,11 @@ describe('Flags', () => { it('should show user history for global moderators', async () => { await Groups.join('Global Moderators', moderatorUid); - const flagData = await request({ - uri: `${nconf.get('url')}/api/flags/1`, + const { body: flagData } = await request.get(`${nconf.get('url')}/api/flags/1`, { jar, headers: { 'x-csrf-token': csrfToken, }, - json: true, }); assert(flagData.history); @@ -895,9 +889,7 @@ describe('Flags', () => { describe('.create()', () => { it('should create a flag with no errors', async () => { - await request({ - method: 'post', - uri: `${nconf.get('url')}/api/v3/flags`, + await request.post(`${nconf.get('url')}/api/v3/flags`, { jar, headers: { 'x-csrf-token': csrfToken, @@ -907,7 +899,6 @@ describe('Flags', () => { id: pid, reason: 'foobar', }, - json: true, }); const exists = await Flags.exists('post', pid, 2); @@ -921,9 +912,7 @@ describe('Flags', () => { content: 'This is flaggable content', }); - const { response } = await request({ - method: 'post', - uri: `${nconf.get('url')}/api/v3/flags`, + const { body } = await request.post(`${nconf.get('url')}/api/v3/flags`, { jar, headers: { 'x-csrf-token': csrfToken, @@ -933,10 +922,9 @@ describe('Flags', () => { id: postData.pid, reason: '"', }, - json: true, }); - const flagData = await Flags.get(response.flagId); + const flagData = await Flags.get(body.response.flagId); assert.strictEqual(flagData.reports[0].value, '"<script>alert('ok');</script>'); }); @@ -953,15 +941,9 @@ describe('Flags', () => { }); const login = await helpers.loginUser('unprivileged', 'abcdef'); const jar3 = login.jar; - const config = await request({ - url: `${nconf.get('url')}/api/config`, - json: true, - jar: jar3, - }); - const csrfToken = config.csrf_token; - const { statusCode, body } = await request({ - method: 'post', - uri: `${nconf.get('url')}/api/v3/flags`, + const csrfToken = await helpers.getCsrfToken(jar3); + + const { response, body } = await request.post(`${nconf.get('url')}/api/v3/flags`, { jar: jar3, headers: { 'x-csrf-token': csrfToken, @@ -971,11 +953,8 @@ describe('Flags', () => { id: result.postData.pid, reason: 'foobar', }, - json: true, - simple: false, - resolveWithFullResponse: true, }); - assert.strictEqual(statusCode, 403); + assert.strictEqual(response.statusCode, 403); // Handle dev mode test delete body.stack; @@ -992,9 +971,7 @@ describe('Flags', () => { describe('.update()', () => { it('should update a flag\'s properties', async () => { - const { response } = await request({ - method: 'put', - uri: `${nconf.get('url')}/api/v3/flags/4`, + const { body } = await request.put(`${nconf.get('url')}/api/v3/flags/4`, { jar, headers: { 'x-csrf-token': csrfToken, @@ -1002,10 +979,9 @@ describe('Flags', () => { body: { state: 'wip', }, - json: true, }); - const { history } = response; + const { history } = body.response; assert(Array.isArray(history)); assert(history[0].fields.hasOwnProperty('state')); assert.strictEqual('[[flags:state-wip]]', history[0].fields.state); @@ -1014,14 +990,11 @@ describe('Flags', () => { describe('.rescind()', () => { it('should remove a flag\'s report', async () => { - const response = await request({ - method: 'delete', - uri: `${nconf.get('url')}/api/v3/flags/4/report`, + const { response } = await request.del(`${nconf.get('url')}/api/v3/flags/4/report`, { jar, headers: { 'x-csrf-token': csrfToken, }, - resolveWithFullResponse: true, }); assert.strictEqual(response.statusCode, 200); @@ -1030,9 +1003,7 @@ describe('Flags', () => { describe('.appendNote()', () => { it('should append a note to the flag', async () => { - const { response } = await request({ - method: 'post', - uri: `${nconf.get('url')}/api/v3/flags/4/notes`, + const { body } = await request.post(`${nconf.get('url')}/api/v3/flags/4/notes`, { jar, headers: { 'x-csrf-token': csrfToken, @@ -1041,9 +1012,8 @@ describe('Flags', () => { note: 'lorem ipsum dolor sit amet', datetime: 1626446956652, }, - json: true, }); - + const { response } = body; assert(response.hasOwnProperty('notes')); assert(Array.isArray(response.notes)); assert.strictEqual('lorem ipsum dolor sit amet', response.notes[0].content); @@ -1058,16 +1028,13 @@ describe('Flags', () => { describe('.deleteNote()', () => { it('should delete a note from a flag', async () => { - const { response } = await request({ - method: 'delete', - uri: `${nconf.get('url')}/api/v3/flags/4/notes/1626446956652`, + const { body } = await request.del(`${nconf.get('url')}/api/v3/flags/4/notes/1626446956652`, { jar, headers: { 'x-csrf-token': csrfToken, }, - json: true, }); - + const { response } = body; assert(Array.isArray(response.history)); assert(Array.isArray(response.notes)); assert.strictEqual(response.notes.length, 0); @@ -1088,7 +1055,7 @@ describe('Flags', () => { before(async () => { uid = await User.create({ username: 'flags-access-control', password: 'abcdef' }); ({ jar, csrf_token } = await helpers.loginUser('flags-access-control', 'abcdef')); - + console.log('cs', csrfToken); flaggerUid = await User.create({ username: 'flags-access-control-flagger', password: 'abcdef' }); }); @@ -1106,68 +1073,44 @@ describe('Flags', () => { }); ({ flagId } = await Flags.create('post', postData.pid, flaggerUid, 'spam')); + const commonOpts = { + jar, + headers: { + 'x-csrf-token': csrf_token, + }, + }; requests = new Set([ { + ...commonOpts, method: 'get', uri: `${nconf.get('url')}/api/v3/flags/${flagId}`, - jar, - headers: { - 'x-csrf-token': csrf_token, - }, - json: true, - simple: false, - resolveWithFullResponse: true, }, { + ...commonOpts, method: 'put', uri: `${nconf.get('url')}/api/v3/flags/${flagId}`, - jar, - headers: { - 'x-csrf-token': csrf_token, - }, body: { state: 'wip', }, - json: true, - simple: false, - resolveWithFullResponse: true, }, { + ...commonOpts, method: 'post', uri: `${nconf.get('url')}/api/v3/flags/${flagId}/notes`, - jar, - headers: { - 'x-csrf-token': csrf_token, - }, body: { note: 'test note', datetime: noteTime, }, - json: true, - simple: false, - resolveWithFullResponse: true, }, { + ...commonOpts, method: 'delete', uri: `${nconf.get('url')}/api/v3/flags/${flagId}/notes/${noteTime}`, - jar, - headers: { - 'x-csrf-token': csrf_token, - }, - json: true, - simple: false, - resolveWithFullResponse: true, }, { + ...commonOpts, method: 'delete', uri: `${nconf.get('url')}/api/v3/flags/${flagId}`, - jar, - headers: { - 'x-csrf-token': csrf_token, - }, - json: true, - simple: false, - resolveWithFullResponse: true, }, ]); }); @@ -1179,7 +1122,8 @@ describe('Flags', () => { delete opts.headers; // eslint-disable-next-line no-await-in-loop - const { statusCode } = await request(opts); + const { response } = await request[opts.method](opts.uri, opts); + const { statusCode } = response; assert(statusCode.toString().startsWith(4), `${opts.method.toUpperCase()} ${opts.uri} => ${statusCode}`); } }); @@ -1187,7 +1131,8 @@ describe('Flags', () => { it('should not allow access to privileged flag endpoints to regular users', async () => { for (const opts of requests) { // eslint-disable-next-line no-await-in-loop - const { statusCode } = await request(opts); + const { response } = await request[opts.method](opts.uri, opts); + const { statusCode } = response; assert(statusCode.toString().startsWith(4), `${opts.method.toUpperCase()} ${opts.uri} => ${statusCode}`); } }); @@ -1197,7 +1142,8 @@ describe('Flags', () => { for (const opts of requests) { // eslint-disable-next-line no-await-in-loop - const { statusCode } = await request(opts); + const { response } = await request[opts.method](opts.uri, opts); + const { statusCode } = response; assert.strictEqual(statusCode, 200, `${opts.method.toUpperCase()} ${opts.uri} => ${statusCode}`); } }); @@ -1207,7 +1153,8 @@ describe('Flags', () => { for (const opts of requests) { // eslint-disable-next-line no-await-in-loop - const { statusCode } = await request(opts); + const { response } = await request[opts.method](opts.uri, opts); + const { statusCode } = response; assert.strictEqual(statusCode, 200, `${opts.method.toUpperCase()} ${opts.uri} => ${statusCode}`); } }); @@ -1217,7 +1164,8 @@ describe('Flags', () => { for (const opts of requests) { // eslint-disable-next-line no-await-in-loop - const { statusCode } = await request(opts); + const { response } = await request[opts.method](opts.uri, opts); + const { statusCode } = response; assert.strictEqual(statusCode, 200, `${opts.method.toUpperCase()} ${opts.uri} => ${statusCode}`); } }); @@ -1231,7 +1179,8 @@ describe('Flags', () => { for (const opts of requests) { // eslint-disable-next-line no-await-in-loop - const { statusCode } = await request(opts); + const { response } = await request[opts.method](opts.uri, opts); + const { statusCode } = response; assert(statusCode.toString().startsWith(4), `${opts.method.toUpperCase()} ${opts.uri} => ${statusCode}`); } }); diff --git a/test/helpers/index.js b/test/helpers/index.js index aea7761e17..e71a05edaa 100644 --- a/test/helpers/index.js +++ b/test/helpers/index.js @@ -1,26 +1,22 @@ 'use strict'; -const request = require('request'); -const requestAsync = require('request-promise-native'); const nconf = require('nconf'); const fs = require('fs'); +const path = require('path'); const winston = require('winston'); -const utils = require('../../src/utils'); +const request = require('../../src/request'); const helpers = module.exports; helpers.getCsrfToken = async (jar) => { - const { csrf_token: token } = await requestAsync({ - url: `${nconf.get('url')}/api/config`, - json: true, + const { body } = await request.get(`${nconf.get('url')}/api/config`, { jar, }); - - return token; + return body.csrf_token; }; -helpers.request = async function (method, uri, options) { +helpers.request = async function (method, uri, options = {}) { const ignoreMethods = ['GET', 'HEAD', 'OPTIONS']; const lowercaseMethod = String(method).toLowerCase(); let csrf_token; @@ -28,79 +24,44 @@ helpers.request = async function (method, uri, options) { csrf_token = await helpers.getCsrfToken(options.jar); } - return new Promise((resolve, reject) => { - options.headers = options.headers || {}; - if (csrf_token) { - options.headers['x-csrf-token'] = csrf_token; - } - request[lowercaseMethod](`${nconf.get('url')}${uri}`, options, (err, res, body) => { - if (err) reject(err); - else resolve({ res, body }); - }); - }); + options.headers = options.headers || {}; + if (csrf_token) { + options.headers['x-csrf-token'] = csrf_token; + } + return await request[lowercaseMethod](`${nconf.get('url')}${uri}`, options); }; helpers.loginUser = async (username, password, payload = {}) => { const jar = request.jar(); - const form = { username, password, ...payload }; + const data = { username, password, ...payload }; - const { statusCode, body: configBody } = await requestAsync({ - url: `${nconf.get('url')}/api/config`, - json: true, + const csrf_token = await helpers.getCsrfToken(jar); + const { response, body } = await request.post(`${nconf.get('url')}/login`, { + body: data, jar: jar, - followRedirect: false, - simple: false, - resolveWithFullResponse: true, - }); - - if (statusCode !== 200) { - throw new Error('[[error:invalid-response]]'); - } - - const { csrf_token } = configBody; - const res = await requestAsync.post(`${nconf.get('url')}/login`, { - form, - json: true, - jar: jar, - followRedirect: false, - simple: false, - resolveWithFullResponse: true, headers: { 'x-csrf-token': csrf_token, }, }); - return { jar, res, body: res.body, csrf_token: csrf_token }; + return { jar, response, body, csrf_token }; }; -helpers.logoutUser = function (jar, callback) { - request({ - url: `${nconf.get('url')}/api/config`, - json: true, - jar: jar, - }, (err, response, body) => { - if (err) { - return callback(err, response, body); - } - - request.post(`${nconf.get('url')}/logout`, { - form: {}, - json: true, - jar: jar, - headers: { - 'x-csrf-token': body.csrf_token, - }, - }, (err, response, body) => { - callback(err, response, body); - }); +helpers.logoutUser = async function (jar) { + const csrf_token = await helpers.getCsrfToken(jar); + const { response, body } = await request.post(`${nconf.get('url')}/logout`, { + body: {}, + jar, + headers: { + 'x-csrf-token': csrf_token, + }, }); + return { response, body }; }; -helpers.connectSocketIO = function (res, csrf_token, callback) { +helpers.connectSocketIO = function (res, csrf_token) { const io = require('socket.io-client'); - let cookies = res.headers['set-cookie']; - cookies = cookies.filter(c => /express.sid=[^;]+;/.test(c)); - const cookie = cookies[0]; + const cookie = res.headers['set-cookie']; const socket = io(nconf.get('base_url'), { path: `${nconf.get('relative_path')}/socket.io`, extraHeaders: { @@ -111,73 +72,71 @@ helpers.connectSocketIO = function (res, csrf_token, callback) { _csrf: csrf_token, }, }); - let error; - socket.on('connect', () => { - if (error) { - return; - } - callback(null, socket); - }); + return new Promise((resolve, reject) => { + let error; + socket.on('connect', () => { + if (error) { + return; + } + resolve(socket); + }); - socket.on('error', (err) => { - error = err; - console.log('socket.io error', err.stack); - callback(err); + socket.on('error', (err) => { + error = err; + console.log('socket.io error', err.stack); + reject(err); + }); }); }; -helpers.uploadFile = function (uploadEndPoint, filePath, body, jar, csrf_token, callback) { - let formData = { - files: [ - fs.createReadStream(filePath), - ], +helpers.uploadFile = async function (uploadEndPoint, filePath, data, jar, csrf_token) { + const mime = require('mime'); + const form = new FormData(); + const file = await fs.promises.readFile(filePath); + const blob = new Blob([file], { type: mime.getType(filePath) }); + + form.append('files', blob, path.basename(filePath)); + + if (data && data.params) { + form.append('params', data.params); + } + + const response = await fetch(uploadEndPoint, { + method: 'post', + body: form, + headers: { + 'x-csrf-token': csrf_token, + cookie: await jar.getCookieString(uploadEndPoint), + }, + }); + const body = await response.json(); + return { + body, + response: { + status: response.status, + statusCode: response.status, + statusText: response.statusText, + headers: Object.fromEntries(response.headers.entries()), + }, }; - formData = utils.merge(formData, body); - request.post({ - url: uploadEndPoint, - formData: formData, - json: true, - jar: jar, +}; + +helpers.registerUser = async function (data) { + const jar = request.jar(); + const csrf_token = await helpers.getCsrfToken(jar); + + if (!data.hasOwnProperty('password-confirm')) { + data['password-confirm'] = data.password; + } + + const { response, body } = await request.post(`${nconf.get('url')}/register`, { + body: data, + jar, headers: { 'x-csrf-token': csrf_token, }, - }, (err, res, body) => { - if (err) { - return callback(err); - } - if (res.statusCode !== 200) { - winston.error(JSON.stringify(body)); - } - callback(null, res, body); - }); -}; - -helpers.registerUser = function (data, callback) { - const jar = request.jar(); - request({ - url: `${nconf.get('url')}/api/config`, - json: true, - jar: jar, - }, (err, response, body) => { - if (err) { - return callback(err); - } - - if (!data.hasOwnProperty('password-confirm')) { - data['password-confirm'] = data.password; - } - - request.post(`${nconf.get('url')}/register`, { - form: data, - json: true, - jar: jar, - headers: { - 'x-csrf-token': body.csrf_token, - }, - }, (err, response, body) => { - callback(err, jar, response, body); - }); }); + return { jar, response, body }; }; // http://stackoverflow.com/a/14387791/583363 @@ -205,37 +164,26 @@ helpers.copyFile = function (source, target, callback) { } }; -helpers.invite = async function (body, uid, jar, csrf_token) { - console.log('making call'); - const res = await requestAsync.post(`${nconf.get('url')}/api/v3/users/${uid}/invites`, { +helpers.invite = async function (data, uid, jar, csrf_token) { + return await request.post(`${nconf.get('url')}/api/v3/users/${uid}/invites`, { jar: jar, - // using "form" since client "api" module make requests with "application/x-www-form-urlencoded" content-type - form: body, + body: data, headers: { 'x-csrf-token': csrf_token, }, - simple: false, - resolveWithFullResponse: true, }); - console.log(res.statusCode, res.body); - - res.body = JSON.parse(res.body); - return { res, body }; }; -helpers.createFolder = function (path, folderName, jar, csrf_token) { - return requestAsync.put(`${nconf.get('url')}/api/v3/files/folder`, { +helpers.createFolder = async function (path, folderName, jar, csrf_token) { + return await request.put(`${nconf.get('url')}/api/v3/files/folder`, { jar, body: { path, folderName, }, - json: true, headers: { 'x-csrf-token': csrf_token, }, - simple: false, - resolveWithFullResponse: true, }); }; diff --git a/test/locale-detect.js b/test/locale-detect.js index 91c3e94194..c6f98142c4 100644 --- a/test/locale-detect.js +++ b/test/locale-detect.js @@ -2,45 +2,34 @@ const assert = require('assert'); const nconf = require('nconf'); -const request = require('request'); const db = require('./mocks/databasemock'); const meta = require('../src/meta'); +const request = require('../src/request'); describe('Language detection', () => { - it('should detect the language for a guest', (done) => { - meta.configs.set('autoDetectLang', 1, (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/api/config`, { - headers: { - 'Accept-Language': 'de-DE,de;q=0.5', - }, - json: true, - }, (err, res, body) => { - assert.ifError(err); - assert.ok(body); + it('should detect the language for a guest', async () => { + await meta.configs.set('autoDetectLang', 1); - assert.strictEqual(body.userLang, 'de'); - done(); - }); + const { body } = await request.get(`${nconf.get('url')}/api/config`, { + headers: { + 'Accept-Language': 'de-DE,de;q=0.5', + }, }); + assert.ok(body); + assert.strictEqual(body.userLang, 'de'); }); - it('should do nothing when disabled', (done) => { - meta.configs.set('autoDetectLang', 0, (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/api/config`, { - headers: { - 'Accept-Language': 'de-DE,de;q=0.5', - }, - json: true, - }, (err, res, body) => { - assert.ifError(err); - assert.ok(body); + it('should do nothing when disabled', async () => { + await meta.configs.set('autoDetectLang', 0); - assert.strictEqual(body.userLang, 'en-GB'); - done(); - }); + const { body } = await request.get(`${nconf.get('url')}/api/config`, { + headers: { + 'Accept-Language': 'de-DE,de;q=0.5', + }, }); + + assert.ok(body); + assert.strictEqual(body.userLang, 'en-GB'); }); }); diff --git a/test/messaging.js b/test/messaging.js index 4709aff351..86008e2021 100644 --- a/test/messaging.js +++ b/test/messaging.js @@ -1,7 +1,7 @@ 'use strict'; const assert = require('assert'); -const request = require('request-promise-native'); + const nconf = require('nconf'); const util = require('util'); @@ -14,7 +14,7 @@ const Groups = require('../src/groups'); const Messaging = require('../src/messaging'); const api = require('../src/api'); const helpers = require('./helpers'); -const socketModules = require('../src/socket.io/modules'); +const request = require('../src/request'); const utils = require('../src/utils'); const translator = require('../src/translator'); @@ -33,12 +33,8 @@ describe('Messaging Library', () => { const callv3API = async (method, path, body, user) => { const options = { - method, body, - json: true, jar: mocks.users[user].jar, - resolveWithFullResponse: true, - simple: false, }; if (method !== 'get') { @@ -47,7 +43,7 @@ describe('Messaging Library', () => { }; } - return request(`${nconf.get('url')}/api/v3${path}`, options); + return request[method](`${nconf.get('url')}/api/v3${path}`, options); }; before(async () => { @@ -162,11 +158,11 @@ describe('Messaging Library', () => { uids: [mocks.users.baz.uid], }, 'foo'); - const { statusCode, body } = await callv3API('post', `/chats`, { + const { response, body } = await callv3API('post', `/chats`, { uids: [mocks.users.baz.uid], }, 'foo'); - assert.equal(statusCode, 400); + assert.equal(response.statusCode, 400); assert.equal(body.status.code, 'bad-request'); assert.equal(body.status.message, await translator.translate('[[error:too-many-messages]]')); meta.config.chatMessageDelay = oldValue; @@ -190,20 +186,20 @@ describe('Messaging Library', () => { assert.strictEqual(messages[0].system, 1); assert.strictEqual(messages[0].content, 'user-join'); - const { statusCode, body: body2 } = await callv3API('put', `/chats/${roomId}/messages/${messages[0].messageId}`, { + const { response, body: body2 } = await callv3API('put', `/chats/${roomId}/messages/${messages[0].messageId}`, { message: 'test', }, 'foo'); - assert.strictEqual(statusCode, 400); + assert.strictEqual(response.statusCode, 400); assert.equal(body2.status.message, await translator.translate('[[error:cant-edit-chat-message]]')); }); it('should fail to add user to room with invalid data', async () => { - let { statusCode, body } = await callv3API('post', `/chats/${roomId}/users`, {}, 'foo'); - assert.strictEqual(statusCode, 400); + let { response, body } = await callv3API('post', `/chats/${roomId}/users`, {}, 'foo'); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, await translator.translate('[[error:required-parameters-missing, uids]]')); - ({ statusCode, body } = await callv3API('post', `/chats/${roomId}/users`, { uids: [null] }, 'foo')); - assert.strictEqual(statusCode, 400); + ({ response, body } = await callv3API('post', `/chats/${roomId}/users`, { uids: [null] }, 'foo')); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, await translator.translate('[[error:no-user]]')); }); @@ -220,38 +216,38 @@ describe('Messaging Library', () => { }); it('should throw error if user is not in room', async () => { - const { statusCode, body } = await callv3API('get', `/chats/${roomId}/users`, {}, 'bar'); - assert.strictEqual(statusCode, 403); + const { response, body } = await callv3API('get', `/chats/${roomId}/users`, {}, 'bar'); + assert.strictEqual(response.statusCode, 403); assert.equal(body.status.message, await translator.translate('[[error:no-privileges]]')); }); it('should fail to add users to room if max is reached', async () => { meta.config.maximumUsersInChatRoom = 2; - const { statusCode, body } = await callv3API('post', `/chats/${roomId}/users`, { uids: [mocks.users.bar.uid] }, 'foo'); - assert.strictEqual(statusCode, 400); + const { response, body } = await callv3API('post', `/chats/${roomId}/users`, { uids: [mocks.users.bar.uid] }, 'foo'); + assert.strictEqual(response.statusCode, 400); assert.equal(body.status.message, await translator.translate('[[error:cant-add-more-users-to-chat-room]]')); meta.config.maximumUsersInChatRoom = 0; }); it('should fail to add users to room if user does not exist', async () => { - const { statusCode, body } = await callv3API('post', `/chats/${roomId}/users`, { uids: [98237498234] }, 'foo'); - assert.strictEqual(statusCode, 400); + const { response, body } = await callv3API('post', `/chats/${roomId}/users`, { uids: [98237498234] }, 'foo'); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, await translator.translate('[[error:no-user]]')); }); it('should fail to add self to room', async () => { - const { statusCode, body } = await callv3API('post', `/chats/${roomId}/users`, { uids: [mocks.users.foo.uid] }, 'foo'); - assert.strictEqual(statusCode, 400); + const { response, body } = await callv3API('post', `/chats/${roomId}/users`, { uids: [mocks.users.foo.uid] }, 'foo'); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, await translator.translate('[[error:cant-chat-with-yourself]]')); }); it('should fail to leave room with invalid data', async () => { - let { statusCode, body } = await callv3API('delete', `/chats/${roomId}/users`, {}, 'foo'); - assert.strictEqual(statusCode, 400); + let { response, body } = await callv3API('delete', `/chats/${roomId}/users`, {}, 'foo'); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, await translator.translate('[[error:required-parameters-missing, uids]]')); - ({ statusCode, body } = await callv3API('delete', `/chats/${roomId}/users`, { uids: [98237423] }, 'foo')); - assert.strictEqual(statusCode, 400); + ({ response, body } = await callv3API('delete', `/chats/${roomId}/users`, { uids: [98237423] }, 'foo')); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, await translator.translate('[[error:no-user]]')); }); @@ -303,9 +299,7 @@ describe('Messaging Library', () => { const { jar: senderJar, csrf_token: senderCsrf } = await helpers.loginUser('deleted_chat_user', 'barbar'); const receiver = await User.create({ username: 'receiver' }); - const { response } = await request(`${nconf.get('url')}/api/v3/chats`, { - method: 'post', - json: true, + const { body } = await request.post(`${nconf.get('url')}/api/v3/chats`, { jar: senderJar, body: { uids: [receiver], @@ -315,31 +309,31 @@ describe('Messaging Library', () => { }, }); await User.deleteAccount(sender); - assert(await Messaging.isRoomOwner(receiver, response.roomId)); + assert(await Messaging.isRoomOwner(receiver, body.response.roomId)); }); it('should fail to remove user from room', async () => { - let { statusCode, body } = await callv3API('delete', `/chats/${roomId}/users`, {}, 'foo'); - assert.strictEqual(statusCode, 400); + let { response, body } = await callv3API('delete', `/chats/${roomId}/users`, {}, 'foo'); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, await translator.translate('[[error:required-parameters-missing, uids]]')); - ({ statusCode, body } = await callv3API('delete', `/chats/${roomId}/users`, { uids: [null] }, 'foo')); - assert.strictEqual(statusCode, 400); + ({ response, body } = await callv3API('delete', `/chats/${roomId}/users`, { uids: [null] }, 'foo')); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, await translator.translate('[[error:no-user]]')); }); it('should fail to remove user from room if user does not exist', async () => { - const { statusCode, body } = await callv3API('delete', `/chats/${roomId}/users`, { uids: [99] }, 'foo'); - assert.strictEqual(statusCode, 400); + const { response, body } = await callv3API('delete', `/chats/${roomId}/users`, { uids: [99] }, 'foo'); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, await translator.translate('[[error:no-user]]')); }); it('should remove user from room', async () => { - const { statusCode, body } = await callv3API('post', `/chats`, { + const { response, body } = await callv3API('post', `/chats`, { uids: [mocks.users.herp.uid], }, 'foo'); const { roomId } = body.response; - assert.strictEqual(statusCode, 200); + assert.strictEqual(response.statusCode, 200); let isInRoom = await Messaging.isUserInRoom(mocks.users.herp.uid, roomId); assert(isInRoom); @@ -488,8 +482,8 @@ describe('Messaging Library', () => { }); it('should rename room', async () => { - const { statusCode } = await callv3API('put', `/chats/${roomId}`, { name: 'new room name' }, 'foo'); - assert.strictEqual(statusCode, 200); + const { response } = await callv3API('put', `/chats/${roomId}`, { name: 'new room name' }, 'foo'); + assert.strictEqual(response.statusCode, 200); }); it('should send a room-rename system message when a room is renamed', async () => { @@ -638,46 +632,46 @@ describe('Messaging Library', () => { }); it('should fail to edit message with invalid data', async () => { - let { statusCode, body } = await callv3API('put', `/chats/1/messages/10000`, { message: 'foo' }, 'foo'); - assert.strictEqual(statusCode, 400); + let { response, body } = await callv3API('put', `/chats/1/messages/10000`, { message: 'foo' }, 'foo'); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, await translator.translate('[[error:invalid-mid]]')); - ({ statusCode, body } = await callv3API('put', `/chats/${roomId}/messages/${mid}`, {}, 'foo')); - assert.strictEqual(statusCode, 400); + ({ response, body } = await callv3API('put', `/chats/${roomId}/messages/${mid}`, {}, 'foo')); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, await translator.translate('[[error:invalid-chat-message]]')); }); it('should fail to edit message if new content is empty string', async () => { - const { statusCode, body } = await callv3API('put', `/chats/${roomId}/messages/${mid}`, { message: ' ' }, 'foo'); - assert.strictEqual(statusCode, 400); + const { response, body } = await callv3API('put', `/chats/${roomId}/messages/${mid}`, { message: ' ' }, 'foo'); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, await translator.translate('[[error:invalid-chat-message]]')); }); it('should fail to edit message if not own message', async () => { - const { statusCode, body } = await callv3API('put', `/chats/${roomId}/messages/${mid}`, { message: 'message edited' }, 'herp'); - assert.strictEqual(statusCode, 400); + const { response, body } = await callv3API('put', `/chats/${roomId}/messages/${mid}`, { message: 'message edited' }, 'herp'); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, await translator.translate('[[error:cant-edit-chat-message]]')); }); it('should fail to edit message if message not in room', async () => { - const { statusCode, body } = await callv3API('put', `/chats/${roomId}/messages/1014`, { message: 'message edited' }, 'herp'); - assert.strictEqual(statusCode, 400); + const { response, body } = await callv3API('put', `/chats/${roomId}/messages/1014`, { message: 'message edited' }, 'herp'); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, await translator.translate('[[error:invalid-mid]]')); }); it('should edit message', async () => { - let { statusCode, body } = await callv3API('put', `/chats/${roomId}/messages/${mid}`, { message: 'message edited' }, 'foo'); - assert.strictEqual(statusCode, 200); + let { response, body } = await callv3API('put', `/chats/${roomId}/messages/${mid}`, { message: 'message edited' }, 'foo'); + assert.strictEqual(response.statusCode, 200); assert.strictEqual(body.response.content, 'message edited'); - ({ statusCode, body } = await callv3API('get', `/chats/${roomId}/messages/${mid}`, {}, 'foo')); - assert.strictEqual(statusCode, 200); + ({ response, body } = await callv3API('get', `/chats/${roomId}/messages/${mid}`, {}, 'foo')); + assert.strictEqual(response.statusCode, 200); assert.strictEqual(body.response.content, 'message edited'); }); it('should fail to delete message if not owner', async () => { - const { statusCode, body } = await callv3API('delete', `/chats/${roomId}/messages/${mid}`, {}, 'herp'); - assert.strictEqual(statusCode, 400); + const { response, body } = await callv3API('delete', `/chats/${roomId}/messages/${mid}`, {}, 'herp'); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, 'You are not allowed to delete this message'); }); @@ -716,8 +710,8 @@ describe('Messaging Library', () => { }); it('should error out if a message is deleted again', async () => { - const { statusCode, body } = await callv3API('delete', `/chats/${roomId}/messages/${mid}`, {}, 'foo'); - assert.strictEqual(statusCode, 400); + const { response, body } = await callv3API('delete', `/chats/${roomId}/messages/${mid}`, {}, 'foo'); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, 'This chat message has already been deleted.'); }); @@ -728,8 +722,8 @@ describe('Messaging Library', () => { }); it('should error out if a message is restored again', async () => { - const { statusCode, body } = await callv3API('post', `/chats/${roomId}/messages/${mid}`, {}, 'foo'); - assert.strictEqual(statusCode, 400); + const { response, body } = await callv3API('post', `/chats/${roomId}/messages/${mid}`, {}, 'foo'); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, 'This chat message has already been restored.'); }); @@ -743,8 +737,8 @@ describe('Messaging Library', () => { }); it('should error out for regular users', async () => { - const { statusCode, body } = await callv3API('delete', `/chats/${roomId}/messages/${mid2}`, {}, 'baz'); - assert.strictEqual(statusCode, 400); + const { response, body } = await callv3API('delete', `/chats/${roomId}/messages/${mid2}`, {}, 'baz'); + assert.strictEqual(response.statusCode, 400); assert.strictEqual(body.status.message, 'chat-message-editing-disabled'); }); @@ -767,33 +761,21 @@ describe('Messaging Library', () => { describe('controller', () => { it('should 404 if chat is disabled', async () => { meta.config.disableChat = 1; - const response = await request(`${nconf.get('url')}/user/baz/chats`, { - resolveWithFullResponse: true, - simple: false, - }); + const { response } = await request.get(`${nconf.get('url')}/user/baz/chats`); assert.equal(response.statusCode, 404); }); it('should 401 for guest with not-authorised status code', async () => { meta.config.disableChat = 0; - const response = await request(`${nconf.get('url')}/api/user/baz/chats`, { - resolveWithFullResponse: true, - simple: false, - json: true, - }); - const { body } = response; + const { response, body } = await request.get(`${nconf.get('url')}/api/user/baz/chats`); assert.equal(response.statusCode, 401); assert.equal(body.status.code, 'not-authorised'); }); it('should 404 for non-existent user', async () => { - const response = await request(`${nconf.get('url')}/user/doesntexist/chats`, { - resolveWithFullResponse: true, - simple: false, - }); - + const { response } = await request.get(`${nconf.get('url')}/user/doesntexist/chats`); assert.equal(response.statusCode, 404); }); }); @@ -805,13 +787,7 @@ describe('Messaging Library', () => { }); it('should return chats page data', async () => { - const response = await request(`${nconf.get('url')}/api/user/herp/chats`, { - resolveWithFullResponse: true, - simple: false, - json: true, - jar, - }); - const { body } = response; + const { response, body } = await request.get(`${nconf.get('url')}/api/user/herp/chats`, { jar }); assert.equal(response.statusCode, 200); assert(Array.isArray(body.rooms)); @@ -820,13 +796,7 @@ describe('Messaging Library', () => { }); it('should return room data', async () => { - const response = await request(`${nconf.get('url')}/api/user/herp/chats/${roomId}`, { - resolveWithFullResponse: true, - simple: false, - json: true, - jar, - }); - const { body } = response; + const { response, body } = await request.get(`${nconf.get('url')}/api/user/herp/chats/${roomId}`, { jar }); assert.equal(response.statusCode, 200); assert.equal(body.roomId, roomId); @@ -834,27 +804,16 @@ describe('Messaging Library', () => { }); it('should redirect to chats page', async () => { - const res = await request(`${nconf.get('url')}/api/chats`, { - resolveWithFullResponse: true, - simple: false, - jar, - json: true, - }); - const { body } = res; + const { response, body } = await request.get(`${nconf.get('url')}/api/chats`, { jar }); - assert.equal(res.statusCode, 200); - assert.equal(res.headers['x-redirect'], '/user/herp/chats'); + assert.equal(response.statusCode, 200); + assert.equal(response.headers['x-redirect'], '/user/herp/chats'); assert.equal(body, '/user/herp/chats'); }); it('should return 404 if user is not in room', async () => { const data = await helpers.loginUser('baz', 'quuxquux'); - const response = await request(`${nconf.get('url')}/api/user/baz/chats/${roomId}`, { - resolveWithFullResponse: true, - simple: false, - json: true, - jar: data.jar, - }); + const { response } = await request.get(`${nconf.get('url')}/api/user/baz/chats/${roomId}`, { jar: data.jar }); assert.equal(response.statusCode, 404); }); diff --git a/test/meta.js b/test/meta.js index 84452561d4..77667ddbf2 100644 --- a/test/meta.js +++ b/test/meta.js @@ -2,13 +2,14 @@ const assert = require('assert'); const async = require('async'); -const request = require('request'); + const nconf = require('nconf'); const db = require('./mocks/databasemock'); const meta = require('../src/meta'); const User = require('../src/user'); const Groups = require('../src/groups'); +const request = require('../src/request'); describe('meta', () => { let fooUid; @@ -489,117 +490,86 @@ describe('meta', () => { }); describe('Access-Control-Allow-Origin', () => { - it('Access-Control-Allow-Origin header should be empty', (done) => { + it('Access-Control-Allow-Origin header should be empty', async () => { const jar = request.jar(); - request.get(`${nconf.get('url')}/api/search?term=bug`, { - form: {}, - json: true, + const { response } = await request.get(`${nconf.get('url')}/api/search?term=bug`, { jar: jar, - }, (err, response, body) => { - assert.ifError(err); - assert.equal(response.headers['access-control-allow-origin'], undefined); - done(); }); + + assert.equal(response.headers['access-control-allow-origin'], undefined); }); - it('should set proper Access-Control-Allow-Origin header', (done) => { + it('should set proper Access-Control-Allow-Origin header', async () => { const jar = request.jar(); const oldValue = meta.config['access-control-allow-origin']; meta.config['access-control-allow-origin'] = 'test.com, mydomain.com'; - request.get(`${nconf.get('url')}/api/search?term=bug`, { - form: { - }, - json: true, + const { response } = await request.get(`${nconf.get('url')}/api/search?term=bug`, { jar: jar, headers: { origin: 'mydomain.com', }, - }, (err, response, body) => { - assert.ifError(err); - assert.equal(response.headers['access-control-allow-origin'], 'mydomain.com'); - meta.config['access-control-allow-origin'] = oldValue; - done(err); }); + + assert.equal(response.headers['access-control-allow-origin'], 'mydomain.com'); + meta.config['access-control-allow-origin'] = oldValue; }); - it('Access-Control-Allow-Origin header should be empty if origin does not match', (done) => { + it('Access-Control-Allow-Origin header should be empty if origin does not match', async () => { const jar = request.jar(); const oldValue = meta.config['access-control-allow-origin']; meta.config['access-control-allow-origin'] = 'test.com, mydomain.com'; - request.get(`${nconf.get('url')}/api/search?term=bug`, { - form: { - }, - json: true, + const { response } = await request.get(`${nconf.get('url')}/api/search?term=bug`, { + data: {}, jar: jar, headers: { origin: 'notallowed.com', }, - }, (err, response, body) => { - assert.ifError(err); - assert.equal(response.headers['access-control-allow-origin'], undefined); - meta.config['access-control-allow-origin'] = oldValue; - done(err); }); + assert.equal(response.headers['access-control-allow-origin'], undefined); + meta.config['access-control-allow-origin'] = oldValue; }); - it('should set proper Access-Control-Allow-Origin header', (done) => { + it('should set proper Access-Control-Allow-Origin header', async () => { const jar = request.jar(); const oldValue = meta.config['access-control-allow-origin-regex']; meta.config['access-control-allow-origin-regex'] = 'match\\.this\\..+\\.domain.com, mydomain\\.com'; - request.get(`${nconf.get('url')}/api/search?term=bug`, { - form: { - }, - json: true, + const { response } = await request.get(`${nconf.get('url')}/api/search?term=bug`, { jar: jar, headers: { origin: 'match.this.anything123.domain.com', }, - }, (err, response, body) => { - assert.ifError(err); - assert.equal(response.headers['access-control-allow-origin'], 'match.this.anything123.domain.com'); - meta.config['access-control-allow-origin-regex'] = oldValue; - done(err); }); + + assert.equal(response.headers['access-control-allow-origin'], 'match.this.anything123.domain.com'); + meta.config['access-control-allow-origin-regex'] = oldValue; }); - it('Access-Control-Allow-Origin header should be empty if origin does not match', (done) => { + it('Access-Control-Allow-Origin header should be empty if origin does not match', async () => { const jar = request.jar(); const oldValue = meta.config['access-control-allow-origin-regex']; meta.config['access-control-allow-origin-regex'] = 'match\\.this\\..+\\.domain.com, mydomain\\.com'; - request.get(`${nconf.get('url')}/api/search?term=bug`, { - form: { - }, - json: true, + const { response } = await request.get(`${nconf.get('url')}/api/search?term=bug`, { jar: jar, headers: { origin: 'notallowed.com', }, - }, (err, response, body) => { - assert.ifError(err); - assert.equal(response.headers['access-control-allow-origin'], undefined); - meta.config['access-control-allow-origin-regex'] = oldValue; - done(err); }); + assert.equal(response.headers['access-control-allow-origin'], undefined); + meta.config['access-control-allow-origin-regex'] = oldValue; }); - it('should not error with invalid regexp', (done) => { + it('should not error with invalid regexp', async () => { const jar = request.jar(); const oldValue = meta.config['access-control-allow-origin-regex']; meta.config['access-control-allow-origin-regex'] = '[match\\.this\\..+\\.domain.com, mydomain\\.com'; - request.get(`${nconf.get('url')}/api/search?term=bug`, { - form: { - }, - json: true, + const { response } = await request.get(`${nconf.get('url')}/api/search?term=bug`, { jar: jar, headers: { origin: 'mydomain.com', }, - }, (err, response, body) => { - assert.ifError(err); - assert.equal(response.headers['access-control-allow-origin'], 'mydomain.com'); - meta.config['access-control-allow-origin-regex'] = oldValue; - done(err); }); + assert.equal(response.headers['access-control-allow-origin'], 'mydomain.com'); + meta.config['access-control-allow-origin-regex'] = oldValue; }); }); diff --git a/test/middleware.js b/test/middleware.js index 818ef371cd..5941488d94 100644 --- a/test/middleware.js +++ b/test/middleware.js @@ -2,13 +2,13 @@ const assert = require('assert'); const nconf = require('nconf'); -const request = require('request-promise-native'); + const db = require('./mocks/databasemock'); const user = require('../src/user'); const groups = require('../src/groups'); const utils = require('../src/utils'); - +const request = require('../src/request'); const helpers = require('./helpers'); describe('Middlewares', () => { @@ -116,81 +116,61 @@ describe('Middlewares', () => { }); it('should be absent on non-existent routes, for guests', async () => { - const res = await request(`${nconf.get('url')}/${utils.generateUUID()}`, { - simple: false, - resolveWithFullResponse: true, - }); + const { response } = await request.get(`${nconf.get('url')}/${utils.generateUUID()}`); - assert.strictEqual(res.statusCode, 404); - assert(!Object.keys(res.headers).includes('cache-control')); + assert.strictEqual(response.statusCode, 404); + assert(!Object.keys(response.headers).includes('cache-control')); }); it('should be set to "private" on non-existent routes, for logged in users', async () => { - const res = await request(`${nconf.get('url')}/${utils.generateUUID()}`, { - simple: false, - resolveWithFullResponse: true, + const { response } = await request.get(`${nconf.get('url')}/${utils.generateUUID()}`, { jar, + headers: { + accept: 'text/html', + }, }); - assert.strictEqual(res.statusCode, 404); - assert(Object.keys(res.headers).includes('cache-control')); - assert.strictEqual(res.headers['cache-control'], 'private'); + assert.strictEqual(response.statusCode, 404); + assert(Object.keys(response.headers).includes('cache-control')); + assert.strictEqual(response.headers['cache-control'], 'private'); }); it('should be absent on regular routes, for guests', async () => { - const res = await request(nconf.get('url'), { - simple: false, - resolveWithFullResponse: true, - }); + const { response } = await request.get(nconf.get('url')); - assert.strictEqual(res.statusCode, 200); - assert(!Object.keys(res.headers).includes('cache-control')); + assert.strictEqual(response.statusCode, 200); + assert(!Object.keys(response.headers).includes('cache-control')); }); it('should be absent on api routes, for guests', async () => { - const res = await request(`${nconf.get('url')}/api`, { - simple: false, - resolveWithFullResponse: true, - }); + const { response } = await request.get(`${nconf.get('url')}/api`); - assert.strictEqual(res.statusCode, 200); - assert(!Object.keys(res.headers).includes('cache-control')); + assert.strictEqual(response.statusCode, 200); + assert(!Object.keys(response.headers).includes('cache-control')); }); it('should be set to "private" on regular routes, for logged-in users', async () => { - const res = await request(nconf.get('url'), { - simple: false, - resolveWithFullResponse: true, - jar, - }); + const { response } = await request.get(nconf.get('url'), { jar }); - assert.strictEqual(res.statusCode, 200); - assert(Object.keys(res.headers).includes('cache-control')); - assert.strictEqual(res.headers['cache-control'], 'private'); + assert.strictEqual(response.statusCode, 200); + assert(Object.keys(response.headers).includes('cache-control')); + assert.strictEqual(response.headers['cache-control'], 'private'); }); it('should be set to "private" on api routes, for logged-in users', async () => { - const res = await request(`${nconf.get('url')}/api`, { - simple: false, - resolveWithFullResponse: true, - jar, - }); + const { response } = await request.get(`${nconf.get('url')}/api`, { jar }); - assert.strictEqual(res.statusCode, 200); - assert(Object.keys(res.headers).includes('cache-control')); - assert.strictEqual(res.headers['cache-control'], 'private'); + assert.strictEqual(response.statusCode, 200); + assert(Object.keys(response.headers).includes('cache-control')); + assert.strictEqual(response.headers['cache-control'], 'private'); }); it('should be set to "private" on apiv3 routes, for logged-in users', async () => { - const res = await request(`${nconf.get('url')}/api/v3/users/${uid}`, { - simple: false, - resolveWithFullResponse: true, - jar, - }); + const { response } = await request.get(`${nconf.get('url')}/api/v3/users/${uid}`, { jar }); - assert.strictEqual(res.statusCode, 200); - assert(Object.keys(res.headers).includes('cache-control')); - assert.strictEqual(res.headers['cache-control'], 'private'); + assert.strictEqual(response.statusCode, 200); + assert(Object.keys(response.headers).includes('cache-control')); + assert.strictEqual(response.headers['cache-control'], 'private'); }); }); }); diff --git a/test/plugins.js b/test/plugins.js index 2ffbf604c3..1260f8fd71 100644 --- a/test/plugins.js +++ b/test/plugins.js @@ -3,11 +3,12 @@ const assert = require('assert'); const path = require('path'); const nconf = require('nconf'); -const request = require('request'); + const fs = require('fs'); const db = require('./mocks/databasemock'); const plugins = require('../src/plugins'); +const request = require('../src/request'); describe('Plugins', () => { it('should load plugin data', (done) => { @@ -290,33 +291,24 @@ describe('Plugins', () => { }); describe('static assets', () => { - it('should 404 if resource does not exist', (done) => { - request.get(`${nconf.get('url')}/plugins/doesnotexist/should404.tpl`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - assert(body); - done(); - }); + it('should 404 if resource does not exist', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/plugins/doesnotexist/should404.tpl`); + assert.equal(response.statusCode, 404); + assert(body); }); - it('should 404 if resource does not exist', (done) => { + it('should 404 if resource does not exist', async () => { const url = `${nconf.get('url')}/plugins/nodebb-plugin-dbsearch/dbsearch/templates/admin/plugins/should404.tpl`; - request.get(url, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 404); - assert(body); - done(); - }); + const { response, body } = await request.get(url); + assert.equal(response.statusCode, 404); + assert(body); }); - it('should get resource', (done) => { + it('should get resource', async () => { const url = `${nconf.get('url')}/assets/templates/admin/plugins/dbsearch.tpl`; - request.get(url, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + const { response, body } = await request.get(url); + assert.equal(response.statusCode, 200); + assert(body); }); }); @@ -371,7 +363,6 @@ describe('Plugins', () => { assert.ifError(err); assert(Array.isArray(data)); data.forEach((pluginData) => { - console.log(pluginData); assert(activePlugins.includes(pluginData)); }); done(); diff --git a/test/posts.js b/test/posts.js index 1289f56eea..e52b5cdf23 100644 --- a/test/posts.js +++ b/test/posts.js @@ -2,8 +2,7 @@ const assert = require('assert'); -const async = require('async'); -const request = require('request-promise-native'); + const nconf = require('nconf'); const path = require('path'); const util = require('util'); @@ -24,6 +23,7 @@ const meta = require('../src/meta'); const file = require('../src/file'); const helpers = require('./helpers'); const utils = require('../src/utils'); +const request = require('../src/request'); describe('Post\'s', () => { let voterUid; @@ -33,52 +33,26 @@ describe('Post\'s', () => { let topicData; let cid; - before((done) => { - async.series({ - voterUid: function (next) { - user.create({ username: 'upvoter' }, next); - }, - voteeUid: function (next) { - user.create({ username: 'upvotee' }, next); - }, - globalModUid: function (next) { - user.create({ username: 'globalmod', password: 'globalmodpwd' }, next); - }, - category: function (next) { - categories.create({ - name: 'Test Category', - description: 'Test category created by testing script', - }, next); - }, - }, (err, results) => { - if (err) { - return done(err); - } + before(async () => { + voterUid = await user.create({ username: 'upvoter' }); + voteeUid = await user.create({ username: 'upvotee' }); + globalModUid = await user.create({ username: 'globalmod', password: 'globalmodpwd' }); + ({ cid } = await categories.create({ + name: 'Test Category', + description: 'Test category created by testing script', + })); - voterUid = results.voterUid; - voteeUid = results.voteeUid; - globalModUid = results.globalModUid; - cid = results.category.cid; - - topics.post({ - uid: results.voteeUid, - cid: results.category.cid, - title: 'Test Topic Title', - content: 'The content of test topic', - }, (err, data) => { - if (err) { - return done(err); - } - postData = data.postData; - topicData = data.topicData; - - groups.join('Global Moderators', globalModUid, done); - }); - }); + ({ topicData, postData } = await topics.post({ + uid: voteeUid, + cid: cid, + title: 'Test Topic Title', + content: 'The content of test topic', + })); + await groups.join('Global Moderators', globalModUid); }); it('should update category teaser properly', async () => { - const getCategoriesAsync = async () => await request(`${nconf.get('url')}/api/categories`, { json: true }); + const getCategoriesAsync = async () => (await request.get(`${nconf.get('url')}/api/categories`, { })).body; const postResult = await topics.post({ uid: globalModUid, cid: cid, title: 'topic title', content: '123456789' }); let data = await getCategoriesAsync(); @@ -372,24 +346,14 @@ describe('Post\'s', () => { assert.strictEqual(isDeleted, 1); }); - it('should not see post content if global mod does not have posts:view_deleted privilege', (done) => { - async.waterfall([ - function (next) { - user.create({ username: 'global mod', password: '123456' }, next); - }, - function (uid, next) { - groups.join('Global Moderators', uid, next); - }, - function (next) { - privileges.categories.rescind(['groups:posts:view_deleted'], cid, 'Global Moderators', next); - }, - async () => { - const { jar } = await helpers.loginUser('global mod', '123456'); - const { posts } = await request(`${nconf.get('url')}/api/topic/${tid}`, { jar, json: true }); - assert.equal(posts[1].content, '[[topic:post-is-deleted]]'); - await privileges.categories.give(['groups:posts:view_deleted'], cid, 'Global Moderators'); - }, - ], done); + it('should not see post content if global mod does not have posts:view_deleted privilege', async () => { + const uid = await user.create({ username: 'global mod', password: '123456' }); + await groups.join('Global Moderators', uid); + await privileges.categories.rescind(['groups:posts:view_deleted'], cid, 'Global Moderators'); + const { jar } = await helpers.loginUser('global mod', '123456'); + const { body } = await request.get(`${nconf.get('url')}/api/topic/${tid}`, { jar }); + assert.equal(body.posts[1].content, '[[topic:post-is-deleted]]'); + await privileges.categories.give(['groups:posts:view_deleted'], cid, 'Global Moderators'); }); it('should restore a post', async () => { @@ -1013,7 +977,8 @@ describe('Post\'s', () => { it('should load queued posts', async () => { ({ jar } = await helpers.loginUser('globalmod', 'globalmodpwd')); - const { posts } = await request(`${nconf.get('url')}/api/post-queue`, { jar: jar, json: true }); + const { body } = await request.get(`${nconf.get('url')}/api/post-queue`, { jar }); + const { posts } = body; assert.equal(posts[0].type, 'topic'); assert.equal(posts[0].data.content, 'queued topic content'); assert.equal(posts[1].type, 'reply'); @@ -1029,21 +994,24 @@ describe('Post\'s', () => { it('should edit post in queue', async () => { await socketPosts.editQueuedContent({ uid: globalModUid }, { id: queueId, content: 'newContent' }); - const { posts } = await request(`${nconf.get('url')}/api/post-queue`, { jar: jar, json: true }); + const { body } = await request.get(`${nconf.get('url')}/api/post-queue`, { jar }); + const { posts } = body; assert.equal(posts[1].type, 'reply'); assert.equal(posts[1].data.content, 'newContent'); }); it('should edit topic title in queue', async () => { await socketPosts.editQueuedContent({ uid: globalModUid }, { id: topicQueueId, title: 'new topic title' }); - const { posts } = await request(`${nconf.get('url')}/api/post-queue`, { jar: jar, json: true }); + const { body } = await request.get(`${nconf.get('url')}/api/post-queue`, { jar }); + const { posts } = body; assert.equal(posts[0].type, 'topic'); assert.equal(posts[0].data.title, 'new topic title'); }); it('should edit topic category in queue', async () => { await socketPosts.editQueuedContent({ uid: globalModUid }, { id: topicQueueId, cid: 2 }); - const { posts } = await request(`${nconf.get('url')}/api/post-queue`, { jar: jar, json: true }); + const { body } = await request.get(`${nconf.get('url')}/api/post-queue`, { jar }); + const { posts } = body; assert.equal(posts[0].type, 'topic'); assert.equal(posts[0].data.cid, 2); await socketPosts.editQueuedContent({ uid: globalModUid }, { id: topicQueueId, cid: cid }); @@ -1063,20 +1031,10 @@ describe('Post\'s', () => { }); }); - it('should accept queued posts and submit', (done) => { - let ids; - async.waterfall([ - function (next) { - db.getSortedSetRange('post:queue', 0, -1, next); - }, - function (_ids, next) { - ids = _ids; - socketPosts.accept({ uid: globalModUid }, { id: ids[0] }, next); - }, - function (next) { - socketPosts.accept({ uid: globalModUid }, { id: ids[1] }, next); - }, - ], done); + it('should accept queued posts and submit', async () => { + const ids = await db.getSortedSetRange('post:queue', 0, -1); + await socketPosts.accept({ uid: globalModUid }, { id: ids[0] }); + await socketPosts.accept({ uid: globalModUid }, { id: ids[1] }); }); it('should not crash if id does not exist', (done) => { diff --git a/test/posts/uploads.js b/test/posts/uploads.js index 875397aaea..9471bca2f7 100644 --- a/test/posts/uploads.js +++ b/test/posts/uploads.js @@ -6,7 +6,6 @@ const path = require('path'); const os = require('os'); const nconf = require('nconf'); -const async = require('async'); const crypto = require('crypto'); const db = require('../mocks/databasemock'); @@ -75,24 +74,15 @@ describe('upload methods', () => { }); }); - it('should remove an image if it is edited out of the post', (done) => { - async.series([ - function (next) { - posts.edit({ - pid: pid, - uid, - content: 'here is an image [alt text](/assets/uploads/files/abracadabra.png)... AND NO MORE!', - }, next); - }, - async.apply(posts.uploads.sync, pid), - ], (err) => { - assert.ifError(err); - db.sortedSetCard(`post:${pid}:uploads`, (err, length) => { - assert.ifError(err); - assert.strictEqual(1, length); - done(); - }); + it('should remove an image if it is edited out of the post', async () => { + await posts.edit({ + pid: pid, + uid, + content: 'here is an image [alt text](/assets/uploads/files/abracadabra.png)... AND NO MORE!', }); + await posts.uploads.sync(pid); + const length = await db.sortedSetCard(`post:${pid}:uploads`); + assert.strictEqual(1, length); }); }); @@ -127,85 +117,52 @@ describe('upload methods', () => { }); describe('.associate()', () => { - it('should add an image to the post\'s maintained list of uploads', (done) => { - async.waterfall([ - async.apply(posts.uploads.associate, pid, 'files/whoa.gif'), - async.apply(posts.uploads.list, pid), - ], (err, uploads) => { - assert.ifError(err); - assert.strictEqual(2, uploads.length); - assert.strictEqual(true, uploads.includes('files/whoa.gif')); - done(); - }); + it('should add an image to the post\'s maintained list of uploads', async () => { + await posts.uploads.associate(pid, 'files/whoa.gif'); + const uploads = await posts.uploads.list(pid); + assert.strictEqual(2, uploads.length); + assert.strictEqual(true, uploads.includes('files/whoa.gif')); }); - it('should allow arrays to be passed in', (done) => { - async.waterfall([ - async.apply(posts.uploads.associate, pid, ['files/amazeballs.jpg', 'files/wut.txt']), - async.apply(posts.uploads.list, pid), - ], (err, uploads) => { - assert.ifError(err); - assert.strictEqual(4, uploads.length); - assert.strictEqual(true, uploads.includes('files/amazeballs.jpg')); - assert.strictEqual(true, uploads.includes('files/wut.txt')); - done(); - }); + it('should allow arrays to be passed in', async () => { + await posts.uploads.associate(pid, ['files/amazeballs.jpg', 'files/wut.txt']); + const uploads = await posts.uploads.list(pid); + assert.strictEqual(4, uploads.length); + assert.strictEqual(true, uploads.includes('files/amazeballs.jpg')); + assert.strictEqual(true, uploads.includes('files/wut.txt')); }); - it('should save a reverse association of md5sum to pid', (done) => { + it('should save a reverse association of md5sum to pid', async () => { const md5 = filename => crypto.createHash('md5').update(filename).digest('hex'); - - async.waterfall([ - async.apply(posts.uploads.associate, pid, ['files/test.bmp']), - function (next) { - db.getSortedSetRange(`upload:${md5('files/test.bmp')}:pids`, 0, -1, next); - }, - ], (err, pids) => { - assert.ifError(err); - assert.strictEqual(true, Array.isArray(pids)); - assert.strictEqual(true, pids.length > 0); - assert.equal(pid, pids[0]); - done(); - }); + await posts.uploads.associate(pid, ['files/test.bmp']); + const pids = await db.getSortedSetRange(`upload:${md5('files/test.bmp')}:pids`, 0, -1); + assert.strictEqual(true, Array.isArray(pids)); + assert.strictEqual(true, pids.length > 0); + assert.equal(pid, pids[0]); }); - it('should not associate a file that does not exist on the local disk', (done) => { - async.waterfall([ - async.apply(posts.uploads.associate, pid, ['files/nonexistant.xls']), - async.apply(posts.uploads.list, pid), - ], (err, uploads) => { - assert.ifError(err); - assert.strictEqual(uploads.length, 5); - assert.strictEqual(false, uploads.includes('files/nonexistant.xls')); - done(); - }); + it('should not associate a file that does not exist on the local disk', async () => { + await posts.uploads.associate(pid, ['files/nonexistant.xls']); + const uploads = await posts.uploads.list(pid); + assert.strictEqual(uploads.length, 5); + assert.strictEqual(false, uploads.includes('files/nonexistant.xls')); }); }); describe('.dissociate()', () => { - it('should remove an image from the post\'s maintained list of uploads', (done) => { - async.waterfall([ - async.apply(posts.uploads.dissociate, pid, 'files/whoa.gif'), - async.apply(posts.uploads.list, pid), - ], (err, uploads) => { - assert.ifError(err); - assert.strictEqual(4, uploads.length); - assert.strictEqual(false, uploads.includes('files/whoa.gif')); - done(); - }); + it('should remove an image from the post\'s maintained list of uploads', async () => { + await posts.uploads.dissociate(pid, 'files/whoa.gif'); + const uploads = await posts.uploads.list(pid); + assert.strictEqual(4, uploads.length); + assert.strictEqual(false, uploads.includes('files/whoa.gif')); }); - it('should allow arrays to be passed in', (done) => { - async.waterfall([ - async.apply(posts.uploads.dissociate, pid, ['files/amazeballs.jpg', 'files/wut.txt']), - async.apply(posts.uploads.list, pid), - ], (err, uploads) => { - assert.ifError(err); - assert.strictEqual(2, uploads.length); - assert.strictEqual(false, uploads.includes('files/amazeballs.jpg')); - assert.strictEqual(false, uploads.includes('files/wut.txt')); - done(); - }); + it('should allow arrays to be passed in', async () => { + await posts.uploads.dissociate(pid, ['files/amazeballs.jpg', 'files/wut.txt']); + const uploads = await posts.uploads.list(pid); + assert.strictEqual(2, uploads.length); + assert.strictEqual(false, uploads.includes('files/amazeballs.jpg')); + assert.strictEqual(false, uploads.includes('files/wut.txt')); }); it('should remove the image\'s user association, if present', async () => { @@ -397,21 +354,14 @@ describe('post uploads management', () => { }); }); - it('should automatically sync uploads on post edit', (done) => { - async.waterfall([ - async.apply(posts.edit, { - pid: reply.pid, - uid, - content: 'no uploads', - }), - function (postData, next) { - posts.uploads.list(reply.pid, next); - }, - ], (err, uploads) => { - assert.ifError(err); - assert.strictEqual(true, Array.isArray(uploads)); - assert.strictEqual(0, uploads.length); - done(); + it('should automatically sync uploads on post edit', async () => { + await posts.edit({ + pid: reply.pid, + uid, + content: 'no uploads', }); + const uploads = await posts.uploads.list(reply.pid); + assert.strictEqual(true, Array.isArray(uploads)); + assert.strictEqual(0, uploads.length); }); }); diff --git a/test/search.js b/test/search.js index 14179ad5cd..f0e285cb9d 100644 --- a/test/search.js +++ b/test/search.js @@ -2,8 +2,6 @@ const assert = require('assert'); -const async = require('async'); -const request = require('request'); const nconf = require('nconf'); const db = require('./mocks/databasemock'); @@ -12,6 +10,7 @@ const categories = require('../src/categories'); const user = require('../src/user'); const search = require('../src/search'); const privileges = require('../src/privileges'); +const request = require('../src/request'); describe('Search', () => { let phoebeUid; @@ -26,103 +25,60 @@ describe('Search', () => { let cid2; let cid3; - before((done) => { - async.waterfall([ - function (next) { - async.series({ - phoebe: function (next) { - user.create({ username: 'phoebe' }, next); - }, - ginger: function (next) { - user.create({ username: 'ginger' }, next); - }, - category1: function (next) { - categories.create({ - name: 'Test Category', - description: 'Test category created by testing script', - }, next); - }, - category2: function (next) { - categories.create({ - name: 'Test Category', - description: 'Test category created by testing script', - }, next); - }, - }, next); - }, - function (results, next) { - phoebeUid = results.phoebe; - gingerUid = results.ginger; - cid1 = results.category1.cid; - cid2 = results.category2.cid; + before(async () => { + phoebeUid = await user.create({ username: 'phoebe' }); + gingerUid = await user.create({ username: 'ginger' }); + cid1 = (await categories.create({ + name: 'Test Category', + description: 'Test category created by testing script', + })).cid; - async.waterfall([ - function (next) { - categories.create({ - name: 'Child Test Category', - description: 'Test category created by testing script', - parentCid: cid2, - }, next); - }, - function (category, next) { - cid3 = category.cid; - topics.post({ - uid: phoebeUid, - cid: cid1, - title: 'nodebb mongodb bugs', - content: 'avocado cucumber apple orange fox', - tags: ['nodebb', 'bug', 'plugin', 'nodebb-plugin', 'jquery'], - }, next); - }, - function (results, next) { - topic1Data = results.topicData; - post1Data = results.postData; + cid2 = (await categories.create({ + name: 'Test Category', + description: 'Test category created by testing script', + })).cid; - topics.post({ - uid: gingerUid, - cid: cid2, - title: 'java mongodb redis', - content: 'avocado cucumber carrot armadillo', - tags: ['nodebb', 'bug', 'plugin', 'nodebb-plugin', 'javascript'], - }, next); - }, - function (results, next) { - topic2Data = results.topicData; - post2Data = results.postData; - topics.reply({ - uid: phoebeUid, - content: 'reply post apple', - tid: topic2Data.tid, - }, next); - }, - function (_post3Data, next) { - post3Data = _post3Data; - setTimeout(next, 500); - }, - ], next); - }, - ], done); + cid3 = (await categories.create({ + name: 'Child Test Category', + description: 'Test category created by testing script', + parentCid: cid2, + })).cid; + + ({ topicData: topic1Data, postData: post1Data } = await topics.post({ + uid: phoebeUid, + cid: cid1, + title: 'nodebb mongodb bugs', + content: 'avocado cucumber apple orange fox', + tags: ['nodebb', 'bug', 'plugin', 'nodebb-plugin', 'jquery'], + })); + + ({ topicData: topic2Data, postData: post2Data } = await topics.post({ + uid: gingerUid, + cid: cid2, + title: 'java mongodb redis', + content: 'avocado cucumber carrot armadillo', + tags: ['nodebb', 'bug', 'plugin', 'nodebb-plugin', 'javascript'], + })); + post3Data = await topics.reply({ + uid: phoebeUid, + content: 'reply post apple', + tid: topic2Data.tid, + }); }); - it('should search term in titles and posts', (done) => { + it('should search term in titles and posts', async () => { const meta = require('../src/meta'); const qs = `/api/search?term=cucumber&in=titlesposts&categories[]=${cid1}&by=phoebe&replies=1&repliesFilter=atleast&sortBy=timestamp&sortDirection=desc&showAs=posts`; - privileges.global.give(['groups:search:content'], 'guests', (err) => { - assert.ifError(err); - request({ - url: nconf.get('url') + qs, - json: true, - }, (err, response, body) => { - assert.ifError(err); - assert(body); - assert.equal(body.matchCount, 1); - assert.equal(body.posts.length, 1); - assert.equal(body.posts[0].pid, post1Data.pid); - assert.equal(body.posts[0].uid, phoebeUid); + await privileges.global.give(['groups:search:content'], 'guests'); - privileges.global.rescind(['groups:search:content'], 'guests', done); - }); - }); + const { body } = await request.get(nconf.get('url') + qs); + assert(body); + assert.equal(body.matchCount, 1); + assert.equal(body.posts.length, 1); + assert.equal(body.posts[0].pid, post1Data.pid); + assert.equal(body.posts[0].uid, phoebeUid); + + await privileges.global.rescind(['groups:search:content'], 'guests'); }); it('should search for a user', (done) => { @@ -225,69 +181,47 @@ describe('Search', () => { }); }); - it('should search child categories', (done) => { - async.waterfall([ - function (next) { - topics.post({ - uid: gingerUid, - cid: cid3, - title: 'child category topic', - content: 'avocado cucumber carrot armadillo', - }, next); - }, - function (result, next) { - search.search({ - query: 'avocado', - searchIn: 'titlesposts', - categories: [cid2], - searchChildren: true, - sortBy: 'topic.timestamp', - sortDirection: 'desc', - }, next); - }, - function (result, next) { - assert(result.posts.length, 2); - assert(result.posts[0].topic.title === 'child category topic'); - assert(result.posts[1].topic.title === 'java mongodb redis'); - next(); - }, - ], done); + it('should search child categories', async () => { + await topics.post({ + uid: gingerUid, + cid: cid3, + title: 'child category topic', + content: 'avocado cucumber carrot armadillo', + }); + const result = await search.search({ + query: 'avocado', + searchIn: 'titlesposts', + categories: [cid2], + searchChildren: true, + sortBy: 'topic.timestamp', + sortDirection: 'desc', + }); + assert(result.posts.length, 2); + assert(result.posts[0].topic.title === 'child category topic'); + assert(result.posts[1].topic.title === 'java mongodb redis'); }); - it('should return json search data with no categories', (done) => { + it('should return json search data with no categories', async () => { const qs = '/api/search?term=cucumber&in=titlesposts&searchOnly=1'; - privileges.global.give(['groups:search:content'], 'guests', (err) => { - assert.ifError(err); - request({ - url: nconf.get('url') + qs, - json: true, - }, (err, response, body) => { - assert.ifError(err); - assert(body); - assert(body.hasOwnProperty('matchCount')); - assert(body.hasOwnProperty('pagination')); - assert(body.hasOwnProperty('pageCount')); - assert(body.hasOwnProperty('posts')); - assert(!body.hasOwnProperty('categories')); + await privileges.global.give(['groups:search:content'], 'guests'); - privileges.global.rescind(['groups:search:content'], 'guests', done); - }); - }); + const { body } = await request.get(nconf.get('url') + qs); + assert(body); + assert(body.hasOwnProperty('matchCount')); + assert(body.hasOwnProperty('pagination')); + assert(body.hasOwnProperty('pageCount')); + assert(body.hasOwnProperty('posts')); + assert(!body.hasOwnProperty('categories')); + + await privileges.global.rescind(['groups:search:content'], 'guests'); }); - it('should not crash without a search term', (done) => { + it('should not crash without a search term', async () => { const qs = '/api/search'; - privileges.global.give(['groups:search:content'], 'guests', (err) => { - assert.ifError(err); - request({ - url: nconf.get('url') + qs, - json: true, - }, (err, response, body) => { - assert.ifError(err); - assert(body); - assert.strictEqual(response.statusCode, 200); - privileges.global.rescind(['groups:search:content'], 'guests', done); - }); - }); + await privileges.global.give(['groups:search:content'], 'guests'); + const { response, body } = await request.get(nconf.get('url') + qs); + assert(body); + assert.strictEqual(response.statusCode, 200); + await privileges.global.rescind(['groups:search:content'], 'guests'); }); }); diff --git a/test/socket.io.js b/test/socket.io.js index f9b8b677df..c2a0a68ad6 100644 --- a/test/socket.io.js +++ b/test/socket.io.js @@ -9,11 +9,7 @@ const util = require('util'); const sleep = util.promisify(setTimeout); const assert = require('assert'); -const async = require('async'); const nconf = require('nconf'); -const request = require('request'); - -const cookies = request.jar(); const db = require('./mocks/databasemock'); const user = require('../src/user'); @@ -52,35 +48,11 @@ describe('socket.io', () => { }); - it('should connect and auth properly', (done) => { - request.get({ - url: `${nconf.get('url')}/api/config`, - jar: cookies, - json: true, - }, (err, res, body) => { - assert.ifError(err); - - request.post(`${nconf.get('url')}/login`, { - jar: cookies, - form: { - username: 'admin', - password: 'adminpwd', - }, - headers: { - 'x-csrf-token': body.csrf_token, - }, - json: true, - }, (err, res) => { - assert.ifError(err); - - helpers.connectSocketIO(res, body.csrf_token, (err, _io) => { - io = _io; - assert.ifError(err); - - done(); - }); - }); - }); + it('should connect and auth properly', async () => { + const { response, csrf_token } = await helpers.loginUser('admin', 'adminpwd'); + io = await helpers.connectSocketIO(response, csrf_token); + assert(io); + assert(io.emit); }); it('should return error for unknown event', (done) => { @@ -459,20 +431,38 @@ describe('socket.io', () => { }); }); - it('should toggle plugin install', function (done) { - this.timeout(0); - const oldValue = process.env.NODE_ENV; - process.env.NODE_ENV = 'development'; - socketAdmin.plugins.toggleInstall({ - uid: adminUid, - }, { - id: 'nodebb-plugin-location-to-map', - version: 'latest', - }, (err, data) => { - assert.ifError(err); - assert.equal(data.name, 'nodebb-plugin-location-to-map'); - process.env.NODE_ENV = oldValue; - done(); + describe('install/upgrade plugin', () => { + it('should toggle plugin install', function (done) { + this.timeout(0); + const oldValue = process.env.NODE_ENV; + process.env.NODE_ENV = 'development'; + socketAdmin.plugins.toggleInstall({ + uid: adminUid, + }, { + id: 'nodebb-plugin-location-to-map', + version: 'latest', + }, (err, data) => { + assert.ifError(err); + assert.equal(data.name, 'nodebb-plugin-location-to-map'); + process.env.NODE_ENV = oldValue; + done(); + }); + }); + + it('should upgrade plugin', function (done) { + this.timeout(0); + const oldValue = process.env.NODE_ENV; + process.env.NODE_ENV = 'development'; + socketAdmin.plugins.upgrade({ + uid: adminUid, + }, { + id: 'nodebb-plugin-location-to-map', + version: 'latest', + }, (err) => { + assert.ifError(err); + process.env.NODE_ENV = oldValue; + done(); + }); }); }); @@ -501,22 +491,6 @@ describe('socket.io', () => { }); }); - it('should upgrade plugin', function (done) { - this.timeout(0); - const oldValue = process.env.NODE_ENV; - process.env.NODE_ENV = 'development'; - socketAdmin.plugins.upgrade({ - uid: adminUid, - }, { - id: 'nodebb-plugin-location-to-map', - version: 'latest', - }, (err) => { - assert.ifError(err); - process.env.NODE_ENV = oldValue; - done(); - }); - }); - it('should error with invalid data', (done) => { socketAdmin.widgets.set({ uid: adminUid }, null, (err) => { assert.equal(err.message, '[[error:invalid-data]]'); @@ -709,60 +683,43 @@ describe('socket.io', () => { assert(pwExpiry > then && pwExpiry < Date.now()); }); - it('should not error on valid email', (done) => { - socketUser.reset.send({ uid: 0 }, 'regular@test.com', (err) => { - assert.ifError(err); + it('should not error on valid email', async () => { + await socketUser.reset.send({ uid: 0 }, 'regular@test.com'); + const [count, eventsData] = await Promise.all([ + db.sortedSetCount('reset:issueDate', 0, Date.now()), + events.getEvents('', 0, 0), + ]); + assert.strictEqual(count, 2); - async.parallel({ - count: async.apply(db.sortedSetCount.bind(db), 'reset:issueDate', 0, Date.now()), - event: async.apply(events.getEvents, '', 0, 0), - }, (err, data) => { - assert.ifError(err); - assert.strictEqual(data.count, 2); - - // Event validity - assert.strictEqual(data.event.length, 1); - const event = data.event[0]; - assert.strictEqual(event.type, 'password-reset'); - assert.strictEqual(event.text, '[[success:success]]'); - - done(); - }); - }); + // Event validity + assert.strictEqual(eventsData.length, 1); + const event = eventsData[0]; + assert.strictEqual(event.type, 'password-reset'); + assert.strictEqual(event.text, '[[success:success]]'); }); - it('should not generate code if rate limited', (done) => { - socketUser.reset.send({ uid: 0 }, 'regular@test.com', (err) => { - assert(err); + it('should not generate code if rate limited', async () => { + await assert.rejects( + socketUser.reset.send({ uid: 0 }, 'regular@test.com'), + { message: '[[error:reset-rate-limited]]' }, + ); + const [count, eventsData] = await Promise.all([ + db.sortedSetCount('reset:issueDate', 0, Date.now()), + events.getEvents('', 0, 0), + ]); + assert.strictEqual(count, 2); - async.parallel({ - count: async.apply(db.sortedSetCount.bind(db), 'reset:issueDate', 0, Date.now()), - event: async.apply(events.getEvents, '', 0, 0), - }, (err, data) => { - assert.ifError(err); - assert.strictEqual(data.count, 2); - - // Event validity - assert.strictEqual(data.event.length, 1); - const event = data.event[0]; - assert.strictEqual(event.type, 'password-reset'); - assert.strictEqual(event.text, '[[error:reset-rate-limited]]'); - - done(); - }); - }); + // Event validity + assert.strictEqual(eventsData.length, 1); + const event = eventsData[0]; + assert.strictEqual(event.type, 'password-reset'); + assert.strictEqual(event.text, '[[error:reset-rate-limited]]'); }); - it('should not error on invalid email (but not generate reset code)', (done) => { - socketUser.reset.send({ uid: 0 }, 'irregular@test.com', (err) => { - assert.ifError(err); - - db.sortedSetCount('reset:issueDate', 0, Date.now(), (err, count) => { - assert.ifError(err); - assert.strictEqual(count, 2); - done(); - }); - }); + it('should not error on invalid email (but not generate reset code)', async () => { + await socketUser.reset.send({ uid: 0 }, 'irregular@test.com'); + const count = await db.sortedSetCount('reset:issueDate', 0, Date.now()); + assert.strictEqual(count, 2); }); it('should error on no email', (done) => { diff --git a/test/topics.js b/test/topics.js index f40925103a..8a32e445f5 100644 --- a/test/topics.js +++ b/test/topics.js @@ -1,12 +1,10 @@ 'use strict'; -const async = require('async'); const path = require('path'); const assert = require('assert'); const validator = require('validator'); const mockdate = require('mockdate'); const nconf = require('nconf'); -const request = require('request'); const util = require('util'); const sleep = util.promisify(setTimeout); @@ -22,14 +20,10 @@ const User = require('../src/user'); const groups = require('../src/groups'); const utils = require('../src/utils'); const helpers = require('./helpers'); -const socketPosts = require('../src/socket.io/posts'); const socketTopics = require('../src/socket.io/topics'); const apiTopics = require('../src/api/topics'); const apiPosts = require('../src/api/posts'); - -const requestType = util.promisify((type, url, opts, cb) => { - request[type](url, opts, (err, res, body) => cb(err, { res: res, body: body })); -}); +const request = require('../src/request'); describe('Topic\'s', () => { let topic; @@ -142,8 +136,8 @@ describe('Topic\'s', () => { }); await privileges.categories.give(['groups:topics:create'], categoryObj.cid, 'guests'); await privileges.categories.give(['groups:topics:reply'], categoryObj.cid, 'guests'); - const result = await requestType('post', `${nconf.get('url')}/api/v3/topics`, { - form: { + const result = await request.post(`${nconf.get('url')}/api/v3/topics`, { + data: { title: 'just a title', cid: categoryObj.cid, content: 'content for the main post', @@ -151,9 +145,8 @@ describe('Topic\'s', () => { headers: { 'x-csrf-token': 'invalid', }, - json: true, }); - assert.strictEqual(result.res.statusCode, 403); + assert.strictEqual(result.response.statusCode, 403); assert.strictEqual(result.body, 'Forbidden'); }); @@ -164,13 +157,12 @@ describe('Topic\'s', () => { }); const jar = request.jar(); const result = await helpers.request('post', `/api/v3/topics`, { - form: { + body: { title: 'just a title', cid: categoryObj.cid, content: 'content for the main post', }, jar: jar, - json: true, }); assert.strictEqual(result.body.status.message, 'You do not have enough privileges for this action.'); }); @@ -185,7 +177,7 @@ describe('Topic\'s', () => { const jar = request.jar(); const result = await helpers.request('post', `/api/v3/topics`, { - form: { + body: { title: 'just a title', cid: categoryObj.cid, content: 'content for the main post', @@ -199,11 +191,10 @@ describe('Topic\'s', () => { assert.strictEqual(result.body.response.user.username, '[[global:guest]]'); const replyResult = await helpers.request('post', `/api/v3/topics/${result.body.response.tid}`, { - form: { + body: { content: 'a reply by guest', }, jar: jar, - json: true, }); assert.strictEqual(replyResult.body.response.content, 'a reply by guest'); assert.strictEqual(replyResult.body.response.user.username, '[[global:guest]]'); @@ -219,14 +210,13 @@ describe('Topic\'s', () => { const oldValue = meta.config.allowGuestHandles; meta.config.allowGuestHandles = 1; const result = await helpers.request('post', `/api/v3/topics`, { - form: { + body: { title: 'just a title', cid: categoryObj.cid, content: 'content for the main post', handle: 'guest123', }, jar: request.jar(), - json: true, }); assert.strictEqual(result.body.status.code, 'ok'); @@ -235,12 +225,11 @@ describe('Topic\'s', () => { assert.strictEqual(result.body.response.user.displayname, 'guest123'); const replyResult = await helpers.request('post', `/api/v3/topics/${result.body.response.tid}`, { - form: { + body: { content: 'a reply by guest', handle: 'guest124', }, jar: request.jar(), - json: true, }); assert.strictEqual(replyResult.body.response.content, 'a reply by guest'); assert.strictEqual(replyResult.body.response.user.username, 'guest124'); @@ -651,40 +640,20 @@ describe('Topic\'s', () => { let followerUid; let moveCid; - before((done) => { - async.waterfall([ - function (next) { - topics.post({ - uid: topic.userId, - title: topic.title, - content: topic.content, - cid: topic.categoryId, - }, (err, result) => { - assert.ifError(err); - newTopic = result.topicData; - next(); - }); - }, - function (next) { - User.create({ username: 'topicFollower', password: '123456' }, next); - }, - function (_uid, next) { - followerUid = _uid; - topics.follow(newTopic.tid, _uid, next); - }, - function (next) { - categories.create({ - name: 'Test Category', - description: 'Test category created by testing script', - }, (err, category) => { - if (err) { - return next(err); - } - moveCid = category.cid; - next(); - }); - }, - ], done); + before(async () => { + ({ topicData: newTopic } = await topics.post({ + uid: topic.userId, + title: topic.title, + content: topic.content, + cid: topic.categoryId, + })); + followerUid = await User.create({ username: 'topicFollower', password: '123456' }); + await topics.follow(newTopic.tid, followerUid); + + ({ cid: moveCid } = await categories.create({ + name: 'Test Category', + description: 'Test category created by testing script', + })); }); it('should load topic tools', (done) => { @@ -753,118 +722,68 @@ describe('Topic\'s', () => { }); }); - it('should properly update sets when post is moved', (done) => { - let movedPost; - let previousPost; - let topic2LastReply; - let tid1; - let tid2; + it('should properly update sets when post is moved', async () => { const cid1 = topic.categoryId; - let cid2; - function checkCidSets(post1, post2, callback) { - async.waterfall([ - function (next) { - async.parallel({ - topicData: function (next) { - topics.getTopicsFields([tid1, tid2], ['lastposttime', 'postcount'], next); - }, - scores1: function (next) { - db.sortedSetsScore([ - `cid:${cid1}:tids`, - `cid:${cid1}:tids:lastposttime`, - `cid:${cid1}:tids:posts`, - ], tid1, next); - }, - scores2: function (next) { - db.sortedSetsScore([ - `cid:${cid2}:tids`, - `cid:${cid2}:tids:lastposttime`, - `cid:${cid2}:tids:posts`, - ], tid2, next); - }, - posts1: function (next) { - db.getSortedSetRangeWithScores(`tid:${tid1}:posts`, 0, -1, next); - }, - posts2: function (next) { - db.getSortedSetRangeWithScores(`tid:${tid2}:posts`, 0, -1, next); - }, - }, next); - }, - function (results, next) { - const assertMsg = `${JSON.stringify(results.posts1)}\n${JSON.stringify(results.posts2)}`; - assert.equal(results.topicData[0].postcount, results.scores1[2], assertMsg); - assert.equal(results.topicData[1].postcount, results.scores2[2], assertMsg); - assert.equal(results.topicData[0].lastposttime, post1.timestamp, assertMsg); - assert.equal(results.topicData[1].lastposttime, post2.timestamp, assertMsg); - assert.equal(results.topicData[0].lastposttime, results.scores1[0], assertMsg); - assert.equal(results.topicData[1].lastposttime, results.scores2[0], assertMsg); - assert.equal(results.topicData[0].lastposttime, results.scores1[1], assertMsg); - assert.equal(results.topicData[1].lastposttime, results.scores2[1], assertMsg); + const category = await categories.create({ + name: 'move to this category', + description: 'Test category created by testing script', + }); + const cid2 = category.cid; + const { topicData } = await topics.post({ uid: adminUid, title: 'topic1', content: 'topic 1 mainPost', cid: cid1 }); + const tid1 = topicData.tid; + const previousPost = await topics.reply({ uid: adminUid, content: 'topic 1 reply 1', tid: tid1 }); + const movedPost = await topics.reply({ uid: adminUid, content: 'topic 1 reply 2', tid: tid1 }); - next(); - }, - ], callback); + const { topicData: anotherTopic } = await topics.post({ uid: adminUid, title: 'topic2', content: 'topic 2 mainpost', cid: cid2 }); + const tid2 = anotherTopic.tid; + const topic2LastReply = await topics.reply({ uid: adminUid, content: 'topic 2 reply 1', tid: tid2 }); + + async function checkCidSets(post1, post2) { + const [topicData, scores1, scores2, posts1, posts2] = await Promise.all([ + topics.getTopicsFields([tid1, tid2], ['lastposttime', 'postcount']), + db.sortedSetsScore([ + `cid:${cid1}:tids`, + `cid:${cid1}:tids:lastposttime`, + `cid:${cid1}:tids:posts`, + ], tid1), + db.sortedSetsScore([ + `cid:${cid2}:tids`, + `cid:${cid2}:tids:lastposttime`, + `cid:${cid2}:tids:posts`, + ], tid2), + db.getSortedSetRangeWithScores(`tid:${tid1}:posts`, 0, -1), + db.getSortedSetRangeWithScores(`tid:${tid2}:posts`, 0, -1), + ]); + const assertMsg = `${JSON.stringify(posts1)}\n${JSON.stringify(posts2)}`; + assert.equal(topicData[0].postcount, scores1[2], assertMsg); + assert.equal(topicData[1].postcount, scores2[2], assertMsg); + assert.equal(topicData[0].lastposttime, post1.timestamp, assertMsg); + assert.equal(topicData[1].lastposttime, post2.timestamp, assertMsg); + assert.equal(topicData[0].lastposttime, scores1[0], assertMsg); + assert.equal(topicData[1].lastposttime, scores2[0], assertMsg); + assert.equal(topicData[0].lastposttime, scores1[1], assertMsg); + assert.equal(topicData[1].lastposttime, scores2[1], assertMsg); } - async.waterfall([ - function (next) { - categories.create({ - name: 'move to this category', - description: 'Test category created by testing script', - }, next); - }, - function (category, next) { - cid2 = category.cid; - topics.post({ uid: adminUid, title: 'topic1', content: 'topic 1 mainPost', cid: cid1 }, next); - }, - function (result, next) { - tid1 = result.topicData.tid; - topics.reply({ uid: adminUid, content: 'topic 1 reply 1', tid: tid1 }, next); - }, - function (postData, next) { - previousPost = postData; - topics.reply({ uid: adminUid, content: 'topic 1 reply 2', tid: tid1 }, next); - }, - function (postData, next) { - movedPost = postData; - topics.post({ uid: adminUid, title: 'topic2', content: 'topic 2 mainpost', cid: cid2 }, next); - }, - function (results, next) { - tid2 = results.topicData.tid; - topics.reply({ uid: adminUid, content: 'topic 2 reply 1', tid: tid2 }, next); - }, - function (postData, next) { - topic2LastReply = postData; - checkCidSets(movedPost, postData, next); - }, - function (next) { - db.isMemberOfSortedSets([`cid:${cid1}:pids`, `cid:${cid2}:pids`], movedPost.pid, next); - }, - function (isMember, next) { - assert.deepEqual(isMember, [true, false]); - categories.getCategoriesFields([cid1, cid2], ['post_count'], next); - }, - function (categoryData, next) { - assert.equal(categoryData[0].post_count, 4); - assert.equal(categoryData[1].post_count, 2); - topics.movePostToTopic(1, movedPost.pid, tid2, next); - }, - function (next) { - checkCidSets(previousPost, topic2LastReply, next); - }, - function (next) { - db.isMemberOfSortedSets([`cid:${cid1}:pids`, `cid:${cid2}:pids`], movedPost.pid, next); - }, - function (isMember, next) { - assert.deepEqual(isMember, [false, true]); - categories.getCategoriesFields([cid1, cid2], ['post_count'], next); - }, - function (categoryData, next) { - assert.equal(categoryData[0].post_count, 3); - assert.equal(categoryData[1].post_count, 3); - next(); - }, - ], done); + await checkCidSets(movedPost, topic2LastReply); + + let isMember = await db.isMemberOfSortedSets([`cid:${cid1}:pids`, `cid:${cid2}:pids`], movedPost.pid); + assert.deepEqual(isMember, [true, false]); + + let categoryData = await categories.getCategoriesFields([cid1, cid2], ['post_count']); + assert.equal(categoryData[0].post_count, 4); + assert.equal(categoryData[1].post_count, 2); + + await topics.movePostToTopic(1, movedPost.pid, tid2); + + await checkCidSets(previousPost, topic2LastReply); + + isMember = await db.isMemberOfSortedSets([`cid:${cid1}:pids`, `cid:${cid2}:pids`], movedPost.pid); + assert.deepEqual(isMember, [false, true]); + + categoryData = await categories.getCategoriesFields([cid1, cid2], ['post_count']); + assert.equal(categoryData[0].post_count, 3); + assert.equal(categoryData[1].post_count, 3); }); it('should fail to purge topic if user does not have privilege', async () => { @@ -915,42 +834,22 @@ describe('Topic\'s', () => { let tid1; let tid2; let tid3; - before((done) => { - function createTopic(callback) { - topics.post({ + before(async () => { + async function createTopic() { + return (await topics.post({ uid: topic.userId, title: 'topic for test', content: 'topic content', cid: topic.categoryId, - }, callback); + })).topicData.tid; } - async.series({ - topic1: function (next) { - createTopic(next); - }, - topic2: function (next) { - createTopic(next); - }, - topic3: function (next) { - createTopic(next); - }, - }, (err, results) => { - assert.ifError(err); - tid1 = results.topic1.topicData.tid; - tid2 = results.topic2.topicData.tid; - tid3 = results.topic3.topicData.tid; - async.series([ - function (next) { - topics.tools.pin(tid1, adminUid, next); - }, - function (next) { - // artificial timeout so pin time is different on redis sometimes scores are indentical - setTimeout(() => { - topics.tools.pin(tid2, adminUid, next); - }, 5); - }, - ], done); - }); + tid1 = await createTopic(); + tid2 = await createTopic(); + tid3 = await createTopic(); + await topics.tools.pin(tid1, adminUid); + // artificial timeout so pin time is different on redis sometimes scores are indentical + await sleep(5); + await topics.tools.pin(tid2, adminUid); }); const socketTopics = require('../src/socket.io/topics'); @@ -1009,110 +908,48 @@ describe('Topic\'s', () => { let newTid; let uid; let newTopic; - before((done) => { + before(async () => { uid = topic.userId; - async.waterfall([ - function (done) { - topics.post({ uid: topic.userId, title: 'Topic to be ignored', content: 'Just ignore me, please!', cid: topic.categoryId }, (err, result) => { - if (err) { - return done(err); - } - - newTopic = result.topicData; - newTid = newTopic.tid; - done(); - }); - }, - function (done) { - topics.markUnread(newTid, uid, done); - }, - ], done); + const result = await topics.post({ uid: topic.userId, title: 'Topic to be ignored', content: 'Just ignore me, please!', cid: topic.categoryId }); + newTopic = result.topicData; + newTid = newTopic.tid; + await topics.markUnread(newTid, uid); }); - it('should not appear in the unread list', (done) => { - async.waterfall([ - function (done) { - topics.ignore(newTid, uid, done); - }, - function (done) { - topics.getUnreadTopics({ cid: 0, uid: uid, start: 0, stop: -1, filter: '' }, done); - }, - function (results, done) { - const { topics } = results; - const tids = topics.map(topic => topic.tid); - assert.equal(tids.indexOf(newTid), -1, 'The topic appeared in the unread list.'); - done(); - }, - ], done); + it('should not appear in the unread list', async () => { + await topics.ignore(newTid, uid); + const { topics: topicData } = await topics.getUnreadTopics({ cid: 0, uid: uid, start: 0, stop: -1, filter: '' }); + const tids = topicData.map(topic => topic.tid); + assert.equal(tids.indexOf(newTid), -1, 'The topic appeared in the unread list.'); }); - it('should not appear as unread in the recent list', (done) => { - async.waterfall([ - function (done) { - topics.ignore(newTid, uid, done); - }, - function (done) { - topics.getLatestTopics({ - uid: uid, - start: 0, - stop: -1, - term: 'year', - }, done); - }, - function (results, done) { - const { topics } = results; - let topic; - let i; - for (i = 0; i < topics.length; i += 1) { - if (topics[i].tid === parseInt(newTid, 10)) { - assert.equal(false, topics[i].unread, 'ignored topic was marked as unread in recent list'); - return done(); - } - } - assert.ok(topic, 'topic didn\'t appear in the recent list'); - done(); - }, - ], done); + it('should not appear as unread in the recent list', async () => { + await topics.ignore(newTid, uid); + const results = await topics.getLatestTopics({ + uid: uid, + start: 0, + stop: -1, + term: 'year', + }); + + const { topics: topicsData } = results; + let topic; + let i; + for (i = 0; i < topicsData.length; i += 1) { + if (topicsData[i].tid === parseInt(newTid, 10)) { + assert.equal(false, topicsData[i].unread, 'ignored topic was marked as unread in recent list'); + return; + } + } + assert.ok(topic, 'topic didn\'t appear in the recent list'); }); - it('should appear as unread again when marked as reading', (done) => { - async.waterfall([ - function (done) { - topics.ignore(newTid, uid, done); - }, - function (done) { - topics.follow(newTid, uid, done); - }, - function (done) { - topics.getUnreadTopics({ cid: 0, uid: uid, start: 0, stop: -1, filter: '' }, done); - }, - function (results, done) { - const { topics } = results; - const tids = topics.map(topic => topic.tid); - assert.notEqual(tids.indexOf(newTid), -1, 'The topic did not appear in the unread list.'); - done(); - }, - ], done); - }); - - it('should appear as unread again when marked as following', (done) => { - async.waterfall([ - function (done) { - topics.ignore(newTid, uid, done); - }, - function (done) { - topics.follow(newTid, uid, done); - }, - function (done) { - topics.getUnreadTopics({ cid: 0, uid: uid, start: 0, stop: -1, filter: '' }, done); - }, - function (results, done) { - const { topics } = results; - const tids = topics.map(topic => topic.tid); - assert.notEqual(tids.indexOf(newTid), -1, 'The topic did not appear in the unread list.'); - done(); - }, - ], done); + it('should appear as unread again when marked as following', async () => { + await topics.ignore(newTid, uid); + await topics.follow(newTid, uid); + const results = await topics.getUnreadTopics({ cid: 0, uid: uid, start: 0, stop: -1, filter: '' }); + const tids = results.topics.map(topic => topic.tid); + assert.ok(tids.includes(newTid), 'The topic did not appear in the unread list.'); }); }); @@ -1121,49 +958,26 @@ describe('Topic\'s', () => { const replies = []; let topicPids; const originalBookmark = 6; - function postReply(next) { - topics.reply({ uid: topic.userId, content: `test post ${replies.length}`, tid: newTopic.tid }, (err, result) => { - assert.equal(err, null, 'was created with error'); - assert.ok(result); - replies.push(result); - next(); - }); + async function postReply() { + const result = await topics.reply({ uid: topic.userId, content: `test post ${replies.length}`, tid: newTopic.tid }); + assert.ok(result); + replies.push(result); } - before((done) => { - async.waterfall([ - function (next) { - groups.join('administrators', topic.userId, next); - }, - function (next) { - topics.post({ - uid: topic.userId, - title: topic.title, - content: topic.content, - cid: topic.categoryId, - }, (err, result) => { - assert.ifError(err); - newTopic = result.topicData; - next(); - }); - }, - function (next) { postReply(next); }, - function (next) { postReply(next); }, - function (next) { postReply(next); }, - function (next) { postReply(next); }, - function (next) { postReply(next); }, - function (next) { postReply(next); }, - function (next) { postReply(next); }, - function (next) { postReply(next); }, - function (next) { postReply(next); }, - function (next) { postReply(next); }, - function (next) { postReply(next); }, - function (next) { postReply(next); }, - function (next) { - topicPids = replies.map(reply => reply.pid); - socketTopics.bookmark({ uid: topic.userId }, { tid: newTopic.tid, index: originalBookmark }, next); - }, - ], done); + before(async () => { + await groups.join('administrators', topic.userId); + ({ topicData: newTopic } = await topics.post({ + uid: topic.userId, + title: topic.title, + content: topic.content, + cid: topic.categoryId, + })); + for (let i = 0; i < 12; i++) { + // eslint-disable-next-line no-await-in-loop + await postReply(); + } + topicPids = replies.map(reply => reply.pid); + await socketTopics.bookmark({ uid: topic.userId }, { tid: newTopic.tid, index: originalBookmark }); }); it('should fail with invalid data', (done) => { @@ -1192,44 +1006,25 @@ describe('Topic\'s', () => { }); }); - it('should not update the user\'s bookmark', (done) => { - async.waterfall([ - function (next) { - socketTopics.createTopicFromPosts({ uid: topic.userId }, { - title: 'Fork test, no bookmark update', - pids: topicPids.slice(-2), - fromTid: newTopic.tid, - }, next); - }, - function (forkedTopicData, next) { - topics.getUserBookmark(newTopic.tid, topic.userId, next); - }, - function (bookmark, next) { - assert.equal(originalBookmark, bookmark); - next(); - }, - ], done); + it('should not update the user\'s bookmark', async () => { + await socketTopics.createTopicFromPosts({ uid: topic.userId }, { + title: 'Fork test, no bookmark update', + pids: topicPids.slice(-2), + fromTid: newTopic.tid, + }); + const bookmark = await topics.getUserBookmark(newTopic.tid, topic.userId); + assert.equal(originalBookmark, bookmark); }); - it('should update the user\'s bookmark ', (done) => { - async.waterfall([ - function (next) { - topics.createTopicFromPosts( - topic.userId, - 'Fork test, no bookmark update', - topicPids.slice(1, 3), - newTopic.tid, - next - ); - }, - function (forkedTopicData, next) { - topics.getUserBookmark(newTopic.tid, topic.userId, next); - }, - function (bookmark, next) { - assert.equal(originalBookmark - 2, bookmark); - next(); - }, - ], done); + it('should update the user\'s bookmark ', async () => { + await topics.createTopicFromPosts( + topic.userId, + 'Fork test, no bookmark update', + topicPids.slice(1, 3), + newTopic.tid, + ); + const bookmark = await topics.getUserBookmark(newTopic.tid, topic.userId); + assert.equal(originalBookmark - 2, bookmark); }); it('should properly update topic vote count after forking', async () => { @@ -1267,162 +1062,114 @@ describe('Topic\'s', () => { }); }); - it('should load topic', (done) => { - request(`${nconf.get('url')}/topic/${topicData.slug}`, (err, response, body) => { - assert.ifError(err); - assert.equal(response.statusCode, 200); - assert(body); - done(); - }); + it('should load topic', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/topic/${topicData.slug}`); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load topic api data', (done) => { - request(`${nconf.get('url')}/api/topic/${topicData.slug}`, { json: true }, (err, response, body) => { - assert.ifError(err); - assert.equal(response.statusCode, 200); - assert.strictEqual(body._header.tags.meta.find(t => t.name === 'description').content, 'topic content'); - assert.strictEqual(body._header.tags.meta.find(t => t.property === 'og:description').content, 'topic content'); - done(); - }); + it('should load topic api data', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/topic/${topicData.slug}`); + assert.equal(response.statusCode, 200); + assert.strictEqual(body._header.tags.meta.find(t => t.name === 'description').content, 'topic content'); + assert.strictEqual(body._header.tags.meta.find(t => t.property === 'og:description').content, 'topic content'); }); - it('should 404 if post index is invalid', (done) => { - request(`${nconf.get('url')}/topic/${topicData.slug}/derp`, (err, response) => { - assert.ifError(err); - assert.equal(response.statusCode, 404); - done(); - }); + it('should 404 if post index is invalid', async () => { + const { response } = await request.get(`${nconf.get('url')}/topic/${topicData.slug}/derp`); + assert.equal(response.statusCode, 404); }); - it('should 404 if topic does not exist', (done) => { - request(`${nconf.get('url')}/topic/123123/does-not-exist`, (err, response) => { - assert.ifError(err); - assert.equal(response.statusCode, 404); - done(); - }); + it('should 404 if topic does not exist', async () => { + const { response } = await request.get(`${nconf.get('url')}/topic/123123/does-not-exist`); + assert.equal(response.statusCode, 404); }); - it('should 401 if not allowed to read as guest', (done) => { + it('should 401 if not allowed to read as guest', async () => { const privileges = require('../src/privileges'); - privileges.categories.rescind(['groups:topics:read'], topicData.cid, 'guests', (err) => { - assert.ifError(err); - request(`${nconf.get('url')}/api/topic/${topicData.slug}`, (err, response, body) => { - assert.ifError(err); - assert.equal(response.statusCode, 401); - assert(body); - privileges.categories.give(['groups:topics:read'], topicData.cid, 'guests', done); - }); - }); + await privileges.categories.rescind(['groups:topics:read'], topicData.cid, 'guests'); + + const { response, body } = await request.get(`${nconf.get('url')}/api/topic/${topicData.slug}`); + assert.equal(response.statusCode, 401); + assert(body); + await privileges.categories.give(['groups:topics:read'], topicData.cid, 'guests'); }); - it('should redirect to correct topic if slug is missing', (done) => { - request(`${nconf.get('url')}/topic/${topicData.tid}/herpderp/1?page=2`, (err, response, body) => { - assert.ifError(err); - assert.equal(response.statusCode, 200); - assert(body); - done(); - }); + it('should redirect to correct topic if slug is missing', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/topic/${topicData.tid}/herpderp/1?page=2`); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should redirect if post index is out of range', (done) => { - request(`${nconf.get('url')}/api/topic/${topicData.slug}/-1`, { json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(res.headers['x-redirect'], `/topic/${topicData.tid}/topic-for-controller-test`); - assert.equal(body, `/topic/${topicData.tid}/topic-for-controller-test`); - done(); - }); + it('should redirect if post index is out of range', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/topic/${topicData.slug}/-1`); + assert.equal(response.statusCode, 200); + assert.equal(response.headers['x-redirect'], `/topic/${topicData.tid}/topic-for-controller-test`); + assert.equal(body, `/topic/${topicData.tid}/topic-for-controller-test`); }); - it('should 404 if page is out of bounds', (done) => { + it('should 404 if page is out of bounds', async () => { const meta = require('../src/meta'); meta.config.usePagination = 1; - request(`${nconf.get('url')}/topic/${topicData.slug}?page=100`, (err, response) => { - assert.ifError(err); - assert.equal(response.statusCode, 404); - done(); - }); + const { response } = await request.get(`${nconf.get('url')}/topic/${topicData.slug}?page=100`); + assert.equal(response.statusCode, 404); }); - it('should mark topic read', (done) => { - request(`${nconf.get('url')}/topic/${topicData.slug}`, { + it('should mark topic read', async () => { + const { response } = await request.get(`${nconf.get('url')}/topic/${topicData.slug}`, { jar: adminJar, - }, (err, res) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - topics.hasReadTopics([topicData.tid], adminUid, (err, hasRead) => { - assert.ifError(err); - assert.equal(hasRead[0], true); - done(); - }); }); + assert.equal(response.statusCode, 200); + const hasRead = await topics.hasReadTopics([topicData.tid], adminUid); + assert.equal(hasRead[0], true); }); - it('should 404 if tid is not a number', (done) => { - request(`${nconf.get('url')}/api/topic/teaser/nan`, { json: true }, (err, response) => { - assert.ifError(err); - assert.equal(response.statusCode, 404); - done(); - }); + it('should 404 if tid is not a number', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/topic/teaser/nan`); + assert.equal(response.statusCode, 404); }); - it('should 403 if cant read', (done) => { - request(`${nconf.get('url')}/api/topic/teaser/${123123}`, { json: true }, (err, response, body) => { - assert.ifError(err); - assert.equal(response.statusCode, 403); - assert.equal(body, '[[error:no-privileges]]'); - - done(); - }); + it('should 403 if cant read', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/topic/teaser/${123123}`); + assert.equal(response.statusCode, 403); + assert.equal(body, '[[error:no-privileges]]'); }); - it('should load topic teaser', (done) => { - request(`${nconf.get('url')}/api/topic/teaser/${topicData.tid}`, { json: true }, (err, response, body) => { - assert.ifError(err); - assert.equal(response.statusCode, 200); - assert(body); - assert.equal(body.tid, topicData.tid); - assert.equal(body.content, 'topic content'); - assert(body.user); - assert(body.topic); - assert(body.category); - done(); - }); + it('should load topic teaser', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/topic/teaser/${topicData.tid}`); + assert.equal(response.statusCode, 200); + assert(body); + assert.equal(body.tid, topicData.tid); + assert.equal(body.content, 'topic content'); + assert(body.user); + assert(body.topic); + assert(body.category); }); - it('should 404 if tid is not a number', (done) => { - request(`${nconf.get('url')}/api/topic/pagination/nan`, { json: true }, (err, response) => { - assert.ifError(err); - assert.equal(response.statusCode, 404); - done(); - }); + it('should 404 if tid is not a number', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/topic/pagination/nan`); + assert.equal(response.statusCode, 404); }); - it('should 404 if tid does not exist', (done) => { - request(`${nconf.get('url')}/api/topic/pagination/1231231`, { json: true }, (err, response) => { - assert.ifError(err); - assert.equal(response.statusCode, 404); - done(); - }); + it('should 404 if tid does not exist', async () => { + const { response } = await request.get(`${nconf.get('url')}/api/topic/pagination/1231231`); + assert.equal(response.statusCode, 404); }); - it('should load pagination', (done) => { - request(`${nconf.get('url')}/api/topic/pagination/${topicData.tid}`, { json: true }, (err, response, body) => { - assert.ifError(err); - assert.equal(response.statusCode, 200); - assert(body); - assert.deepEqual(body.pagination, { - prev: { page: 1, active: false }, - next: { page: 1, active: false }, - first: { page: 1, active: true }, - last: { page: 1, active: true }, - rel: [], - pages: [], - currentPage: 1, - pageCount: 1, - }); - done(); + it('should load pagination', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/topic/pagination/${topicData.tid}`); + assert.equal(response.statusCode, 200); + assert(body); + assert.deepEqual(body.pagination, { + prev: { page: 1, active: false }, + next: { page: 1, active: false }, + first: { page: 1, active: true }, + last: { page: 1, active: true }, + rel: [], + pages: [], + currentPage: 1, + pageCount: 1, }); }); }); @@ -1464,23 +1211,12 @@ describe('Topic\'s', () => { describe('suggested topics', () => { let tid1; let tid3; - before((done) => { - async.series({ - topic1: function (next) { - topics.post({ uid: adminUid, tags: ['nodebb'], title: 'topic title 1', content: 'topic 1 content', cid: topic.categoryId }, next); - }, - topic2: function (next) { - topics.post({ uid: adminUid, tags: ['nodebb'], title: 'topic title 2', content: 'topic 2 content', cid: topic.categoryId }, next); - }, - topic3: function (next) { - topics.post({ uid: adminUid, tags: [], title: 'topic title 3', content: 'topic 3 content', cid: topic.categoryId }, next); - }, - }, (err, results) => { - assert.ifError(err); - tid1 = results.topic1.topicData.tid; - tid3 = results.topic3.topicData.tid; - done(); - }); + before(async () => { + const topic1 = await topics.post({ uid: adminUid, tags: ['nodebb'], title: 'topic title 1', content: 'topic 1 content', cid: topic.categoryId }); + const topic2 = await topics.post({ uid: adminUid, tags: ['nodebb'], title: 'topic title 2', content: 'topic 2 content', cid: topic.categoryId }); + const topic3 = await topics.post({ uid: adminUid, tags: [], title: 'topic title 3', content: 'topic 3 content', cid: topic.categoryId }); + tid1 = topic1.topicData.tid; + tid3 = topic3.topicData.tid; }); it('should return suggested topics', (done) => { @@ -1503,23 +1239,11 @@ describe('Topic\'s', () => { describe('unread', () => { const socketTopics = require('../src/socket.io/topics'); let tid; - let mainPid; let uid; - before((done) => { - async.parallel({ - topic: function (next) { - topics.post({ uid: topic.userId, title: 'unread topic', content: 'unread topic content', cid: topic.categoryId }, next); - }, - joeUid: function (next) { - User.create({ username: 'regularJoe' }, next); - }, - }, (err, results) => { - assert.ifError(err); - tid = results.topic.topicData.tid; - mainPid = results.topic.postData.pid; - uid = results.joeUid; - done(); - }); + before(async () => { + const { topicData } = await topics.post({ uid: topic.userId, title: 'unread topic', content: 'unread topic content', cid: topic.categoryId }); + uid = await User.create({ username: 'regularJoe' }); + tid = topicData.tid; }); it('should fail with invalid data', async () => { @@ -1651,93 +1375,45 @@ describe('Topic\'s', () => { }); }); - it('should not return topics in category you cant read', (done) => { - let privateCid; - let privateTid; - async.waterfall([ - function (next) { - categories.create({ - name: 'private category', - description: 'private category', - }, next); - }, - function (category, next) { - privateCid = category.cid; - privileges.categories.rescind(['groups: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); - }, - function (data, next) { - privateTid = data.topicData.tid; - topics.getUnreadTids({ uid: uid }, next); - }, - function (unreadTids, next) { - unreadTids = unreadTids.map(String); - assert(!unreadTids.includes(String(privateTid))); - next(); - }, - ], done); + it('should not return topics in category you cant read', async () => { + const { cid: privateCid } = await categories.create({ + name: 'private category', + description: 'private category', + }); + privileges.categories.rescind(['groups:topics:read'], privateCid, 'registered-users'); + + const { topicData } = await topics.post({ uid: adminUid, title: 'topic in private category', content: 'registered-users cant see this', cid: privateCid }); + const privateTid = topicData.tid; + + const unreadTids = (await topics.getUnreadTids({ uid: uid })).map(String); + assert(!unreadTids.includes(String(privateTid))); }); - it('should not return topics in category you ignored/not watching', (done) => { - let ignoredCid; - let tid; - async.waterfall([ - function (next) { - categories.create({ - name: 'ignored category', - description: 'ignored category', - }, next); - }, - function (category, next) { - ignoredCid = category.cid; - privileges.categories.rescind(['groups: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); - }, - function (data, next) { - tid = data.topicData.tid; - User.ignoreCategory(uid, ignoredCid, next); - }, - function (next) { - topics.getUnreadTids({ uid: uid }, next); - }, - function (unreadTids, next) { - unreadTids = unreadTids.map(String); - assert(!unreadTids.includes(String(tid))); - next(); - }, - ], done); + it('should not return topics in category you ignored/not watching', async () => { + const category = await categories.create({ + name: 'ignored category', + description: 'ignored category', + }); + const ignoredCid = category.cid; + await privileges.categories.rescind(['groups:topics:read'], ignoredCid, 'registered-users'); + + const { topicData } = await topics.post({ uid: adminUid, title: 'topic in private category', content: 'registered-users cant see this', cid: ignoredCid }); + const { tid } = topicData; + + await User.ignoreCategory(uid, ignoredCid); + const unreadTids = (await topics.getUnreadTids({ uid: uid })).map(String); + assert(!unreadTids.includes(String(tid))); }); - it('should not return topic as unread if new post is from blocked user', (done) => { - let blockedUid; - let topic; - async.waterfall([ - function (next) { - topics.post({ uid: adminUid, title: 'will not get as unread', content: 'not unread', cid: categoryObj.cid }, next); - }, - function (result, next) { - topic = result.topicData; - User.create({ username: 'blockedunread' }, next); - }, - function (uid, next) { - blockedUid = uid; - User.blocks.add(uid, adminUid, next); - }, - function (next) { - topics.reply({ uid: blockedUid, content: 'post from blocked user', tid: topic.tid }, next); - }, - function (result, next) { - topics.getUnreadTids({ cid: 0, uid: adminUid }, next); - }, - function (unreadTids, next) { - assert(!unreadTids.includes(topic.tid)); - User.blocks.remove(blockedUid, adminUid, next); - }, - ], done); + it('should not return topic as unread if new post is from blocked user', async () => { + const { topicData } = await topics.post({ uid: adminUid, title: 'will not get as unread', content: 'not unread', cid: categoryObj.cid }); + const blockedUid = await User.create({ username: 'blockedunread' }); + await User.blocks.add(blockedUid, adminUid); + await topics.reply({ uid: blockedUid, content: 'post from blocked user', tid: topic.tid }); + + const unreadTids = await topics.getUnreadTids({ cid: 0, uid: adminUid }); + assert(!unreadTids.includes(topicData.tid)); + await User.blocks.remove(blockedUid, adminUid); }); it('should not return topic as unread if topic is deleted', async () => { @@ -1753,18 +1429,9 @@ describe('Topic\'s', () => { const socketTopics = require('../src/socket.io/topics'); const socketAdmin = require('../src/socket.io/admin'); - before((done) => { - async.series([ - function (next) { - topics.post({ uid: adminUid, tags: ['php', 'nosql', 'psql', 'nodebb', 'node icon'], title: 'topic title 1', content: 'topic 1 content', cid: topic.categoryId }, next); - }, - function (next) { - topics.post({ uid: adminUid, tags: ['javascript', 'mysql', 'python', 'nodejs'], title: 'topic title 2', content: 'topic 2 content', cid: topic.categoryId }, next); - }, - ], (err) => { - assert.ifError(err); - done(); - }); + before(async () => { + await topics.post({ uid: adminUid, tags: ['php', 'nosql', 'psql', 'nodebb', 'node icon'], title: 'topic title 1', content: 'topic 1 content', cid: topic.categoryId }); + await topics.post({ uid: adminUid, tags: ['javascript', 'mysql', 'python', 'nodejs'], title: 'topic title 2', content: 'topic 2 content', cid: topic.categoryId }); }); it('should return empty array if query is falsy', (done) => { @@ -2309,20 +1976,9 @@ describe('Topic\'s', () => { describe('teasers', () => { let topic1; let topic2; - before((done) => { - async.series([ - function (next) { - topics.post({ uid: adminUid, title: 'topic 1', content: 'content 1', cid: categoryObj.cid }, next); - }, - function (next) { - topics.post({ uid: adminUid, title: 'topic 2', content: 'content 2', cid: categoryObj.cid }, next); - }, - ], (err, results) => { - assert.ifError(err); - topic1 = results[0]; - topic2 = results[1]; - done(); - }); + before(async () => { + topic1 = await topics.post({ uid: adminUid, title: 'topic 1', content: 'content 1', cid: categoryObj.cid }); + topic2 = await topics.post({ uid: adminUid, title: 'topic 2', content: 'content 2', cid: categoryObj.cid }); }); after((done) => { @@ -2416,47 +2072,23 @@ describe('Topic\'s', () => { }); }); - it('should not return teaser if user is blocked', (done) => { - let blockedUid; - async.waterfall([ - function (next) { - User.create({ username: 'blocked' }, next); - }, - function (uid, next) { - blockedUid = uid; - User.blocks.add(uid, adminUid, next); - }, - function (next) { - topics.reply({ uid: blockedUid, content: 'post from blocked user', tid: topic2.topicData.tid }, next); - }, - function (result, next) { - topics.getTeaser(topic2.topicData.tid, adminUid, next); - }, - function (teaser, next) { - assert.equal(teaser.content, 'content 2'); - User.blocks.remove(blockedUid, adminUid, next); - }, - ], done); + it('should not return teaser if user is blocked', async () => { + const blockedUid = await User.create({ username: 'blocked' }); + await User.blocks.add(blockedUid, adminUid); + await topics.reply({ uid: blockedUid, content: 'post from blocked user', tid: topic2.topicData.tid }); + const teaser = await topics.getTeaser(topic2.topicData.tid, adminUid); + assert.equal(teaser.content, 'content 2'); + await User.blocks.remove(blockedUid, adminUid); }); }); describe('tag privilege', () => { let uid; let cid; - before((done) => { - async.waterfall([ - function (next) { - User.create({ username: 'tag_poster' }, next); - }, - function (_uid, next) { - uid = _uid; - categories.create({ name: 'tag category' }, next); - }, - function (categoryObj, next) { - cid = categoryObj.cid; - next(); - }, - ], done); + before(async () => { + uid = await User.create({ username: 'tag_poster' }); + const category = await categories.create({ name: 'tag category' }); + cid = category.cid; }); it('should fail to post if user does not have tag privilege', (done) => { @@ -2507,27 +2139,14 @@ describe('Topic\'s', () => { return await topics.getTopicWithPosts(topicData, `tid:${topicData.tid}:posts`, adminUid, 0, 19, false); } - before((done) => { - async.waterfall([ - function (next) { - User.create({ username: 'mergevictim' }, next); - }, - function (_uid, next) { - uid = _uid; - topics.post({ uid: uid, cid: categoryObj.cid, title: 'topic 1', content: 'topic 1 OP' }, next); - }, - function (result, next) { - topic1Data = result.topicData; - topics.post({ uid: uid, cid: categoryObj.cid, title: 'topic 2', content: 'topic 2 OP' }, next); - }, - function (result, next) { - topic2Data = result.topicData; - topics.reply({ uid: uid, content: 'topic 1 reply', tid: topic1Data.tid }, next); - }, - function (postData, next) { - topics.reply({ uid: uid, content: 'topic 2 reply', tid: topic2Data.tid }, next); - }, - ], done); + before(async () => { + uid = await User.create({ username: 'mergevictim' }); + let result = await topics.post({ uid: uid, cid: categoryObj.cid, title: 'topic 1', content: 'topic 1 OP' }); + topic1Data = result.topicData; + result = await topics.post({ uid: uid, cid: categoryObj.cid, title: 'topic 2', content: 'topic 2 OP' }); + topic2Data = result.topicData; + await topics.reply({ uid: uid, content: 'topic 1 reply', tid: topic1Data.tid }); + await topics.reply({ uid: uid, content: 'topic 2 reply', tid: topic2Data.tid }); }); it('should error if data is not an array', (done) => { @@ -2565,14 +2184,11 @@ describe('Topic\'s', () => { assert.equal(topic1.title, 'topic 1'); }); - it('should return properly for merged topic', (done) => { - request(`${nconf.get('url')}/api/topic/${topic2Data.slug}`, { jar: adminJar, json: true }, (err, response, body) => { - assert.ifError(err); - assert.equal(response.statusCode, 200); - assert(body); - assert.deepStrictEqual(body.posts, []); - done(); - }); + it('should return properly for merged topic', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/topic/${topic2Data.slug}`, { jar: adminJar }); + assert.equal(response.statusCode, 200); + assert(body); + assert.deepStrictEqual(body.posts, []); }); it('should merge 2 topics with options mainTid', async () => { @@ -2645,26 +2261,24 @@ describe('Topic\'s', () => { await topics.reply({ uid: topic.userId, content: 'topic 2 reply', tid: topic2Result.topicData.tid }); }); - it('should get sorted topics in category', (done) => { + it('should get sorted topics in category', async () => { const filters = ['', 'watched', 'unreplied', 'new']; - async.map(filters, (filter, next) => { - topics.getSortedTopics({ + const data = await Promise.all(filters.map( + async filter => topics.getSortedTopics({ cids: [category.cid], uid: topic.userId, start: 0, stop: -1, filter: filter, sort: 'votes', - }, next); - }, (err, data) => { - assert.ifError(err); - assert(data); - data.forEach((filterTopics) => { - assert(Array.isArray(filterTopics.topics)); - }); - done(); + }) + )); + assert(data); + data.forEach((filterTopics) => { + assert(Array.isArray(filterTopics.topics)); }); }); + it('should get topics recent replied first', async () => { const data = await topics.getSortedTopics({ cids: [category.cid], @@ -2697,15 +2311,13 @@ describe('Topic\'s', () => { let adminApiOpts; let postData; const replyData = { - form: { + body: { content: 'a reply by guest', }, - json: true, }; before(async () => { adminApiOpts = { - json: true, jar: adminJar, headers: { 'x-csrf-token': csrf_token, @@ -2750,85 +2362,85 @@ describe('Topic\'s', () => { }); it('should not load topic for an unprivileged user', async () => { - const response = await requestType('get', `${nconf.get('url')}/topic/${topicData.slug}`); + const { response, body } = await request.get(`${nconf.get('url')}/topic/${topicData.slug}`); assert.strictEqual(response.statusCode, 404); - assert(response.body); + assert(body); }); it('should load topic for a privileged user', async () => { - const response = (await requestType('get', `${nconf.get('url')}/topic/${topicData.slug}`, { jar: adminJar })).res; + const { response, body } = await request.get(`${nconf.get('url')}/topic/${topicData.slug}`, { jar: adminJar }); assert.strictEqual(response.statusCode, 200); - assert(response.body); + assert(body); }); it('should not be amongst topics of the category for an unprivileged user', async () => { - const response = await requestType('get', `${nconf.get('url')}/api/category/${categoryObj.slug}`, { json: true }); - assert.strictEqual(response.body.topics.filter(topic => topic.tid === topicData.tid).length, 0); + const { body } = await request.get(`${nconf.get('url')}/api/category/${categoryObj.slug}`); + assert.strictEqual(body.topics.filter(topic => topic.tid === topicData.tid).length, 0); }); it('should be amongst topics of the category for a privileged user', async () => { - const response = await requestType('get', `${nconf.get('url')}/api/category/${categoryObj.slug}`, { json: true, jar: adminJar }); - const topic = response.body.topics.filter(topic => topic.tid === topicData.tid)[0]; + const { body } = await request.get(`${nconf.get('url')}/api/category/${categoryObj.slug}`, { jar: adminJar }); + const topic = body.topics.filter(topic => topic.tid === topicData.tid)[0]; assert.strictEqual(topic && topic.tid, topicData.tid); }); it('should load topic for guests if privilege is given', async () => { await privileges.categories.give(['groups:topics:schedule'], categoryObj.cid, 'guests'); - const response = await requestType('get', `${nconf.get('url')}/topic/${topicData.slug}`); + const { response, body } = await request.get(`${nconf.get('url')}/topic/${topicData.slug}`); assert.strictEqual(response.statusCode, 200); - assert(response.body); + assert(body); }); it('should be amongst topics of the category for guests if privilege is given', async () => { - const response = await requestType('get', `${nconf.get('url')}/api/category/${categoryObj.slug}`, { json: true }); - const topic = response.body.topics.filter(topic => topic.tid === topicData.tid)[0]; + const { body } = await request.get(`${nconf.get('url')}/api/category/${categoryObj.slug}`); + const topic = body.topics.filter(topic => topic.tid === topicData.tid)[0]; assert.strictEqual(topic && topic.tid, topicData.tid); }); it('should not allow deletion of a scheduled topic', async () => { - const response = await requestType('delete', `${nconf.get('url')}/api/v3/topics/${topicData.tid}/state`, adminApiOpts); - assert.strictEqual(response.res.statusCode, 400); + const { response } = await request.delete(`${nconf.get('url')}/api/v3/topics/${topicData.tid}/state`, adminApiOpts); + assert.strictEqual(response.statusCode, 400); }); it('should not allow to unpin a scheduled topic', async () => { - const response = await requestType('delete', `${nconf.get('url')}/api/v3/topics/${topicData.tid}/pin`, adminApiOpts); - assert.strictEqual(response.res.statusCode, 400); + const { response } = await request.delete(`${nconf.get('url')}/api/v3/topics/${topicData.tid}/pin`, adminApiOpts); + assert.strictEqual(response.statusCode, 400); }); it('should not allow to restore a scheduled topic', async () => { - const response = await requestType('put', `${nconf.get('url')}/api/v3/topics/${topicData.tid}/state`, adminApiOpts); - assert.strictEqual(response.res.statusCode, 400); + const { response } = await request.put(`${nconf.get('url')}/api/v3/topics/${topicData.tid}/state`, adminApiOpts); + assert.strictEqual(response.statusCode, 400); }); it('should not allow unprivileged to reply', async () => { await privileges.categories.rescind(['groups:topics:schedule'], categoryObj.cid, 'guests'); await privileges.categories.give(['groups:topics:reply'], categoryObj.cid, 'guests'); - const response = await requestType('post', `${nconf.get('url')}/api/v3/topics/${topicData.tid}`, replyData); - assert.strictEqual(response.res.statusCode, 403); + const { response } = await request.post(`${nconf.get('url')}/api/v3/topics/${topicData.tid}`, replyData); + assert.strictEqual(response.statusCode, 403); }); it('should allow guests to reply if privilege is given', async () => { await privileges.categories.give(['groups:topics:schedule'], categoryObj.cid, 'guests'); - const response = await helpers.request('post', `/api/v3/topics/${topicData.tid}`, { + const { body } = await helpers.request('post', `/api/v3/topics/${topicData.tid}`, { ...replyData, jar: request.jar(), }); - assert.strictEqual(response.body.response.content, 'a reply by guest'); - assert.strictEqual(response.body.response.user.username, '[[global:guest]]'); + assert.strictEqual(body.response.content, 'a reply by guest'); + assert.strictEqual(body.response.user.username, '[[global:guest]]'); }); it('should have replies with greater timestamp than the scheduled topics itself', async () => { - const response = await requestType('get', `${nconf.get('url')}/api/topic/${topicData.slug}`, { json: true }); - postData = response.body.posts[1]; - assert(postData.timestamp > response.body.posts[0].timestamp); + const { body } = await request.get(`${nconf.get('url')}/api/topic/${topicData.slug}`); + postData = body.posts[1]; + assert(postData.timestamp > body.posts[0].timestamp); }); it('should have post edits with greater timestamp than the original', async () => { - const editData = { ...adminApiOpts, form: { content: 'an edit by the admin' } }; - const result = await requestType('put', `${nconf.get('url')}/api/v3/posts/${postData.pid}`, editData); + const editData = { ...adminApiOpts, body: { content: 'an edit by the admin' } }; + const result = await request.put(`${nconf.get('url')}/api/v3/posts/${postData.pid}`, editData); assert(result.body.response.edited > postData.timestamp); - const diffsResult = await requestType('get', `${nconf.get('url')}/api/v3/posts/${postData.pid}/diffs`, adminApiOpts); + const diffsResult = await request.get(`${nconf.get('url')}/api/v3/posts/${postData.pid}/diffs`, adminApiOpts); const { revisions } = diffsResult.body.response; // diffs are LIFO assert(revisions[0].timestamp > revisions[1].timestamp); @@ -2836,8 +2448,8 @@ describe('Topic\'s', () => { it('should able to reschedule', async () => { const newDate = new Date(Date.now() + (5 * 86400000)).getTime(); - const editData = { ...adminApiOpts, form: { ...topic, pid: topicData.mainPid, timestamp: newDate } }; - const response = await requestType('put', `${nconf.get('url')}/api/v3/posts/${topicData.mainPid}`, editData); + const editData = { ...adminApiOpts, body: { ...topic, pid: topicData.mainPid, timestamp: newDate } }; + await request.put(`${nconf.get('url')}/api/v3/posts/${topicData.mainPid}`, editData); const editedTopic = await topics.getTopicFields(topicData.tid, ['lastposttime', 'timestamp']); const editedPost = await posts.getPostFields(postData.pid, ['timestamp']); @@ -2875,17 +2487,16 @@ describe('Topic\'s', () => { it('should not be able to schedule a "published" topic', async () => { const newDate = new Date(Date.now() + 86400000).getTime(); - const editData = { ...adminApiOpts, form: { ...topic, pid: topicData.mainPid, timestamp: newDate } }; - const response = await requestType('put', `${nconf.get('url')}/api/v3/posts/${topicData.mainPid}`, editData); - assert.strictEqual(response.body.response.timestamp, Date.now()); - + const editData = { ...adminApiOpts, body: { ...topic, pid: topicData.mainPid, timestamp: newDate } }; + const { body } = await request.put(`${nconf.get('url')}/api/v3/posts/${topicData.mainPid}`, editData); + assert.strictEqual(body.response.timestamp, Date.now()); mockdate.reset(); }); it('should allow to purge a scheduled topic', async () => { topicData = (await topics.post(topic)).topicData; - const response = await requestType('delete', `${nconf.get('url')}/api/v3/topics/${topicData.tid}`, adminApiOpts); - assert.strictEqual(response.res.statusCode, 200); + const { response } = await request.delete(`${nconf.get('url')}/api/v3/topics/${topicData.tid}`, adminApiOpts); + assert.strictEqual(response.statusCode, 200); }); it('should remove from topics:scheduled on purge', async () => { diff --git a/test/topics/thumbs.js b/test/topics/thumbs.js index 272d5cac32..2c396c7794 100644 --- a/test/topics/thumbs.js +++ b/test/topics/thumbs.js @@ -324,20 +324,14 @@ describe('Topic thumbs', () => { createFiles(); }); - it('should succeed with a valid tid', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/1/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 200); - done(); - }); + it('should succeed with a valid tid', async () => { + const { response } = await helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/1/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF); + assert.strictEqual(response.statusCode, 200); }); - it('should succeed with a uuid', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 200); - done(); - }); + it('should succeed with a uuid', async () => { + const { response } = await helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF); + assert.strictEqual(response.statusCode, 200); }); it('should succeed with uploader plugins', async () => { @@ -350,63 +344,49 @@ describe('Topic thumbs', () => { method: hookMethod, }); - await new Promise((resolve) => { - helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 200); - resolve(); - }); - }); + const { response } = await helpers.uploadFile( + `${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, + path.join(__dirname, '../files/test.png'), + {}, + adminJar, + adminCSRF + ); + assert.strictEqual(response.statusCode, 200); await plugins.hooks.unregister('test', 'filter:uploadFile', hookMethod); }); - it('should fail with a non-existant tid', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/4/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 404); - done(); - }); + it('should fail with a non-existant tid', async () => { + const { response } = await helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/4/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF); + assert.strictEqual(response.statusCode, 404); }); - it('should fail when garbage is passed in', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/abracadabra/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 404); - done(); - }); + it('should fail when garbage is passed in', async () => { + const { response } = await helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/abracadabra/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF); + assert.strictEqual(response.statusCode, 404); }); - it('should fail when calling user cannot edit the tid', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/2/thumbs`, path.join(__dirname, '../files/test.png'), {}, fooJar, fooCSRF, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 403); - done(); - }); + it('should fail when calling user cannot edit the tid', async () => { + const { response } = await helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/2/thumbs`, path.join(__dirname, '../files/test.png'), {}, fooJar, fooCSRF); + assert.strictEqual(response.statusCode, 403); }); - it('should fail if thumbnails are not enabled', (done) => { + it('should fail if thumbnails are not enabled', async () => { meta.config.allowTopicsThumbnail = 0; - helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 503); - assert(body && body.status); - assert.strictEqual(body.status.message, 'Topic thumbnails are disabled.'); - done(); - }); + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF); + assert.strictEqual(response.statusCode, 503); + assert(body && body.status); + assert.strictEqual(body.status.message, 'Topic thumbnails are disabled.'); }); - it('should fail if file is not image', (done) => { + it('should fail if file is not image', async () => { meta.config.allowTopicsThumbnail = 1; - helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/503.html'), {}, adminJar, adminCSRF, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 500); - assert(body && body.status); - assert.strictEqual(body.status.message, 'Invalid File'); - done(); - }); + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/503.html'), {}, adminJar, adminCSRF); + assert.strictEqual(response.statusCode, 500); + assert(body && body.status); + assert.strictEqual(body.status.message, 'Invalid File'); }); }); diff --git a/test/uploads.js b/test/uploads.js index 7a00000737..5df32ba2fd 100644 --- a/test/uploads.js +++ b/test/uploads.js @@ -5,9 +5,6 @@ const assert = require('assert'); const nconf = require('nconf'); const path = require('path'); const fs = require('fs').promises; -const request = require('request'); -const requestAsync = require('request-promise-native'); -const util = require('util'); const db = require('./mocks/databasemock'); const categories = require('../src/categories'); @@ -21,6 +18,7 @@ const socketUser = require('../src/socket.io/user'); const helpers = require('./helpers'); const file = require('../src/file'); const image = require('../src/image'); +const request = require('../src/request'); const emptyUploadsFolder = async () => { const files = await fs.readdir(`${nconf.get('upload_path')}/files`); @@ -83,31 +81,25 @@ describe('Upload Controllers', () => { await privileges.global.give(['groups:upload:post:file'], 'registered-users'); }); - it('should fail if the user exceeds the upload rate limit threshold', (done) => { + it('should fail if the user exceeds the upload rate limit threshold', async () => { const oldValue = meta.config.allowedFileExtensions; meta.config.allowedFileExtensions = 'png,jpg,bmp,html'; require('../src/middleware/uploads').clearCache(); const times = meta.config.uploadRateLimitThreshold + 1; - async.timesSeries(times, (i, next) => { - helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/503.html'), {}, jar, csrf_token, (err, res, body) => { - if (i + 1 >= times) { - assert.strictEqual(res.statusCode, 500); - assert.strictEqual(body.error, '[[error:upload-ratelimit-reached]]'); - } else { - assert.ifError(err); - assert.strictEqual(res.statusCode, 200); - assert(body && body.status && body.response && body.response.images); - assert(Array.isArray(body.response.images)); - assert(body.response.images[0].url); - } - - next(err); - }); - }, (err) => { - meta.config.allowedFileExtensions = oldValue; - assert.ifError(err); - done(); - }); + for (let i = 0; i < times; i++) { + // eslint-disable-next-line no-await-in-loop + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/503.html'), {}, jar, csrf_token); + if (i + 1 >= times) { + assert.strictEqual(response.statusCode, 500); + assert.strictEqual(body.error, '[[error:upload-ratelimit-reached]]'); + } else { + assert.strictEqual(response.statusCode, 200); + assert(body && body.status && body.response && body.response.images); + assert(Array.isArray(body.response.images)); + assert(body.response.images[0].url); + } + } + meta.config.allowedFileExtensions = oldValue; }); }); @@ -121,34 +113,26 @@ describe('Upload Controllers', () => { await privileges.global.give(['groups:upload:post:file'], 'registered-users'); }); - it('should upload an image to a post', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body && body.status && body.response && body.response.images); - assert(Array.isArray(body.response.images)); - assert(body.response.images[0].url); - done(); - }); + it('should upload an image to a post', async () => { + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token); + assert.equal(response.statusCode, 200); + assert(body && body.status && body.response && body.response.images); + assert(Array.isArray(body.response.images)); + assert(body.response.images[0].url); }); - it('should upload an image to a post and then delete the upload', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 200); - assert(body && body.status && body.response && body.response.images); - assert(Array.isArray(body.response.images)); - assert(body.response.images[0].url); - const name = body.response.images[0].url.replace(`${nconf.get('relative_path') + nconf.get('upload_url')}/`, ''); - socketUser.deleteUpload({ uid: regularUid }, { uid: regularUid, name: name }, (err) => { - assert.ifError(err); - db.getSortedSetRange(`uid:${regularUid}:uploads`, 0, -1, (err, uploads) => { - assert.ifError(err); - assert.equal(uploads.includes(name), false); - done(); - }); - }); - }); + it('should upload an image to a post and then delete the upload', async () => { + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token); + + assert.strictEqual(response.statusCode, 200); + assert(body && body.status && body.response && body.response.images); + assert(Array.isArray(body.response.images)); + assert(body.response.images[0].url); + const name = body.response.images[0].url.replace(`${nconf.get('relative_path') + nconf.get('upload_url')}/`, ''); + await socketUser.deleteUpload({ uid: regularUid }, { uid: regularUid, name: name }); + + const uploads = await db.getSortedSetRange(`uid:${regularUid}:uploads`, 0, -1); + assert.equal(uploads.includes(name), false); }); it('should not allow deleting if path is not correct', (done) => { @@ -165,55 +149,45 @@ describe('Upload Controllers', () => { }); }); - it('should resize and upload an image to a post', (done) => { + it('should resize and upload an image to a post', async () => { const oldValue = meta.config.resizeImageWidth; meta.config.resizeImageWidth = 10; meta.config.resizeImageWidthThreshold = 10; - helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body && body.status && body.response && body.response.images); - assert(Array.isArray(body.response.images)); - assert(body.response.images[0].url); - assert(body.response.images[0].url.match(/\/assets\/uploads\/files\/\d+-test-resized\.png/)); - meta.config.resizeImageWidth = oldValue; - meta.config.resizeImageWidthThreshold = 1520; - done(); - }); + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token); + + assert.equal(response.statusCode, 200); + assert(body && body.status && body.response && body.response.images); + assert(Array.isArray(body.response.images)); + assert(body.response.images[0].url); + assert(body.response.images[0].url.match(/\/assets\/uploads\/files\/\d+-test-resized\.png/)); + meta.config.resizeImageWidth = oldValue; + meta.config.resizeImageWidthThreshold = 1520; }); - it('should upload a file to a post', (done) => { + it('should upload a file to a post', async () => { const oldValue = meta.config.allowedFileExtensions; meta.config.allowedFileExtensions = 'png,jpg,bmp,html'; - helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/503.html'), {}, jar, csrf_token, (err, res, body) => { - meta.config.allowedFileExtensions = oldValue; - assert.ifError(err); - assert.strictEqual(res.statusCode, 200); - assert(body && body.status && body.response && body.response.images); - assert(Array.isArray(body.response.images)); - assert(body.response.images[0].url); - done(); - }); + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/503.html'), {}, jar, csrf_token); + meta.config.allowedFileExtensions = oldValue; + + assert.strictEqual(response.statusCode, 200); + assert(body && body.status && body.response && body.response.images); + assert(Array.isArray(body.response.images)); + assert(body.response.images[0].url); }); - it('should fail to upload image to post if image dimensions are too big', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/toobig.png'), {}, jar, csrf_token, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 500); - assert(body && body.status && body.status.message); - assert.strictEqual(body.status.message, 'Image dimensions are too big'); - done(); - }); + it('should fail to upload image to post if image dimensions are too big', async () => { + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/toobig.png'), {}, jar, csrf_token); + assert.strictEqual(response.statusCode, 500); + assert(body && body.status && body.status.message); + assert.strictEqual(body.status.message, 'Image dimensions are too big'); }); - it('should fail to upload image to post if image is broken', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/brokenimage.png'), {}, jar, csrf_token, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 500); - assert(body && body.status && body.status.message); - assert.strictEqual(body.status.message, 'Input file contains unsupported image format'); - done(); - }); + it('should fail to upload image to post if image is broken', async () => { + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/brokenimage.png'), {}, jar, csrf_token); + assert.strictEqual(response.statusCode, 500); + assert(body && body.status && body.status.message); + assert.strictEqual(body.status.message, 'Input file contains unsupported image format'); }); it('should fail if file is not an image', (done) => { @@ -286,39 +260,22 @@ describe('Upload Controllers', () => { }); }); - it('should delete users uploads if account is deleted', (done) => { - let uid; - let url; + it('should delete users uploads if account is deleted', async () => { + const uid = await user.create({ username: 'uploader', password: 'barbar' }); const file = require('../src/file'); + const data = await helpers.loginUser('uploader', 'barbar'); + const { body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, data.jar, data.csrf_token); - async.waterfall([ - function (next) { - user.create({ username: 'uploader', password: 'barbar' }, next); - }, - function (_uid, next) { - uid = _uid; - helpers.loginUser('uploader', 'barbar', next); - }, - function (data, next) { - helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, data.jar, data.csrf_token, next); - }, - function (res, body, next) { - assert(body && body.status && body.response && body.response.images); - assert(Array.isArray(body.response.images)); - assert(body.response.images[0].url); - url = body.response.images[0].url; + assert(body && body.status && body.response && body.response.images); + assert(Array.isArray(body.response.images)); + assert(body.response.images[0].url); + const { url } = body.response.images[0]; - user.delete(1, uid, next); - }, - function (userData, next) { - const filePath = path.join(nconf.get('upload_path'), url.replace('/assets/uploads', '')); - file.exists(filePath, next); - }, - function (exists, next) { - assert(!exists); - done(); - }, - ], done); + await user.delete(1, uid); + + const filePath = path.join(nconf.get('upload_path'), url.replace('/assets/uploads', '')); + const exists = await file.exists(filePath); + assert(!exists); }); after(emptyUploadsFolder); @@ -337,173 +294,147 @@ describe('Upload Controllers', () => { regular_csrf_token = regularLogin.csrf_token; }); - it('should upload site logo', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadlogo`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 200); - assert(Array.isArray(body)); - assert.equal(body[0].url, `${nconf.get('relative_path')}/assets/uploads/system/site-logo.png`); - done(); - }); + it('should upload site logo', async () => { + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadlogo`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token); + assert.strictEqual(response.statusCode, 200); + assert(Array.isArray(body)); + assert.equal(body[0].url, `${nconf.get('relative_path')}/assets/uploads/system/site-logo.png`); }); - it('should fail to upload invalid file type', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/admin/category/uploadpicture`, path.join(__dirname, '../test/files/503.html'), { params: JSON.stringify({ cid: cid }) }, jar, csrf_token, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 500); - assert.equal(body.error, '[[error:invalid-image-type, image/png&#44; image/jpeg&#44; image/pjpeg&#44; image/jpg&#44; image/gif&#44; image/svg+xml]]'); - done(); - }); + it('should fail to upload invalid file type', async () => { + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/category/uploadpicture`, path.join(__dirname, '../test/files/503.html'), { params: JSON.stringify({ cid: cid }) }, jar, csrf_token); + assert.strictEqual(response.statusCode, 500); + assert.equal(body.error, '[[error:invalid-image-type, image/png&#44; image/jpeg&#44; image/pjpeg&#44; image/jpg&#44; image/gif&#44; image/svg+xml]]'); }); - it('should fail to upload category image with invalid json params', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/admin/category/uploadpicture`, path.join(__dirname, '../test/files/test.png'), { params: 'invalid json' }, jar, csrf_token, (err, res, body) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 500); - assert.equal(body.error, '[[error:invalid-json]]'); - done(); - }); + it('should fail to upload category image with invalid json params', async () => { + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/category/uploadpicture`, path.join(__dirname, '../test/files/test.png'), { params: 'invalid json' }, jar, csrf_token); + assert.strictEqual(response.statusCode, 500); + assert.equal(body.error, '[[error:invalid-json]]'); }); - it('should upload category image', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/admin/category/uploadpicture`, path.join(__dirname, '../test/files/test.png'), { params: JSON.stringify({ cid: cid }) }, jar, csrf_token, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(Array.isArray(body)); - assert.equal(body[0].url, `${nconf.get('relative_path')}/assets/uploads/category/category-1.png`); - done(); - }); + it('should upload category image', async () => { + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/category/uploadpicture`, path.join(__dirname, '../test/files/test.png'), { params: JSON.stringify({ cid: cid }) }, jar, csrf_token); + assert.equal(response.statusCode, 200); + assert(Array.isArray(body)); + assert.equal(body[0].url, `${nconf.get('relative_path')}/assets/uploads/category/category-1.png`); }); - it('should upload default avatar', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadDefaultAvatar`, path.join(__dirname, '../test/files/test.png'), { }, jar, csrf_token, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(body[0].url, `${nconf.get('relative_path')}/assets/uploads/system/avatar-default.png`); - done(); - }); + it('should upload default avatar', async () => { + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadDefaultAvatar`, path.join(__dirname, '../test/files/test.png'), { }, jar, csrf_token); + assert.equal(response.statusCode, 200); + assert.equal(body[0].url, `${nconf.get('relative_path')}/assets/uploads/system/avatar-default.png`); }); - it('should upload og image', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadOgImage`, path.join(__dirname, '../test/files/test.png'), { }, jar, csrf_token, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert.equal(body[0].url, `${nconf.get('relative_path')}/assets/uploads/system/og-image.png`); - done(); - }); + it('should upload og image', async () => { + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadOgImage`, path.join(__dirname, '../test/files/test.png'), { }, jar, csrf_token); + assert.equal(response.statusCode, 200); + assert.equal(body[0].url, `${nconf.get('relative_path')}/assets/uploads/system/og-image.png`); }); - it('should upload favicon', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadfavicon`, path.join(__dirname, '../test/files/favicon.ico'), {}, jar, csrf_token, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(Array.isArray(body)); - assert.equal(body[0].url, '/assets/uploads/system/favicon.ico'); - done(); - }); + it('should upload favicon', async () => { + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadfavicon`, path.join(__dirname, '../test/files/favicon.ico'), {}, jar, csrf_token); + assert.equal(response.statusCode, 200); + assert(Array.isArray(body)); + assert.equal(body[0].url, '/assets/uploads/system/favicon.ico'); }); - it('should upload touch icon', (done) => { + it('should upload touch icon', async () => { const touchiconAssetPath = '/assets/uploads/system/touchicon-orig.png'; - helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadTouchIcon`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(Array.isArray(body)); - assert.equal(body[0].url, touchiconAssetPath); - meta.config['brand:touchIcon'] = touchiconAssetPath; - request(`${nconf.get('url')}/apple-touch-icon`, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); - }); + const { response, body } = await helpers.uploadFile( + `${nconf.get('url')}/api/admin/uploadTouchIcon`, + path.join(__dirname, '../test/files/test.png'), + {}, + jar, + csrf_token + ); + + assert.equal(response.statusCode, 200); + assert(Array.isArray(body)); + assert.equal(body[0].url, touchiconAssetPath); + meta.config['brand:touchIcon'] = touchiconAssetPath; + const { response: res1, body: body1 } = await request.get(`${nconf.get('url')}/apple-touch-icon`); + assert.equal(res1.statusCode, 200); + assert(body1); }); - it('should upload regular file', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/admin/upload/file`, path.join(__dirname, '../test/files/test.png'), { + it('should upload regular file', async () => { + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/upload/file`, path.join(__dirname, '../test/files/test.png'), { params: JSON.stringify({ folder: 'system', }), - }, jar, csrf_token, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(Array.isArray(body)); - assert.equal(body[0].url, '/assets/uploads/system/test.png'); - assert(file.existsSync(path.join(nconf.get('upload_path'), 'system', 'test.png'))); - done(); - }); + }, jar, csrf_token); + + assert.equal(response.statusCode, 200); + assert(Array.isArray(body)); + assert.equal(body[0].url, '/assets/uploads/system/test.png'); + assert(file.existsSync(path.join(nconf.get('upload_path'), 'system', 'test.png'))); }); - it('should fail to upload regular file in wrong directory', (done) => { - helpers.uploadFile(`${nconf.get('url')}/api/admin/upload/file`, path.join(__dirname, '../test/files/test.png'), { + it('should fail to upload regular file in wrong directory', async () => { + const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/upload/file`, path.join(__dirname, '../test/files/test.png'), { params: JSON.stringify({ folder: '../../system', }), - }, jar, csrf_token, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 500); - assert.strictEqual(body.error, '[[error:invalid-path]]'); - done(); - }); + }, jar, csrf_token); + + assert.equal(response.statusCode, 500); + assert.strictEqual(body.error, '[[error:invalid-path]]'); }); describe('ACP uploads screen', () => { it('should create a folder', async () => { - const res = await helpers.createFolder('', 'myfolder', jar, csrf_token); - assert.strictEqual(res.statusCode, 200); + const { response } = await helpers.createFolder('', 'myfolder', jar, csrf_token); + assert.strictEqual(response.statusCode, 200); assert(file.existsSync(path.join(nconf.get('upload_path'), 'myfolder'))); }); it('should fail to create a folder if it already exists', async () => { - const res = await helpers.createFolder('', 'myfolder', jar, csrf_token); - assert.strictEqual(res.statusCode, 403); - assert.deepStrictEqual(res.body.status, { + const { response, body } = await helpers.createFolder('', 'myfolder', jar, csrf_token); + assert.strictEqual(response.statusCode, 403); + assert.deepStrictEqual(body.status, { code: 'forbidden', message: 'Folder exists', }); }); it('should fail to create a folder as a non-admin', async () => { - const res = await helpers.createFolder('', 'hisfolder', regularJar, regular_csrf_token); - assert.strictEqual(res.statusCode, 403); - assert.deepStrictEqual(res.body.status, { + const { response, body } = await helpers.createFolder('', 'hisfolder', regularJar, regular_csrf_token); + assert.strictEqual(response.statusCode, 403); + assert.deepStrictEqual(body.status, { code: 'forbidden', message: 'You are not authorised to make this call', }); }); it('should fail to create a folder in wrong directory', async () => { - const res = await helpers.createFolder('../traversing', 'unexpectedfolder', jar, csrf_token); - assert.strictEqual(res.statusCode, 403); - assert.deepStrictEqual(res.body.status, { + const { response, body } = await helpers.createFolder('../traversing', 'unexpectedfolder', jar, csrf_token); + assert.strictEqual(response.statusCode, 403); + assert.deepStrictEqual(body.status, { code: 'forbidden', message: 'Invalid path', }); }); it('should use basename of given folderName to create new folder', async () => { - const res = await helpers.createFolder('/myfolder', '../another folder', jar, csrf_token); - assert.strictEqual(res.statusCode, 200); + const { response } = await helpers.createFolder('/myfolder', '../another folder', jar, csrf_token); + assert.strictEqual(response.statusCode, 200); const slugifiedName = 'another-folder'; assert(file.existsSync(path.join(nconf.get('upload_path'), 'myfolder', slugifiedName))); }); it('should fail to delete a file as a non-admin', async () => { - const res = await requestAsync.delete(`${nconf.get('url')}/api/v3/files`, { + const { response, body } = await request.delete(`${nconf.get('url')}/api/v3/files`, { body: { path: '/system/test.png', }, jar: regularJar, - json: true, headers: { 'x-csrf-token': regular_csrf_token, }, - simple: false, - resolveWithFullResponse: true, }); - assert.strictEqual(res.statusCode, 403); - assert.deepStrictEqual(res.body.status, { + assert.strictEqual(response.statusCode, 403); + assert.deepStrictEqual(body.status, { code: 'forbidden', message: 'You are not authorised to make this call', }); diff --git a/test/user.js b/test/user.js index 907a43f388..421a8c2cf4 100644 --- a/test/user.js +++ b/test/user.js @@ -1,14 +1,12 @@ 'use strict'; const assert = require('assert'); -const async = require('async'); const fs = require('fs'); const path = require('path'); const nconf = require('nconf'); const validator = require('validator'); -const request = require('request'); -const requestAsync = require('request-promise-native'); const jwt = require('jsonwebtoken'); +const { setTimeout } = require('node:timers/promises'); const db = require('./mocks/databasemock'); const User = require('../src/user'); @@ -24,6 +22,7 @@ const socketUser = require('../src/socket.io/user'); const apiUser = require('../src/api/users'); const utils = require('../src/utils'); const privileges = require('../src/privileges'); +const request = require('../src/request'); describe('User', () => { let userData; @@ -174,7 +173,7 @@ describe('User', () => { }); describe('.uniqueUsername()', () => { - it('should deal with collisions', (done) => { + it('should deal with collisions', async () => { const users = []; for (let i = 0; i < 10; i += 1) { users.push({ @@ -182,25 +181,16 @@ describe('User', () => { email: `jane.doe${i}@example.com`, }); } + for (const user of users) { + // eslint-disable-next-line no-await-in-loop + await User.create(user); + } - async.series([ - function (next) { - async.eachSeries(users, (user, next) => { - User.create(user, next); - }, next); - }, - function (next) { - User.uniqueUsername({ - username: 'Jane Doe', - userslug: 'jane-doe', - }, (err, username) => { - assert.ifError(err); - - assert.strictEqual(username, 'Jane Doe 9'); - next(); - }); - }, - ], done); + const username = await User.uniqueUsername({ + username: 'Jane Doe', + userslug: 'jane-doe', + }); + assert.strictEqual(username, 'Jane Doe 9'); }); }); @@ -252,12 +242,10 @@ describe('User', () => { }); describe('.getModeratorUids()', () => { - before((done) => { - async.series([ - async.apply(groups.create, { name: 'testGroup' }), - async.apply(groups.join, 'cid:1:privileges:groups:moderate', 'testGroup'), - async.apply(groups.join, 'testGroup', 1), - ], done); + before(async () => { + await groups.create({ name: 'testGroup' }); + await groups.join('cid:1:privileges:groups:moderate', 'testGroup'); + await groups.join('testGroup', 1); }); it('should retrieve all users with moderator bit in category privilege', (done) => { @@ -269,38 +257,13 @@ describe('User', () => { }); }); - after((done) => { - async.series([ - async.apply(groups.leave, 'cid:1:privileges:groups:moderate', 'testGroup'), - async.apply(groups.destroy, 'testGroup'), - ], done); + after(async () => { + groups.leave('cid:1:privileges:groups:moderate', 'testGroup'); + groups.destroy('testGroup'); }); }); describe('.isReadyToPost()', () => { - it('should error when a user makes two posts in quick succession', (done) => { - meta.config = meta.config || {}; - meta.config.postDelay = '10'; - - async.series([ - async.apply(Topics.post, { - uid: testUid, - title: 'Topic 1', - content: 'lorem ipsum', - cid: testCid, - }), - async.apply(Topics.post, { - uid: testUid, - title: 'Topic 2', - content: 'lorem ipsum', - cid: testCid, - }), - ], (err) => { - assert(err); - done(); - }); - }); - it('should allow a post if the last post time is > 10 seconds', (done) => { User.setUserField(testUid, 'lastposttime', +new Date() - (11 * 1000), () => { Topics.post({ @@ -355,13 +318,12 @@ describe('User', () => { const titles = new Array(10).fill('topic title'); const res = await Promise.allSettled(titles.map(async (title) => { const { body } = await helpers.request('post', '/api/v3/topics', { - form: { + body: { cid: testCid, title: title, content: 'the content', }, jar: jar, - json: true, }); return body.status; })); @@ -486,32 +448,19 @@ describe('User', () => { assert.equal(data.users[0].username, 'ipsearch_filter'); }); - it('should sort results by username', (done) => { - async.waterfall([ - function (next) { - User.create({ username: 'brian' }, next); - }, - function (uid, next) { - User.create({ username: 'baris' }, next); - }, - function (uid, next) { - User.create({ username: 'bzari' }, next); - }, - function (uid, next) { - User.search({ - uid: testUid, - query: 'b', - sortBy: 'username', - paginate: false, - }, next); - }, - ], (err, data) => { - assert.ifError(err); - assert.equal(data.users[0].username, 'baris'); - assert.equal(data.users[1].username, 'brian'); - assert.equal(data.users[2].username, 'bzari'); - done(); + it('should sort results by username', async () => { + await User.create({ username: 'brian' }); + await User.create({ username: 'baris' }); + await User.create({ username: 'bzari' }); + const data = await User.search({ + uid: testUid, + query: 'b', + sortBy: 'username', + paginate: false, }); + assert.equal(data.users[0].username, 'baris'); + assert.equal(data.users[1].username, 'brian'); + assert.equal(data.users[2].username, 'bzari'); }); }); @@ -991,10 +940,8 @@ describe('User', () => { it('should let you set an external image', async () => { const token = await helpers.getCsrfToken(jar); - const body = await requestAsync(`${nconf.get('url')}/api/v3/users/${uid}/picture`, { + const { body } = await request.put(`${nconf.get('url')}/api/v3/users/${uid}/picture`, { jar, - method: 'put', - json: true, headers: { 'x-csrf-token': token, }, @@ -1193,46 +1140,34 @@ describe('User', () => { }); }); - it('should load profile page', (done) => { - request(`${nconf.get('url')}/api/user/updatedagain`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load profile page', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/updatedagain`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); - it('should load settings page', (done) => { - request(`${nconf.get('url')}/api/user/updatedagain/settings`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body.settings); - assert(body.languages); - assert(body.homePageRoutes); - done(); - }); + it('should load settings page', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/updatedagain/settings`, { jar }); + assert.equal(response.statusCode, 200); + assert(body.settings); + assert(body.languages); + assert(body.homePageRoutes); }); - it('should load edit page', (done) => { - request(`${nconf.get('url')}/api/user/updatedagain/edit`, { jar: jar, json: true }, (err, res, body) => { - assert.ifError(err); - assert.equal(res.statusCode, 200); - assert(body); - done(); - }); + it('should load edit page', async () => { + const { response, body } = await request.get(`${nconf.get('url')}/api/user/updatedagain/edit`, { jar }); + assert.equal(response.statusCode, 200); + assert(body); }); it('should load edit/email page', async () => { - const res = await requestAsync(`${nconf.get('url')}/api/user/updatedagain/edit/email`, { jar: jar, json: true, resolveWithFullResponse: true }); - assert.strictEqual(res.statusCode, 200); - assert(res.body); + const { response, body } = await request.get(`${nconf.get('url')}/api/user/updatedagain/edit/email`, { jar }); + assert.strictEqual(response.statusCode, 200); + assert(body); // Accessing this page will mark the user's account as needing an updated email, below code undo's. - await requestAsync({ - uri: `${nconf.get('url')}/register/abort`, + await request.post(`${nconf.get('url')}/register/abort`, { jar, - method: 'POST', - simple: false, headers: { 'x-csrf-token': csrf_token, }, @@ -1246,7 +1181,7 @@ describe('User', () => { }); await groups.join('Test', uid); - const body = await requestAsync(`${nconf.get('url')}/api/user/updatedagain/groups`, { jar: jar, json: true }); + const { body } = await request.get(`${nconf.get('url')}/api/user/updatedagain/groups`, { jar }); assert(Array.isArray(body.groups)); assert.equal(body.groups[0].name, 'Test'); @@ -1279,30 +1214,12 @@ describe('User', () => { assert.equal(data[0].timestamp, now); }); - it('should return the correct ban reason', (done) => { - async.series([ - function (next) { - User.bans.ban(testUserUid, 0, '', (err) => { - assert.ifError(err); - next(err); - }); - }, - function (next) { - User.getModerationHistory(testUserUid, (err, data) => { - assert.ifError(err); - assert.equal(data.bans.length, 1, 'one ban'); - assert.equal(data.bans[0].reason, '[[user:info.banned-no-reason]]', 'no ban reason'); - - next(err); - }); - }, - ], (err) => { - assert.ifError(err); - User.bans.unban(testUserUid, (err) => { - assert.ifError(err); - done(); - }); - }); + it('should return the correct ban reason', async () => { + await User.bans.ban(testUserUid, 0, ''); + const data = await User.getModerationHistory(testUserUid); + assert.equal(data.bans.length, 1, 'one ban'); + assert.equal(data.bans[0].reason, '[[user:info.banned-no-reason]]', 'no ban reason'); + await User.bans.unban(testUserUid); }); it('should ban user permanently', (done) => { @@ -1316,22 +1233,14 @@ describe('User', () => { }); }); - it('should ban user temporarily', (done) => { - User.bans.ban(testUserUid, Date.now() + 2000, (err) => { - assert.ifError(err); - - User.bans.isBanned(testUserUid, (err, isBanned) => { - assert.ifError(err); - assert.equal(isBanned, true); - setTimeout(() => { - User.bans.isBanned(testUserUid, (err, isBanned) => { - assert.ifError(err); - assert.equal(isBanned, false); - User.bans.unban(testUserUid, done); - }); - }, 3000); - }); - }); + it('should ban user temporarily', async () => { + await User.bans.ban(testUserUid, Date.now() + 2000); + let isBanned = await User.bans.isBanned(testUserUid); + assert.equal(isBanned, true); + await setTimeout(3000); + isBanned = await User.bans.isBanned(testUserUid); + assert.equal(isBanned, false); + await User.bans.unban(testUserUid); }); it('should error if until is NaN', (done) => { @@ -1409,26 +1318,19 @@ describe('User', () => { describe('Digest.getSubscribers', () => { const uidIndex = {}; - before((done) => { + before(async () => { const testUsers = ['daysub', 'offsub', 'nullsub', 'weeksub']; - async.each(testUsers, (username, next) => { - async.waterfall([ - async.apply(User.create, { username: username, email: `${username}@example.com` }), - function (uid, next) { - if (username === 'nullsub') { - return setImmediate(next); - } + await Promise.all(testUsers.map(async (username) => { + const uid = await User.create({ username, email: `${username}@example.com` }); + if (username === 'nullsub') { + return; + } + uidIndex[username] = uid; - uidIndex[username] = uid; - - const sub = username.slice(0, -3); - async.parallel([ - async.apply(User.updateDigestSetting, uid, sub), - async.apply(User.setSetting, uid, 'dailyDigestFreq', sub), - ], next); - }, - ], next); - }, done); + const sub = username.slice(0, -3); + await User.updateDigestSetting(uid, sub); + await User.setSetting(uid, 'dailyDigestFreq', sub); + })); }); it('should accurately build digest list given ACP default "null" (not set)', (done) => { @@ -1440,71 +1342,38 @@ describe('User', () => { }); }); - it('should accurately build digest list given ACP default "day"', (done) => { - async.series([ - async.apply(meta.configs.set, 'dailyDigestFreq', 'day'), - function (next) { - User.digest.getSubscribers('day', (err, subs) => { - assert.ifError(err); - assert.strictEqual(subs.includes(uidIndex.daysub.toString()), true); // daysub does get emailed - assert.strictEqual(subs.includes(uidIndex.weeksub.toString()), false); // weeksub does not get emailed - assert.strictEqual(subs.includes(uidIndex.offsub.toString()), false); // offsub doesn't get emailed + it('should accurately build digest list given ACP default "day"', async () => { + await meta.configs.set('dailyDigestFreq', 'day'); + const subs = await User.digest.getSubscribers('day'); - next(); - }); - }, - ], done); + assert.strictEqual(subs.includes(uidIndex.daysub.toString()), true); // daysub does get emailed + assert.strictEqual(subs.includes(uidIndex.weeksub.toString()), false); // weeksub does not get emailed + assert.strictEqual(subs.includes(uidIndex.offsub.toString()), false); // offsub doesn't get emailed }); - it('should accurately build digest list given ACP default "week"', (done) => { - async.series([ - async.apply(meta.configs.set, 'dailyDigestFreq', 'week'), - function (next) { - User.digest.getSubscribers('week', (err, subs) => { - assert.ifError(err); - assert.strictEqual(subs.includes(uidIndex.weeksub.toString()), true); // weeksub gets emailed - assert.strictEqual(subs.includes(uidIndex.daysub.toString()), false); // daysub gets emailed - assert.strictEqual(subs.includes(uidIndex.offsub.toString()), false); // offsub does not get emailed + it('should accurately build digest list given ACP default "week"', async () => { + await meta.configs.set('dailyDigestFreq', 'week'); + const subs = await User.digest.getSubscribers('week'); - next(); - }); - }, - ], done); + assert.strictEqual(subs.includes(uidIndex.weeksub.toString()), true); // weeksub gets emailed + assert.strictEqual(subs.includes(uidIndex.daysub.toString()), false); // daysub gets emailed + assert.strictEqual(subs.includes(uidIndex.offsub.toString()), false); // offsub does not get emailed }); - it('should accurately build digest list given ACP default "off"', (done) => { - async.series([ - async.apply(meta.configs.set, 'dailyDigestFreq', 'off'), - function (next) { - User.digest.getSubscribers('day', (err, subs) => { - assert.ifError(err); - assert.strictEqual(subs.length, 1); - - next(); - }); - }, - ], done); + it('should accurately build digest list given ACP default "off"', async () => { + await meta.configs.set('dailyDigestFreq', 'off'); + const subs = await User.digest.getSubscribers('day'); + assert.strictEqual(subs.length, 1); }); }); describe('digests', () => { let uid; - before((done) => { - async.waterfall([ - function (next) { - User.create({ username: 'digestuser', email: 'test@example.com' }, next); - }, - function (_uid, next) { - uid = _uid; - User.updateDigestSetting(uid, 'day', next); - }, - function (next) { - User.setSetting(uid, 'dailyDigestFreq', 'day', next); - }, - function (next) { - User.setSetting(uid, 'notificationType_test', 'notificationemail', next); - }, - ], done); + before(async () => { + uid = await User.create({ username: 'digestuser', email: 'test@example.com' }); + await User.updateDigestSetting(uid, 'day'); + await User.setSetting(uid, 'dailyDigestFreq', 'day'); + await User.setSetting(uid, 'notificationType_test', 'notificationemail'); }); it('should send digests', async () => { @@ -1549,106 +1418,65 @@ describe('User', () => { }); describe('unsubscribe via POST', () => { - it('should unsubscribe from digest if one-click unsubscribe is POSTed', (done) => { + it('should unsubscribe from digest if one-click unsubscribe is POSTed', async () => { const token = jwt.sign({ template: 'digest', uid: uid, }, nconf.get('secret')); - request({ - method: 'post', - url: `${nconf.get('url')}/email/unsubscribe/${token}`, - }, (err, res) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 200); - - db.getObjectField(`user:${uid}:settings`, 'dailyDigestFreq', (err, value) => { - assert.ifError(err); - assert.strictEqual(value, 'off'); - done(); - }); - }); + const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/${token}`); + assert.strictEqual(response.statusCode, 200); + const value = await db.getObjectField(`user:${uid}:settings`, 'dailyDigestFreq'); + assert.strictEqual(value, 'off'); }); - it('should unsubscribe from notifications if one-click unsubscribe is POSTed', (done) => { + it('should unsubscribe from notifications if one-click unsubscribe is POSTed', async () => { const token = jwt.sign({ template: 'notification', type: 'test', uid: uid, }, nconf.get('secret')); - request({ - method: 'post', - url: `${nconf.get('url')}/email/unsubscribe/${token}`, - }, (err, res) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 200); + const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/${token}`); + assert.strictEqual(response.statusCode, 200); - db.getObjectField(`user:${uid}:settings`, 'notificationType_test', (err, value) => { - assert.ifError(err); - assert.strictEqual(value, 'notification'); - done(); - }); - }); + const value = await db.getObjectField(`user:${uid}:settings`, 'notificationType_test'); + assert.strictEqual(value, 'notification'); }); - it('should return errors on missing template in token', (done) => { + it('should return errors on missing template in token', async () => { const token = jwt.sign({ uid: uid, }, nconf.get('secret')); - request({ - method: 'post', - url: `${nconf.get('url')}/email/unsubscribe/${token}`, - }, (err, res) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 404); - done(); - }); + const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/${token}`); + assert.strictEqual(response.statusCode, 404); }); - it('should return errors on wrong template in token', (done) => { + it('should return errors on wrong template in token', async () => { const token = jwt.sign({ template: 'user', uid: uid, }, nconf.get('secret')); - request({ - method: 'post', - url: `${nconf.get('url')}/email/unsubscribe/${token}`, - }, (err, res) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 404); - done(); - }); + const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/${token}`); + assert.strictEqual(response.statusCode, 404); }); - it('should return errors on missing token', (done) => { - request({ - method: 'post', - url: `${nconf.get('url')}/email/unsubscribe/`, - }, (err, res) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 404); - done(); - }); + it('should return errors on missing token', async () => { + const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/`); + assert.strictEqual(response.statusCode, 404); }); - it('should return errors on token signed with wrong secret (verify-failure)', (done) => { + it('should return errors on token signed with wrong secret (verify-failure)', async () => { const token = jwt.sign({ template: 'notification', type: 'test', uid: uid, }, `${nconf.get('secret')}aababacaba`); - request({ - method: 'post', - url: `${nconf.get('url')}/email/unsubscribe/${token}`, - }, (err, res) => { - assert.ifError(err); - assert.strictEqual(res.statusCode, 403); - done(); - }); + const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/${token}`); + assert.strictEqual(response.statusCode, 403); }); }); }); @@ -1848,36 +1676,17 @@ describe('User', () => { } }); - it('should set moderation note', (done) => { - let adminUid; - async.waterfall([ - function (next) { - User.create({ username: 'noteadmin' }, next); - }, - function (_adminUid, next) { - adminUid = _adminUid; - groups.join('administrators', adminUid, next); - }, - function (next) { - socketUser.setModerationNote({ uid: adminUid }, { uid: testUid, note: 'this is a test user' }, next); - }, - function (next) { - setTimeout(next, 50); - }, - function (next) { - socketUser.setModerationNote({ uid: adminUid }, { uid: testUid, note: ' { - assert.ifError(err); - assert.equal(notes[0].note, '<svg/onload=alert(document.location);//'); - assert.equal(notes[0].uid, adminUid); - assert.equal(notes[1].note, 'this is a test user'); - assert(notes[0].timestamp); - done(); - }); + it('should set moderation note', async () => { + const adminUid = await User.create({ username: 'noteadmin' }); + await groups.join('administrators', adminUid); + await socketUser.setModerationNote({ uid: adminUid }, { uid: testUid, note: 'this is a test user' }); + await setTimeout(50); + await socketUser.setModerationNote({ uid: adminUid }, { uid: testUid, note: ' { @@ -1974,89 +1783,66 @@ describe('User', () => { gdpr_consent: true, }); const { jar } = await helpers.loginUser('admin', '123456'); - const { users } = await requestAsync(`${nconf.get('url')}/api/admin/manage/registration`, { jar, json: true }); + const { body: { users } } = await request.get(`${nconf.get('url')}/api/admin/manage/registration`, { jar }); assert.equal(users[0].username, 'rejectme'); assert.equal(users[0].email, '<script>alert("ok")<script>reject@me.com'); }); - it('should fail to add user to queue if username is taken', (done) => { - helpers.registerUser({ + it('should fail to add user to queue if username is taken', async () => { + const { body } = await helpers.registerUser({ username: 'rejectme', password: '123456', 'password-confirm': '123456', email: '