@goa/goa
Version:
The Goa Source Code For Compilation Into @Goa/Koa That Includes Modules, Tests, Types And Dependencies.
596 lines (505 loc) • 14 kB
JavaScript
import { equal, deepEqual } from '@zoroaster/assert'
import Context, { ConsoleMock } from '../../context'
import { createReadStream } from 'fs'
import statuses from '@goa/statuses'
/** @type {TestSuite} */
const TS = {
context: Context,
persistentContext: ConsoleMock,
'when ctx.respond === false': {
async 'should function (ctx)'({ app, startApp }) {
app.use(ctx => {
ctx.body = 'Hello'
ctx.respond = false
const { res } = ctx
res.statusCode = 200
setImmediate(() => {
res.setHeader('Content-Type', 'text/plain')
res.end('lol')
})
})
await startApp()
.get('/')
.assert(200, 'lol')
},
async 'ignores set header after header sent'({ app, startApp }) {
app.use(ctx => {
ctx.body = 'Hello'
ctx.respond = false
const { res } = ctx
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain')
res.end('lol')
ctx.set('foo', 'bar')
})
await startApp()
.get('/')
.assert(200, 'lol')
.assert('foo', null)
},
async 'ignores set status after header sent'({ app, startApp }) {
app.use(ctx => {
ctx.body = 'Hello'
ctx.respond = false
const { res } = ctx
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain')
res.end('lol')
ctx.status = 201
})
await startApp()
.get('/')
.assert(200, 'lol')
},
},
'when this.type === null': {
async 'does not send Content-Type header'({ app, startApp }) {
app.use(ctx => {
ctx.body = ''
ctx.type = null
})
await startApp()
.get('/')
.assert(200)
.assert('content-type', null)
},
},
'when HEAD is used': {
async 'does not respond with the body'({ app, startApp }) {
app.use(ctx => {
ctx.body = 'Hello'
})
await startApp()
.head('/')
.assert(200, '')
.assert('content-type', 'text/plain; charset=utf-8')
.assert('content-length', 5)
},
async 'keeps json headers'({ app, startApp }) {
app.use(ctx => {
ctx.body = { hello: 'world' }
})
await startApp()
.head('/')
.assert(200, '')
.assert('content-type', 'application/json; charset=utf-8')
.assert('content-length', 17)
},
async 'keeps string headers'({ app, startApp }) {
app.use(ctx => {
ctx.body = 'hello world'
})
await startApp()
.head('/')
.assert(200, '')
.assert('content-type', 'text/plain; charset=utf-8')
.assert('content-length', 11)
},
async 'keeps buffer headers'({ app, startApp }) {
app.use(ctx => {
ctx.body = Buffer.from('hello world')
})
await startApp()
.head('/')
.assert(200, '')
.assert('content-type', 'application/octet-stream')
.assert('content-length', 11)
},
async 'responds with a 404 if no body was set'({ app, startApp }) {
app.use(() => {
})
await startApp()
.head('/')
.assert(404)
},
async 'responds with a 200 if body = ""'({ app, startApp }) {
app.use(ctx => {
ctx.body = ''
})
await startApp()
.head('/')
.assert(200)
},
async 'does not overwrite the content-type'({ app, startApp }) {
app.use(ctx => {
ctx.status = 200
ctx.type = 'application/javascript'
})
await startApp()
.head('/')
.assert('content-type', /application\/javascript/)
.assert(200)
},
},
'when no middleware are present': {
async 'should 404'({ /* keep app as getter */ app, startApp }) { // eslint-disable-line
await startApp()
.get('/')
.assert(404)
},
},
'when res has already been written to': {
async 'should not cause an app error'({ app, startApp }) {
app.use((ctx) => {
const { res } = ctx
ctx.status = 200
res.setHeader('Content-Type', 'text/html')
res.write('Hello')
setTimeout(() => res.end('Goodbye'), 0)
})
app.on('error', err => { throw err })
await startApp()
.get('/')
.assert(200)
},
async 'sends the right body'({ app, startApp }) {
app.use(async (ctx) => {
const { res } = ctx
ctx.status = 200
res.setHeader('Content-Type', 'text/html')
res.write('Hello')
await new Promise(resolve => {
setTimeout(() => {
res.end('Goodbye')
resolve()
}, 0)
})
})
await startApp()
.get('/')
.assert(200, 'HelloGoodbye')
},
},
'when .body is missing': {
'with status=400': {
async 'responds with the associated status message'({ app, startApp }) {
app.use(ctx => {
ctx.status = 400
})
await startApp()
.get('/')
.assert(400, 'Bad Request')
.assert('Content-Length', 11)
},
},
'with status=204': {
async 'responds without a body'({ app, startApp }) {
app.use(ctx => {
ctx.status = 204
})
await startApp()
.get('/')
.assert(204, '')
.assert('content-type', null)
},
},
'with status=205': {
async 'responds without a body'({ app, startApp }) {
app.use(ctx => {
ctx.status = 205
})
await startApp()
.get('/')
.assert(205, '')
.assert('content-type', null)
},
},
'with status=304': {
async 'responds without a body'({ app, startApp }) {
app.use(ctx => {
ctx.status = 304
})
await startApp()
.get('/')
.assert(304, '')
.assert('content-type', null)
},
},
'with custom status=700': {
async 'responds with the associated status message'({ app, startApp }) {
statuses['700'] = 'custom status'
app.use(ctx => {
ctx.status = 700
})
await startApp()
.get('/')
.assert(700, 'custom status')
.assert(({ statusMessage }) => {
equal(statusMessage, 'custom status')
})
},
},
'with custom statusMessage=ok': {
async 'responds with the custom status message'({ app, startApp }) {
app.use(ctx => {
ctx.status = 200
ctx.message = 'ok'
})
await startApp()
.get('/')
.assert(200, 'ok')
.assert(({ statusMessage }) => {
equal(statusMessage, 'ok')
})
},
},
'with custom status without message': {
async 'responds with the status code number'({ app, startApp }) {
app.use(ctx => {
ctx.res.statusCode = 701
})
await startApp()
.get('/')
.assert(701, '701')
},
},
},
'when .body is a null': {
async 'responds 204 by default'({ app, startApp }) {
app.use(ctx => {
ctx.body = null
})
await startApp()
.get('/')
.assert(204, '')
.assert('content-type', null)
},
async 'responds 204 with status=200'({ app, startApp }) {
app.use(ctx => {
ctx.status = 200
ctx.body = null
})
await startApp()
.get('/')
.assert(204, '')
.assert('content-type', null)
},
async 'responds 205 with status=205'({ app, startApp }) {
app.use(ctx => {
ctx.status = 205
ctx.body = null
})
await startApp()
.get('/')
.assert(205, '')
.assert('content-type', null)
},
async 'responds 304 with status=304'({ app, startApp }) {
app.use(ctx => {
ctx.status = 304
ctx.body = null
})
await startApp()
.get('/')
.assert(304, '')
.assert('content-type', null)
},
},
'when .body is a string': {
async 'responds'({ app, startApp }) {
app.use(ctx => {
ctx.body = 'Hello'
})
await startApp()
.get('/')
.assert(200, 'Hello')
},
},
'when .body is a Buffer': {
async 'responds'({ app, startApp }) {
app.use(ctx => {
ctx.body = Buffer.from('Hello')
})
await startApp()
.get('/')
.assert(200, `${Buffer.from('Hello')}`)
},
},
'when .body is a Stream': {
async 'responds'({ app, startApp, stream, pkg }) {
app.use(ctx => {
ctx.body = stream
ctx.set('Content-Type', 'application/json; charset=utf-8')
})
await startApp()
.get('/')
.assert('Content-Type', 'application/json; charset=utf-8')
.assert('content-length', null)
.assert(({ body }) => {
deepEqual(body, pkg)
})
},
async 'strips content-length when overwriting'({ app, startApp, stream, pkg }) {
app.use(ctx => {
ctx.body = 'hello'
ctx.body = stream
ctx.set('Content-Type', 'application/json; charset=utf-8')
})
await startApp()
.get('/')
.assert('Content-Type', 'application/json; charset=utf-8')
.assert('content-length', null)
.assert(({ body }) => {
deepEqual(body, pkg)
})
},
async 'keeps content-length if not overwritten'({ app, startApp, readFileSync, stream, pkg }) {
const { length } = readFileSync()
app.use(ctx => {
ctx.length = length
ctx.body = stream
ctx.set('Content-Type', 'application/json; charset=utf-8')
})
await startApp()
.get('/')
.assert('Content-Type', 'application/json; charset=utf-8')
.assert('content-length', length)
.assert(({ body }) => {
deepEqual(body, pkg)
})
},
async 'keeps content-length if overwritten with the same stream'({ app, startApp, readFileSync, stream, pkg }) {
const { length } = readFileSync()
app.use(ctx => {
ctx.length = length
ctx.body = stream
ctx.body = stream
ctx.set('Content-Type', 'application/json; charset=utf-8')
})
await startApp()
.get('/')
.assert('Content-Type', 'application/json; charset=utf-8')
.assert('content-length', length)
.assert(({ body }) => {
deepEqual(body, pkg)
})
},
async 'handles errors'({ app, startApp }) {
app.use(ctx => {
ctx.set('Content-Type', 'application/json; charset=utf-8')
ctx.body = createReadStream('does not exist')
})
await startApp()
.get('/')
.assert('Content-Type', 'text/plain; charset=utf-8')
.assert(404)
},
async 'handles errors when no content status'({ app, startApp }) {
app.use(ctx => {
ctx.status = 204
ctx.body = createReadStream('does not exist')
})
await startApp()
.get('/')
.assert(204)
},
async 'handles all intermediate stream body errors'({ app, startApp }) {
app.use(ctx => {
ctx.body = createReadStream('does not exist')
ctx.body = createReadStream('does not exist')
ctx.body = createReadStream('does not exist')
})
await startApp()
.get('/')
.assert(404)
},
},
'when .body is an Object': {
async 'responds with json'({ app, startApp }) {
const body = { hello: 'world' }
app.use(ctx => {
ctx.body = body
})
await startApp()
.get('/')
.assert('Content-Type', 'application/json; charset=utf-8', body)
},
},
'when an error occurs': {
async 'emits "error" on the app'({ app, startApp, expectError }) {
app.use(() => {
throw new Error('boom')
})
const p = expectError({ message: 'boom' })
await startApp()
.get('/')
await p
},
'with an .expose property': {
async ' expose the message'({ app, startApp }) {
app.use(() => {
const err = new Error('sorry!')
err.status = 403
err.expose = true
throw err
})
await startApp()
.get('/')
.assert(403, 'sorry!')
},
},
'with a .status property': {
async 'responds with .status'({ app, startApp }) {
app.use(() => {
const err = new Error('s3 explodes')
err.status = 403
throw err
})
await startApp()
.get('/')
.assert(403, 'Forbidden')
},
},
async 'responds with 500'({ app, startApp }) {
app.use(() => {
throw new Error('boom!')
})
await startApp()
.get('/')
.assert(500, 'Internal Server Error')
},
async 'is catchable'({ app, startApp }) {
app.use((ctx, next) => {
return next().then(() => {
ctx.body = 'Hello'
}).catch(() => {
ctx.body = 'Got error'
})
})
app.use(() => {
throw new Error('boom!')
})
await startApp()
.get('/')
.assert(200, 'Got error')
},
},
'when status and body property': {
async 'should 200'({ app, startApp }) {
app.use(ctx => {
ctx.status = 304
ctx.body = 'hello'
ctx.status = 200
})
await startApp()
.get('/')
.assert(200, 'hello')
},
async 'should 204'({ app, startApp }) {
app.use(ctx => {
ctx.status = 200
ctx.body = 'hello'
ctx.set('content-type', 'text/plain; charset=utf8')
ctx.status = 204
})
await startApp()
.get('/')
.assert(204)
.assert('content-type', null)
},
},
}
export default TS
/**
* @typedef {import('../../context').TestSuite} TestSuite
*/