import { Eltro as t, spy, assert, stub} from 'eltro' import { FileResponse, Flaska, FlaskaRouter } from '../flaska.mjs' import { fakeHttp, createCtx } 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 } const assertStatus = 501 // Calculated manually just in case const assertBodyLength = 7 let onFinish = cb.finish(function(body) { assert.strictEqual(ctx.status, assertStatus) assert.strictEqual(ctx.body, assertBody) assert.strictEqual(ctx.headers['Content-Type'], 'application/json; charset=utf-8') assert.strictEqual(ctx.headers['Content-Length'], assertBodyLength) assert.ok(body) assert.strictEqual(body, '{"a":1}') assert.ok(ctx.res.writeHead.called) assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status) assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers) }) 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 = assertStatus inctx.body = assertBody }) flaska.requestEnd(assertError, ctx) }) t.test('calls backup onerror correctly if error throws', function(cb) { const assertErrorNotSeen = new Error('should not be seen') const assertError = new Error('test') let onFinish = cb.finish(function(body) { assert.strictEqual(ctx.status, 500) assert.strictEqual(ctx.body.status, 500) assert.strictEqual(ctx.body.message, 'Internal Server Error') }) const ctx = createCtx({}, onFinish) let flaska = new Flaska({}, fakerHttp, fakeStream) flaska.onerror(function() { throw assertError }) flaska.requestEnd(assertErrorNotSeen, ctx) }) let testMethods = ['GET', 'HEAD'] testMethods.forEach(function(method) { t.describe(method, function() { 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 = cb.finish(function(body) { assert.strictEqual(ctx.status, assertStatus) assert.strictEqual(ctx.body, assertBody) assert.strictEqual(ctx.headers['Content-Type'], 'application/json; charset=utf-8') assert.strictEqual(ctx.headers['Content-Length'], assertBodyLength) if (method === 'GET') { assert.ok(body) assert.strictEqual(body, '{"a":1}') } else { assert.notOk(body) } assert.ok(ctx.res.writeHead.called) assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status) assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers) }) const ctx = createCtx({ method: method, 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 buffers', function(cb) { const assertStatus = 202 // Calculated manually just in case const assertBodyLength = 11 const assertBody = Buffer.from('hello world') let onFinish = cb.finish(function(body) { assert.strictEqual(ctx.status, assertStatus) assert.strictEqual(ctx.body, assertBody) assert.strictEqual(ctx.headers['Content-Type'], 'application/octet-stream') assert.strictEqual(ctx.headers['Content-Length'], assertBodyLength) if (method === 'GET') { assert.ok(body) assert.strictEqual(body, assertBody) } else { assert.notOk(body) } assert.ok(ctx.res.writeHead.called) assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status) assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers) }) const ctx = createCtx({ method: method, 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 null and no status override', function(cb) { // Calculated manually just in case const assertBodyLength = 0 const assertBody = null let onFinish = cb.finish(function(body) { assert.strictEqual(ctx.status, 204) assert.strictEqual(ctx.body, assertBody) assert.notOk(ctx.headers['Content-Type']) assert.notOk(ctx.headers['Content-Length']) assert.strictEqual(body, undefined) assert.ok(ctx.res.writeHead.called) assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status) assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers) }) const ctx = createCtx({ method: method, status: 200, }, onFinish) ctx.body = assertBody let flaska = new Flaska({}, fakerHttp, fakeStream) flaska.requestEnd(null, ctx) }) t.test('call res and end correctly when dealing with null and status override', function(cb) { const assertStatus = 202 // Calculated manually just in case const assertBody = null let onFinish = cb.finish(function(body) { assert.strictEqual(ctx.status, assertStatus) assert.strictEqual(ctx.body, assertBody) assert.notOk(ctx.headers['Content-Type']) assert.strictEqual(ctx.headers['Content-Length'], 0) assert.strictEqual(body, undefined) assert.ok(ctx.res.writeHead.called) assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status) assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers) }) const ctx = createCtx({ method: method, 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 = cb.finish(function(body) { assert.strictEqual(ctx.status, assertStatus) assert.strictEqual(ctx.body, assertBody) assert.strictEqual(ctx.headers['Content-Type'], 'text/plain; charset=utf-8') assert.strictEqual(ctx.headers['Content-Length'], assertBodyLength) if (method === 'GET') { assert.ok(body) assert.strictEqual(body, assertBody) } else { assert.notOk(body) } assert.ok(ctx.res.writeHead.called) assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status) assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers) }) const ctx = createCtx({ method: method, 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 = cb.finish(function(body) { assert.strictEqual(ctx.status, assertStatus) assert.strictEqual(ctx.body, assertBody) assert.strictEqual(ctx.headers['Content-Type'], 'text/plain; charset=utf-8') assert.strictEqual(ctx.headers['Content-Length'], assertBodyLength) if (method === 'GET') { assert.ok(body) assert.strictEqual(body, assertBody.toString()) } else { assert.notOk(body) } assert.ok(ctx.res.writeHead.called) assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status) assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers) }) const ctx = createCtx({ method: method, status: assertStatus, }, onFinish) ctx.body = assertBody let flaska = new Flaska({}, fakerHttp, fakeStream) flaska.requestEnd(null, ctx) }) if (method === 'GET') { t.test('call handleRequest if body contains it', function(cb) { const assertContentType = 'test/test' const assertContentLength = 1241241 const assertHandle = stub().returnWith(function(checkCtx, secondary) { assert.notOk(secondary) assert.strictEqual(checkCtx, ctx) assert.notOk(ctx.res.writeHead.called) ctx.type = assertContentType ctx.headers['Content-Length'] = assertContentLength return assertBody }) let body = new FileResponse() let assertBody = { pipe: function() {} } let onFinish = cb.finish(function(source, target, callback) { assert.ok(assertHandle.called) assert.ok(ctx.res.writeHead) assert.strictEqual(ctx.body, assertBody) assert.strictEqual(source, assertBody) assert.strictEqual(target, ctx.res) assert.strictEqual(typeof(callback), 'function') assert.ok(ctx.res.writeHead.called) assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status) assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers) assert.strictEqual(ctx.headers['Content-Type'], assertContentType) assert.strictEqual(ctx.headers['Content-Length'], assertContentLength) }) const ctx = createCtx({ }) ctx.body = body fakeStream.pipeline = onFinish body.handleRequest = assertHandle let flaska = new Flaska({}, fakerHttp, fakeStream) try { flaska.requestEnd(null, ctx) } catch (err) { cb(err) } }) t.test('call pipeline correctly when dealing with pipe', function(cb) { const assertStatus = 211 const assertType = 'herp/derp' const assertBody = { pipe: function() {} } let onFinish = cb.finish(function(source, target, callback) { assert.strictEqual(ctx.status, assertStatus) assert.strictEqual(ctx.headers['Content-Type'], assertType) assert.strictEqual(source, assertBody) assert.strictEqual(target, ctx.res) assert.strictEqual(typeof(callback), 'function') assert.ok(ctx.res.writeHead.called) assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status) assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers) }) const ctx = createCtx({ status: assertStatus, }) fakeStream.pipeline = onFinish ctx.body = assertBody ctx.type = assertType let flaska = new Flaska({}, fakerHttp, fakeStream) flaska.requestEnd(null, ctx) }) } else { t.test('call handleRequest if body contains it', function(cb) { const assertContentType = 'test/test' const assertContentLength = 1241241 const assertHandle = stub().returnWith(function(checkCtx, secondary) { assert.notOk(secondary) assert.strictEqual(checkCtx, ctx) assert.notOk(ctx.res.writeHead.called) ctx.type = assertContentType ctx.headers['Content-Length'] = assertContentLength return null }) let body = new FileResponse() let onFinish = cb.finish(function(body) { assert.ok(assertHandle.called) assert.ok(ctx.res.writeHead) assert.strictEqual(ctx.body, null) assert.notOk(body) assert.ok(ctx.res.writeHead.called) assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status) assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers) assert.strictEqual(ctx.headers['Content-Type'], assertContentType) assert.strictEqual(ctx.headers['Content-Length'], assertContentLength) }) const ctx = createCtx({ method: method, }, onFinish) ctx.body = body body.handleRequest = assertHandle let flaska = new Flaska({}, fakerHttp, fakeStream) try { flaska.requestEnd(null, ctx) } catch (err) { cb(err) } }) t.test('call pipeline correctly when dealing with pipe', function(cb) { const assertStatus = 211 const assertType = 'herp/derp' const assertBody = { pipe: function() {}, destroy: stub() } let onFinish = cb.finish(function(body) { assert.strictEqual(ctx.status, assertStatus) assert.strictEqual(ctx.headers['Content-Type'], assertType) assert.notOk(ctx.headers['Content-Length']) assert.notOk(body) assert.ok(assertBody.destroy.called) assert.ok(ctx.res.writeHead.called) assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status) assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers) }) const ctx = createCtx({ method: method, status: assertStatus, }, onFinish) ctx.body = assertBody ctx.type = assertType let flaska = new Flaska({}, fakerHttp, fakeStream) flaska.requestEnd(null, ctx) }) } }) }) t.test('call _onerror with error if handleRequest throws error', function() { let body = new FileResponse() const assertError = new Error('Secrets of the Goddess') let assertBody = { a : 1 } const ctx = createCtx({ status: 204, }) ctx.body = body const assertHandle = stub().throws(assertError) body.handleRequest = assertHandle let flaska = new Flaska({}, fakerHttp, fakeStream) flaska._onerror = stub().returnWith(function(err, checkCtx) { assert.strictEqual(err, assertError) checkCtx.body = assertBody }) flaska.requestEnd(null, ctx) assert.ok(assertHandle.called) assert.ok(flaska._onerror.called) assert.strictEqual(flaska._onerror.firstCall[0], assertError) assert.ok(ctx.res.writeHead) assert.strictEqual(ctx.body, assertBody) }) 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 = cb.finish(function(body) { assert.strictEqual(ctx.status, assertStatus) assert.strictEqual(ctx.headers['Content-Type'], assertType) assert.strictEqual(body, assertBody) assert.ok(ctx.res.writeHead.called) assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status) assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers) }) const ctx = createCtx({ status: assertStatus, body: assertBody, }, onFinish) 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 = cb.finish(function(source, target) { assert.strictEqual(ctx.status, 200) assert.strictEqual(ctx.headers['Content-Type'], 'application/octet-stream') assert.strictEqual(source, ctx.body) assert.strictEqual(target, ctx.res) assert.ok(ctx.res.writeHead.called) assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status) assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers) }) const ctx = createCtx({}) ctx.body = { pipe: function() {} } fakeStream.pipeline = onFinish let flaska = new Flaska({}, fakerHttp, fakeStream) flaska.requestEnd(null, ctx) }) let tests = [ ['png', 'image/png'], ['jpg', 'image/jpeg'], ['bmp', 'image/bmp'], ['js', 'text/javascript'], ] tests.forEach(function(test) { t.test(`call pipe with file extension ${test[0]} should mimetype ${test[1]}`, function(cb) { let onFinish = cb.finish(function(source, target) { assert.strictEqual(ctx.status, 200) assert.strictEqual(ctx.headers['Content-Type'], test[1]) assert.strictEqual(source, ctx.body) assert.strictEqual(target, ctx.res) assert.ok(ctx.res.writeHead.called) assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status) assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers) }) const ctx = createCtx({}) ctx.body = { pipe: function() {}, path: './bla/test/temp.' + test[0] } fakeStream.pipeline = onFinish let flaska = new Flaska({}, fakerHttp, fakeStream) flaska.requestEnd(null, ctx) }) }) t.test('should return immediately if req is finished', function() { let onFinish = function() { throw new Error('should not be called') } const ctx = createCtx({ }, onFinish) ctx.res.writableEnded = true 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 = cb.finish(function(body) { assert.strictEqual(ctx.status, assertStatus) assert.strictEqual(ctx.res.setHeader.callCount, 0) assert.ok(ctx.res.writeHead.called) assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status) assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers) assert.notOk(body) }) 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 destroy if body is pipe and is not closed', function() { const ctx = createCtx({ body: { pipe: {}, closed: false, destroy: spy(), }, }) let flaska = new Flaska({}, fakerHttp) flaska._afterCompiled = function() {} flaska.requestEnded(ctx) assert.ok(ctx.body.destroy.called) }) t.test('not call destroy if body is pipe but is closed', function() { const ctx = createCtx({ body: { pipe: {}, closed: true, destroy: spy(), } }) let flaska = new Flaska({}, fakerHttp) flaska._afterCompiled = function() {} flaska.requestEnded(ctx) assert.notOk(ctx.body.destroy.called) }) 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) }) })