Skip to content
This repository has been archived by the owner on Dec 13, 2021. It is now read-only.

🚧 Update test suite #64

Open
wants to merge 7 commits into
base: node
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"express": "^4.17.1",
"express-minify": "^1.0.0",
"i18n": "^0.8.3",
"jsdom": "^16.0.0",
"kill-port": "^1.6.0",
"knex": "^0.19.4",
"lighthouse": "^5.2.0",
Expand Down
1 change: 0 additions & 1 deletion src/Routes/Authentication.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ class AuthenticationRoute extends BaseRoute {
req.session = null;
res.redirect('/');
});

}

get getRouter() {
Expand Down
1 change: 0 additions & 1 deletion src/Routes/Index.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ class IndexRoute extends BaseRoute {
});
});


this.router.get('/sitemap', (req, res) => {
sitemap.get(this.db).then(data => {
sitemap.save(data).then(() => {
Expand Down
6 changes: 4 additions & 2 deletions src/Structure/BaseRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ class BaseRoute {

isMod(req, res, next) {
if (req.session.user.mod || req.session.user.admin) return next();
res.render('error', { title: 'Page not found', status: 404, message: 'The page you were looking for could not be found.' });
// res.render('error', { title: 'Page not found', status: 404, message: 'The page you were looking for could not be found.' });
res.status(403).render('authRequired', { title: 'Authentication is required' });
}

isAdmin(req, res, next) {
if (req.session.user.admin) return next();
res.render('error', { title: 'Page not found', status: 404, message: 'The page you were looking for could not be found.' });
// res.render('error', { title: 'Page not found', status: 404, message: 'The page you were looking for could not be found.' });
res.status(403).render('authRequired', { title: 'Authentication is required' });
}

}
Expand Down
28 changes: 27 additions & 1 deletion src/Website.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,39 @@ class Website {
this.app.use('/assets', express.static(path.join(__dirname, 'Assets')));
this.app.use('/codemirror', express.static(path.join(__dirname, '..', 'node_modules', 'codemirror')));
this.app.use((req, res, next) => {
// Test suite logic
res.locals.adblock = req.headers['x-disable-adsense'] === config.secret;
if (req.headers['x-auth-as-user'] === config.secret ||
req.headers['x-auth-as-admin'] === config.secret ||
req.headers['x-auth-as-mod'] === config.secret) {
req.session.user = {
id: '123456789012345678',
username: 'User',
avatar: '',
discriminator: '1234',
locale: 'en-US',
mfa_enabled: false,
flags: 0,
access_token: '',
expires_in: 604800,
refresh_token: '',
scope: 'identify',
token_type: 'Bearer',
admin: false,
mod: false
};
if (req.headers['x-auth-as-mod'] || req.headers['x-auth-as-admin']) req.session.user.mod = true;
if (req.headers['x-auth-as-admin']) req.session.user.admin = true;
}
if (req.headers['x-auth-as-anon'] === config.secret) req.session.user = undefined;

// App locals
const host = req.get('host');
res.locals.route = req.connection.encrypted ? 'https://' : 'http://' + host + req.path;
res.locals.isProduction = host.toLowerCase().trim() === 'botblock.org';
res.locals.isStaging = host.toLowerCase().trim() === 'staging.botblock.org';
res.locals.isDevelopment = !res.locals.isProduction && !res.locals.isStaging;
res.locals.language = req.cookies.lang;
res.locals.adblock = req.headers['x-disable-adsense'] && req.headers['x-disable-adsense'] === config.secret;
res.locals.breadcrumb = req.path.split('/').splice(1, 3, null);
res.locals.user = req.session.user;
res.cookie('url', req.path.startsWith('/auth') ? '/' : req.path);
Expand Down
202 changes: 101 additions & 101 deletions test/Routes/API.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { describe, it, expect, request, ratelimitBypass, resetRatelimits, ratelimitTest, locale, titleCheck, db } = require('../base');
const { describe, it, expect, request, ratelimitBypass, resetRatelimits, checks, locale, db } = require('../base');
const listProps = require('../../src/Util/listProps');

describe('Invalid route (/api/helloworld)', () => {
Expand Down Expand Up @@ -71,7 +71,7 @@ describe('/api/docs', () => {
it('has the correct page title', done => {
test().end((err, res) => {
expect(res).to.be.html;
titleCheck(res, `API Docs - ${locale('site_name')} - ${locale('short_desc')}`);
checks.title(res, `API Docs - ${locale('site_name')} - ${locale('short_desc')}`);
done();
});
});
Expand Down Expand Up @@ -191,7 +191,7 @@ describe('/api/docs/libs', () => {
it('has the correct page title', done => {
test().end((err, res) => {
expect(res).to.be.html;
titleCheck(res, `Libraries - API Docs - ${locale('site_name')} - ${locale('short_desc')}`);
checks.title(res, `Libraries - API Docs - ${locale('site_name')} - ${locale('short_desc')}`);
done();
});
});
Expand Down Expand Up @@ -355,7 +355,7 @@ describe('/api/lists', () => {
});
});
it('does not ratelimit requests spaced correctly', function (done) {
ratelimitTest(this, 1, test, done);
checks.ratelimit(this, 1, test, done);
});
});

Expand Down Expand Up @@ -505,7 +505,7 @@ describe('/api/lists/:id', () => {
});
});
it('does not ratelimit requests spaced correctly', function (done) {
ratelimitTest(this, 1, test, done, 404);
checks.ratelimit(this, 1, test, done, 404);
});
});

Expand Down Expand Up @@ -535,6 +535,100 @@ describe('/api/lists/:id', () => {
});
});

describe('/api/legacy-ids', () => {
describe('GET', () => {
const test = () => ratelimitBypass(request().get('/api/legacy-ids'));
it('returns an OK status code', done => {
test().end((err, res) => {
expect(res).to.have.status(200);
done();
});
});
it('has a permissive CORS header', done => {
test().end((err, res) => {
expect(res).to.have.header('Access-Control-Allow-Origin', '*');
done();
});
});
it('returns a valid JSON body', done => {
test().end((err, res) => {
expect(res).to.be.json;
done();
});
});
it('contains an object of strings', done => {
test().end((err, res) => {
expect(res.body).to.be.a('object');
const entries = Object.values(res.body);
entries.forEach(entry => {
expect(entry).to.be.a('string');
});
done();
});
});
});

describe('GET (Ratelimited)', () => {
const test = () => request().get('/api/legacy-ids');
it('ratelimits spam requests', done => {
resetRatelimits().end(() => {
test().end(() => {
});
setTimeout(() => {
test().end((err, res) => {
expect(res).to.have.status(429);
expect(res).to.be.json;

expect(res.body).to.have.property('error', true);
expect(res.body).to.have.property('status', 429);

expect(res.body).to.have.property('retry_after');
expect(res.body.retry_after).to.be.a('number');

expect(res.body).to.have.property('ratelimit_reset');
expect(res.body.ratelimit_reset).to.be.a('number');

expect(res.body).to.have.property('ratelimit_ip');
expect(res.body.ratelimit_ip).to.be.a('string');

expect(res.body).to.have.property('ratelimit_route', '/api/legacy-ids');
expect(res.body).to.have.property('ratelimit_bot_id', '');
done();
});
}, 200);
});
});
it('does not ratelimit requests spaced correctly', function (done) {
checks.ratelimit(this, 1, test, done);
});
});

describe('POST', () => {
const test = () => ratelimitBypass(request().post('/api/legacy-ids'));
it('returns a Not Found status code', done => {
test().end((err, res) => {
expect(res).to.have.status(404);
done();
});
});
it('has a permissive CORS header', done => {
test().end((err, res) => {
expect(res).to.have.header('Access-Control-Allow-Origin', '*');
done();
});
});
it('returns an error JSON body', done => {
test().end((err, res) => {
expect(res).to.be.json;
expect(res.body).to.have.property('error', true);
expect(res.body).to.have.property('status', 404);
expect(res.body).to.have.property('message', 'Endpoint not found');
done();
});
});
});
});

describe('/api/count', () => {
describe('GET', () => {
const test = () => ratelimitBypass(request().get('/api/count'));
Expand Down Expand Up @@ -1069,7 +1163,7 @@ describe('/api/count', () => {
bot_id: '123456789123456789',
server_count: 10
});
ratelimitTest(this, 120, test, done);
checks.ratelimit(this, 120, test, done);
});
});
});
Expand Down Expand Up @@ -1281,7 +1375,7 @@ describe('/api/bots/:id', () => {
});
});
it('does not ratelimit requests spaced correctly', function (done) {
ratelimitTest(this, 30, test, done, 400);
checks.ratelimit(this, 30, test, done, 400);
});
});

Expand Down Expand Up @@ -1332,97 +1426,3 @@ describe('/api/bots/:id', () => {
});
});
});

describe('/api/legacy-ids', () => {
describe('GET', () => {
const test = () => ratelimitBypass(request().get('/api/legacy-ids'));
it('returns an OK status code', done => {
test().end((err, res) => {
expect(res).to.have.status(200);
done();
});
});
it('has a permissive CORS header', done => {
test().end((err, res) => {
expect(res).to.have.header('Access-Control-Allow-Origin', '*');
done();
});
});
it('returns a valid JSON body', done => {
test().end((err, res) => {
expect(res).to.be.json;
done();
});
});
it('contains an object of strings', done => {
test().end((err, res) => {
expect(res.body).to.be.a('object');
const entries = Object.values(res.body);
entries.forEach(entry => {
expect(entry).to.be.a('string');
});
done();
});
});
});

describe('GET (Ratelimited)', () => {
const test = () => request().get('/api/legacy-ids');
it('ratelimits spam requests', done => {
resetRatelimits().end(() => {
test().end(() => {
});
setTimeout(() => {
test().end((err, res) => {
expect(res).to.have.status(429);
expect(res).to.be.json;

expect(res.body).to.have.property('error', true);
expect(res.body).to.have.property('status', 429);

expect(res.body).to.have.property('retry_after');
expect(res.body.retry_after).to.be.a('number');

expect(res.body).to.have.property('ratelimit_reset');
expect(res.body.ratelimit_reset).to.be.a('number');

expect(res.body).to.have.property('ratelimit_ip');
expect(res.body.ratelimit_ip).to.be.a('string');

expect(res.body).to.have.property('ratelimit_route', '/api/legacy-ids');
expect(res.body).to.have.property('ratelimit_bot_id', '');
done();
});
}, 200);
});
});
it('does not ratelimit requests spaced correctly', function (done) {
ratelimitTest(this, 1, test, done);
});
});

describe('POST', () => {
const test = () => ratelimitBypass(request().post('/api/legacy-ids'));
it('returns a Not Found status code', done => {
test().end((err, res) => {
expect(res).to.have.status(404);
done();
});
});
it('has a permissive CORS header', done => {
test().end((err, res) => {
expect(res).to.have.header('Access-Control-Allow-Origin', '*');
done();
});
});
it('returns an error JSON body', done => {
test().end((err, res) => {
expect(res).to.be.json;
expect(res.body).to.have.property('error', true);
expect(res.body).to.have.property('status', 404);
expect(res.body).to.have.property('message', 'Endpoint not found');
done();
});
});
});
});
46 changes: 40 additions & 6 deletions test/Routes/Authentication.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { describe, it, expect, request } = require('../base');
const { describe, it, expect, request, auth } = require('../base');

describe('/auth', () => {
describe('GET', () => {
Expand All @@ -24,11 +24,45 @@ describe('/auth', () => {

describe('/auth/logout', () => {
describe('GET', () => {
const test = () => request().get('/auth/logout').redirects(0);
it('redirects to back to the homepage', done => {
test().end((err, res) => {
expect(res).to.redirectTo('/');
done();
describe('As an anonymous user', () => {
it('redirects to back to the homepage', done => {
auth.asAnon(request().get('/')).end((err1, res1) => {
expect(res1.text).to.include('<a href="/auth">Sign in with Discord</a>');

auth.asPrevious(request().get('/')).end((err2, res2) => {
expect(res2.text).to.include('<a href="/auth">Sign in with Discord</a>');

auth.asPrevious(request().get('/auth/logout')).redirects(0).end((err3, res3) => {
expect(res3).to.redirectTo('/');

auth.asPrevious(request().get('/')).end((err4, res4) => {
expect(res4.text).to.include('<a href="/auth">Sign in with Discord</a>');
done();
});
});
});
});
});
});

describe('As a logged in user', () => {
it('redirects to back to the homepage', done => {
auth.asUser(request().get('/')).end((err1, res1) => {
expect(res1.text).to.include('<p class="menu-label">User#1234</p>');

auth.asPrevious(request().get('/')).end((err2, res2) => {
expect(res2.text).to.include('<p class="menu-label">User#1234</p>');

auth.asPrevious(request().get('/auth/logout')).redirects(0).end((err3, res3) => {
expect(res3).to.redirectTo('/');

auth.asPrevious(request().get('/')).end((err4, res4) => {
expect(res4.text).to.include('<a href="/auth">Sign in with Discord</a>');
done();
});
});
});
});
});
});
});
Expand Down
Loading