import { Eltro as t, assert, stub, spy } from 'eltro' import { Flaska, HttpError } from '../flaska.mjs' import { createCtx, fakeHttp } from './helper.mjs' const faker = fakeHttp() t.describe('#constructor', function() { t.test('should be able to override the http', function() { let flaska = new Flaska({}, faker) assert.strictEqual(flaska.http, faker) }) t.test('it should have all the common verbs', function() { let flaska = new Flaska({}, faker) assert.ok(flaska.get) assert.strictEqual(typeof(flaska.get), 'function') assert.ok(flaska.post) assert.strictEqual(typeof(flaska.post), 'function') assert.ok(flaska.put) assert.strictEqual(typeof(flaska.put), 'function') assert.ok(flaska.delete) assert.strictEqual(typeof(flaska.delete), 'function') assert.ok(flaska.options) assert.strictEqual(typeof(flaska.options), 'function') assert.ok(flaska.patch) assert.strictEqual(typeof(flaska.patch), 'function') }) t.test('the verbs GET and HEAD should be identical', function() { let flaska = new Flaska({}, faker) assert.ok(flaska.get) assert.strictEqual(typeof(flaska.get), 'function') assert.notOk(flaska.head) assert.ok(flaska.routers['HEAD']) assert.strictEqual(flaska.routers['GET'], flaska.routers['HEAD']) }) t.test('should have before default header generator', function() { let flaska = new Flaska({}, faker) assert.strictEqual(flaska._before.length, 1) let ctx = {} flaska._before[0](ctx) assert.deepEqual( Object.keys(ctx.headers).sort(), ['Server','X-Content-Type-Options','Content-Security-Policy','Cross-Origin-Opener-Policy','Cross-Origin-Resource-Policy','Cross-Origin-Embedder-Policy','Date'].sort() ) assert.strictEqual(ctx.headers['Server'], 'Flaska') assert.strictEqual(ctx.headers['X-Content-Type-Options'], 'nosniff') assert.strictEqual(ctx.headers['Content-Security-Policy'], `default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'`) assert.strictEqual(ctx.headers['Cross-Origin-Opener-Policy'], 'same-origin') assert.strictEqual(ctx.headers['Cross-Origin-Resource-Policy'], 'same-origin') assert.strictEqual(ctx.headers['Cross-Origin-Embedder-Policy'], 'require-corp') assert.ok(new Date(ctx.headers['Date']).getDate()) assert.strictEqual(flaska._after.length, 0) }) t.test('should have before ready setting headers on context if defaultHeaders is specified', function() { const defaultHeaders = { 'Server': 'nginx/1.16.1', 'Content-Type': 'applicat"ion/json; charset=utf-8', 'Content-Length': '1646', 'Connection': 'keep-alive', 'vary': 'Origin', 'Link': 'Link goes here', 'X-Frame-Options': 'DENY', 'X-Content-Type-Options': 'nosniff', } let flaska = new Flaska({ defaultHeaders: defaultHeaders, }, faker) assert.strictEqual(flaska._before.length, 1) let ctx = {} flaska._before[0](ctx) let keys = Object.keys(defaultHeaders) assert.strictEqual(Object.keys(ctx.headers).length, keys.length + 1) for (let key of keys) { assert.strictEqual(ctx.headers[key], defaultHeaders[key]) } assert.ok(ctx.headers['Date']) assert.strictEqual(flaska._after.length, 0) }) t.test('should have before ready setting headers on context if appendHeaders is specified', function() { const appendHeaders = { 'Server': 'nginx/1.16.1', 'Herp': 'Derp', } let flaska = new Flaska({ appendHeaders: appendHeaders, }, faker) assert.strictEqual(flaska._before.length, 1) let ctx = {} flaska._before[0](ctx) assert.deepEqual( Object.keys(ctx.headers).sort(), ['Server', 'Herp', 'X-Content-Type-Options','Content-Security-Policy','Cross-Origin-Opener-Policy','Cross-Origin-Resource-Policy','Cross-Origin-Embedder-Policy','Date'].sort() ) assert.notStrictEqual(ctx.headers['Server'], 'Flaska') assert.strictEqual(ctx.headers['Server'], appendHeaders.Server) assert.strictEqual(ctx.headers['Herp'], 'Derp') assert.strictEqual(ctx.headers['X-Content-Type-Options'], 'nosniff') assert.strictEqual(ctx.headers['Content-Security-Policy'], `default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'`) assert.strictEqual(ctx.headers['Cross-Origin-Opener-Policy'], 'same-origin') assert.strictEqual(ctx.headers['Cross-Origin-Resource-Policy'], 'same-origin') assert.strictEqual(ctx.headers['Cross-Origin-Embedder-Policy'], 'require-corp') assert.ok(new Date(ctx.headers['Date']).getDate()) assert.strictEqual(flaska._after.length, 0) }) }) t.describe('#_nonce', function() { t.test('should support nonce parameter with cached pre-filled entries', function() { let flaska = new Flaska({ nonce: ['script-src', 'style-src'], }, faker) assert.ok(flaska._nonces) assert.strictEqual(flaska._noncesIndex + 1, flaska._nonces.length) assert.strictEqual(flaska._nonces.length, 25) assert.ok(flaska._nonces[flaska._noncesIndex]) //Check they're all unique let set = new Set() flaska._nonces.forEach(function(entry) { set.add(entry) }) assert.strictEqual(set.size, flaska._nonces.length) let ctx = createCtx() assert.notOk(ctx.state.nonce) let oldIndex = flaska._noncesIndex flaska._before[0](ctx) assert.ok(ctx.state.nonce) assert.strictEqual(flaska._noncesIndex, oldIndex - 1) assert.strictEqual(flaska._nonces[oldIndex], ctx.state.nonce) assert.strictEqual(ctx.headers['Server'], 'Flaska') assert.strictEqual(ctx.headers['X-Content-Type-Options'], 'nosniff') assert.strictEqual(ctx.headers['Content-Security-Policy'], `default-src 'self'; style-src 'self' 'unsafe-inline' 'nonce-${ctx.state.nonce}'; img-src * data: blob:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'; script-src 'nonce-${ctx.state.nonce}'`) assert.strictEqual(ctx.headers['Cross-Origin-Opener-Policy'], 'same-origin') assert.strictEqual(ctx.headers['Cross-Origin-Resource-Policy'], 'same-origin') assert.strictEqual(ctx.headers['Cross-Origin-Embedder-Policy'], 'require-corp') assert.ok(new Date(ctx.headers['Date']).getDate()) }) t.test('should always return nonce values even if it runs out in cache', function() { let flaska = new Flaska({ nonce: ['script-src'], nonceCacheLength: 5, }, faker) let ctx = createCtx() for (let i = 0; i < 5; i++) { let nextNonce = flaska._nonces[flaska._noncesIndex] flaska._before[0](ctx) assert.strictEqual(ctx.state.nonce, nextNonce) assert.strictEqual(ctx.headers['Content-Security-Policy'], `default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'; script-src 'nonce-${ctx.state.nonce}'`) } assert.notOk(flaska._nonces[flaska._noncesIndex]) flaska._before[0](ctx) assert.notOk(flaska._nonces[flaska._noncesIndex]) assert.ok(ctx.state.nonce) for (let i = 0; i < flaska._nonces.length; i++) { assert.notStrictEqual(ctx.state.nonce, flaska._nonces[i]) } assert.strictEqual(ctx.headers['Content-Security-Policy'], `default-src 'self'; style-src 'self' 'unsafe-inline'; img-src * data: blob:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'; script-src 'nonce-${ctx.state.nonce}'`) }) t.test('should have after that regenerates lost hashes', function() { let flaska = new Flaska({ nonce: ['script-src'], nonceCacheLength: 5, }, faker) let ctx = createCtx() assert.strictEqual(flaska._after.length, 1) //Check they're all unique let set = new Set() flaska._nonces.forEach(function(entry) { set.add(entry) }) assert.strictEqual(set.size, 5) flaska._before[0](ctx) flaska._before[0](ctx) flaska._before[0](ctx) assert.strictEqual(flaska._noncesIndex, 1) flaska._after[0](ctx) assert.strictEqual(flaska._noncesIndex, 2) set.add(flaska._nonces[flaska._noncesIndex]) assert.strictEqual(set.size, 6) flaska._after[0](ctx) assert.strictEqual(flaska._noncesIndex, 3) set.add(flaska._nonces[flaska._noncesIndex]) assert.strictEqual(set.size, 7) flaska._after[0](ctx) assert.strictEqual(flaska._noncesIndex, 4) set.add(flaska._nonces[flaska._noncesIndex]) assert.strictEqual(set.size, 8) flaska._after[0](ctx) assert.strictEqual(flaska._noncesIndex, 4) set.add(flaska._nonces[flaska._noncesIndex]) assert.strictEqual(set.size, 8) }) t.test('after should not generate keys outside range', function() { let flaska = new Flaska({ nonce: ['script-src'], nonceCacheLength: 2, }, faker) let ctx = createCtx() assert.strictEqual(flaska._after.length, 1) //Check they're all unique let set = new Set() flaska._nonces.forEach(function(entry) { set.add(entry) }) assert.strictEqual(set.size, 2) flaska._before[0](ctx) flaska._before[0](ctx) assert.strictEqual(flaska._noncesIndex, -1) flaska._before[0](ctx) assert.strictEqual(flaska._noncesIndex, -2) set.add(ctx.state.nonce) assert.strictEqual(set.size, 3) flaska._before[0](ctx) assert.strictEqual(flaska._noncesIndex, -3) set.add(ctx.state.nonce) assert.strictEqual(set.size, 4) flaska._before[0](ctx) assert.strictEqual(flaska._noncesIndex, -4) set.add(ctx.state.nonce) assert.strictEqual(set.size, 5) assert.strictEqual(Object.keys(flaska._nonces).length, 2) flaska._after[0](ctx) assert.strictEqual(flaska._noncesIndex, 0) set.add(flaska._nonces[flaska._noncesIndex]) assert.strictEqual(set.size, 6) assert.strictEqual(Object.keys(flaska._nonces).length, 2) flaska._after[0](ctx) assert.strictEqual(flaska._noncesIndex, 1) set.add(flaska._nonces[flaska._noncesIndex]) assert.strictEqual(set.size, 7) assert.strictEqual(Object.keys(flaska._nonces).length, 2) flaska._after[0](ctx) assert.strictEqual(flaska._noncesIndex, 1) set.add(flaska._nonces[flaska._noncesIndex]) assert.strictEqual(set.size, 7) assert.strictEqual(Object.keys(flaska._nonces).length, 2) }) }) t.describe('#log', function() { t.test('default have a logger valid', function() { let flaska = new Flaska({}, faker) assert.strictEqual(typeof(flaska.log.error), 'function') assert.strictEqual(typeof(flaska.log.info), 'function') assert.strictEqual(typeof(flaska.log.warn), 'function') }) t.test('allow overwriting in options', function() { const assertFunction = function() { return 1 } let flaska = new Flaska({ log: { error: assertFunction, info: assertFunction, warn: assertFunction, debug: assertFunction, } }, faker) assert.strictEqual(flaska.log.error, assertFunction) assert.strictEqual(flaska.log.info, assertFunction) assert.strictEqual(flaska.log.warn, assertFunction) assert.strictEqual(flaska.log.debug, assertFunction) }) }) let specialHandlers = ['on404', 'onerror', 'onreqerror', 'onreserror'] specialHandlers.forEach(function(type) { t.describe(`#${type}()`, function() { t.test('exist', function() { let flaska = new Flaska({}, faker) assert.strictEqual(typeof(flaska[type]), 'function') }) t.test('validate handler', function() { let flaska = new Flaska({}, faker) assert.throws(function() { flaska[type]() }, /[Ff]unction/) assert.throws(function() { flaska[type]('asdf') }, /[Ff]unction/) assert.throws(function() { flaska[type]('123') }, /[Ff]unction/) assert.throws(function() { flaska[type]([]) }, /[Ff]unction/) assert.throws(function() { flaska[type]({}) }, /[Ff]unction/) assert.throws(function() { flaska[type](1234) }, /[Ff]unction/) }) if (type !== 'on404') { t.test('should call ctx.log.error correctly', function() { const assertError = new Error('Samuraism') let flaska = new Flaska({}, faker) let ctx = createCtx() flaska['_' + type](assertError, ctx) assert.strictEqual(ctx.log.error.callCount, 1) assert.strictEqual(ctx.log.error.firstCall[0], assertError) }) } if (type === 'onreqerror') { t.test('default sends 400 immediately', function() { let flaska = new Flaska({}, faker) let ctx = createCtx() flaska['_' + type](new Error(), ctx) assert.strictEqual(ctx.res.statusCode, 400) assert.strictEqual(ctx.res.end.callCount, 1) assert.strictEqual(ctx.res.end.firstCall.length, 0) }) } t.test('register it into flaska', function() { const assertFunction = function() { return true } let flaska = new Flaska({}, faker) flaska[type](assertFunction) assert.strictEqual(flaska['_' + type], assertFunction) }) }) }) t.describe('_on404', function() { t.test('default valid handling of context', function() { let ctx = createCtx() let flaska = new Flaska({}, faker) flaska._on404(ctx) assert.strictEqual(ctx.status, 404) assert.deepStrictEqual(ctx.body, { status: 404, message: 'Not Found', }) }) t.test('should do nothing if body is not null', function() { const assertBody = { a: 1 } let ctx = createCtx() let flaska = new Flaska({}, faker) ctx.body = assertBody flaska._on404(ctx) assert.strictEqual(ctx.status, 200) assert.strictEqual(ctx.body, assertBody) }) t.test('should do nothing if body is null but status is 204', function() { let ctx = createCtx() let flaska = new Flaska({}, faker) ctx.status = 204 ctx.body = null flaska._on404(ctx) assert.strictEqual(ctx.status, 204) assert.strictEqual(ctx.body, null) }) }) t.describe('_onerror', function() { t.test('a valid function', function() { let flaska = new Flaska({}, faker) assert.strictEqual(typeof(flaska._onerror), 'function') }) t.test('default valid handling of context', function() { const assertError = new Error('should not be seen') let ctx = createCtx() let flaska = new Flaska({}, faker) flaska._onerror(assertError, ctx) assert.strictEqual(ctx.log.error.callCount, 1) assert.strictEqual(ctx.log.error.firstCall[0], assertError) assert.strictEqual(ctx.status, 500) assert.deepStrictEqual(ctx.body, { status: 500, message: 'Internal Server Error', }) }) t.test('default valid handling of HttpError', function() { const assertStatus = 431 const assertBody = { a: 1 } const assertError = new HttpError(assertStatus, 'should not be seen', assertBody) let ctx = createCtx() let flaska = new Flaska({}, faker) flaska._onerror(assertError, ctx) assert.strictEqual(ctx.log.error.callCount, 1) assert.strictEqual(ctx.log.error.firstCall[0], assertError) assert.strictEqual(ctx.status, assertStatus) assert.strictEqual(ctx.body, assertBody) }) t.test('default valid handling of HttpError with no body', function() { const assertStatus = 413 const assertError = new HttpError(assertStatus, 'should not be seen') let ctx = createCtx() let flaska = new Flaska({}, faker) flaska._onerror(assertError, ctx) assert.strictEqual(ctx.log.error.callCount, 1) assert.strictEqual(ctx.log.error.firstCall[0], assertError) assert.strictEqual(ctx.status, assertStatus) assert.deepStrictEqual(ctx.body, { status: assertStatus, message: 'Payload Too Large', }) }) t.test('default valid handling of HttpError with missing status message', function() { const assertStatus = 432 const assertError = new HttpError(assertStatus, 'should not be seen') let ctx = createCtx() let flaska = new Flaska({}, faker) flaska._onerror(assertError, ctx) assert.strictEqual(ctx.log.error.callCount, 1) assert.strictEqual(ctx.log.error.firstCall[0], assertError) assert.strictEqual(ctx.status, assertStatus) assert.deepStrictEqual(ctx.body, { status: assertStatus, message: 'Internal Server Error', }) }) }) t.describe('_onreqerror', function() { t.test('a valid function', function() { let flaska = new Flaska({}, faker) assert.strictEqual(typeof(flaska._onreqerror), 'function') }) t.test('default valid handling of aborted', function() { const assertError = new Error('aborted') let flaska = new Flaska({}, faker) let ctx = createCtx() flaska._onreqerror(assertError, ctx) assert.strictEqual(ctx.log.info.callCount, 0) assert.strictEqual(ctx.log.error.callCount, 0) }) }) t.describe('#devMode()', function() { t.test('turns on debug mode', function() { const assertErrorMessage = 'Fascination' const assertError = new Error(assertErrorMessage) let ctx = createCtx() let flaska = new Flaska({}, faker) flaska._onerror(assertError, ctx) assert.strictEqual(ctx.log.error.callCount, 1) assert.strictEqual(ctx.log.error.firstCall[0], assertError) assert.strictEqual(ctx.status, 500) assert.deepStrictEqual(ctx.body, { status: 500, message: 'Internal Server Error', }) ctx = createCtx() flaska._backuperror(assertError, ctx) assert.strictEqual(ctx.log.error.callCount, 1) assert.strictEqual(ctx.log.error.firstCall[0], assertError) assert.strictEqual(ctx.status, 500) assert.deepStrictEqual(ctx.body, { status: 500, message: 'Internal Server Error', }) flaska.devMode() ctx = createCtx() flaska._onerror(assertError, ctx) assert.strictEqual(ctx.log.error.callCount, 1) assert.strictEqual(ctx.log.error.firstCall[0], assertError) assert.strictEqual(ctx.status, 500) assert.strictEqual(ctx.body.status, 500) assert.match(ctx.body.message, /Internal Server Error/) assert.match(ctx.body.message, new RegExp(assertErrorMessage)) assert.ok(ctx.body.stack) ctx = createCtx() flaska._backuperror(assertError, ctx) assert.strictEqual(ctx.log.error.callCount, 1) assert.strictEqual(ctx.log.error.firstCall[0], assertError) assert.strictEqual(ctx.status, 500) assert.strictEqual(ctx.body.status, 500) assert.match(ctx.body.message, /Internal Server Error/) assert.match(ctx.body.message, new RegExp(assertErrorMessage)) assert.ok(ctx.body.stack) }) t.test('default valid handling of HttpError', function() { const assertStatus = 431 const assertBody = { a: 1 } const assertError = new HttpError(assertStatus, 'should not be seen', assertBody) let ctx = createCtx() let flaska = new Flaska({}, faker) flaska.devMode() flaska._onerror(assertError, ctx) assert.strictEqual(ctx.log.error.callCount, 1) assert.strictEqual(ctx.log.error.firstCall[0], assertError) assert.strictEqual(ctx.status, assertStatus) assert.strictEqual(ctx.body, assertBody) }) t.test('default valid handling of HttpError with no body', function() { const assertStatus = 413 const assertErrorMessage = 'A day' const assertError = new HttpError(assertStatus, assertErrorMessage) let ctx = createCtx() let flaska = new Flaska({}, faker) flaska.devMode() flaska._onerror(assertError, ctx) assert.strictEqual(ctx.log.error.callCount, 1) assert.strictEqual(ctx.log.error.firstCall[0], assertError) assert.strictEqual(ctx.status, assertStatus) assert.strictEqual(ctx.body.status, assertStatus) assert.match(ctx.body.message, /Payload Too Large/) assert.match(ctx.body.message, new RegExp(assertErrorMessage)) }) t.test('default valid handling of HttpError with missing status message', function() { const assertStatus = 432 const assertErrorMessage = 'Jet Coaster Ride' const assertError = new HttpError(assertStatus, assertErrorMessage) let ctx = createCtx() let flaska = new Flaska({}, faker) flaska.devMode() flaska._onerror(assertError, ctx) assert.strictEqual(ctx.log.error.callCount, 1) assert.strictEqual(ctx.log.error.firstCall[0], assertError) assert.strictEqual(ctx.status, assertStatus) assert.strictEqual(ctx.body.status, assertStatus) assert.match(ctx.body.message, /Internal Server Error/) }) }) t.describe('#before()', function() { t.test('should throw if not a function', function() { let flaska = new Flaska({}, faker) assert.throws(function() { flaska.before() }, /[Ff]unction/) assert.throws(function() { flaska.before('asdf') }, /[Ff]unction/) assert.throws(function() { flaska.before('123') }, /[Ff]unction/) assert.throws(function() { flaska.before([]) }, /[Ff]unction/) assert.throws(function() { flaska.before({}) }, /[Ff]unction/) assert.throws(function() { flaska.before(1234) }, /[Ff]unction/) }) t.test('add handler to preflight list', function() { const assertFunction = function() {} let flaska = new Flaska({}, faker) assert.ok(flaska._before) flaska.before(assertFunction) assert.strictEqual(flaska._before.length, 2) assert.strictEqual(flaska._before[1], assertFunction) }) }) t.describe('#beforeAsync()', function() { t.test('should throw if not a function', function() { let flaska = new Flaska({}, faker) assert.throws(function() { flaska.beforeAsync() }, /[Ff]unction/) assert.throws(function() { flaska.beforeAsync('asdf') }, /[Ff]unction/) assert.throws(function() { flaska.beforeAsync('123') }, /[Ff]unction/) assert.throws(function() { flaska.beforeAsync([]) }, /[Ff]unction/) assert.throws(function() { flaska.beforeAsync({}) }, /[Ff]unction/) assert.throws(function() { flaska.beforeAsync(1234) }, /[Ff]unction/) }) t.test('add handler to preflight list', function() { const assertFunction = function() {} let flaska = new Flaska({}, faker) assert.ok(flaska._beforeAsync) flaska.beforeAsync(assertFunction) assert.strictEqual(flaska._beforeAsync.length, 1) assert.strictEqual(flaska._beforeAsync[0], assertFunction) }) }) t.describe('#after()', function() { t.test('should throw if not a function', function() { let flaska = new Flaska({}, faker) assert.throws(function() { flaska.after() }, /[Ff]unction/) assert.throws(function() { flaska.after('asdf') }, /[Ff]unction/) assert.throws(function() { flaska.after('123') }, /[Ff]unction/) assert.throws(function() { flaska.after([]) }, /[Ff]unction/) assert.throws(function() { flaska.after({}) }, /[Ff]unction/) assert.throws(function() { flaska.after(1234) }, /[Ff]unction/) }) t.test('add handler to preflight list', function() { const assertFunction = function() {} let flaska = new Flaska({}, faker) assert.ok(flaska._after) flaska.after(assertFunction) assert.strictEqual(flaska._after.length, 1) assert.strictEqual(flaska._after[0], assertFunction) }) }) t.describe('#afterAsync()', function() { t.test('should throw if not a function', function() { let flaska = new Flaska({}, faker) assert.throws(function() { flaska.afterAsync() }, /[Ff]unction/) assert.throws(function() { flaska.afterAsync('asdf') }, /[Ff]unction/) assert.throws(function() { flaska.afterAsync('123') }, /[Ff]unction/) assert.throws(function() { flaska.afterAsync([]) }, /[Ff]unction/) assert.throws(function() { flaska.afterAsync({}) }, /[Ff]unction/) assert.throws(function() { flaska.afterAsync(1234) }, /[Ff]unction/) }) t.test('add handler to preflight list', function() { const assertFunction = function() {} let flaska = new Flaska({}, faker) assert.ok(flaska._afterAsync) flaska.afterAsync(assertFunction) assert.strictEqual(flaska._afterAsync.length, 1) assert.strictEqual(flaska._afterAsync[0], assertFunction) }) }) t.describe('#compile()', function() { t.test('join all before together in one function', function() { let flaska = new Flaska({}, faker) flaska.before(function(ctx) { ctx.a = 1 }) flaska.before(function(ctx) { ctx.b = 2 }) flaska.before(function(ctx) { ctx.c = 3 }) flaska.before(function(ctx) { ctx.d = 4 }) assert.notOk(flaska._beforeCompiled) flaska.compile() assert.ok(flaska._beforeCompiled) assert.notOk(flaska._beforeAsyncCompiled) assert.strictEqual(typeof(flaska._beforeCompiled), 'function') let ctx = createCtx() flaska._beforeCompiled(ctx) assert.strictEqual(ctx.a, 1) assert.strictEqual(ctx.b, 2) assert.strictEqual(ctx.c, 3) assert.strictEqual(ctx.d, 4) }) t.test('join all beforeAsync together in one function', function() { let flaska = new Flaska({}, faker) flaska.beforeAsync(function(ctx) { return Promise.resolve().then(function() { ctx.a = 1 }) }) flaska.beforeAsync(function(ctx) { ctx.b = 2 }) flaska.beforeAsync(function(ctx) { return new Promise(function(res) { ctx.c = 3; res() }) }) flaska.beforeAsync(function(ctx) { ctx.d = 4 }) assert.notOk(flaska._beforeAsyncCompiled) flaska.compile() assert.ok(flaska._beforeAsyncCompiled) assert.strictEqual(typeof(flaska._beforeAsyncCompiled), 'function') let ctx = createCtx() return flaska._beforeAsyncCompiled(ctx).then(function() { assert.strictEqual(ctx.a, 1) assert.strictEqual(ctx.b, 2) assert.strictEqual(ctx.c, 3) assert.strictEqual(ctx.d, 4) }) }) t.test('join all after together in one function', function() { let flaska = new Flaska({}, faker) flaska.after(function(ctx) { ctx.a = 1 }) flaska.after(function(ctx) { ctx.b = 2 }) flaska.after(function(ctx) { ctx.c = 3 }) flaska.after(function(ctx) { ctx.d = 4 }) assert.notOk(flaska._afterCompiled) flaska.compile() assert.ok(flaska._afterCompiled) assert.notOk(flaska._afterAsyncCompiled) assert.strictEqual(typeof(flaska._afterCompiled), 'function') let ctx = createCtx() flaska._afterCompiled(ctx) assert.strictEqual(ctx.a, 1) assert.strictEqual(ctx.b, 2) assert.strictEqual(ctx.c, 3) assert.strictEqual(ctx.d, 4) }) t.test('join all afterAsync together in one function', function() { let flaska = new Flaska({}, faker) flaska.afterAsync(function(ctx) { return Promise.resolve().then(function() { ctx.a = 1 }) }) flaska.afterAsync(function(ctx) { ctx.b = 2 }) flaska.afterAsync(function(ctx) { return new Promise(function(res) { ctx.c = 3; res() }) }) flaska.afterAsync(function(ctx) { ctx.d = 4 }) assert.notOk(flaska._afterAsyncCompiled) flaska.compile() assert.ok(flaska._afterAsyncCompiled) assert.strictEqual(typeof(flaska._afterAsyncCompiled), 'function') let ctx = createCtx() return flaska._afterAsyncCompiled(ctx).then(function() { assert.strictEqual(ctx.a, 1) assert.strictEqual(ctx.b, 2) assert.strictEqual(ctx.c, 3) assert.strictEqual(ctx.d, 4) }) }) }) t.describe('#runHandlers()', function() { t.test('should work with empty array', function() { let flaska = new Flaska({}, faker) flaska.runHandlers({}, [], 0) }) t.test('should work with correct index', function() { let checkIsTrue = false let flaska = new Flaska({}, faker) flaska.runHandlers({}, [ function() { throw new Error('should not be thrown') }, function() { throw new Error('should not be thrown') }, function() { throw new Error('should not be thrown') }, function() { checkIsTrue = true }, ], 3) assert.strictEqual(checkIsTrue, true) }) t.test('should work with static functions', function() { const assertCtx = createCtx({ a: 1 }) let checkCounter = 0 let flaska = new Flaska({}, faker) flaska.runHandlers(assertCtx, [ function(ctx) { assert.strictEqual(ctx, assertCtx); checkCounter++ }, function(ctx) { assert.strictEqual(ctx, assertCtx); checkCounter++ }, function(ctx) { assert.strictEqual(ctx, assertCtx); checkCounter++ }, function(ctx) { assert.strictEqual(ctx, assertCtx); checkCounter++ }, function(ctx) { assert.strictEqual(ctx, assertCtx); checkCounter++ }, ], 0) assert.strictEqual(checkCounter, 5) }) t.test('should work with random promises inbetween', function() { const assertCtx = createCtx({ a: 1 }) let checkCounter = 0 let flaska = new Flaska({}, faker) let result = flaska.runHandlers(assertCtx, [ function(ctx) { assert.strictEqual(ctx, assertCtx); assert.strictEqual(checkCounter, 0); checkCounter++ }, function(ctx) { assert.strictEqual(ctx, assertCtx); assert.strictEqual(checkCounter, 1); checkCounter++ }, function(ctx) { return new Promise(function(res) { assert.strictEqual(ctx, assertCtx); assert.strictEqual(checkCounter, 2); checkCounter++; res() }) }, function(ctx) { return Promise.resolve().then(function() { assert.strictEqual(ctx, assertCtx); assert.strictEqual(checkCounter, 3); checkCounter++ }) }, function(ctx) { assert.strictEqual(ctx, assertCtx); assert.strictEqual(checkCounter, 4); checkCounter++ }, function(ctx) { return Promise.resolve().then(function() { assert.strictEqual(ctx, assertCtx); assert.strictEqual(checkCounter, 5); checkCounter++ }) }, function(ctx) { return Promise.resolve().then(function() { assert.strictEqual(ctx, assertCtx); assert.strictEqual(checkCounter, 6); checkCounter++ }) }, function(ctx) { assert.strictEqual(ctx, assertCtx); assert.strictEqual(checkCounter, 7); checkCounter++ }, function(ctx) { assert.strictEqual(ctx, assertCtx); assert.strictEqual(checkCounter, 8); checkCounter++ }, ], 0) assert.ok(result) assert.strictEqual(typeof(result.then), 'function') return result.then(function() { assert.strictEqual(checkCounter, 9) }) }) t.test('should work with rejected promises inbetween', async function() { const assertError = { a: 1 } let checkCounter = 0 let flaska = new Flaska({}, faker) let err = await assert.isRejected(flaska.runHandlers({}, [ function() { }, function() { return new Promise(function(res, rej) { rej(assertError) }) }, function() { throw new Error('should not be seen') }, ], 0)) assert.strictEqual(err, assertError) err = await assert.isRejected(flaska.runHandlers({}, [ function() { }, function() { return Promise.reject(assertError) }, function() { throw new Error('should not be seen') }, ], 0)) assert.strictEqual(err, assertError) err = await assert.isRejected(flaska.runHandlers({}, [ function() { }, function() { return Promise.resolve() }, function() { throw assertError }, ], 0)) assert.strictEqual(err, assertError) }) }) t.describe('#listen()', function() { t.test('it should throw if missing port', function() { let flaska = new Flaska({}, faker) assert.throws(function() { flaska.listen() }, /[Pp]ort/) assert.throws(function() { flaska.listen('asdf') }, /[Pp]ort/) assert.throws(function() { flaska.listen('123') }, /[Pp]ort/) assert.throws(function() { flaska.listen([]) }, /[Pp]ort/) assert.throws(function() { flaska.listen({}) }, /[Pp]ort/) assert.throws(function() { flaska.listen(function() {}) }, /[Pp]ort/) }) t.test('it should automatically call compile', function() { let assertCalled = false let flaska = new Flaska({}, faker) flaska.compile = function() { assertCalled = true } flaska.listen(404) assert.strictEqual(assertCalled, true) }) t.test('call http correctly', function() { const assertPort = 325897235 const assertIp = 'asdga' const assertCb = function() { } let checkPort = null let checkIp = null let checkListenCb = null let testFaker = fakeHttp(null, function(port, ip, cb) { checkPort = port checkIp = ip checkListenCb = cb }) let flaska = new Flaska({}, testFaker) assert.ok(flaska.requestStart) flaska.requestStart = function() { checkInternalThis = this checkIsTrue = true } flaska.listen(assertPort, assertIp, assertCb) assert.strictEqual(checkPort, assertPort) assert.strictEqual(checkIp, assertIp) assert.strictEqual(checkListenCb, assertCb) }) t.test('call http correctly if only port is specified', function() { const assertPort = 325897235 const assertCb = function() { } let checkPort = null let checkIp = null let checkListenCb = null let testFaker = fakeHttp(null, function(port, ip, cb) { checkPort = port checkIp = ip checkListenCb = cb }) let flaska = new Flaska({}, testFaker) assert.ok(flaska.requestStart) flaska.requestStart = function() { checkInternalThis = this checkIsTrue = true } flaska.listen(assertPort, assertCb) assert.strictEqual(checkPort, assertPort) assert.strictEqual(checkIp, '::') assert.strictEqual(checkListenCb, assertCb) }) t.test('register requestStart if no async', function() { let checkIsTrue = false let checkInternalThis = null let checkHandler = null let testFaker = fakeHttp(function(cb) { checkHandler = cb }) let flaska = new Flaska({}, testFaker) assert.ok(flaska.requestStart) flaska.requestStart = function() { checkInternalThis = this checkIsTrue = true } flaska.listen(404) assert.strictEqual(typeof(checkHandler), 'function') assert.notStrictEqual(checkHandler, flaska.requestStart) assert.notStrictEqual(checkIsTrue, true) assert.notStrictEqual(checkInternalThis, flaska) checkHandler() assert.strictEqual(checkIsTrue, true) assert.strictEqual(checkInternalThis, flaska) }) }) t.describe('#listenAsync()', function() { t.test('it should throw if missing port', async function() { let flaska = new Flaska({}, faker) let tests = [ undefined, 'asdf', '123', [], {}, function() {}, ] let errors = await Promise.all(tests.map(x => assert.isRejected(flaska.listenAsync(x)))) assert.strictEqual(errors.length, tests.length) for (let i = 0; i < errors.length; i++) { assert.match(errors[i].message, /[Pp]ort/) } }) t.test('it should automatically call compile', async function() { let assertCalled = false let flaska = new Flaska({}, faker) flaska.compile = function() { assertCalled = true } await flaska.listenAsync(404) assert.strictEqual(assertCalled, true) }) t.test('call http correctly', async function() { const assertPort = 325897235 const assertIp = 'asdf' let checkPort = null let checkIp = null let testFaker = fakeHttp(null, function(port, ip, cb) { checkPort = port checkIp = ip cb() }) let flaska = new Flaska({}, testFaker) assert.ok(flaska.requestStart) flaska.requestStart = function() { checkInternalThis = this checkIsTrue = true } await flaska.listenAsync(assertPort, assertIp) assert.strictEqual(checkPort, assertPort) assert.strictEqual(checkIp, assertIp) }) t.test('call http and listen correctly if only port specified', async function() { const assertPort = 325897235 let checkPort = null let checkIp = null let testFaker = fakeHttp(null, function(port, ip, cb) { checkPort = port checkIp = ip cb() }) let flaska = new Flaska({}, testFaker) assert.ok(flaska.requestStart) flaska.requestStart = function() { checkInternalThis = this checkIsTrue = true } await flaska.listenAsync(assertPort) assert.strictEqual(checkPort, assertPort) assert.strictEqual(checkIp, '::') }) t.test('call http and listenAsync correctly if supported', async function() { const assertPort = 4632 const assertIp = 'asdf' const assertReturns = { a: 1 } const stubListenAsync = stub().resolves(assertReturns) let flaska = new Flaska({}, { createServer: function() { return { listenAsync: stubListenAsync, on: stub(), } } }) assert.ok(flaska.requestStart) flaska.requestStart = function() { checkInternalThis = this checkIsTrue = true } let res = await flaska.listenAsync(assertPort, assertIp) assert.strictEqual(res, assertReturns) assert.strictEqual(stubListenAsync.firstCall[0], assertPort) assert.strictEqual(stubListenAsync.firstCall[1], assertIp) }) t.test('call http and listenAsync correctly if supported and ip is null', async function() { const assertPort = 325897235 const assertReturns = { a: 1 } const stubListenAsync = stub().resolves(assertReturns) let flaska = new Flaska({}, { createServer: function() { return { listenAsync: stubListenAsync, on: stub(), } } }) assert.ok(flaska.requestStart) flaska.requestStart = function() { checkInternalThis = this checkIsTrue = true } let res = await flaska.listenAsync(assertPort) assert.strictEqual(res, assertReturns) assert.strictEqual(stubListenAsync.firstCall[0], assertPort) assert.strictEqual(stubListenAsync.firstCall[1], '::') }) t.test('register requestStart if no async', async function() { let checkIsTrue = false let checkInternalThis = null let checkHandler = null let testFaker = fakeHttp(function(cb) { checkHandler = cb }) let flaska = new Flaska({}, testFaker) assert.ok(flaska.requestStart) flaska.requestStart = function() { checkInternalThis = this checkIsTrue = true } await flaska.listenAsync(404) assert.strictEqual(typeof(checkHandler), 'function') assert.notStrictEqual(checkHandler, flaska.requestStart) assert.notStrictEqual(checkIsTrue, true) assert.notStrictEqual(checkInternalThis, flaska) checkHandler() assert.strictEqual(checkIsTrue, true) assert.strictEqual(checkInternalThis, flaska) }) }) t.describe('#closeAsync()', function() { t.test('it return if server is null', function() { let flaska = new Flaska() flaska.server = null return flaska.closeAsync() }) t.test('it should call close on server correctly', async function() { const assertError = new Error('Pirate Fight') let flaska = new Flaska() flaska.server = { close: stub() } flaska.server.close.returnWith(function(cb) { cb(assertError) }) let err = await assert.isRejected(flaska.closeAsync()) assert.strictEqual(err, assertError) }) t.test('it should call closeAllConnections', async function() { const assertError = new Error('Pirate Fight') let flaska = new Flaska() flaska.server = { close: stub(), closeAllConnections: stub(), } flaska.server.closeAllConnections.throws(assertError) let err = await assert.isRejected(flaska.closeAsync()) assert.strictEqual(err, assertError) }) t.test('should otherwise work', async function() { let flaska = new Flaska() flaska.server = { close: stub(), closeAllConnections: stub(), } flaska.server.close.returnWith(function(cb) { cb(null, { a: 1 }) }) let res = await flaska.closeAsync() assert.notOk(res) assert.ok(false) }) })