mirror of
https://github.com/serverless/serverless.git
synced 2025-12-08 19:46:03 +00:00
261 lines
8.0 KiB
JavaScript
261 lines
8.0 KiB
JavaScript
'use strict'
|
|
|
|
const { expect } = require('chai')
|
|
const log = require('log').get('serverless:test')
|
|
const awsRequest = require('@serverless/test/aws-request')
|
|
const CloudFormationService = require('aws-sdk').CloudFormation
|
|
const fixtures = require('../../fixtures/programmatic')
|
|
|
|
const { confirmCloudWatchLogs } = require('../../utils/misc')
|
|
const {
|
|
deployService,
|
|
removeService,
|
|
fetch,
|
|
} = require('../../utils/integration')
|
|
|
|
describe('AWS - API Gateway Integration Test', function () {
|
|
this.timeout(1000 * 60 * 10) // Involves time-taking deploys
|
|
let serviceName
|
|
let endpoint
|
|
let stackName
|
|
let serviceDir
|
|
let updateConfig
|
|
let apiKey
|
|
let isDeployed = false
|
|
const stage = 'dev'
|
|
|
|
const resolveEndpoint = async () => {
|
|
const result = await awsRequest(CloudFormationService, 'describeStacks', {
|
|
StackName: stackName,
|
|
})
|
|
const endpointOutput = result.Stacks[0].Outputs.find(
|
|
(output) => output.OutputKey === 'ServiceEndpoint',
|
|
).OutputValue
|
|
endpoint = endpointOutput.match(
|
|
/https:\/\/.+\.execute-api\..+\.amazonaws\.com.+/,
|
|
)[0]
|
|
}
|
|
|
|
before(async () => {
|
|
const serviceData = await fixtures.setup('api-gateway-extended')
|
|
;({ servicePath: serviceDir, updateConfig } = serviceData)
|
|
serviceName = serviceData.serviceConfig.service
|
|
apiKey = `${serviceName}-api-key-1`
|
|
stackName = `${serviceName}-${stage}`
|
|
await deployService(serviceDir)
|
|
isDeployed = true
|
|
return resolveEndpoint()
|
|
})
|
|
|
|
after(async () => {
|
|
if (!isDeployed) return
|
|
log.notice('Removing service...')
|
|
await removeService(serviceDir)
|
|
})
|
|
|
|
describe('Minimal Setup', () => {
|
|
const expectedMessage = 'Hello from API Gateway! - (minimal)'
|
|
|
|
it('should expose an accessible GET HTTP endpoint', async () => {
|
|
const testEndpoint = `${endpoint}`
|
|
|
|
return fetch(testEndpoint, { method: 'GET' })
|
|
.then((response) => response.json())
|
|
.then((json) => expect(json.message).to.equal(expectedMessage))
|
|
})
|
|
|
|
it('should expose an accessible POST HTTP endpoint', async () => {
|
|
const testEndpoint = `${endpoint}/minimal-1`
|
|
|
|
return fetch(testEndpoint, { method: 'POST' })
|
|
.then((response) => response.json())
|
|
.then((json) => expect(json.message).to.equal(expectedMessage))
|
|
})
|
|
|
|
it('should expose an accessible PUT HTTP endpoint', async () => {
|
|
const testEndpoint = `${endpoint}/minimal-2`
|
|
|
|
return fetch(testEndpoint, { method: 'PUT' })
|
|
.then((response) => response.json())
|
|
.then((json) => expect(json.message).to.equal(expectedMessage))
|
|
})
|
|
|
|
it('should expose an accessible DELETE HTTP endpoint', async () => {
|
|
const testEndpoint = `${endpoint}/minimal-3`
|
|
|
|
return fetch(testEndpoint, { method: 'DELETE' })
|
|
.then((response) => response.json())
|
|
.then((json) => expect(json.message).to.equal(expectedMessage))
|
|
})
|
|
})
|
|
|
|
describe('CORS', () => {
|
|
it('should setup simple CORS support via cors: true config', async () => {
|
|
const testEndpoint = `${endpoint}/simple-cors`
|
|
|
|
return fetch(testEndpoint, { method: 'OPTIONS' }).then((response) => {
|
|
const headers = response.headers
|
|
const allowHeaders = [
|
|
'Content-Type',
|
|
'X-Amz-Date',
|
|
'Authorization',
|
|
'X-Api-Key',
|
|
'X-Amz-Security-Token',
|
|
'X-Amz-User-Agent',
|
|
'X-Amzn-Trace-Id',
|
|
].join(',')
|
|
expect(headers.get('access-control-allow-headers')).to.equal(
|
|
allowHeaders,
|
|
)
|
|
expect(headers.get('access-control-allow-methods')).to.equal(
|
|
'OPTIONS,GET',
|
|
)
|
|
expect(headers.get('access-control-allow-credentials')).to.equal(null)
|
|
expect(headers.get('access-control-allow-origin')).to.equal('*')
|
|
})
|
|
})
|
|
|
|
it('should setup CORS support with complex object config', async () => {
|
|
const testEndpoint = `${endpoint}/complex-cors`
|
|
|
|
return fetch(testEndpoint, { method: 'OPTIONS' }).then((response) => {
|
|
const headers = response.headers
|
|
const allowHeaders = [
|
|
'Content-Type',
|
|
'X-Amz-Date',
|
|
'Authorization',
|
|
'X-Api-Key',
|
|
'X-Amz-Security-Token',
|
|
'X-Amz-User-Agent',
|
|
'X-Amzn-Trace-Id',
|
|
].join(',')
|
|
expect(headers.get('access-control-allow-headers')).to.equal(
|
|
allowHeaders,
|
|
)
|
|
expect(headers.get('access-control-allow-methods')).to.equal(
|
|
'OPTIONS,GET',
|
|
)
|
|
expect(headers.get('access-control-allow-credentials')).to.equal('true')
|
|
expect(headers.get('access-control-allow-origin')).to.equal('*')
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('Custom Authorizers', () => {
|
|
let testEndpoint
|
|
|
|
before(() => {
|
|
testEndpoint = `${endpoint}/custom-auth`
|
|
})
|
|
|
|
it('should reject requests without authorization', async () => {
|
|
return fetch(testEndpoint).then((response) => {
|
|
expect(response.status).to.equal(401)
|
|
})
|
|
})
|
|
|
|
it('should reject requests with wrong authorization', async () => {
|
|
return fetch(testEndpoint, {
|
|
headers: { Authorization: 'Bearer ShouldNotBeAuthorized' },
|
|
}).then((response) => {
|
|
expect(response.status).to.equal(401)
|
|
})
|
|
})
|
|
|
|
it('should authorize requests with correct authorization', async () => {
|
|
return fetch(testEndpoint, {
|
|
headers: { Authorization: 'Bearer ShouldBeAuthorized' },
|
|
})
|
|
.then((response) => response.json())
|
|
.then((json) => {
|
|
expect(json.message).to.equal(
|
|
'Hello from API Gateway! - (customAuthorizers)',
|
|
)
|
|
expect(json.event.requestContext.authorizer.principalId).to.equal(
|
|
'SomeRandomId',
|
|
)
|
|
expect(json.event.headers.Authorization).to.equal(
|
|
'Bearer ShouldBeAuthorized',
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('API Keys', () => {
|
|
let testEndpoint
|
|
let startTime
|
|
|
|
before(() => {
|
|
testEndpoint = `${endpoint}/api-keys`
|
|
startTime = Date.now()
|
|
})
|
|
|
|
it('should succeed if correct API key is given', async function self() {
|
|
const response = await fetch(testEndpoint, {
|
|
headers: { 'X-API-Key': apiKey },
|
|
})
|
|
const result = await response.json()
|
|
// API Key may take a moment to propagate, retry
|
|
if (response.status === 403 && startTime > Date.now() - 1000 * 60 * 3) {
|
|
log.notice('API Key rejected, retry')
|
|
return self()
|
|
}
|
|
expect(response.status).to.equal(200)
|
|
expect(result.message).to.equal('Hello from API Gateway! - (apiKeys)')
|
|
return null
|
|
})
|
|
|
|
it('should reject a request with an invalid API Key', async () => {
|
|
return fetch(testEndpoint).then((response) => {
|
|
expect(response.status).to.equal(403)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('Using stage specific configuration', () => {
|
|
before(async () => {
|
|
await updateConfig({
|
|
provider: {
|
|
tags: {
|
|
foo: 'bar',
|
|
baz: 'qux',
|
|
},
|
|
tracing: {
|
|
apiGateway: true,
|
|
},
|
|
logs: {
|
|
restApi: true,
|
|
},
|
|
},
|
|
})
|
|
await deployService(serviceDir)
|
|
})
|
|
|
|
it('should update the stage without service interruptions', async () => {
|
|
// re-using the endpoint from the "minimal" test case
|
|
const testEndpoint = `${endpoint}`
|
|
|
|
return confirmCloudWatchLogs(
|
|
`/aws/api-gateway/${stackName}`,
|
|
() =>
|
|
fetch(`${testEndpoint}`, { method: 'GET' })
|
|
.then((response) => response.json())
|
|
// Confirm that APIGW responds as expected
|
|
.then((json) =>
|
|
expect(json.message).to.equal(
|
|
'Hello from API Gateway! - (minimal)',
|
|
),
|
|
),
|
|
// Confirm that CloudWatch logs for APIGW are written
|
|
).then((events) => expect(events.length > 0).to.equal(true))
|
|
})
|
|
})
|
|
|
|
describe('Integration Lambda Timeout', () => {
|
|
it('should result with 504 status code', async () =>
|
|
fetch(`${endpoint}/integration-lambda-timeout`).then((response) =>
|
|
expect(response.status).to.equal(504),
|
|
))
|
|
})
|
|
})
|