pretty much fully finished version 1
This commit is contained in:
parent
26c9b4a27e
commit
c8290126fa
13 changed files with 2045 additions and 212 deletions
13
LICENSE
Normal file
13
LICENSE
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
Version 2, December 2004
|
||||||
|
|
||||||
|
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
copies of this license document, and changing it is allowed as long
|
||||||
|
as the name is changed.
|
||||||
|
|
||||||
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
@ -1,7 +1,7 @@
|
||||||
import assert from 'assert'
|
import assert from 'assert'
|
||||||
import Benchmark from 'benchmarkjs-pretty'
|
import Benchmark from 'benchmarkjs-pretty'
|
||||||
import { koaRouter1, koaRouter2 } from './router_koa.js'
|
import { koaRouter1, koaRouter2 } from './router_koa.js'
|
||||||
import { flaskaRouter1, flaskaRouter2, flaskaClassRouter1, flaskaClassRouter2 } from './router_flaska.js'
|
import { flaskaRouter1, flaskaRouter2 } from './router_flaska.js'
|
||||||
import { expressRouter1, expressRouter2 } from './router_express.js'
|
import { expressRouter1, expressRouter2 } from './router_express.js'
|
||||||
import * as consts from './const.js'
|
import * as consts from './const.js'
|
||||||
|
|
||||||
|
@ -10,6 +10,216 @@ let testData = null
|
||||||
|
|
||||||
consts.overrideDummy(function() { testData = assertOk })
|
consts.overrideDummy(function() { testData = assertOk })
|
||||||
|
|
||||||
|
function TestPromiseCreators() {
|
||||||
|
let resolveTrue = Promise.resolve(true)
|
||||||
|
|
||||||
|
let asyncFunc = async function() {
|
||||||
|
let check = await resolveTrue
|
||||||
|
if (check !== true) {
|
||||||
|
throw new Error('false')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Benchmark.default('test different promise creation methods')
|
||||||
|
.add('new Promise()', function() {
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
try {
|
||||||
|
resolveTrue.then(function(check) {
|
||||||
|
res(check)
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
rej(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.add('new Promise() static', function() {
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
res(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.add('new Promise() static with try catch', function() {
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
try {
|
||||||
|
res(true)
|
||||||
|
} catch (err) {
|
||||||
|
rej(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.add('new Promise() static with one then()', function() {
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
res(true)
|
||||||
|
}).then(function(check) {
|
||||||
|
return check
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.add('Promise.resolve()', function() {
|
||||||
|
return Promise.resolve().then(function() {
|
||||||
|
return resolveTrue
|
||||||
|
}).then(function(check) {
|
||||||
|
return check
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.add('resolved promise one then()', function() {
|
||||||
|
return resolveTrue.then(function(check) {
|
||||||
|
return check
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.add('resolved promise but four then()', function() {
|
||||||
|
return resolveTrue.then(function(check) {
|
||||||
|
return check
|
||||||
|
})
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
})
|
||||||
|
.add('resolved promise but twenty then()', function() {
|
||||||
|
return resolveTrue.then(function(check) {
|
||||||
|
if (check !== true) {
|
||||||
|
throw new Error('false')
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
.then(function(item) { return item })
|
||||||
|
})
|
||||||
|
.add('async function()', async function() {
|
||||||
|
let check = await resolveTrue
|
||||||
|
if (check !== true) {
|
||||||
|
throw new Error('false')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.run()
|
||||||
|
.then(function() {}, function(e) {
|
||||||
|
console.error('error:', e)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function TestObjectAssign() {
|
||||||
|
const base = {
|
||||||
|
state: {},
|
||||||
|
log: {
|
||||||
|
info: function() { },
|
||||||
|
warn: function() { },
|
||||||
|
error: function() { },
|
||||||
|
},
|
||||||
|
router1: flaskaRouter1,
|
||||||
|
router2: flaskaRouter2,
|
||||||
|
}
|
||||||
|
const req = {
|
||||||
|
test: 1,
|
||||||
|
test2: 2,
|
||||||
|
koa1: koaRouter1,
|
||||||
|
koa2: koaRouter2,
|
||||||
|
test: function() {},
|
||||||
|
}
|
||||||
|
const res = {
|
||||||
|
express1: expressRouter1,
|
||||||
|
express2: expressRouter2,
|
||||||
|
test: function() {},
|
||||||
|
}
|
||||||
|
let propMaker = {}
|
||||||
|
let propMakerAlt = {}
|
||||||
|
Object.keys(base).forEach(function(key) {
|
||||||
|
propMaker[key] = {
|
||||||
|
enumerable: true,
|
||||||
|
value: base[key],
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
}
|
||||||
|
propMakerAlt[key] = {
|
||||||
|
enumerable: true,
|
||||||
|
value: base[key],
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
propMakerAlt.req = {
|
||||||
|
enumerable: true,
|
||||||
|
get: function() {
|
||||||
|
return req
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
}
|
||||||
|
propMakerAlt.res = {
|
||||||
|
enumerable: true,
|
||||||
|
get: function() {
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
function register1(ctx) {
|
||||||
|
ctx.log = base.log
|
||||||
|
}
|
||||||
|
function register2(ctx) {
|
||||||
|
ctx.router1 = flaskaRouter1
|
||||||
|
}
|
||||||
|
function register3(ctx) {
|
||||||
|
ctx.router2 = flaskaRouter2
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Benchmark.default('test different method to initialize objects)')
|
||||||
|
.add('Object.assign()', function() {
|
||||||
|
let ctx = {
|
||||||
|
req: req,
|
||||||
|
res: res,
|
||||||
|
}
|
||||||
|
Object.assign(ctx, base)
|
||||||
|
ctx.log.info()
|
||||||
|
})
|
||||||
|
.add('register functions with direct assignment', function() {
|
||||||
|
let ctx = {
|
||||||
|
state: {},
|
||||||
|
req: req,
|
||||||
|
res: res,
|
||||||
|
}
|
||||||
|
register1(ctx)
|
||||||
|
register2(ctx)
|
||||||
|
register3(ctx)
|
||||||
|
ctx.log.info()
|
||||||
|
})
|
||||||
|
/*.add('Object.create() all props', function() {
|
||||||
|
let ctx = Object.create({}, propMakerAlt)
|
||||||
|
ctx.log.info()
|
||||||
|
})
|
||||||
|
.add('Object.defineProperties()', function() {
|
||||||
|
let ctx = {
|
||||||
|
req: req,
|
||||||
|
res: res,
|
||||||
|
}
|
||||||
|
Object.defineProperties(ctx, propMaker)
|
||||||
|
ctx.log.info()
|
||||||
|
})
|
||||||
|
.add('Object.defineProperties() all props', function() {
|
||||||
|
let ctx = { }
|
||||||
|
Object.defineProperties(ctx, propMakerAlt)
|
||||||
|
ctx.log.info()
|
||||||
|
})*/
|
||||||
|
.run()
|
||||||
|
.then(function() {}, function(e) {
|
||||||
|
console.error('error:', e)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function TestSmallStaticRoute() {
|
function TestSmallStaticRoute() {
|
||||||
return new Benchmark.default('Small router static route benchmark: /api/staff (16 routes registered)')
|
return new Benchmark.default('Small router static route benchmark: /api/staff (16 routes registered)')
|
||||||
.add('expressjs', function() {
|
.add('expressjs', function() {
|
||||||
|
@ -28,10 +238,6 @@ function TestSmallStaticRoute() {
|
||||||
testData = flaskaRouter1.match('/api/staff')
|
testData = flaskaRouter1.match('/api/staff')
|
||||||
assert.ok(testData.handler)
|
assert.ok(testData.handler)
|
||||||
})
|
})
|
||||||
.add('bottle-router-alt', function() {
|
|
||||||
testData = flaskaClassRouter1.match('/api/staff')
|
|
||||||
assert.ok(testData.handler)
|
|
||||||
})
|
|
||||||
.run()
|
.run()
|
||||||
.then(function() {}, function(e) {
|
.then(function() {}, function(e) {
|
||||||
console.error('error:', e)
|
console.error('error:', e)
|
||||||
|
@ -57,10 +263,6 @@ function TestSmallParamRoute() {
|
||||||
testData = flaskaRouter1.match('/api/staff/justatest')
|
testData = flaskaRouter1.match('/api/staff/justatest')
|
||||||
assert.ok(testData.handler)
|
assert.ok(testData.handler)
|
||||||
})
|
})
|
||||||
.add('bottle-router-alt', function() {
|
|
||||||
testData = flaskaClassRouter1.match('/api/staff/justatest')
|
|
||||||
assert.ok(testData.handler)
|
|
||||||
})
|
|
||||||
.run()
|
.run()
|
||||||
.then(function() {}, function(e) {
|
.then(function() {}, function(e) {
|
||||||
console.error('error:', e)
|
console.error('error:', e)
|
||||||
|
@ -86,10 +288,6 @@ function TestLargeStaticRoute() {
|
||||||
testData = flaskaRouter2.match('/api/staff')
|
testData = flaskaRouter2.match('/api/staff')
|
||||||
assert.ok(testData.handler)
|
assert.ok(testData.handler)
|
||||||
})
|
})
|
||||||
.add('bottle-router-alt', function() {
|
|
||||||
testData = flaskaClassRouter2.match('/api/staff')
|
|
||||||
assert.ok(testData.handler)
|
|
||||||
})
|
|
||||||
.run()
|
.run()
|
||||||
.then(function() {}, function(e) {
|
.then(function() {}, function(e) {
|
||||||
console.error('error:', e)
|
console.error('error:', e)
|
||||||
|
@ -115,10 +313,6 @@ function TestLargeParamRoute() {
|
||||||
testData = flaskaRouter2.match('/api/staff/justatest')
|
testData = flaskaRouter2.match('/api/staff/justatest')
|
||||||
assert.ok(testData.handler)
|
assert.ok(testData.handler)
|
||||||
})
|
})
|
||||||
.add('bottle-router-alt', function() {
|
|
||||||
testData = flaskaClassRouter2.match('/api/staff/justatest')
|
|
||||||
assert.ok(testData.handler)
|
|
||||||
})
|
|
||||||
.run()
|
.run()
|
||||||
.then(function() {}, function(e) {
|
.then(function() {}, function(e) {
|
||||||
console.error('error:', e)
|
console.error('error:', e)
|
||||||
|
@ -144,10 +338,6 @@ function TestLargeParamLargeUrlRoute() {
|
||||||
testData = flaskaRouter2.match('/api/products/justatest/sub_products/foobar')
|
testData = flaskaRouter2.match('/api/products/justatest/sub_products/foobar')
|
||||||
assert.ok(testData.handler)
|
assert.ok(testData.handler)
|
||||||
})
|
})
|
||||||
.add('bottle-router-alt', function() {
|
|
||||||
testData = flaskaClassRouter2.match('/api/products/justatest/sub_products/foobar')
|
|
||||||
assert.ok(testData.handler)
|
|
||||||
})
|
|
||||||
.run()
|
.run()
|
||||||
.then(function() {}, function(e) {
|
.then(function() {}, function(e) {
|
||||||
console.error('error:', e)
|
console.error('error:', e)
|
||||||
|
@ -156,6 +346,8 @@ function TestLargeParamLargeUrlRoute() {
|
||||||
}
|
}
|
||||||
|
|
||||||
TestSmallStaticRoute()
|
TestSmallStaticRoute()
|
||||||
|
// TestObjectAssign()
|
||||||
|
// TestPromiseCreators()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return TestSmallParamRoute()
|
return TestSmallParamRoute()
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,25 +1,18 @@
|
||||||
import { FlaskaRouter, FlaskaRouterClass } from '../flaska.mjs'
|
import { FlaskaRouter } from '../flaska.mjs'
|
||||||
import * as consts from './const.js'
|
import * as consts from './const.js'
|
||||||
|
|
||||||
const router1 = new FlaskaRouter()
|
const router1 = new FlaskaRouter()
|
||||||
const router2 = new FlaskaRouter()
|
const router2 = new FlaskaRouter()
|
||||||
|
|
||||||
const classRouter1 = new FlaskaRouterClass()
|
|
||||||
const classRouter2 = new FlaskaRouterClass()
|
|
||||||
|
|
||||||
for (let key in consts.allRoutes) {
|
for (let key in consts.allRoutes) {
|
||||||
router1.addRoute(consts.allRoutes[key], consts.dummy)
|
router1.addRoute(consts.allRoutes[key], consts.dummy)
|
||||||
classRouter1.addRoute(consts.allRoutes[key], consts.dummy)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let key in consts.allManyRoutes) {
|
for (let key in consts.allManyRoutes) {
|
||||||
router2.addRoute(consts.allManyRoutes[key], consts.dummy)
|
router2.addRoute(consts.allManyRoutes[key], consts.dummy)
|
||||||
classRouter2.addRoute(consts.allManyRoutes[key], consts.dummy)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
router1 as flaskaRouter1,
|
router1 as flaskaRouter1,
|
||||||
router2 as flaskaRouter2,
|
router2 as flaskaRouter2,
|
||||||
classRouter1 as flaskaClassRouter1,
|
|
||||||
classRouter2 as flaskaClassRouter2,
|
|
||||||
}
|
}
|
||||||
|
|
483
flaska.mjs
483
flaska.mjs
|
@ -1,3 +1,6 @@
|
||||||
|
import http from 'http'
|
||||||
|
import stream from 'stream'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Router
|
* Router
|
||||||
*/
|
*/
|
||||||
|
@ -12,9 +15,41 @@ class Branch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ErrorCodes = {
|
||||||
|
ERR_CONNECTION_ABORTED: 'ERR_CON_ABORTED'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Taken from https://github.com/nfp-projects/koa-lite/blob/master/lib/statuses.js
|
||||||
|
const statuses = {
|
||||||
|
100: 'Continue', 101: 'Switching Protocols', 102: 'Processing', 103: 'Early Hints',
|
||||||
|
200: 'OK', 201: 'Created', 202: 'Accepted', 203: 'Non-Authoritative Information', 204: 'No Content', 205: 'Reset Content', 206: 'Partial Content', 207: 'Multi-Status', 208: 'Already Reported', 226: 'IM Used',
|
||||||
|
300: 'Multiple Choices', 301: 'Moved Permanently', 302: 'Found', 303: 'See Other', 304: 'Not Modified', 305: 'Use Proxy', 306: '(Unused)', 307: 'Temporary Redirect', 308: 'Permanent Redirect',
|
||||||
|
400: 'Bad Request', 401: 'Unauthorized', 402: 'Payment Required', 403: 'Forbidden', 404: 'Not Found', 405: 'Method Not Allowed', 406: 'Not Acceptable', 407: 'Proxy Authentication Required', 408: 'Request Timeout', 409: 'Conflict', 410: 'Gone', 411: 'Length Required', 412: 'Precondition Failed', 413: 'Payload Too Large', 414: 'URI Too Long', 415: 'Unsupported Media Type', 416: 'Range Not Satisfiable', 417: 'Expectation Failed', 418: 'I\'m a teapot', 421: 'Misdirected Request', 422: 'Unprocessable Entity', 423: 'Locked', 424: 'Failed Dependency', 425: 'Too Early', 426: 'Upgrade Required', 428: 'Precondition Required', 429: 'Too Many Requests', 431: 'Request Header Fields Too Large', 451: 'Unavailable For Legal Reasons',
|
||||||
|
500: 'Internal Server Error', 501: 'Not Implemented', 502: 'Bad Gateway', 503: 'Service Unavailable', 504: 'Gateway Timeout', 505: 'HTTP Version Not Supported', 506: 'Variant Also Negotiates', 507: 'Insufficient Storage', 508: 'Loop Detected', 509: 'Bandwidth Limit Exceeded', 510: 'Not Extended', 511: 'Network Authentication Required',
|
||||||
|
redirect: {
|
||||||
|
300: true,
|
||||||
|
301: true,
|
||||||
|
302: true,
|
||||||
|
303: true,
|
||||||
|
305: true,
|
||||||
|
307: true,
|
||||||
|
308: true
|
||||||
|
},
|
||||||
|
empty: {
|
||||||
|
204: true,
|
||||||
|
205: true,
|
||||||
|
304: true
|
||||||
|
}
|
||||||
|
}
|
||||||
const __paramMapName = '__param'
|
const __paramMapName = '__param'
|
||||||
const __fullParamMapName = '__fullparam'
|
const __fullParamMapName = '__fullparam'
|
||||||
|
|
||||||
|
function assertIsHandler(handler, name) {
|
||||||
|
if (typeof(handler) !== 'function') {
|
||||||
|
throw new Error(`${name} was called with a handler that was not a function`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class FlaskaRouter {
|
export class FlaskaRouter {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.root = new Branch()
|
this.root = new Branch()
|
||||||
|
@ -33,9 +68,7 @@ export class FlaskaRouter {
|
||||||
if (middlewares && typeof(middlewares) === 'function') {
|
if (middlewares && typeof(middlewares) === 'function') {
|
||||||
middlewares = [middlewares]
|
middlewares = [middlewares]
|
||||||
}
|
}
|
||||||
if (typeof(handler) !== 'function') {
|
assertIsHandler(handler, 'addRoute()')
|
||||||
throw new Error(`route "${route}" was missing a handler`)
|
|
||||||
}
|
|
||||||
|
|
||||||
let start = 1
|
let start = 1
|
||||||
let end = 1
|
let end = 1
|
||||||
|
@ -150,14 +183,34 @@ export class FlaskaRouter {
|
||||||
else if (output = branch.children.get(__paramMapName)) {
|
else if (output = branch.children.get(__paramMapName)) {
|
||||||
branch = output
|
branch = output
|
||||||
params[branch.paramName] = name
|
params[branch.paramName] = name
|
||||||
|
}
|
||||||
|
else if (output = branch.children.get(__fullParamMapName)) {
|
||||||
|
params[output.fullparamName] = url.slice(start)
|
||||||
|
return {
|
||||||
|
handler: output.handler,
|
||||||
|
middlewares: output.middlewares,
|
||||||
|
params: params,
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (output = this.root.children.get(__fullParamMapName)) {
|
||||||
|
params = {
|
||||||
|
[output.fullparamName]: url.slice(1)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
handler: output.handler,
|
||||||
|
middlewares: output.middlewares,
|
||||||
|
params: params,
|
||||||
|
}
|
||||||
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
end = start = i
|
end = start = i
|
||||||
char = url[i]
|
char = url[i]
|
||||||
}
|
}
|
||||||
if (i >= url.length) {
|
// Check branch.handler. This can happen if route /::path is added
|
||||||
|
// and request is '/' it will attempt to match root which will fail
|
||||||
|
if (i >= url.length && branch.handler) {
|
||||||
return {
|
return {
|
||||||
handler: branch.handler,
|
handler: branch.handler,
|
||||||
middlewares: branch.middlewares,
|
middlewares: branch.middlewares,
|
||||||
|
@ -170,167 +223,313 @@ export class FlaskaRouter {
|
||||||
end++
|
end++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (output = this.root.children.get(__fullParamMapName)) {
|
||||||
|
params = {
|
||||||
|
[output.fullparamName]: url.slice(1)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
handler: output.handler,
|
||||||
|
middlewares: output.middlewares,
|
||||||
|
params: params,
|
||||||
|
}
|
||||||
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FlaskaRouter() {
|
/**
|
||||||
this.root = new Branch()
|
* Flaska
|
||||||
}
|
*/
|
||||||
|
export class Flaska {
|
||||||
|
constructor(opts, orgHttp = http, orgStream = stream) {
|
||||||
|
this._before = []
|
||||||
|
this._beforeCompiled = null
|
||||||
|
this._beforeAsync = []
|
||||||
|
this._beforeAsyncCompiled = null
|
||||||
|
this._after = []
|
||||||
|
this._afterCompiled = null
|
||||||
|
this._afterAsync = []
|
||||||
|
this._afterAsyncCompiled = null
|
||||||
|
this._on404 = function(ctx) {
|
||||||
|
ctx.status = 404
|
||||||
|
ctx.body = {
|
||||||
|
status: 404,
|
||||||
|
message: statuses[404],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._backuperror = this._onerror = function(err, ctx) {
|
||||||
|
ctx.log.error(err)
|
||||||
|
ctx.status = 500
|
||||||
|
ctx.body = {
|
||||||
|
status: 500,
|
||||||
|
message: statuses[500],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._onreqerror = function(err, ctx) {
|
||||||
|
ctx.log.error(err)
|
||||||
|
ctx.res.statusCode = ctx.statusCode = 400
|
||||||
|
ctx.res.end()
|
||||||
|
ctx.finished = true
|
||||||
|
}
|
||||||
|
this._onreserror = function(err, ctx) {
|
||||||
|
ctx.log.error(err)
|
||||||
|
}
|
||||||
|
|
||||||
FlaskaRouter.prototype.addRoute = function(route, orgMiddlewares, orgHandler) {
|
let options = opts || {}
|
||||||
if (route[0] !== '/')
|
|
||||||
throw new Error(`route "${route}" must start with forward slash`)
|
|
||||||
|
|
||||||
let middlewares = orgMiddlewares
|
this.log = options.log || console
|
||||||
let handler = orgHandler
|
this.http = orgHttp
|
||||||
if (!orgHandler) {
|
this.stream = orgStream
|
||||||
handler = orgMiddlewares
|
this.server = null
|
||||||
middlewares = []
|
this.routers = {
|
||||||
}
|
'GET': new FlaskaRouter(),
|
||||||
if (middlewares && typeof(middlewares) === 'function') {
|
'POST': new FlaskaRouter(),
|
||||||
middlewares = [middlewares]
|
'PUT': new FlaskaRouter(),
|
||||||
}
|
'DELETE': new FlaskaRouter(),
|
||||||
if (typeof(handler) !== 'function') {
|
'OPTIONS': new FlaskaRouter(),
|
||||||
throw new Error(`route "${route}" was missing a handler`)
|
'PATCH': new FlaskaRouter(),
|
||||||
|
}
|
||||||
|
this.get = this.routers.GET.addRoute.bind(this.routers.GET)
|
||||||
|
this.post = this.routers.POST.addRoute.bind(this.routers.POST)
|
||||||
|
this.put = this.routers.PUT.addRoute.bind(this.routers.PUT)
|
||||||
|
this.delete = this.routers.DELETE.addRoute.bind(this.routers.DELETE)
|
||||||
|
this.options = this.routers.OPTIONS.addRoute.bind(this.routers.OPTIONS)
|
||||||
|
this.patch = this.routers.PATCH.addRoute.bind(this.routers.PATCH)
|
||||||
}
|
}
|
||||||
|
|
||||||
let start = 1
|
_assertIsHandler(handler, name) {
|
||||||
let end = 1
|
if (typeof(handler) !== 'function') {
|
||||||
let name = ''
|
throw new Error(`${name} was called with a handler that was not a function`)
|
||||||
let param = ''
|
|
||||||
let isParam = false
|
|
||||||
let isFullParam = false
|
|
||||||
let branch = this.root
|
|
||||||
|
|
||||||
if (route.indexOf(':') < 0) {
|
|
||||||
let name = route
|
|
||||||
if (name.length > 1 && name[name.length - 1] === '/') {
|
|
||||||
name = name.slice(0, -1)
|
|
||||||
}
|
}
|
||||||
let child = new Branch()
|
|
||||||
branch.children.set(name, child)
|
|
||||||
child.handler = handler
|
|
||||||
child.middlewares = middlewares
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 1; i <= route.length; i++) {
|
devMode() {
|
||||||
if ((i === route.length || route[i] === '/') && end > start) {
|
this._backuperror = this._onerror = function(err, ctx) {
|
||||||
if (branch.fullparamName) {
|
ctx.log.error(err)
|
||||||
throw new Error(`route "${route}" conflicts with a sub-branch that has a full param child`)
|
ctx.status = 500
|
||||||
|
ctx.body = {
|
||||||
|
status: 500,
|
||||||
|
message: `${statuses[500]}: ${err.message}`,
|
||||||
|
stack: err.stack || '',
|
||||||
}
|
}
|
||||||
let child
|
|
||||||
name = route.substring(start, end)
|
|
||||||
if (isFullParam) {
|
|
||||||
param = name
|
|
||||||
name = __fullParamMapName
|
|
||||||
} else if (isParam) {
|
|
||||||
param = name
|
|
||||||
name = __paramMapName
|
|
||||||
}
|
|
||||||
if (branch.children.has(name)) {
|
|
||||||
child = branch.children.get(name)
|
|
||||||
}
|
|
||||||
else if (isParam && !isFullParam && branch.children.has(__fullParamMapName)) {
|
|
||||||
throw new Error(`route "${route}" conflicts with a sub-branch that has a full param child`)
|
|
||||||
}
|
|
||||||
else if (isFullParam && branch.children.has(__paramMapName)) {
|
|
||||||
throw new Error(`route "${route}" conflicts with a sub-branch that has a partial param child`)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
child = new Branch()
|
|
||||||
branch.children.set(name, child)
|
|
||||||
}
|
|
||||||
branch = child
|
|
||||||
end = i
|
|
||||||
start = i
|
|
||||||
if (isParam) {
|
|
||||||
if (branch.paramName && branch.paramName !== param) {
|
|
||||||
throw new Error(`route "${route}" conflicts with pre-existing param name of ${branch.paramName} instead of ${param}`)
|
|
||||||
}
|
|
||||||
if (isFullParam) {
|
|
||||||
branch.fullparamName = param
|
|
||||||
} else {
|
|
||||||
branch.paramName = param
|
|
||||||
}
|
|
||||||
isParam = false
|
|
||||||
}
|
|
||||||
} else if (route[i] === '/' && end === start) {
|
|
||||||
throw new Error(`route "${route}" has missing path name inbetween slashes`)
|
|
||||||
}
|
|
||||||
if (i === route.length) {
|
|
||||||
branch.handler = handler
|
|
||||||
branch.middlewares = middlewares
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (route[i] === ':') {
|
|
||||||
if (isParam) {
|
|
||||||
isFullParam = true
|
|
||||||
}
|
|
||||||
isParam = true
|
|
||||||
end = start = i + 1
|
|
||||||
}
|
|
||||||
else if (route[i] === '/') {
|
|
||||||
end = start = i + 1
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
end++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
FlaskaRouter.prototype.match = function(orgUrl) {
|
on404(handler) {
|
||||||
let url = orgUrl
|
assertIsHandler(handler, 'on404()')
|
||||||
if (url.length > 1 && url[url.length - 1] === '/') {
|
this._on404 = handler
|
||||||
url = url.slice(0, -1)
|
|
||||||
}
|
}
|
||||||
let branch = this.root
|
|
||||||
let start = 1
|
onerror(handler) {
|
||||||
let end = 1
|
assertIsHandler(handler, 'onerror()')
|
||||||
let output
|
this._onerror = handler
|
||||||
let name
|
}
|
||||||
let char
|
|
||||||
let params = {}
|
onreqerror(handler) {
|
||||||
if (output = branch.children.get(url)) {
|
assertIsHandler(handler, 'onreqerror()')
|
||||||
return {
|
this._onreqerror = handler
|
||||||
handler: output.handler,
|
}
|
||||||
middlewares: output.middlewares,
|
|
||||||
params: params,
|
onreserror(handler) {
|
||||||
|
assertIsHandler(handler, 'onreserror()')
|
||||||
|
this._onreserror = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
onerror(handler) {
|
||||||
|
assertIsHandler(handler, 'onerror()')
|
||||||
|
this._onerror = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
before(handler) {
|
||||||
|
assertIsHandler(handler, 'before()')
|
||||||
|
this._before.push(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeAsync(handler) {
|
||||||
|
assertIsHandler(handler, 'beforeAsync()')
|
||||||
|
this._beforeAsync.push(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
after(handler) {
|
||||||
|
assertIsHandler(handler, 'after()')
|
||||||
|
this._after.push(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
afterAsync(handler) {
|
||||||
|
assertIsHandler(handler, 'afterAsync()')
|
||||||
|
this._afterAsync.push(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
requestStart(req, res) {
|
||||||
|
let url = req.url
|
||||||
|
let search = ''
|
||||||
|
let hasSearch = url.indexOf('?')
|
||||||
|
|
||||||
|
if (hasSearch > 0) {
|
||||||
|
search = url.slice(hasSearch)
|
||||||
|
url = url.slice(0, hasSearch)
|
||||||
|
}
|
||||||
|
|
||||||
|
let ctx = {
|
||||||
|
req: req,
|
||||||
|
res: res,
|
||||||
|
method: req.method,
|
||||||
|
url: url,
|
||||||
|
search: search,
|
||||||
|
state: {},
|
||||||
|
status: 200,
|
||||||
|
body: null,
|
||||||
|
type: null,
|
||||||
|
length: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
req.on('error', (err) => {
|
||||||
|
this._onreqerror(err, ctx)
|
||||||
|
})
|
||||||
|
res.on('error', (err) => {
|
||||||
|
this._onreserror(err, ctx)
|
||||||
|
})
|
||||||
|
|
||||||
|
req.on('aborted', function() {
|
||||||
|
ctx.aborted = true
|
||||||
|
})
|
||||||
|
req.on('close', () => {
|
||||||
|
this.requestEnded()
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
this._beforeCompiled(ctx)
|
||||||
|
if (this._beforeAsyncCompiled) {
|
||||||
|
return this._beforeAsyncCompiled(ctx)
|
||||||
|
.then(() => {
|
||||||
|
this.requestStartInternal(ctx)
|
||||||
|
}).catch(err => {
|
||||||
|
this.requestEnd(err, ctx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.requestStartInternal(ctx)
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.requestEnd(err, ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (let i = 1; i <= url.length; i++) {
|
|
||||||
char = url[i]
|
requestStartInternal(ctx) {
|
||||||
if ((i === url.length || char === '/') && end > start) {
|
let route = this.routers[ctx.method].match(ctx.url)
|
||||||
name = url.slice(start, end)
|
ctx.params = route.params
|
||||||
if (output = branch.children.get(name)) {
|
|
||||||
branch = output
|
if (route.middlewares.length) {
|
||||||
}
|
let middle = this.handleMiddleware(ctx, route.middlewares, 0)
|
||||||
else if (output = branch.children.get(__paramMapName)) {
|
|
||||||
branch = output
|
if (middle && middle.then) {
|
||||||
params[branch.paramName] = name
|
return middle.then(() => {
|
||||||
} else {
|
return route.handler(ctx)
|
||||||
return null
|
})
|
||||||
}
|
.then(() => {
|
||||||
i++
|
this.requestEnd(null, ctx)
|
||||||
end = start = i
|
}, err => {
|
||||||
char = url[i]
|
this.requestEnd(err, ctx)
|
||||||
}
|
})
|
||||||
if (i >= url.length) {
|
|
||||||
return {
|
|
||||||
handler: branch.handler,
|
|
||||||
middlewares: branch.middlewares,
|
|
||||||
params: params,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (char === '/') {
|
let handler = route.handler(ctx)
|
||||||
end = start = i + 1
|
if (handler && handler.then) {
|
||||||
|
return handler.then(() => {
|
||||||
|
this.requestEnd(null, ctx)
|
||||||
|
}, err => {
|
||||||
|
this.requestEnd(err, ctx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.requestEnd(null, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMiddleware(ctx, middles, index) {
|
||||||
|
for (let i = index; i < middles.length; i++) {
|
||||||
|
let res = middles[i](ctx)
|
||||||
|
if (res && res.then) {
|
||||||
|
return res.then(() => {
|
||||||
|
return this.handleMiddleware(ctx, middles, i + 1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestEnd(err, ctx) {
|
||||||
|
if (err) {
|
||||||
|
this._onerror(err, ctx)
|
||||||
|
return ctx.res.end()
|
||||||
|
}
|
||||||
|
if (ctx.req.complete) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.res.statusCode = ctx.status
|
||||||
|
if (statuses.empty[ctx.status]) {
|
||||||
|
return ctx.res.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = ctx.body
|
||||||
|
let length = 0
|
||||||
|
if (body && typeof(body.pipe) === 'function') {
|
||||||
|
ctx.res.setHeader('Content-Type', ctx.type || 'application/octet-stream')
|
||||||
|
return this.stream.pipeline(body, ctx.res, function() {})
|
||||||
|
}
|
||||||
|
if (typeof(body) === 'object') {
|
||||||
|
body = JSON.stringify(body)
|
||||||
|
length = Buffer.byteLength(body)
|
||||||
|
ctx.res.setHeader('Content-Type', 'application/json; charset=utf-8')
|
||||||
} else {
|
} else {
|
||||||
end++
|
body = body.toString()
|
||||||
|
length = Buffer.byteLength(body)
|
||||||
|
ctx.res.setHeader('Content-Type', ctx.type || 'text/plain; charset=utf-8')
|
||||||
|
}
|
||||||
|
ctx.res.setHeader('Content-Length', length)
|
||||||
|
ctx.res.end(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
requestEnded(ctx) {
|
||||||
|
this._afterCompiled(ctx)
|
||||||
|
if (this._afterAsyncCompiled) {
|
||||||
|
return this._afterAsyncCompiled(ctx).then()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Flaska() {
|
compile() {
|
||||||
|
let types = ['before', 'after']
|
||||||
|
for (let i = 0; i < types.length; i++) {
|
||||||
|
let type = types[i]
|
||||||
|
let args = ''
|
||||||
|
let body = ''
|
||||||
|
for (let i = 0; i < this['_' + type].length; i++) {
|
||||||
|
args += `a${i}, `
|
||||||
|
body += `a${i}(ctx);`
|
||||||
|
}
|
||||||
|
args += 'ctx'
|
||||||
|
|
||||||
|
let func = new Function(args, body)
|
||||||
|
this[`_${type}Compiled`] = func.bind(this, ...this['_' + type])
|
||||||
|
|
||||||
|
if (this[`_${type}Async`].length) {
|
||||||
|
args = ''
|
||||||
|
body = 'return Promise.all(['
|
||||||
|
for (let i = 0; i < this[`_${type}Async`].length; i++) {
|
||||||
|
args += `a${i}, `
|
||||||
|
body += `a${i}(ctx),`
|
||||||
|
}
|
||||||
|
args += 'ctx'
|
||||||
|
body += '])'
|
||||||
|
func = new Function(args, body)
|
||||||
|
this[`_${type}AsyncCompiled`] = func.bind(this, ...this[`_${type}Async`])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listen(port, cb) {
|
||||||
|
if (typeof(port) !== 'number') {
|
||||||
|
throw new Error('Flaska.listen() called with non-number in port')
|
||||||
|
}
|
||||||
|
this.compile()
|
||||||
|
this.server = this.http.createServer(this.requestStart.bind(this))
|
||||||
|
this.server.listen(port, cb)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
17
test.mjs
Normal file
17
test.mjs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { Flaska } from './flaska.mjs'
|
||||||
|
|
||||||
|
const port = 51024
|
||||||
|
const flaska = new Flaska({}, )
|
||||||
|
|
||||||
|
flaska.get('/', function(ctx) {
|
||||||
|
return new Promise(function(res, rej) {
|
||||||
|
ctx.body = { status: true }
|
||||||
|
setTimeout(res, 1000)
|
||||||
|
}).then(function() {
|
||||||
|
process.stdout.write(".")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
flaska.listen(port, function() {
|
||||||
|
console.log('listening on port', port)
|
||||||
|
})
|
|
@ -1,39 +1,6 @@
|
||||||
import http from 'http'
|
import http from 'http'
|
||||||
import { URL } from 'url'
|
import { URL } from 'url'
|
||||||
|
import { defaults } from './helper.mjs'
|
||||||
// taken from isobject npm library
|
|
||||||
function isObject(val) {
|
|
||||||
return val != null && typeof val === 'object' && Array.isArray(val) === false
|
|
||||||
}
|
|
||||||
|
|
||||||
function defaults(options, def) {
|
|
||||||
let out = { }
|
|
||||||
|
|
||||||
if (options) {
|
|
||||||
Object.keys(options || {}).forEach(key => {
|
|
||||||
out[key] = options[key]
|
|
||||||
|
|
||||||
if (Array.isArray(out[key])) {
|
|
||||||
out[key] = out[key].map(item => {
|
|
||||||
if (isObject(item)) return defaults(item)
|
|
||||||
return item
|
|
||||||
})
|
|
||||||
} else if (out[key] && typeof out[key] === 'object') {
|
|
||||||
out[key] = defaults(options[key], def && def[key])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (def) {
|
|
||||||
Object.keys(def).forEach(function(key) {
|
|
||||||
if (typeof out[key] === 'undefined') {
|
|
||||||
out[key] = def[key]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Client(port, opts) {
|
export default function Client(port, opts) {
|
||||||
this.options = defaults(opts, {})
|
this.options = defaults(opts, {})
|
||||||
|
@ -48,6 +15,7 @@ Client.prototype.customRequest = function(method = 'GET', path, body, options) {
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const opts = defaults(defaults(options, {
|
const opts = defaults(defaults(options, {
|
||||||
|
agent: false,
|
||||||
method: method,
|
method: method,
|
||||||
timeout: 500,
|
timeout: 500,
|
||||||
protocol: urlObj.protocol,
|
protocol: urlObj.protocol,
|
||||||
|
@ -64,7 +32,10 @@ Client.prototype.customRequest = function(method = 'GET', path, body, options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
req.on('error', reject)
|
req.on('error', reject)
|
||||||
req.on('timeout', function() { reject(new Error(`Request ${method} ${path} timed out`)) })
|
req.on('timeout', function() {
|
||||||
|
console.log(req.destroy())
|
||||||
|
reject(new Error(`Request ${method} ${path} timed out`))
|
||||||
|
})
|
||||||
req.on('response', res => {
|
req.on('response', res => {
|
||||||
res.setEncoding('utf8')
|
res.setEncoding('utf8')
|
||||||
let output = ''
|
let output = ''
|
||||||
|
@ -79,7 +50,7 @@ Client.prototype.customRequest = function(method = 'GET', path, body, options) {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return reject(new Error(`${e.message} while decoding: ${output}`))
|
return reject(new Error(`${e.message} while decoding: ${output}`))
|
||||||
}
|
}
|
||||||
if (output.status) {
|
if (output.status && typeof(output.status) === 'number') {
|
||||||
let err = new Error(`Request failed [${output.status}]: ${output.message}`)
|
let err = new Error(`Request failed [${output.status}]: ${output.message}`)
|
||||||
err.body = output
|
err.body = output
|
||||||
return reject(err)
|
return reject(err)
|
||||||
|
@ -88,6 +59,7 @@ Client.prototype.customRequest = function(method = 'GET', path, body, options) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
req.end()
|
req.end()
|
||||||
|
return req
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
487
test/flaska.api.test.mjs
Normal file
487
test/flaska.api.test.mjs
Normal file
|
@ -0,0 +1,487 @@
|
||||||
|
import { Eltro as t, assert} from 'eltro'
|
||||||
|
import { Flaska } from '../flaska.mjs'
|
||||||
|
import { spy, createCtx, fakeHttp } from './helper.mjs'
|
||||||
|
|
||||||
|
const faker = fakeHttp()
|
||||||
|
|
||||||
|
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.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.finished, true)
|
||||||
|
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('a valid function', function() {
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
assert.strictEqual(typeof(flaska._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.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.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.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, 1)
|
||||||
|
assert.strictEqual(flaska._before[0], 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('#handleMiddleware()', function() {
|
||||||
|
t.test('should work with empty array', function() {
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
flaska.handleMiddleware({}, [], 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should work with correct index', function() {
|
||||||
|
let checkIsTrue = false
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
flaska.handleMiddleware({}, [
|
||||||
|
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.handleMiddleware(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.handleMiddleware(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.handleMiddleware({}, [
|
||||||
|
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.handleMiddleware({}, [
|
||||||
|
function() { },
|
||||||
|
function() { return Promise.reject(assertError) },
|
||||||
|
function() { throw new Error('should not be seen') },
|
||||||
|
], 0))
|
||||||
|
assert.strictEqual(err, assertError)
|
||||||
|
err = await assert.isRejected(flaska.handleMiddleware({}, [
|
||||||
|
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 assertCb = function() { }
|
||||||
|
let checkPort = null
|
||||||
|
let checkListenCb = null
|
||||||
|
|
||||||
|
let testFaker = fakeHttp(null, function(port, cb) {
|
||||||
|
checkPort = port
|
||||||
|
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(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)
|
||||||
|
})
|
||||||
|
})
|
495
test/flaska.in.test.mjs
Normal file
495
test/flaska.in.test.mjs
Normal file
|
@ -0,0 +1,495 @@
|
||||||
|
import { Eltro as t, assert} from 'eltro'
|
||||||
|
import { Flaska } from '../flaska.mjs'
|
||||||
|
import { fakeHttp, createReq, spy, createRes } from './helper.mjs'
|
||||||
|
|
||||||
|
const faker = fakeHttp()
|
||||||
|
|
||||||
|
t.describe('#requestStart()', function() {
|
||||||
|
t.test('calls on correct events on both req and res', function(cb) {
|
||||||
|
const assertErrorOne = new Error('Erics Birth')
|
||||||
|
const assertErrorTwo = new Error('Contactic Eric')
|
||||||
|
const onReqError = spy()
|
||||||
|
const onResError = spy()
|
||||||
|
const onEnded = spy()
|
||||||
|
const assertReq = createReq({ url: '', a: 1 })
|
||||||
|
const assertRes = createRes({ b: 2 })
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
|
||||||
|
flaska.onreqerror(onReqError)
|
||||||
|
flaska.onreserror(onResError)
|
||||||
|
flaska.requestEnded = onEnded
|
||||||
|
|
||||||
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
try {
|
||||||
|
assert.ok(err)
|
||||||
|
assert.strictEqual(assertReq.on.callCount, 3)
|
||||||
|
assert.strictEqual(assertRes.on.callCount, 1)
|
||||||
|
|
||||||
|
|
||||||
|
assert.strictEqual(assertRes.on.firstCall[0], 'error')
|
||||||
|
assert.strictEqual(typeof(assertRes.on.firstCall[1]), 'function')
|
||||||
|
assertRes.on.firstCall[1](assertErrorTwo, ctx)
|
||||||
|
assert.strictEqual(onResError.callCount, 1)
|
||||||
|
assert.strictEqual(onResError.firstCall[0], assertErrorTwo)
|
||||||
|
assert.strictEqual(onResError.firstCall[1], ctx)
|
||||||
|
|
||||||
|
assert.strictEqual(assertReq.on.firstCall[0], 'error')
|
||||||
|
assert.strictEqual(typeof(assertReq.on.firstCall[1]), 'function')
|
||||||
|
assertReq.on.firstCall[1](assertErrorOne, ctx)
|
||||||
|
assert.strictEqual(onReqError.callCount, 1)
|
||||||
|
assert.strictEqual(onReqError.firstCall[0], assertErrorOne)
|
||||||
|
assert.strictEqual(onReqError.firstCall[1], ctx)
|
||||||
|
|
||||||
|
assert.strictEqual(assertReq.on.secondCall[0], 'aborted')
|
||||||
|
assert.strictEqual(typeof(assertReq.on.secondCall[1]), 'function')
|
||||||
|
assert.notStrictEqual(ctx.aborted, false)
|
||||||
|
assertReq.on.secondCall[1]()
|
||||||
|
assert.strictEqual(ctx.aborted, true)
|
||||||
|
|
||||||
|
assert.strictEqual(assertReq.on.thirdCall[0], 'close')
|
||||||
|
assert.strictEqual(typeof(assertReq.on.thirdCall[1]), 'function')
|
||||||
|
assert.strictEqual(onEnded.called, false)
|
||||||
|
assertReq.on.thirdCall[1]()
|
||||||
|
assert.strictEqual(onEnded.called, true)
|
||||||
|
|
||||||
|
// Test abort and close
|
||||||
|
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
flaska._beforeCompiled = function(ctx) {
|
||||||
|
throw new Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestStart(assertReq, assertRes)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('calls beforeCompiled correctly', function(cb) {
|
||||||
|
const assertError = new Error('test')
|
||||||
|
const assertReq = createReq({ url: '', a: 1 })
|
||||||
|
const assertRes = createRes({ b: 2 })
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
|
||||||
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.ok(err)
|
||||||
|
assert.strictEqual(err, assertError)
|
||||||
|
assert.deepStrictEqual(ctx.state, {})
|
||||||
|
assert.strictEqual(ctx.req, assertReq)
|
||||||
|
assert.strictEqual(ctx.res, assertRes)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
flaska._beforeCompiled = function(ctx) {
|
||||||
|
assert.strictEqual(ctx.req, assertReq)
|
||||||
|
assert.strictEqual(ctx.res, assertRes)
|
||||||
|
throw assertError
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestStart(assertReq, assertRes)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('calls beforeAsyncCompiled correctly if defined', function(cb) {
|
||||||
|
const assertError = new Error('test')
|
||||||
|
const assertReq = createReq({ url: '', a: 1 })
|
||||||
|
const assertRes = createRes({ b: 2 })
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
flaska.compile()
|
||||||
|
|
||||||
|
flaska._beforeAsyncCompiled = function(ctx) {
|
||||||
|
assert.strictEqual(ctx.req, assertReq)
|
||||||
|
assert.strictEqual(ctx.res, assertRes)
|
||||||
|
return Promise.resolve().then(function() { return Promise.reject(assertError) })
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.ok(err)
|
||||||
|
assert.strictEqual(err, assertError)
|
||||||
|
assert.deepStrictEqual(ctx.state, {})
|
||||||
|
assert.strictEqual(ctx.req, assertReq)
|
||||||
|
assert.strictEqual(ctx.res, assertRes)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestStart(assertReq, assertRes)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('calls correct router with correct url and context', function(cb) {
|
||||||
|
const assertError = new Error('test')
|
||||||
|
const assertMethod = 'test'
|
||||||
|
const assertPath = '/test/me'
|
||||||
|
const assertSearch = '?asdf=test'
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
flaska.compile()
|
||||||
|
|
||||||
|
flaska.routers.test = {
|
||||||
|
match: function(path) {
|
||||||
|
assert.strictEqual(path, assertPath)
|
||||||
|
throw assertError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.ok(err)
|
||||||
|
assert.strictEqual(err, assertError)
|
||||||
|
assert.strictEqual(ctx.url, assertPath)
|
||||||
|
assert.strictEqual(ctx.search, assertSearch)
|
||||||
|
assert.strictEqual(ctx.method, assertMethod)
|
||||||
|
assert.strictEqual(ctx.status, 200)
|
||||||
|
assert.strictEqual(ctx.body, null)
|
||||||
|
assert.strictEqual(ctx.type, null)
|
||||||
|
assert.strictEqual(ctx.length, null)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestStart(createReq({
|
||||||
|
url: assertPath + assertSearch,
|
||||||
|
method: assertMethod,
|
||||||
|
}), createRes())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('calls correct router with correct url and context if beforeAsync', function(cb) {
|
||||||
|
const assertError = new Error('test')
|
||||||
|
const assertMethod = 'test'
|
||||||
|
const assertPath = '/test/me'
|
||||||
|
const assertSearch = '?asdf=test'
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
flaska.compile()
|
||||||
|
flaska._beforeAsyncCompiled = function() { return Promise.resolve() }
|
||||||
|
|
||||||
|
flaska.routers.test = {
|
||||||
|
match: function(path) {
|
||||||
|
assert.strictEqual(path, assertPath)
|
||||||
|
throw assertError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.ok(err)
|
||||||
|
assert.strictEqual(err, assertError)
|
||||||
|
assert.strictEqual(ctx.url, assertPath)
|
||||||
|
assert.strictEqual(ctx.search, assertSearch)
|
||||||
|
assert.strictEqual(ctx.method, assertMethod)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestStart(createReq({
|
||||||
|
url: assertPath + assertSearch,
|
||||||
|
method: assertMethod,
|
||||||
|
}), createRes())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('calls handleMiddleware correctly', function(cb) {
|
||||||
|
const assertError = new Error('test')
|
||||||
|
const assertMiddles = [1, 2]
|
||||||
|
const assertParams = { a: 1, b: 2 }
|
||||||
|
let checkMiddleCtx = null
|
||||||
|
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
flaska.compile()
|
||||||
|
|
||||||
|
flaska.routers.GET.match = function() {
|
||||||
|
return {
|
||||||
|
handler: function() {},
|
||||||
|
middlewares: assertMiddles,
|
||||||
|
params: assertParams,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flaska.handleMiddleware = function(ctx, middles, index) {
|
||||||
|
assert.strictEqual(index, 0)
|
||||||
|
assert.strictEqual(middles, assertMiddles)
|
||||||
|
checkMiddleCtx = ctx
|
||||||
|
throw assertError
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.ok(err)
|
||||||
|
assert.ok(ctx)
|
||||||
|
assert.strictEqual(err, assertError)
|
||||||
|
assert.strictEqual(ctx, checkMiddleCtx)
|
||||||
|
assert.strictEqual(ctx.params, assertParams)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestStart(createReq({
|
||||||
|
url: '',
|
||||||
|
method: 'GET',
|
||||||
|
}), createRes({}))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('calls route handler correctly', function(cb) {
|
||||||
|
const assertError = new Error('test')
|
||||||
|
let checkHandlerCtx = null
|
||||||
|
let handler = function(ctx) {
|
||||||
|
assert.strictEqual(ctx.params.id, 'test')
|
||||||
|
checkHandlerCtx = ctx
|
||||||
|
throw assertError
|
||||||
|
}
|
||||||
|
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
flaska.get('/:id', handler)
|
||||||
|
flaska.compile()
|
||||||
|
|
||||||
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.ok(err)
|
||||||
|
assert.ok(ctx)
|
||||||
|
assert.strictEqual(err, assertError)
|
||||||
|
assert.strictEqual(ctx, checkHandlerCtx)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestStart(createReq({
|
||||||
|
url: '/test',
|
||||||
|
method: 'GET',
|
||||||
|
}), createRes())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should work with synchronous handler', function(cb) {
|
||||||
|
const assertBody = { a: 1 }
|
||||||
|
let handler = function(ctx) {
|
||||||
|
assert.strictEqual(ctx.params.path, 'test/something/here')
|
||||||
|
ctx.body = assertBody
|
||||||
|
}
|
||||||
|
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
flaska.get('/::path', handler)
|
||||||
|
flaska.compile()
|
||||||
|
flaska.handleMiddleware = function() {
|
||||||
|
throw new Error('should not be called')
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
try {
|
||||||
|
assert.notOk(err)
|
||||||
|
assert.ok(ctx)
|
||||||
|
assert.strictEqual(ctx.body, assertBody)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestStart(createReq({
|
||||||
|
url: '/test/something/here',
|
||||||
|
method: 'GET',
|
||||||
|
}), createRes())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('calls handleMiddleware correctly if is promise', function(cb) {
|
||||||
|
const assertError = new Error('test')
|
||||||
|
const assertMiddles = [1]
|
||||||
|
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
flaska.compile()
|
||||||
|
|
||||||
|
flaska.routers.GET.match = function() {
|
||||||
|
return {
|
||||||
|
handler: function() {},
|
||||||
|
middlewares: assertMiddles,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flaska.handleMiddleware = function() {
|
||||||
|
return Promise.resolve().then(function() { return Promise.reject(assertError) })
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.ok(err)
|
||||||
|
assert.ok(ctx)
|
||||||
|
assert.strictEqual(err, assertError)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestStart(createReq({
|
||||||
|
url: '',
|
||||||
|
method: 'GET',
|
||||||
|
}), createRes())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('calls route handler correctly with promise middle', function(cb) {
|
||||||
|
const assertError = new Error('test')
|
||||||
|
let checkHandlerCtx = null
|
||||||
|
let handler = function(ctx) {
|
||||||
|
assert.strictEqual(ctx.params.id, 'test')
|
||||||
|
checkHandlerCtx = ctx
|
||||||
|
throw assertError
|
||||||
|
}
|
||||||
|
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
flaska.get('/:id', [function() { return Promise.resolve() }], handler)
|
||||||
|
flaska.compile()
|
||||||
|
|
||||||
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.ok(err)
|
||||||
|
assert.ok(ctx)
|
||||||
|
assert.strictEqual(err, assertError)
|
||||||
|
assert.strictEqual(ctx, checkHandlerCtx)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestStart(createReq({
|
||||||
|
url: '/test',
|
||||||
|
method: 'GET',
|
||||||
|
}), createRes())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('calls promise route handler correctly with promise middle', function(cb) {
|
||||||
|
const assertError = new Error('test')
|
||||||
|
let checkHandlerCtx = null
|
||||||
|
let handler = function(ctx) {
|
||||||
|
assert.strictEqual(ctx.params.id, 'test')
|
||||||
|
checkHandlerCtx = ctx
|
||||||
|
return Promise.resolve().then(function() { return Promise.reject(assertError) })
|
||||||
|
}
|
||||||
|
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
flaska.get('/:id', [function() { return Promise.resolve() }], handler)
|
||||||
|
flaska.compile()
|
||||||
|
|
||||||
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.ok(err)
|
||||||
|
assert.ok(ctx)
|
||||||
|
assert.strictEqual(err, assertError)
|
||||||
|
assert.strictEqual(ctx, checkHandlerCtx)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestStart(createReq({
|
||||||
|
url: '/test',
|
||||||
|
method: 'GET',
|
||||||
|
}), createRes())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should work with promise middle and promise handler correctly', function(cb) {
|
||||||
|
const assertBody = { a: 1 }
|
||||||
|
const assertState = { b: 2 }
|
||||||
|
let middle = function(ctx) {
|
||||||
|
return Promise.resolve().then(function() {
|
||||||
|
ctx.state = assertState
|
||||||
|
})
|
||||||
|
}
|
||||||
|
let handler = function(ctx) {
|
||||||
|
assert.strictEqual(ctx.params.path, 'test/something/here')
|
||||||
|
assert.strictEqual(ctx.state, assertState)
|
||||||
|
|
||||||
|
return Promise.resolve().then(function() {
|
||||||
|
ctx.body = assertBody
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
flaska.get('/::path', [middle], handler)
|
||||||
|
flaska.compile()
|
||||||
|
|
||||||
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
try {
|
||||||
|
assert.notOk(err)
|
||||||
|
assert.ok(ctx)
|
||||||
|
assert.strictEqual(ctx.body, assertBody)
|
||||||
|
assert.strictEqual(ctx.state, assertState)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestStart(createReq({
|
||||||
|
url: '/test/something/here',
|
||||||
|
method: 'GET',
|
||||||
|
}), createRes())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('calls route handler correctly if promise', function(cb) {
|
||||||
|
const assertError = new Error('test')
|
||||||
|
let checkHandlerCtx = null
|
||||||
|
let handler = function(ctx) {
|
||||||
|
assert.strictEqual(ctx.params.id, 'test')
|
||||||
|
checkHandlerCtx = ctx
|
||||||
|
return Promise.resolve().then(function() {
|
||||||
|
return Promise.reject(assertError)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
flaska.get('/:id', handler)
|
||||||
|
flaska.compile()
|
||||||
|
|
||||||
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
if (err && err !== assertError) return cb(err)
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert.ok(err)
|
||||||
|
assert.ok(ctx)
|
||||||
|
assert.strictEqual(err, assertError)
|
||||||
|
assert.strictEqual(ctx, checkHandlerCtx)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestStart(createReq({
|
||||||
|
url: '/test',
|
||||||
|
method: 'GET',
|
||||||
|
}), createRes())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should work with promise handler', function(cb) {
|
||||||
|
const assertBody = { a: 1 }
|
||||||
|
let handler = function(ctx) {
|
||||||
|
return Promise.resolve().then(function() {
|
||||||
|
assert.strictEqual(ctx.params.path, 'test/something/here')
|
||||||
|
ctx.body = assertBody
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let flaska = new Flaska({}, faker)
|
||||||
|
flaska.get('/::path', [], handler)
|
||||||
|
flaska.compile()
|
||||||
|
|
||||||
|
flaska.handleMiddleware = function() {
|
||||||
|
throw new Error('should not be called')
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestEnd = function(err, ctx) {
|
||||||
|
try {
|
||||||
|
assert.notOk(err)
|
||||||
|
assert.ok(ctx)
|
||||||
|
assert.strictEqual(ctx.body, assertBody)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
|
||||||
|
flaska.requestStart(createReq({
|
||||||
|
url: '/test/something/here',
|
||||||
|
method: 'GET',
|
||||||
|
}), createRes())
|
||||||
|
})
|
||||||
|
})
|
249
test/flaska.out.test.mjs
Normal file
249
test/flaska.out.test.mjs
Normal file
|
@ -0,0 +1,249 @@
|
||||||
|
import { Eltro as t, assert} from 'eltro'
|
||||||
|
import { Flaska, FlaskaRouter } from '../flaska.mjs'
|
||||||
|
import { fakeHttp, createCtx, spy } from './helper.mjs'
|
||||||
|
|
||||||
|
const fakerHttp = fakeHttp()
|
||||||
|
const fakeStream = { pipeline: spy() }
|
||||||
|
|
||||||
|
t.describe('#requestEnd()', function() {
|
||||||
|
t.test('calls onerror correctly on error', function(cb) {
|
||||||
|
const assertError = new Error('test')
|
||||||
|
const assertBody = { a: 1 }
|
||||||
|
let onFinish = function() {
|
||||||
|
try {
|
||||||
|
assert.strictEqual(ctx.status, 501)
|
||||||
|
assert.strictEqual(ctx.body, assertBody)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
const ctx = createCtx({}, onFinish)
|
||||||
|
|
||||||
|
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||||
|
flaska.onerror(function(err, inctx) {
|
||||||
|
assert.strictEqual(err, assertError)
|
||||||
|
assert.strictEqual(inctx, ctx)
|
||||||
|
inctx.status = 501
|
||||||
|
inctx.body = assertBody
|
||||||
|
})
|
||||||
|
|
||||||
|
flaska.requestEnd(assertError, ctx)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('call res and end correctly when dealing with objects', function(cb) {
|
||||||
|
const assertStatus = 202
|
||||||
|
// Calculated manually just in case
|
||||||
|
const assertBodyLength = 7
|
||||||
|
const assertBody = { a: 1 }
|
||||||
|
let onFinish = function(body) {
|
||||||
|
try {
|
||||||
|
assert.strictEqual(ctx.status, assertStatus)
|
||||||
|
assert.strictEqual(ctx.body, assertBody)
|
||||||
|
assert.strictEqual(ctx.res.statusCode, assertStatus)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.callCount, 2)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[0], 'Content-Type')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[1], 'application/json; charset=utf-8')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.secondCall[0], 'Content-Length')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.secondCall[1], assertBodyLength)
|
||||||
|
assert.ok(body)
|
||||||
|
assert.strictEqual(body, '{"a":1}')
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
const ctx = createCtx({
|
||||||
|
status: assertStatus,
|
||||||
|
}, onFinish)
|
||||||
|
ctx.body = assertBody
|
||||||
|
|
||||||
|
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||||
|
flaska.requestEnd(null, ctx)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('call res and end correctly when dealing with strings', function(cb) {
|
||||||
|
const assertStatus = 206
|
||||||
|
// Calculated manually just in case
|
||||||
|
const assertBodyLength = 4
|
||||||
|
const assertBody = 'eða'
|
||||||
|
let onFinish = function(body) {
|
||||||
|
try {
|
||||||
|
assert.strictEqual(ctx.status, assertStatus)
|
||||||
|
assert.strictEqual(ctx.body, assertBody)
|
||||||
|
assert.strictEqual(ctx.res.statusCode, assertStatus)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.callCount, 2)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[0], 'Content-Type')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[1], 'text/plain; charset=utf-8')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.secondCall[0], 'Content-Length')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.secondCall[1], assertBodyLength)
|
||||||
|
assert.ok(body)
|
||||||
|
assert.strictEqual(body, assertBody)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
const ctx = createCtx({
|
||||||
|
status: assertStatus,
|
||||||
|
}, onFinish)
|
||||||
|
ctx.body = assertBody
|
||||||
|
|
||||||
|
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||||
|
flaska.requestEnd(null, ctx)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('call res and end correctly when dealing with numbers', function(cb) {
|
||||||
|
const assertStatus = 211
|
||||||
|
// Calculated manually just in case
|
||||||
|
const assertBodyLength = 7
|
||||||
|
const assertBody = 4214124
|
||||||
|
let onFinish = function(body) {
|
||||||
|
try {
|
||||||
|
assert.strictEqual(ctx.status, assertStatus)
|
||||||
|
assert.strictEqual(ctx.body, assertBody)
|
||||||
|
assert.strictEqual(ctx.res.statusCode, assertStatus)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.callCount, 2)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[0], 'Content-Type')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[1], 'text/plain; charset=utf-8')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.secondCall[0], 'Content-Length')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.secondCall[1], assertBodyLength)
|
||||||
|
assert.ok(body)
|
||||||
|
assert.strictEqual(body, assertBody.toString())
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
const ctx = createCtx({
|
||||||
|
status: assertStatus,
|
||||||
|
}, onFinish)
|
||||||
|
ctx.body = assertBody
|
||||||
|
|
||||||
|
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||||
|
flaska.requestEnd(null, ctx)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('call res and end correctly when dealing with custom type', function(cb) {
|
||||||
|
const assertStatus = 209
|
||||||
|
const assertBody = 'test'
|
||||||
|
const assertType = 'something/else'
|
||||||
|
let onFinish = function(body) {
|
||||||
|
try {
|
||||||
|
assert.strictEqual(ctx.status, assertStatus)
|
||||||
|
assert.strictEqual(ctx.res.statusCode, assertStatus)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[0], 'Content-Type')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[1], assertType)
|
||||||
|
assert.strictEqual(body, assertBody)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
const ctx = createCtx({
|
||||||
|
status: assertStatus,
|
||||||
|
body: assertBody,
|
||||||
|
}, onFinish)
|
||||||
|
ctx.type = assertType
|
||||||
|
|
||||||
|
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||||
|
flaska.requestEnd(null, ctx)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('call pipeline correctly when dealing with pipe', function(cb) {
|
||||||
|
const assertStatus = 211
|
||||||
|
const assertType = 'herp/derp'
|
||||||
|
const assertBody = { pipe: function() {} }
|
||||||
|
|
||||||
|
let onFinish = function(source, target, callback) {
|
||||||
|
try {
|
||||||
|
assert.strictEqual(ctx.status, assertStatus)
|
||||||
|
assert.strictEqual(ctx.res.statusCode, assertStatus)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[0], 'Content-Type')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[1], assertType)
|
||||||
|
assert.strictEqual(source, assertBody)
|
||||||
|
assert.strictEqual(target, ctx.res)
|
||||||
|
assert.strictEqual(typeof(callback), 'function')
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
const ctx = createCtx({
|
||||||
|
status: assertStatus,
|
||||||
|
})
|
||||||
|
fakeStream.pipeline = onFinish
|
||||||
|
|
||||||
|
ctx.body = assertBody
|
||||||
|
ctx.type = assertType
|
||||||
|
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||||
|
flaska.requestEnd(null, ctx)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('call pipe should have default type', function(cb) {
|
||||||
|
let onFinish = function(source, target) {
|
||||||
|
try {
|
||||||
|
assert.strictEqual(ctx.res.statusCode, 200)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[0], 'Content-Type')
|
||||||
|
assert.strictEqual(ctx.res.setHeader.firstCall[1], 'application/octet-stream')
|
||||||
|
assert.strictEqual(source, ctx.body)
|
||||||
|
assert.strictEqual(target, ctx.res)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
const ctx = createCtx({})
|
||||||
|
ctx.body = { pipe: function() {} }
|
||||||
|
fakeStream.pipeline = onFinish
|
||||||
|
|
||||||
|
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||||
|
flaska.requestEnd(null, ctx)
|
||||||
|
})
|
||||||
|
|
||||||
|
const emptyStatuses = [204, 205, 304]
|
||||||
|
|
||||||
|
emptyStatuses.forEach(function(status) {
|
||||||
|
t.test('call res and end correctly when dealing with status ' + status, function(cb) {
|
||||||
|
const assertStatus = status
|
||||||
|
const assertNotBody = 'test'
|
||||||
|
const assertNotType = 'something/else'
|
||||||
|
let onFinish = function(body) {
|
||||||
|
try {
|
||||||
|
assert.strictEqual(ctx.status, assertStatus)
|
||||||
|
assert.strictEqual(ctx.res.statusCode, assertStatus)
|
||||||
|
assert.strictEqual(ctx.res.setHeader.callCount, 0)
|
||||||
|
assert.notOk(body)
|
||||||
|
cb()
|
||||||
|
} catch (err) { cb(err) }
|
||||||
|
}
|
||||||
|
const ctx = createCtx({
|
||||||
|
status: assertStatus,
|
||||||
|
body: assertNotBody,
|
||||||
|
}, onFinish)
|
||||||
|
ctx.type = assertNotType
|
||||||
|
|
||||||
|
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||||
|
flaska.requestEnd(null, ctx)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.describe('#requestEnded()', function() {
|
||||||
|
t.test('calls afterCompiled correctly', function() {
|
||||||
|
const assertError = new Error('test')
|
||||||
|
const assertCtx = createCtx()
|
||||||
|
let flaska = new Flaska({}, fakerHttp)
|
||||||
|
|
||||||
|
flaska._afterCompiled = function(ctx) {
|
||||||
|
assert.strictEqual(ctx, assertCtx)
|
||||||
|
throw assertError
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.throws(function() {
|
||||||
|
flaska.requestEnded(assertCtx)
|
||||||
|
}, /test/)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('calls afterAsyncCompiled correctly if defined', async function() {
|
||||||
|
const assertError = new Error('test')
|
||||||
|
const assertCtx = createCtx()
|
||||||
|
let flaska = new Flaska({}, fakerHttp)
|
||||||
|
flaska.compile()
|
||||||
|
|
||||||
|
flaska._afterAsyncCompiled = function(ctx) {
|
||||||
|
assert.strictEqual(ctx, assertCtx)
|
||||||
|
return Promise.resolve().then(function() { return Promise.reject(assertError) })
|
||||||
|
}
|
||||||
|
|
||||||
|
let err = await assert.isRejected(flaska.requestEnded(assertCtx))
|
||||||
|
|
||||||
|
assert.strictEqual(err, assertError)
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,5 +0,0 @@
|
||||||
import { Eltro as t, assert} from 'eltro'
|
|
||||||
import { Flaska } from '../flaska.mjs'
|
|
||||||
|
|
||||||
t.describe('Flaska', function() {
|
|
||||||
})
|
|
118
test/helper.mjs
Normal file
118
test/helper.mjs
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
const indexMap = [
|
||||||
|
'firstCall',
|
||||||
|
'secondCall',
|
||||||
|
'thirdCall',
|
||||||
|
]
|
||||||
|
|
||||||
|
export function spy() {
|
||||||
|
let calls = []
|
||||||
|
let called = 0
|
||||||
|
let func = function(...args) {
|
||||||
|
func.called = true
|
||||||
|
calls.push(args)
|
||||||
|
if (called < indexMap.length) {
|
||||||
|
func[indexMap[called]] = args
|
||||||
|
}
|
||||||
|
called++
|
||||||
|
func.callCount = called
|
||||||
|
}
|
||||||
|
func.called = false
|
||||||
|
func.callCount = called
|
||||||
|
func.onCall = function(i) {
|
||||||
|
return calls[i]
|
||||||
|
}
|
||||||
|
for (let i = 0; i < indexMap.length; i++) {
|
||||||
|
func[indexMap] = null
|
||||||
|
}
|
||||||
|
return func
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fakeHttp(inj1, inj2) {
|
||||||
|
let intermediate = {
|
||||||
|
createServer: function(cb) {
|
||||||
|
if (inj1) inj1(cb)
|
||||||
|
intermediate.fakeRequest = cb
|
||||||
|
return {
|
||||||
|
listen: function(port, cb) {
|
||||||
|
if (inj2) inj2(port, cb)
|
||||||
|
else if (cb) cb()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return intermediate
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createReq(def) {
|
||||||
|
return defaults(def, {
|
||||||
|
on: spy(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createRes(def) {
|
||||||
|
return defaults(def, {
|
||||||
|
statusCode: 0,
|
||||||
|
end: spy(),
|
||||||
|
setHeader: spy(),
|
||||||
|
write: spy(),
|
||||||
|
on: spy(),
|
||||||
|
writeHead: spy(),
|
||||||
|
pipe: spy(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCtx(def, endHandler) {
|
||||||
|
return defaults(def, {
|
||||||
|
req: createReq(),
|
||||||
|
res: createRes({ end: endHandler || spy() }),
|
||||||
|
finished: false,
|
||||||
|
method: 'GET',
|
||||||
|
url: '/test',
|
||||||
|
search: '',
|
||||||
|
state: {},
|
||||||
|
status: 200,
|
||||||
|
body: null,
|
||||||
|
type: null,
|
||||||
|
length: null,
|
||||||
|
log: {
|
||||||
|
error: spy(),
|
||||||
|
info: spy(),
|
||||||
|
warn: spy(),
|
||||||
|
debug: spy(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// taken from isobject npm library
|
||||||
|
function isObject(val) {
|
||||||
|
return val != null && typeof val === 'object' && Array.isArray(val) === false
|
||||||
|
}
|
||||||
|
|
||||||
|
export function defaults(options, def) {
|
||||||
|
let out = { }
|
||||||
|
|
||||||
|
if (options) {
|
||||||
|
Object.keys(options || {}).forEach(key => {
|
||||||
|
out[key] = options[key]
|
||||||
|
|
||||||
|
if (Array.isArray(out[key])) {
|
||||||
|
out[key] = out[key].map(item => {
|
||||||
|
if (isObject(item)) return defaults(item)
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
} else if (out[key] && typeof out[key] === 'object') {
|
||||||
|
out[key] = defaults(options[key], def && def[key])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (def) {
|
||||||
|
Object.keys(def).forEach(function(key) {
|
||||||
|
if (typeof out[key] === 'undefined') {
|
||||||
|
out[key] = def[key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
29
test/http.test.mjs
Normal file
29
test/http.test.mjs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { Eltro as t, assert} from 'eltro'
|
||||||
|
import { Flaska } from '../flaska.mjs'
|
||||||
|
import Client from './client.mjs'
|
||||||
|
|
||||||
|
const port = 51024
|
||||||
|
const flaska = new Flaska({})
|
||||||
|
const client = new Client(port)
|
||||||
|
|
||||||
|
flaska.get('/', function(ctx) {
|
||||||
|
ctx.body = { status: true }
|
||||||
|
})
|
||||||
|
|
||||||
|
t.before(function(cb) {
|
||||||
|
flaska.listen(port, cb)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.describe('', function() {
|
||||||
|
t.test('/ should return status true', function() {
|
||||||
|
return client.get().then(function(body) {
|
||||||
|
assert.deepEqual(body, { status: true })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.after(function(cb) {
|
||||||
|
setTimeout(function() {
|
||||||
|
flaska.server.close(cb)
|
||||||
|
}, 1000)
|
||||||
|
})
|
|
@ -200,6 +200,50 @@ t.describe('#match()', function() {
|
||||||
assert.strictEqual(result.params.id, assertParameter)
|
assert.strictEqual(result.params.id, assertParameter)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.test('match full path variable paths', function() {
|
||||||
|
const assertParameter = 'bla/bla/bla'
|
||||||
|
let assertMatched = false
|
||||||
|
let router = new FlaskaRouter()
|
||||||
|
router.addRoute('/test/::id', function() { assertMatched = true })
|
||||||
|
let result = router.match('/test/' + assertParameter)
|
||||||
|
assert.ok(result.handler)
|
||||||
|
assert.ok(result.middlewares)
|
||||||
|
assert.strictEqual(result.middlewares.length, 0)
|
||||||
|
result.handler()
|
||||||
|
assert.strictEqual(assertMatched, true)
|
||||||
|
assert.strictEqual(result.params.id, assertParameter)
|
||||||
|
|
||||||
|
// Test with extra slash at the end
|
||||||
|
assertMatched = false
|
||||||
|
result = router.match('/test/' + assertParameter + '/')
|
||||||
|
assert.ok(result.handler)
|
||||||
|
assert.ok(result.middlewares)
|
||||||
|
assert.strictEqual(result.middlewares.length, 0)
|
||||||
|
result.handler()
|
||||||
|
assert.strictEqual(assertMatched, true)
|
||||||
|
assert.strictEqual(result.params.id, assertParameter)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('match full path root path properly', function() {
|
||||||
|
const assertParamFunc = function() { }
|
||||||
|
const assertFullFunc = function() { }
|
||||||
|
let router = new FlaskaRouter()
|
||||||
|
router.addRoute('/test/:bla', assertParamFunc)
|
||||||
|
router.addRoute('/::id', assertFullFunc)
|
||||||
|
let result = router.match('/test/123')
|
||||||
|
assert.strictEqual(result.handler, assertParamFunc)
|
||||||
|
assert.ok(result.middlewares)
|
||||||
|
assert.strictEqual(result.middlewares.length, 0)
|
||||||
|
assert.strictEqual(result.params.bla, '123')
|
||||||
|
|
||||||
|
result = router.match('/test/123/asdf')
|
||||||
|
assert.strictEqual(result.handler, assertFullFunc)
|
||||||
|
assert.ok(result.middlewares)
|
||||||
|
assert.strictEqual(result.middlewares.length, 0)
|
||||||
|
assert.strictEqual(result.params.id, 'test/123/asdf')
|
||||||
|
assert.notOk(result.params.bla)
|
||||||
|
})
|
||||||
|
|
||||||
t.test('match paths properly', function() {
|
t.test('match paths properly', function() {
|
||||||
let assertMatched = true
|
let assertMatched = true
|
||||||
let router = new FlaskaRouter()
|
let router = new FlaskaRouter()
|
||||||
|
@ -223,6 +267,36 @@ t.describe('#match()', function() {
|
||||||
assert.strictEqual(result.params.id, 'asdf')
|
assert.strictEqual(result.params.id, 'asdf')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.test('more comprehensive testing', function() {
|
||||||
|
const assertFunction = function() { }
|
||||||
|
const assertRootFunction = function() { }
|
||||||
|
const assertFailFunction = function() { }
|
||||||
|
let router = new FlaskaRouter()
|
||||||
|
router.addRoute('/test/:id', assertFunction)
|
||||||
|
router.addRoute('/test/test/::path', assertFunction)
|
||||||
|
router.addRoute('/foo/::path', assertFunction)
|
||||||
|
router.addRoute('/::path', assertFailFunction)
|
||||||
|
|
||||||
|
assert.strictEqual(router.match('/test/123').handler, assertFunction)
|
||||||
|
assert.strictEqual(router.match('/test/asdfasdg').handler, assertFunction)
|
||||||
|
assert.strictEqual(router.match('/test/test/sdafsda').handler, assertFunction)
|
||||||
|
assert.strictEqual(router.match('/test/test/sdafsda/gdfshe4/43y34/wtaw').handler, assertFunction)
|
||||||
|
assert.strictEqual(router.match('/foo/123').handler, assertFunction)
|
||||||
|
assert.strictEqual(router.match('/foo/bar/baz/test').handler, assertFunction)
|
||||||
|
assert.ok(router.match('/test/123/yweherher/reher/h34h34/'))
|
||||||
|
assert.strictEqual(router.match('/test/123/yweherher/reher/h34h34/').handler, assertFailFunction)
|
||||||
|
assert.ok(router.match('/test/foo/bar'))
|
||||||
|
assert.strictEqual(router.match('/test/foo/bar').handler, assertFailFunction)
|
||||||
|
assert.ok(router.match('/'))
|
||||||
|
assert.strictEqual(router.match('/').handler, assertFailFunction)
|
||||||
|
assert.ok(router.match('/something/else/goes/here'))
|
||||||
|
assert.strictEqual(router.match('/something/else/goes/here').handler, assertFailFunction)
|
||||||
|
|
||||||
|
router.addRoute('/', assertRootFunction)
|
||||||
|
assert.ok(router.match('/'))
|
||||||
|
assert.strictEqual(router.match('/').handler, assertRootFunction)
|
||||||
|
})
|
||||||
|
|
||||||
t.test('return null when no match is found', function() {
|
t.test('return null when no match is found', function() {
|
||||||
let router = new FlaskaRouter()
|
let router = new FlaskaRouter()
|
||||||
router.addRoute('/test/:id', function() { })
|
router.addRoute('/test/:id', function() { })
|
||||||
|
|
Loading…
Reference in a new issue