Files
shields/services/service-tester.js
Paul Melnikow 5c665a70da Overhaul initialization pattern for server + server tests (#2519)
Because `server.js` was long a monolith, there are a bunch of shims in place to facilitate unit testing. A few of the test suites share port 1111 which means if one of them fails to set up, the port won't be freed and other unrelated tests will fail. Some of the tests which trigger server setup include timeouts which were added to give setup code time to run. In one the test suites, we actually modify `process.argv`, which seems completely gross.

This implements a few changes which improve this:

1. Separate the server from the server startup script, splitting out `lib/server.js`.
2. Inject config into the server and validate the config schema.
3. Inject config into the service test runner.
4. Use `portfinder`, a popular utility for grabbing open ports during testing.
5. Switch more of the setup code from callbacks to async-await.

Overall it leaves everything acting more reliably and looking rather cleaner, if in a few places more verbose.

It also fixes the root cause of #1455, a `setTimeout` in `rate-limit`. Off and on during development of this changeset, Mocha would decide not to exit, and that turned out to be the culprit.

Fix #1455
2018-12-23 11:24:22 -05:00

110 lines
2.7 KiB
JavaScript

'use strict'
const emojic = require('emojic')
const frisby = require('./icedfrisby-no-nock')(
require('icedfrisby-nock')(require('icedfrisby'))
)
const trace = require('./trace')
/**
* Encapsulate a suite of tests. Create new tests using create() and register
* them with Mocha using toss().
*/
class ServiceTester {
/**
* @param attrs { id, title, pathPrefix } The `id` is used to specify which
* tests to run from the CLI or pull requests. The `title` prints in the
* Mocha output. The `path` is the path prefix which is automatically
* prepended to each tested URI. The default is `/${attrs.id}`.
*/
constructor({ id, title, pathPrefix }) {
if (pathPrefix === undefined) {
pathPrefix = `/${id}`
}
Object.assign(this, {
id,
title,
pathPrefix,
specs: [],
_only: false,
})
}
static forServiceClass(ServiceClass) {
const id = ServiceClass.name
const pathPrefix = ServiceClass.route.base
? `/${ServiceClass.route.base}`
: ''
return new this({
id,
title: id,
pathPrefix,
})
}
/**
* Invoked before each test. This is a stub which can be overridden on
* instances.
*/
beforeEach() {}
/**
* Create a new test. The hard work is delegated to IcedFrisby.
* https://github.com/MarkHerhold/IcedFrisby/#show-me-some-code
*
* Note: The caller should not invoke toss() on the Frisby chain, as it's
* invoked automatically by the tester.
* @param msg The name of the test
*/
create(msg) {
const spec = frisby
.create(msg)
.before(() => {
this.beforeEach()
})
// eslint-disable-next-line mocha/prefer-arrow-callback
.finally(function() {
// `this` is the IcedFrisby instance.
let responseBody
try {
responseBody = JSON.parse(this._response.body)
} catch (e) {
responseBody = this._response.body
}
trace.logTrace('outbound', emojic.shield, 'Response', responseBody)
})
this.specs.push(spec)
return spec
}
/**
* Run only this tester. This can be invoked using the --only argument to
* the CLI, or directly on the tester.
*/
only() {
this._only = true
}
/**
* Register the tests with Mocha.
*/
toss({ baseUrl, skipIntercepted }) {
const { specs, pathPrefix } = this
const testerBaseUrl = `${baseUrl}${pathPrefix}`
const fn = this._only ? describe.only : describe
// eslint-disable-next-line mocha/prefer-arrow-callback
fn(this.title, function() {
specs.forEach(spec => {
if (!skipIntercepted || !spec.intercepted) {
spec.baseUri(testerBaseUrl)
spec.toss()
}
})
})
}
}
module.exports = ServiceTester