mirror of
https://github.com/serverless/serverless.git
synced 2026-01-25 15:07:39 +00:00
Currently it fails due to permissions issue in scope of our testing account. Once that's resolved we should bring this test back
316 lines
11 KiB
JavaScript
316 lines
11 KiB
JavaScript
'use strict';
|
|
|
|
const _ = require('lodash');
|
|
const { expect } = require('chai');
|
|
const log = require('log').get('serverless:test');
|
|
const awsRequest = require('@serverless/test/aws-request');
|
|
const fixtures = require('../../fixtures');
|
|
const { confirmCloudWatchLogs } = require('../../utils/misc');
|
|
|
|
const { getTmpDirPath } = require('../../utils/fs');
|
|
const {
|
|
createTestService,
|
|
deployService,
|
|
removeService,
|
|
fetch,
|
|
} = require('../../utils/integration');
|
|
|
|
describe('HTTP API Integration Test', function() {
|
|
this.timeout(1000 * 60 * 20); // Involves time-taking deploys
|
|
let serviceName;
|
|
let endpoint;
|
|
let stackName;
|
|
let tmpDirPath;
|
|
const stage = 'dev';
|
|
|
|
const resolveEndpoint = async () => {
|
|
const result = await awsRequest('CloudFormation', 'describeStacks', { StackName: stackName });
|
|
const endpointOutput = _.find(result.Stacks[0].Outputs, { OutputKey: 'HttpApiUrl' })
|
|
.OutputValue;
|
|
endpoint = endpointOutput.match(/https:\/\/.+\.execute-api\..+\.amazonaws\.com/)[0];
|
|
};
|
|
|
|
describe('Specific endpoints', () => {
|
|
let poolId;
|
|
let clientId;
|
|
const userName = 'test-http-api';
|
|
const userPassword = 'razDwa3!';
|
|
|
|
before(async () => {
|
|
tmpDirPath = getTmpDirPath();
|
|
log.debug('temporary path %s', tmpDirPath);
|
|
poolId = (
|
|
await awsRequest('CognitoIdentityServiceProvider', 'createUserPool', {
|
|
PoolName: `test-http-api-${process.hrtime()[1]}`,
|
|
})
|
|
).UserPool.Id;
|
|
[clientId] = await Promise.all([
|
|
awsRequest('CognitoIdentityServiceProvider', 'createUserPoolClient', {
|
|
ClientName: 'test-http-api',
|
|
UserPoolId: poolId,
|
|
ExplicitAuthFlows: ['ALLOW_USER_PASSWORD_AUTH', 'ALLOW_REFRESH_TOKEN_AUTH'],
|
|
PreventUserExistenceErrors: 'ENABLED',
|
|
}).then(result => result.UserPoolClient.ClientId),
|
|
awsRequest('CognitoIdentityServiceProvider', 'adminCreateUser', {
|
|
UserPoolId: poolId,
|
|
Username: userName,
|
|
}).then(() =>
|
|
awsRequest('CognitoIdentityServiceProvider', 'adminSetUserPassword', {
|
|
UserPoolId: poolId,
|
|
Username: userName,
|
|
Password: userPassword,
|
|
Permanent: true,
|
|
})
|
|
),
|
|
]);
|
|
|
|
const serverlessConfig = await createTestService(tmpDirPath, {
|
|
templateDir: await fixtures.extend('httpApi', {
|
|
provider: {
|
|
httpApi: {
|
|
cors: { exposedResponseHeaders: 'X-foo' },
|
|
authorizers: {
|
|
someAuthorizer: {
|
|
identitySource: '$request.header.Authorization',
|
|
issuerUrl: `https://cognito-idp.us-east-1.amazonaws.com/${poolId}`,
|
|
audience: clientId,
|
|
},
|
|
},
|
|
},
|
|
// TODO: Bring back after resolving permissions issue on AWS side
|
|
// logs: { httpApi: true },
|
|
},
|
|
functions: {
|
|
foo: {
|
|
events: [
|
|
{
|
|
httpApi: {
|
|
authorizer: 'someAuthorizer',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
other: {
|
|
events: [
|
|
{
|
|
httpApi: {
|
|
timeout: 1,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
}),
|
|
});
|
|
serviceName = serverlessConfig.service;
|
|
stackName = `${serviceName}-${stage}`;
|
|
log.notice('deploying %s service', serviceName);
|
|
await deployService(tmpDirPath);
|
|
return resolveEndpoint();
|
|
});
|
|
|
|
after(async () => {
|
|
await awsRequest('CognitoIdentityServiceProvider', 'deleteUserPool', { UserPoolId: poolId });
|
|
if (!serviceName) return;
|
|
log.notice('Removing service...');
|
|
await removeService(tmpDirPath);
|
|
});
|
|
|
|
it('should expose an accessible POST HTTP endpoint', async () => {
|
|
const testEndpoint = `${endpoint}/some-post`;
|
|
|
|
const response = await fetch(testEndpoint, { method: 'POST' });
|
|
const json = await response.json();
|
|
expect(json).to.deep.equal({ method: 'POST', path: '/some-post' });
|
|
});
|
|
|
|
it('should expose an accessible paramed GET HTTP endpoint', async () => {
|
|
const testEndpoint = `${endpoint}/bar/whatever`;
|
|
|
|
const response = await fetch(testEndpoint, { method: 'GET' });
|
|
const json = await response.json();
|
|
expect(json).to.deep.equal({ method: 'GET', path: '/bar/whatever' });
|
|
});
|
|
|
|
it('should return 404 on not supported method', async () => {
|
|
const testEndpoint = `${endpoint}/foo`;
|
|
|
|
const response = await fetch(testEndpoint, { method: 'POST' });
|
|
expect(response.status).to.equal(404);
|
|
});
|
|
|
|
it('should return 404 on not configured path', async () => {
|
|
const testEndpoint = `${endpoint}/not-configured`;
|
|
|
|
const response = await fetch(testEndpoint, { method: 'GET' });
|
|
expect(response.status).to.equal(404);
|
|
});
|
|
|
|
it('should respect timeout settings', async () => {
|
|
const testEndpoint = `${endpoint}/bar/timeout`;
|
|
|
|
const response = await fetch(testEndpoint, { method: 'GET' });
|
|
expect(response.status).to.equal(503);
|
|
});
|
|
|
|
it('should support CORS when indicated', async () => {
|
|
const testEndpoint = `${endpoint}/bar/whatever`;
|
|
|
|
const response = await fetch(testEndpoint, {
|
|
method: 'GET',
|
|
headers: { Origin: 'https://serverless.com' },
|
|
});
|
|
expect(response.headers.get('access-control-allow-origin')).to.equal('*');
|
|
expect(response.headers.get('access-control-expose-headers')).to.equal('x-foo');
|
|
});
|
|
|
|
it('should expose a GET HTTP endpoint backed by JWT authorization', async () => {
|
|
const testEndpoint = `${endpoint}/foo`;
|
|
|
|
const responseUnauthorized = await fetch(testEndpoint, {
|
|
method: 'GET',
|
|
});
|
|
expect(responseUnauthorized.status).to.equal(401);
|
|
|
|
const token = (
|
|
await awsRequest('CognitoIdentityServiceProvider', 'initiateAuth', {
|
|
AuthFlow: 'USER_PASSWORD_AUTH',
|
|
AuthParameters: { USERNAME: userName, PASSWORD: userPassword },
|
|
ClientId: clientId,
|
|
})
|
|
).AuthenticationResult.IdToken;
|
|
const responseAuthorized = await fetch(testEndpoint, {
|
|
method: 'GET',
|
|
headers: { Authorization: token },
|
|
});
|
|
const json = await responseAuthorized.json();
|
|
expect(json).to.deep.equal({ method: 'GET', path: '/foo' });
|
|
});
|
|
|
|
// TODO: Bring back after resolving permissions issue on AWS side
|
|
it.skip('should expose access logs when configured to', () =>
|
|
confirmCloudWatchLogs(`/aws/http-api/${stackName}`, async () => {
|
|
const response = await fetch(`${endpoint}/some-post`, { method: 'POST' });
|
|
await response.json();
|
|
}).then(events => {
|
|
expect(events.length > 0).to.equal(true);
|
|
}));
|
|
});
|
|
|
|
describe('Catch-all endpoints', () => {
|
|
before(async () => {
|
|
tmpDirPath = getTmpDirPath();
|
|
log.debug('temporary path %s', tmpDirPath);
|
|
const serverlessConfig = await createTestService(tmpDirPath, {
|
|
templateDir: fixtures.map.httpApiCatchAll,
|
|
});
|
|
serviceName = serverlessConfig.service;
|
|
stackName = `${serviceName}-${stage}`;
|
|
log.notice('deploying %s service', serviceName);
|
|
await deployService(tmpDirPath);
|
|
return resolveEndpoint();
|
|
});
|
|
|
|
after(async () => {
|
|
log.notice('Removing service...');
|
|
await removeService(tmpDirPath);
|
|
});
|
|
|
|
it('should catch all root endpoint', async () => {
|
|
const testEndpoint = `${endpoint}`;
|
|
|
|
const response = await fetch(testEndpoint, { method: 'GET' });
|
|
const json = await response.json();
|
|
expect(json).to.deep.equal({ method: 'GET', path: '/' });
|
|
});
|
|
|
|
it('should catch all whatever endpoint', async () => {
|
|
const testEndpoint = `${endpoint}/whatever`;
|
|
|
|
const response = await fetch(testEndpoint, { method: 'PATCH' });
|
|
const json = await response.json();
|
|
expect(json).to.deep.equal({ method: 'PATCH', path: '/whatever' });
|
|
});
|
|
|
|
it('should catch all methods on method catch all endpoint', async () => {
|
|
const testEndpoint = `${endpoint}/foo`;
|
|
|
|
const response = await fetch(testEndpoint, { method: 'PATCH' });
|
|
const json = await response.json();
|
|
expect(json).to.deep.equal({ method: 'PATCH', path: '/foo' });
|
|
});
|
|
});
|
|
|
|
describe('Shared API', () => {
|
|
let exportServicePath;
|
|
|
|
before(async () => {
|
|
exportServicePath = getTmpDirPath();
|
|
log.debug('service #1 path %s', exportServicePath);
|
|
|
|
const exportServiceConfig = await createTestService(exportServicePath, {
|
|
templateDir: fixtures.map.httpApiExport,
|
|
});
|
|
log.notice('deploying %s service', exportServiceConfig.service);
|
|
await deployService(exportServicePath);
|
|
const httpApiId = (
|
|
await awsRequest('CloudFormation', 'describeStacks', {
|
|
StackName: `${exportServiceConfig.service}-${stage}`,
|
|
})
|
|
).Stacks[0].Outputs[0].OutputValue;
|
|
|
|
tmpDirPath = getTmpDirPath();
|
|
log.debug('sevice #2 path %s', tmpDirPath);
|
|
const serverlessConfig = await createTestService(tmpDirPath, {
|
|
templateDir: await fixtures.extend('httpApi', {
|
|
provider: { httpApi: { id: httpApiId } },
|
|
}),
|
|
});
|
|
serviceName = serverlessConfig.service;
|
|
stackName = `${serviceName}-${stage}`;
|
|
log.notice('deploying %s service', serviceName);
|
|
await deployService(tmpDirPath);
|
|
endpoint = (await awsRequest('ApiGatewayV2', 'getApi', { ApiId: httpApiId })).ApiEndpoint;
|
|
});
|
|
|
|
after(async () => {
|
|
if (serviceName) {
|
|
log.notice('removing service #2');
|
|
await removeService(tmpDirPath);
|
|
}
|
|
log.notice('removing service #1');
|
|
await removeService(exportServicePath);
|
|
});
|
|
|
|
it('should expose an accessible POST HTTP endpoint', async () => {
|
|
const testEndpoint = `${endpoint}/some-post`;
|
|
|
|
const response = await fetch(testEndpoint, { method: 'POST' });
|
|
const json = await response.json();
|
|
expect(json).to.deep.equal({ method: 'POST', path: '/some-post' });
|
|
});
|
|
|
|
it('should expose an accessible paramed GET HTTP endpoint', async () => {
|
|
const testEndpoint = `${endpoint}/bar/whatever`;
|
|
|
|
const response = await fetch(testEndpoint, { method: 'GET' });
|
|
const json = await response.json();
|
|
expect(json).to.deep.equal({ method: 'GET', path: '/bar/whatever' });
|
|
});
|
|
|
|
it('should return 404 on not supported method', async () => {
|
|
const testEndpoint = `${endpoint}/foo`;
|
|
|
|
const response = await fetch(testEndpoint, { method: 'POST' });
|
|
expect(response.status).to.equal(404);
|
|
});
|
|
|
|
it('should return 404 on not configured path', async () => {
|
|
const testEndpoint = `${endpoint}/not-configured`;
|
|
|
|
const response = await fetch(testEndpoint, { method: 'GET' });
|
|
expect(response.status).to.equal(404);
|
|
});
|
|
});
|
|
});
|