test-openapi
Version:
Automated client requests
108 lines (90 loc) • 3.21 kB
JavaScript
const { mapKeys, difference, intersection } = require('lodash')
const { STATUS_CODES } = require('statuses')
const { TestOpenApiError } = require('../../../errors')
const { getWordsList, merge } = require('../../../utils')
const {
utils: { parseStatus, serializeStatus },
} = require('../../validate')
// Add OpenAPI specification to `task.validate.*`
// Use the specification response matching both the current operation and
// the received status code `{ '200': validate, default: validate, ... }`
const addSpecToValidate = function({
validate = {},
pluginNames,
operation: { responses, operationId },
}) {
// Optional dependency
if (!pluginNames.includes('validate')) {
return
}
const status = getSpecStatus({ validate, responses, operationId })
const responsesA = mapKeys(responses, (value, responseStatus) =>
handleDefaultResponse({ responseStatus, responses }),
)
const validateA = merge({ status }, validate, responsesA)
return validateA
}
// Modifies `validate.status` to only allow status codes described in the
// specification.
// If `validate.status` already exists, intersects with it.
const getSpecStatus = function({
validate: { status = DEFAULT_STATUS },
responses,
operationId,
}) {
// If `default` is used, any status code is allowed.
if (responses.default !== undefined) {
return status
}
// `validate.status` as an array
const statuses = parseStatus({ status, property: 'task.validate.status' })
// Specification statuses
const responseStatuses = Object.keys(responses)
// Only keep specification statuses from `validate.status`
const statusesA = intersection(statuses, responseStatuses)
validateEmptyStatus({
statuses: statusesA,
responseStatuses,
status,
operationId,
})
// Serialize back to same format as `validate.status` input
const statusA = serializeStatus({ statuses: statusesA })
return statusA
}
// The default value of `validate.status` is assigned later, so we need to
// assign it now
const DEFAULT_STATUS = '2xx'
// Can only specify `validate.status` of status codes described in specification
const validateEmptyStatus = function({
statuses,
responseStatuses,
status,
operationId,
}) {
if (statuses.length !== 0) {
return
}
const expected = responseStatuses.map(Number)
const responseStatusesStr = getWordsList(responseStatuses)
throw new TestOpenApiError(
`Specification for '${operationId}' describes the following status codes: ${responseStatusesStr} but 'task.validate.status' only allows the following status codes: ${status}`,
{ value: status, expected, property: 'task.validate.status' },
)
}
// If there is a `default` response, it becomes `validate.STATUSES` where
// `STATUSES` are all the other valid HTTP statuses.
const handleDefaultResponse = function({ responseStatus, responses }) {
if (responseStatus !== 'default') {
return responseStatus
}
const responseStatuses = Object.keys(responses)
const validStatuses = Object.keys(STATUS_CODES)
const statuses = difference(validStatuses, responseStatuses)
const key = serializeStatus({ statuses })
return key
}
module.exports = {
addSpecToValidate,
}