serverless/test/unit/lib/classes/console.test.js

1034 lines
36 KiB
JavaScript

'use strict';
const chai = require('chai');
const sinon = require('sinon');
const path = require('path');
const fsp = require('fs').promises;
const _ = require('lodash');
const log = require('log').get('serverless:test');
const runServerless = require('../../../utils/run-serverless');
// Configure chai
chai.use(require('chai-as-promised'));
const expect = require('chai').expect;
const createApiStub = () => {
const requests = [];
return {
requests,
stub: sinon.stub().callsFake(async (pathname, { method } = { method: 'GET' }) => {
log.debug('api request %s', pathname);
if (pathname.includes('orgs/name/')) {
requests.push('/orgs/name/{org}');
const orgName = pathname.split('/').filter(Boolean).pop();
return { orgId: `${orgName}id` };
} else if (pathname.includes('/org/')) {
if (method.toUpperCase() === 'GET') {
requests.push('get-token');
return {
status: 'existing_token',
token: { accessToken: 'accesss-token' },
};
}
} else if (pathname.endsWith('/token')) {
if (method.toUpperCase() === 'PATCH') {
requests.push('activate-token');
return '';
}
} else if (pathname.includes('/tokens?')) {
if (method.toUpperCase() === 'DELETE') {
requests.push(
pathname.includes('token=') ? 'deactivate-other-tokens' : 'deactivate-all-tokens'
);
return '';
}
} else if (pathname.includes('/token?')) {
if (method.toUpperCase() === 'DELETE') {
requests.push('deactivate-token');
return '';
}
}
throw new Error(`Unexpected request: ${pathname}`);
}),
};
};
let serviceName = 'irrelevant';
const createAwsRequestStubMap = () => ({
CloudFormation: {
describeStacks: { Stacks: [{ Outputs: [] }] },
describeStackResource: {
StackResourceDetail: { PhysicalResourceId: 'deployment-bucket' },
},
},
Lambda: {
getFunction: {
Configuration: {
LastModified: '2020-05-20T15:34:16.494+0000',
},
},
},
S3: {
headObject: async ({ Key: s3Key }) => {
if (s3Key.includes('sls-otel.')) {
throw Object.assign(new Error('Not found'), {
code: 'AWS_S3_HEAD_OBJECT_NOT_FOUND',
});
}
return {
Metadata: { filesha256: 'RRYyTm4Ri8mocpvx44pvas4JKLYtdJS3Z8MOlrZrDXA=' },
};
},
listObjectsV2: () => ({
Contents: [
{
Key: `serverless/${serviceName}/dev/1589988704359-2020-05-20T15:31:44.359Z/artifact.zip`,
LastModified: new Date(),
ETag: '"5102a4cf710cae6497dba9e61b85d0a4"',
Size: 356,
StorageClass: 'STANDARD',
},
{
Key: `serverless/${serviceName}/dev/1589988704359-2020-05-20T15:31:44.359Z/compiled-cloudformation-template.json`,
LastModified: new Date(),
ETag: '"5102a4cf710cae6497dba9e61b85d0a4"',
Size: 356,
StorageClass: 'STANDARD',
},
{
Key: `serverless/${serviceName}/dev/1589988704359-2020-05-20T15:31:44.359Z/serverless-state.json`,
LastModified: new Date(),
ETag: '"5102a4cf710cae6497dba9e61b85d0a4"',
Size: 356,
StorageClass: 'STANDARD',
},
],
}),
headBucket: {},
upload: sinon.stub().callsFake(async ({ Body: body }) => {
if (typeof body.destroy === 'function') {
// Ensure to drain eventual file streams, otherwise file remain locked and
// on Windows they cannot be removed, resulting with homedir being dirty for next test runs
await new Promise((resolve, reject) => {
body.on('data', () => {});
body.on('end', resolve);
body.on('error', reject);
});
}
return {};
}),
},
STS: {
getCallerIdentity: {
ResponseMetadata: { RequestId: 'ffffffff-ffff-ffff-ffff-ffffffffffff' },
UserId: 'XXXXXXXXXXXXXXXXXXXXX',
Account: '999999999999',
Arn: 'arn:aws:iam::999999999999:user/test',
},
},
});
describe('test/unit/lib/classes/console.test.js', () => {
describe('enabled', () => {
describe('deploy', () => {
let serverless;
let servicePath;
let cfTemplate;
let awsNaming;
let uploadStub;
let apiStub;
let otelIngenstionRequests;
before(async () => {
const awsRequestStubMap = createAwsRequestStubMap();
uploadStub = awsRequestStubMap.S3.upload;
({ requests: otelIngenstionRequests, stub: apiStub } = createApiStub());
({
serverless,
cfTemplate,
awsNaming,
fixtureData: { servicePath },
} = await runServerless({
fixture: 'packaging',
command: 'deploy',
lastLifecycleHookName: 'aws:deploy:deploy:uploadArtifacts',
configExt: { console: true, org: 'testorg' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: apiStub,
},
awsRequestStubMap,
}));
});
it('should setup needed environment variables on supported functions', () => {
const fnVariablesList = [
cfTemplate.Resources[awsNaming.getLambdaLogicalId('fnService')].Properties.Environment
.Variables,
cfTemplate.Resources[awsNaming.getLambdaLogicalId('fnIndividual')].Properties.Environment
.Variables,
];
for (const fnVariables of fnVariablesList) {
expect(fnVariables).to.have.property('SLS_OTEL_REPORT_REQUEST_HEADERS');
expect(fnVariables).to.have.property('SLS_OTEL_REPORT_METRICS_URL');
expect(fnVariables).to.have.property('SLS_OTEL_REPORT_LOGS_URL');
expect(fnVariables).to.have.property('AWS_LAMBDA_EXEC_WRAPPER');
}
const notSupportedFnVariables = _.get(
cfTemplate.Resources[awsNaming.getLambdaLogicalId('fnGo')].Properties,
'Environment.Variables',
{}
);
expect(notSupportedFnVariables).to.not.have.property('SLS_OTEL_REPORT_REQUEST_HEADERS');
expect(notSupportedFnVariables).to.not.have.property('SLS_OTEL_REPORT_METRICS_URL');
expect(notSupportedFnVariables).to.not.have.property('AWS_LAMBDA_EXEC_WRAPPER');
});
it('should reflect default userSettings', () => {
const userSettingsString =
cfTemplate.Resources[awsNaming.getLambdaLogicalId('fnService')].Properties.Environment
.Variables.SLS_OTEL_USER_SETTINGS;
if (!userSettingsString) return;
expect(JSON.parse(userSettingsString)).to.deep.equal({});
});
it('should package extension layer', async () => {
expect(cfTemplate.Resources).to.have.property(
awsNaming.getConsoleExtensionLayerLogicalId()
);
await fsp.access(
path.resolve(
servicePath,
'.serverless',
await serverless.console.deferredExtensionLayerBasename
)
);
});
it('should upload extension layer to S3', async () => {
const consoleExtensionLayerBasename = await serverless.console
.deferredExtensionLayerBasename;
log.debug(
'layer basename: %s, s3Keys: %o',
consoleExtensionLayerBasename,
uploadStub.args.map(([{ Key: s3Key }]) => s3Key)
);
const uploadArgs = uploadStub.args.find(([{ Key: s3Key }]) =>
s3Key.endsWith(consoleExtensionLayerBasename)
)[0];
// Confirm that Bucket is properly resolved in outer `uploadZipFile` method
expect(typeof uploadArgs.Bucket).to.equal('string');
});
it('should activate otel ingestion token', () => {
otelIngenstionRequests.includes('activate-token');
});
});
describe('package', () => {
let userSettings;
before(async () => {
const { stub: apiStub } = createApiStub();
const { cfTemplate, awsNaming } = await runServerless({
fixture: 'function',
command: 'package',
configExt: {
console: {
disableLogsCollection: true,
disableRequestResponseCollection: true,
},
org: 'testorg',
},
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: apiStub,
},
});
userSettings = JSON.parse(
cfTemplate.Resources[awsNaming.getLambdaLogicalId('basic')].Properties.Environment
.Variables.SLS_OTEL_USER_SETTINGS
);
});
it('should propagate `disableLogsCollection`', () => {
expect(userSettings.disableLogsMonitoring).to.be.true;
});
it('should propagate `disableRequestResponseCollection`', () => {
expect(userSettings.disableRequestResponseMonitoring).to.be.true;
});
});
describe('package with "provider.layers" configuration', () => {
it('should setup console wihout errors', async () => {
const { cfTemplate, awsNaming } = await runServerless({
fixture: 'function-layers',
command: 'package',
configExt: {
console: true,
org: 'testorg',
layers: {
extra1: { path: 'test-layer' },
extra2: { path: 'test-layer' },
},
package: {
individually: true,
},
provider: { layers: [{ Ref: 'Extra1LambdaLayer' }, { Ref: 'Extra2LambdaLayer' }] },
functions: { layerFunc: { layers: null }, capitalLayerFunc: { layers: null } },
},
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: createApiStub().stub,
},
awsRequestStubMap: createAwsRequestStubMap(),
env: { SLS_ORG_TOKEN: 'dummy' },
});
const configuredLayers =
cfTemplate.Resources[awsNaming.getLambdaLogicalId('layerFunc')].Properties.Layers;
expect(
configuredLayers.some(
({ Ref: logicalId }) => logicalId === awsNaming.getConsoleExtensionLayerLogicalId()
)
).to.be.true;
});
});
describe('disable logs collection', () => {
it('should not setup report logs url', async () => {
const { cfTemplate, awsNaming } = await runServerless({
fixture: 'function',
command: 'package',
configExt: {
console: { disableLogsCollection: true },
org: 'testorg',
},
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: createApiStub().stub,
},
awsRequestStubMap: createAwsRequestStubMap(),
env: { SLS_ORG_TOKEN: 'dummy' },
});
const fnVariables =
cfTemplate.Resources[awsNaming.getLambdaLogicalId('basic')].Properties.Environment
.Variables;
expect(fnVariables).to.have.property('SLS_OTEL_REPORT_REQUEST_HEADERS');
expect(fnVariables).to.not.have.property('SLS_OTEL_REPORT_LOGS_URL');
expect(fnVariables).to.have.property('AWS_LAMBDA_EXEC_WRAPPER');
});
});
describe('package for custom deployment bucket', () => {
let cfTemplate;
let awsNaming;
before(async () => {
({ cfTemplate, awsNaming } = await runServerless({
fixture: 'function',
command: 'package',
configExt: { console: true, org: 'testorg', provider: { deploymentBucket: 'custom' } },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: createApiStub().stub,
},
}));
});
it('should not reference default deployment bucket anywhere', () => {
expect(JSON.stringify(cfTemplate.Resources)).to.not.contain('ServerlessDeploymentBucket');
});
it('should reference custom S3 bucket at layer version', () => {
expect(
cfTemplate.Resources[awsNaming.getConsoleExtensionLayerLogicalId()].Properties.Content
.S3Bucket
).to.equal('custom');
});
});
});
describe('deploy --package', () => {
let consolePackage;
let consoleDeploy;
let servicePath;
let uploadStub;
let apiStub;
let otelIngenstionRequests;
before(async () => {
({ requests: otelIngenstionRequests, stub: apiStub } = createApiStub());
({
serverless: { console: consolePackage },
fixtureData: { servicePath },
} = await runServerless({
fixture: 'function',
command: 'package',
options: { package: 'package-dir' },
configExt: { console: true, org: 'testorg' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: apiStub,
},
}));
const awsRequestStubMap = createAwsRequestStubMap();
uploadStub = awsRequestStubMap.S3.upload;
({
serverless: { console: consoleDeploy },
} = await runServerless({
cwd: servicePath,
command: 'deploy',
lastLifecycleHookName: 'aws:deploy:deploy:uploadArtifacts',
options: { package: 'package-dir' },
configExt: { console: true, org: 'testorg' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: apiStub,
},
awsRequestStubMap,
}));
});
it('should use service id as stored in the state', () => {
expect(consoleDeploy.serviceId).to.equal(consolePackage.serviceId);
});
it('should upload extension layer to S3', async () => {
const extensionLayerFilename = await consoleDeploy.deferredExtensionLayerBasename;
expect(uploadStub.args.some(([{ Key: s3Key }]) => s3Key.endsWith(extensionLayerFilename))).to
.be.true;
});
it('should activate otel ingestion token', () => {
otelIngenstionRequests.includes('activate-token');
});
});
describe('deploy function', () => {
let serverless;
let uploadStub;
let updateFunctionStub;
let publishLayerStub;
let apiStub;
let otelIngenstionRequests;
before(async () => {
updateFunctionStub = sinon.stub().resolves({});
publishLayerStub = sinon.stub().resolves({});
const awsRequestStubMap = createAwsRequestStubMap();
uploadStub = awsRequestStubMap.S3.upload;
let isFirstLayerVersionsQuery = true;
({ requests: otelIngenstionRequests, stub: apiStub } = createApiStub());
({ serverless } = await runServerless({
fixture: 'function',
command: 'deploy function',
options: { function: 'basic' },
configExt: { console: true, org: 'testorg' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: apiStub,
},
awsRequestStubMap: {
...awsRequestStubMap,
Lambda: {
getFunction: { Configuration: { State: 'Active', LastUpdateStatus: 'Successful' } },
listLayerVersions() {
if (isFirstLayerVersionsQuery) {
isFirstLayerVersionsQuery = false;
return { LayerVersions: [] };
}
return { LayerVersions: [{ LayerVersionArn: 'extension-arn' }] };
},
publishLayerVersion: publishLayerStub,
updateFunctionConfiguration: updateFunctionStub,
updateFunctionCode: {},
},
},
}));
});
it('should setup needed environment variables', () => {
const fnVariables = updateFunctionStub.args[0][0].Environment.Variables;
expect(fnVariables).to.have.property('SLS_OTEL_REPORT_REQUEST_HEADERS');
expect(fnVariables).to.have.property('SLS_OTEL_REPORT_METRICS_URL');
expect(fnVariables).to.have.property('AWS_LAMBDA_EXEC_WRAPPER');
});
it('should upload extension layer to S3', async () => {
const extensionLayerFilename = await serverless.console.deferredExtensionLayerBasename;
expect(uploadStub.args.some(([{ Key: s3Key }]) => s3Key.endsWith(extensionLayerFilename))).to
.be.true;
});
it('should activate otel ingestion token', () => {
otelIngenstionRequests.includes('activate-token');
});
});
describe('rollback', () => {
let slsConsole;
let apiStub;
let otelIngenstionRequests;
before(async () => {
const awsRequestStubMap = createAwsRequestStubMap();
({ requests: otelIngenstionRequests, stub: apiStub } = createApiStub());
({
serverless: { console: slsConsole },
} = await runServerless({
fixture: 'function',
command: 'rollback',
options: { timestamp: '2020-05-20T15:31:44.359Z' },
configExt: { console: true, org: 'testorg' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: apiStub,
},
awsRequestStubMap: {
...awsRequestStubMap,
S3: {
...awsRequestStubMap.S3,
getObject: async ({ Key: s3Key }) => {
if (s3Key.endsWith('/serverless-state.json')) {
return {
Body: JSON.stringify({
console: {
schemaVersion: '1',
otelIngestionToken: 'rollback-token',
service: 'test-console',
stage: 'dev',
orgId: 'testorgid',
},
}),
};
}
throw new Error(`Unexpected request: ${s3Key}`);
},
},
CloudFormation: {
...awsRequestStubMap.CloudFormation,
deleteChangeSet: {},
createChangeSet: {},
describeChangeSet: {
Status: 'CREATE_COMPLETE',
},
executeChangeSet: {},
describeStackEvents: {
StackEvents: [
{
EventId: '1',
ResourceType: 'AWS::CloudFormation::Stack',
ResourceStatus: 'UPDATE_COMPLETE',
},
],
},
},
},
hooks: {
beforeInstanceRun: (serverless) => {
serviceName = serverless.service.service;
},
},
}));
});
it('should resolve otel ingestion token from the state', async () => {
expect(await slsConsole.deferredOtelIngestionToken).to.equal('rollback-token');
});
it('should activate otel ingestion token', () => {
otelIngenstionRequests.includes('activate-token');
});
});
describe('remove', () => {
let otelIngenstionRequests;
let apiStub;
before(async () => {
const awsRequestStubMap = createAwsRequestStubMap();
({ requests: otelIngenstionRequests, stub: apiStub } = createApiStub());
await runServerless({
fixture: 'function',
command: 'remove',
configExt: { console: true, org: 'testorg' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: apiStub,
},
awsRequestStubMap: {
...awsRequestStubMap,
CloudFormation: {
...awsRequestStubMap.CloudFormation,
deleteStack: {},
describeStackEvents: {
StackEvents: [
{
EventId: '1',
ResourceType: 'AWS::CloudFormation::Stack',
ResourceStatus: 'DELETE_COMPLETE',
},
],
},
},
ECR: {
async describeRepositories() {
throw Object.assign(new Error('RepositoryNotFoundException'), {
providerError: { code: 'RepositoryNotFoundException' },
});
},
},
S3: {
...awsRequestStubMap.S3,
deleteObjects: {},
},
},
});
});
it('should deactivate all ingestion tokens', () => {
otelIngenstionRequests.includes('deactivate-all-token');
});
});
it('should support "console.org"', async () => {
const { requests: otelIngenstionRequests, stub: apiStub } = createApiStub();
await runServerless({
fixture: 'function',
command: 'package',
configExt: {
console: {
org: 'testorg',
},
org: 'ignore',
},
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: apiStub,
},
});
for (const [url] of apiStub.args) expect(url).to.not.include('/ignoreid/');
otelIngenstionRequests.includes('activate-token');
});
describe('errors', () => {
it('should abort when console enabled but not authenticated', async () => {
await expect(
runServerless({
fixture: 'function',
command: 'package',
configExt: { console: true, org: 'testorg' },
})
).to.eventually.be.rejected.and.have.property('code', 'CONSOLE_NOT_AUTHENTICATED');
});
it('should abort when function has already maximum numbers of layers configured', async () => {
await expect(
runServerless({
fixture: 'function-layers',
command: 'package',
configExt: {
console: true,
org: 'testorg',
layers: {
extra1: { path: 'test-layer' },
extra2: { path: 'test-layer' },
},
functions: {
layerFuncWithConfig: {
layers: [
{ Ref: 'TestLayerLambdaLayer' },
{ Ref: 'TestLayerWithCapitalsLambdaLayer' },
{ Ref: 'TestLayerWithNoNameLambdaLayer' },
{ Ref: 'Extra1LambdaLayer' },
{ Ref: 'Extra2LambdaLayer' },
],
},
},
},
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: createApiStub().stub,
},
awsRequestStubMap: createAwsRequestStubMap(),
env: { SLS_ORG_TOKEN: 'dummy' },
})
).to.eventually.be.rejected.and.have.property('code', 'TOO_MANY_LAYERS_TO_SETUP_CONSOLE');
});
it(
'should throw integration error when attempting to deploy package, ' +
'packaged with different console integration version',
async () => {
const {
fixtureData: { servicePath },
} = await runServerless({
fixture: 'function',
command: 'package',
options: { package: 'package-dir' },
configExt: { console: true, org: 'testorg' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: createApiStub().stub,
},
awsRequestStubMap: createAwsRequestStubMap(),
});
const stateFilename = path.resolve(servicePath, 'package-dir', 'serverless-state.json');
const state = JSON.parse(await fsp.readFile(stateFilename, 'utf-8'));
state.console.schemaVersion = 'other';
await fsp.writeFile(stateFilename, JSON.stringify(state));
await expect(
runServerless({
cwd: servicePath,
command: 'deploy',
lastLifecycleHookName: 'aws:deploy:deploy:uploadArtifacts',
options: { package: 'package-dir' },
configExt: { console: true, org: 'testorg' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: createApiStub().stub,
},
awsRequestStubMap: createAwsRequestStubMap(),
})
).to.eventually.be.rejected.and.have.property('code', 'CONSOLE_INTEGRATION_MISMATCH');
}
);
it(
'should throw mismatch error when attempting to deploy package, ' +
'packaged with different org',
async () => {
const {
fixtureData: { servicePath, updateConfig },
} = await runServerless({
fixture: 'function',
command: 'package',
options: { package: 'package-dir' },
configExt: { console: true, org: 'other' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: createApiStub().stub,
},
});
await updateConfig({ org: 'testorg' });
await expect(
runServerless({
cwd: servicePath,
command: 'deploy',
lastLifecycleHookName: 'aws:deploy:deploy:uploadArtifacts',
options: { package: 'package-dir' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: createApiStub().stub,
},
awsRequestStubMap: createAwsRequestStubMap(),
})
).to.eventually.be.rejected.and.have.property('code', 'CONSOLE_ORG_MISMATCH');
}
);
it(
'should throw mismatch error when attempting to deploy package, ' +
'packaged with different region',
async () => {
const {
fixtureData: { servicePath, updateConfig },
} = await runServerless({
fixture: 'function',
command: 'package',
options: { package: 'package-dir' },
configExt: { console: true, org: 'testorg' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: createApiStub().stub,
},
});
await updateConfig({ provider: { region: 'us-east-2' } });
await expect(
runServerless({
cwd: servicePath,
command: 'deploy',
lastLifecycleHookName: 'aws:deploy:deploy:uploadArtifacts',
options: { package: 'package-dir' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: createApiStub().stub,
},
awsRequestStubMap: createAwsRequestStubMap(),
})
).to.eventually.be.rejected.and.have.property('code', 'CONSOLE_REGION_MISMATCH');
}
);
it(
'should throw activation mismatch error when attempting to deploy with ' +
'console integration off, but packaged with console integration on',
async () => {
const {
fixtureData: { servicePath, updateConfig },
} = await runServerless({
fixture: 'function',
command: 'package',
options: { package: 'package-dir' },
configExt: { console: true, org: 'testorg' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: createApiStub().stub,
},
});
const stateFilename = path.resolve(servicePath, 'package-dir', 'serverless-state.json');
const state = JSON.parse(await fsp.readFile(stateFilename, 'utf-8'));
state.console.orgId = 'other';
await fsp.writeFile(stateFilename, JSON.stringify(state));
await updateConfig({ org: null, console: null });
await expect(
runServerless({
cwd: servicePath,
command: 'deploy',
lastLifecycleHookName: 'aws:deploy:deploy:uploadArtifacts',
options: { package: 'package-dir' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: createApiStub().stub,
},
awsRequestStubMap: createAwsRequestStubMap(),
})
).to.eventually.be.rejected.and.have.property('code', 'CONSOLE_ACTIVATION_MISMATCH');
}
);
it(
'should throw integration error when attempting to rollback deployment, ' +
'to one deployed with different console integration version',
async () => {
const awsRequestStubMap = createAwsRequestStubMap();
await expect(
runServerless({
fixture: 'function',
command: 'rollback',
lastLifecycleHookName: 'aws:deploy:deploy:uploadArtifacts',
options: { timestamp: '2020-05-20T15:31:44.359Z' },
configExt: { console: true, org: 'testorg' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: createApiStub().stub,
},
awsRequestStubMap: {
...awsRequestStubMap,
S3: {
...awsRequestStubMap.S3,
getObject: async ({ Key: s3Key }) => {
if (s3Key.endsWith('/serverless-state.json')) {
return {
Body: JSON.stringify({
console: {
schemaVersion: '2',
otelIngestionToken: 'rollback-token',
service: 'test-console',
stage: 'dev',
orgId: 'testorgid',
},
}),
};
}
throw new Error(`Unexpected request: ${s3Key}`);
},
},
CloudFormation: {
...awsRequestStubMap.CloudFormation,
deleteChangeSet: {},
createChangeSet: {},
describeChangeSet: {
Status: 'CREATE_COMPLETE',
},
executeChangeSet: {},
describeStackEvents: {
StackEvents: [
{
EventId: '1',
ResourceType: 'AWS::CloudFormation::Stack',
ResourceStatus: 'UPDATE_COMPLETE',
},
],
},
},
},
hooks: {
beforeInstanceRun: (serverless) => {
serviceName = serverless.service.service;
},
},
})
).to.eventually.be.rejected.and.have.property(
'code',
'CONSOLE_INTEGRATION_MISMATCH_ROLLBACK'
);
}
);
it(
'should throw integration error when attempting to rollback deployment, ' +
'to one deployed with different org',
async () => {
const awsRequestStubMap = createAwsRequestStubMap();
await expect(
runServerless({
fixture: 'function',
command: 'rollback',
lastLifecycleHookName: 'aws:deploy:deploy:uploadArtifacts',
options: { timestamp: '2020-05-20T15:31:44.359Z' },
configExt: { console: true, org: 'testorg' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: createApiStub().stub,
},
awsRequestStubMap: {
...awsRequestStubMap,
S3: {
...awsRequestStubMap.S3,
getObject: async ({ Key: s3Key }) => {
if (s3Key.endsWith('/serverless-state.json')) {
return {
Body: JSON.stringify({
console: {
schemaVersion: '1',
otelIngestionToken: 'rollback-token',
service: 'test-console',
stage: 'dev',
orgId: 'othertestorgid',
},
}),
};
}
throw new Error(`Unexpected request: ${s3Key}`);
},
},
CloudFormation: {
...awsRequestStubMap.CloudFormation,
deleteChangeSet: {},
createChangeSet: {},
describeChangeSet: {
Status: 'CREATE_COMPLETE',
},
executeChangeSet: {},
describeStackEvents: {
StackEvents: [
{
EventId: '1',
ResourceType: 'AWS::CloudFormation::Stack',
ResourceStatus: 'UPDATE_COMPLETE',
},
],
},
},
},
hooks: {
beforeInstanceRun: (serverless) => {
serviceName = serverless.service.service;
},
},
})
).to.eventually.be.rejected.and.have.property('code', 'CONSOLE_ORG_MISMATCH_ROLLBACK');
}
);
it(
'should throw integration error when attempting to rollback deployment, ' +
'deployed with console, while having console disabled',
async () => {
const awsRequestStubMap = createAwsRequestStubMap();
await expect(
runServerless({
fixture: 'function',
command: 'rollback',
lastLifecycleHookName: 'aws:deploy:deploy:uploadArtifacts',
options: { timestamp: '2020-05-20T15:31:44.359Z' },
env: { SLS_ORG_TOKEN: 'dummy' },
modulesCacheStub: {
[require.resolve('@serverless/utils/api-request')]: createApiStub().stub,
},
awsRequestStubMap: {
...awsRequestStubMap,
S3: {
...awsRequestStubMap.S3,
getObject: async ({ Key: s3Key }) => {
if (s3Key.endsWith('/serverless-state.json')) {
return {
Body: JSON.stringify({
console: {
schemaVersion: '1',
otelIngestionToken: 'rollback-token',
service: 'test-console',
stage: 'dev',
orgId: 'othertestorgid',
},
}),
};
}
throw new Error(`Unexpected request: ${s3Key}`);
},
},
CloudFormation: {
...awsRequestStubMap.CloudFormation,
deleteChangeSet: {},
createChangeSet: {},
describeChangeSet: {
Status: 'CREATE_COMPLETE',
},
executeChangeSet: {},
describeStackEvents: {
StackEvents: [
{
EventId: '1',
ResourceType: 'AWS::CloudFormation::Stack',
ResourceStatus: 'UPDATE_COMPLETE',
},
],
},
},
},
hooks: {
beforeInstanceRun: (serverless) => {
serviceName = serverless.service.service;
},
},
})
).to.eventually.be.rejected.and.have.property(
'code',
'CONSOLE_ACTIVATION_MISMATCH_ROLLBACK'
);
}
);
});
describe('disabled', () => {
it('should not enable console when no `console: true`', async () => {
const { serverless } = await runServerless({
fixture: 'function',
command: 'package',
configExt: { org: 'testorg' },
});
expect(serverless.console.isEnabled).to.be.false;
});
it('should not enable console when not supported command', async () => {
const { serverless } = await runServerless({
fixture: 'function',
command: 'print',
configExt: { console: true, org: 'testorg' },
});
expect(serverless.console.isEnabled).to.be.false;
});
it('should not enable when no supported functions', async () => {
const { serverless } = await runServerless({
fixture: 'aws',
command: 'package',
configExt: { console: true, org: 'testorg' },
});
expect(serverless.console.isEnabled).to.be.false;
});
});
});