Skip to content

Commit

Permalink
Pulls request listener out of Armadietto so it can share tests w/ mod…
Browse files Browse the repository at this point in the history
…ular
  • Loading branch information
DougReeder committed Feb 24, 2024
1 parent 8f77c92 commit 3849941
Show file tree
Hide file tree
Showing 14 changed files with 312 additions and 156 deletions.
4 changes: 2 additions & 2 deletions lib/app.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
const createError = require('http-errors');
const express = require('express');
const path = require('path');
const logger = require('morgan');
Expand Down Expand Up @@ -45,7 +44,8 @@ app.use('/', indexRouter);

// catches 404 and forwards to error handler
app.use(function (req, res, next) {
next(createError(404));
const name = req.path.slice(1);
errorPage(req, res, 404, { title: 'Not Found', message: `“${name}” doesn't exist` });
});

// error handler
Expand Down
34 changes: 19 additions & 15 deletions lib/armadietto.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,29 @@ class Armadietto {
this._cacheViews = options.cacheViews !== false;
this._basePath = options.basePath || '';
this._server = null;
configureLogger(options.logging);
this.init();
if (options.bare) {
return this.handle.bind(this);
} else {
configureLogger(options.logging);
this.init();

// These are the happy paths for shutdown.
process.on('SIGINT', this.stop.bind(this));
process.on('SIGTERM', this.stop.bind(this));
// These are the happy paths for shutdown.
process.on('SIGINT', this.stop.bind(this));
process.on('SIGTERM', this.stop.bind(this));

// Without these listeners, these would not be logged, only sent to stdout or stderr.
process.on('uncaughtExceptionMonitor', (err, origin) => {
getLogger().crit(`${origin} ${err}`);
});
// Without these listeners, these would not be logged, only sent to stdout or stderr.
process.on('uncaughtExceptionMonitor', (err, origin) => {
getLogger().crit(`${origin} ${err}`);
});

process.on('warning', (warning) => {
getLogger().warning(`${warning.name} ${warning.message} ${warning.stack}`);
});
process.on('warning', (warning) => {
getLogger().warning(`${warning.name} ${warning.message} ${warning.stack}`);
});

process.on('multipleResolves', (type, promise, reason) => {
getLogger().debug(`multipleResolves ${type}${reason}”`);
});
process.on('multipleResolves', (type, promise, reason) => {
getLogger().debug(`multipleResolves ${type}${reason}”`);
});
}
}

init () {
Expand Down
26 changes: 26 additions & 0 deletions spec/armadietto/a_not_found_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* eslint-env mocha, chai, node */

const Armadietto = require('../../lib/armadietto');
const { shouldHandleNonexistingResource } = require('../not_found.spec');

const mockStore = {
authorize (clientId, username, permissions) {
return 'a_token';
},
authenticate (params) {
}
};

describe('Nonexistant resource (monolithic)', function () {
beforeEach(function () {
this.app = new Armadietto({
bare: true,
store: mockStore,
allow: { signup: true },
http: { },
logging: { log_dir: './test-log', stdout: [], log_files: ['error'] }
});
});

shouldHandleNonexistingResource();
});
41 changes: 41 additions & 0 deletions spec/armadietto/a_root_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* eslint-env mocha, chai, node */

const Armadietto = require('../../lib/armadietto');
const { shouldBeWelcomeWithoutSignup, shouldBeWelcomeWithSignup } = require('../root.spec');

const store = {
authorize (clientId, username, permissions) {
return 'a_token';
},
authenticate (params) {
}
};

describe('root page (monolithic)', function () {
describe('w/o signup', function () {
beforeEach(function () {
this.app = new Armadietto({
bare: true,
store,
http: { },
logging: { log_dir: './test-log', stdout: [], log_files: ['error'] }
});
});

shouldBeWelcomeWithoutSignup();
});

describe('with signup', function () {
beforeEach(function () {
this.app = new Armadietto({
bare: true,
allow: { signup: true },
store,
http: { },
logging: { log_dir: './test-log', stdout: [], log_files: ['error'] }
});
});

shouldBeWelcomeWithSignup();
});
});
25 changes: 25 additions & 0 deletions spec/armadietto/a_static_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* eslint-env mocha, chai, node */

const Armadietto = require('../../lib/armadietto');
const { shouldServeStaticFiles } = require('../static_files.spec');

const mockStore = {
authorize (clientId, username, permissions) {
return 'a_token';
},
authenticate (params) {
}
};

describe('Static asset handler (monolithic)', function () {
beforeEach(function () {
this.app = new Armadietto({
bare: true,
store: mockStore,
http: { },
logging: { log_dir: './test-log', stdout: [], log_files: ['error'] }
});
});

shouldServeStaticFiles();
});
20 changes: 20 additions & 0 deletions spec/modular/m_not_found.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const app = require('../../lib/app');
const { configureLogger } = require('../../lib/logger');
const { shouldHandleNonexistingResource } = require('../not_found.spec');

/* eslint-env mocha */

/** This suite starts a server on an open port on each test */
describe('Nonexistant resource (modular)', function () {
before(async function () {
configureLogger({ log_dir: './test-log', stdout: [], log_files: ['error'] });

app.locals.title = 'Test Armadietto';
app.locals.basePath = '';
app.locals.host = 'localhost:xxxx';
app.locals.signup = true;
this.app = app;
});

shouldHandleNonexistingResource();
});
69 changes: 69 additions & 0 deletions spec/modular/m_root.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
const chai = require('chai');
const chaiHttp = require('chai-http');
const expect = chai.expect;
const app = require('../../lib/app');
const { configureLogger } = require('../../lib/logger');
const { shouldBeWelcomeWithoutSignup, shouldBeWelcomeWithSignup } = require('../root.spec');

/* eslint-env mocha */

chai.use(chaiHttp);

describe('root page (modular)', function () {
describe('w/o signup', function () {
beforeEach(function () {
configureLogger({ log_dir: './test-log', stdout: [], log_files: ['error'] });

this.app = app;
this.app.locals.title = 'Armadietto without Signup';
this.app.locals.basePath = '';
this.app.locals.host = 'localhost:xxxx';
this.app.locals.signup = false;
});

shouldBeWelcomeWithoutSignup();
});

describe('with signup', function () {
beforeEach(function () {
configureLogger({ log_dir: './test-log', stdout: [], log_files: ['error'] });

this.app = app;
this.app.locals.title = 'Armadietto with Signup';
this.app.locals.basePath = '';
this.app.locals.host = 'localhost:xxxx';
this.app.locals.signup = true;
});

shouldBeWelcomeWithSignup();
});

/** This suite starts a server on an open port on each test */
describe('Headers', () => {
before(async () => {
configureLogger({});

app.locals.title = 'Test Armadietto';
app.locals.basePath = '';
app.locals.host = 'localhost:xxxx';
app.locals.signup = false;
});

it('should return Welcome page w/ security headers', async () => {
const res = await chai.request(app).get('/');
expect(res).to.have.status(200);
expect(res).to.have.header('Content-Security-Policy', 'sandbox allow-scripts allow-forms allow-popups allow-same-origin;default-src \'self\';script-src \'self\';script-src-attr \'none\';style-src \'self\';img-src \'self\';font-src \'self\';object-src \'none\';child-src \'none\';connect-src \'none\';base-uri \'self\';frame-ancestors \'none\';form-action https:;upgrade-insecure-requests');
expect(res).to.have.header('Cross-Origin-Opener-Policy', 'same-origin');
expect(res).to.have.header('Cross-Origin-Resource-Policy', 'same-origin');
expect(res).to.have.header('Origin-Agent-Cluster');
expect(res).to.have.header('Referrer-Policy', 'no-referrer');
expect(res).to.have.header('X-Content-Type-Options', 'nosniff');
expect(res).to.have.header('Strict-Transport-Security', /^max-age=/);
expect(res).not.to.have.header('X-Powered-By');
expect(res).to.have.header('X-XSS-Protection', '0'); // disabled because counterproductive
expect(res).to.have.header('Content-Type', /^text\/html/);
expect(parseInt(res.get('Content-Length'))).to.be.greaterThan(2500);
expect(res).to.have.header('ETag');
});
});
});
20 changes: 20 additions & 0 deletions spec/modular/m_static.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* eslint-env mocha */

const app = require('../../lib/app');
const { configureLogger } = require('../../lib/logger');
const { shouldServeStaticFiles } = require('../static_files.spec');

/** This suite starts a server on an open port on each test */
describe('Static asset handler (modular)', function () {
before(function () {
configureLogger({ log_dir: './test-log', stdout: [], log_files: ['error'] });

app.locals.title = 'Test Armadietto';
app.locals.basePath = '';
app.locals.host = 'localhost:xxxx';
app.locals.signup = false;
this.app = app;
});

shouldServeStaticFiles();
});
45 changes: 0 additions & 45 deletions spec/modular/notfound.spec.js

This file was deleted.

61 changes: 0 additions & 61 deletions spec/modular/static.spec.js

This file was deleted.

Loading

0 comments on commit 3849941

Please sign in to comment.