Skip to content

Commit

Permalink
test: unit tests + e2e tests for custom resources (#8602)
Browse files Browse the repository at this point in the history
* test: unit tests for custom resources

* chore: remove empty unit test file

* fix: add all required types in override-helper package

* chore: update override helper dependencies

* test: unit test for dependency management utils

* test: add coverage data and add more unit tests for cfn dependency management

* test: add E2E tests for custom resources

* chore: address PR review comments

Co-authored-by: Ghosh <kaustavg@3c22fb229ff6.ant.amazon.com>
  • Loading branch information
2 people authored and sachscode committed Nov 11, 2021
1 parent 8f4ffa2 commit bfaa35e
Show file tree
Hide file tree
Showing 39 changed files with 1,196 additions and 60 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ module.exports = {
'/packages/amplify-category-auth/resources/auth-custom-resource',
'/packages/amplify-category-custom/lib',
'/packages/amplify-category-custom/resources',
'/packages/amplify-category-custom/src/utils/build-custom-resources.ts',
'/packages/amplify-category-custom/src/utils/generate-cfn-from-cdk.ts',

// Ignore CHANGELOG.md files
'/packages/*/CHANGELOG.md',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/* Add Amplify Helper dependencies */
import { AmplifyAuthCognitoStackTemplate } from '@aws-amplify/cli-overrides-helper';

/* TODO: Need to change props to Root-Stack specific props when props are ready */
export function overrideProps(props: any): void {
/* TODO: Add snippet of how to override in comments */
export function overrideProps(props: AmplifyAuthCognitoStackTemplate) {
return props;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
},
"dependencies": {
"@types/fs-extra": "^9.0.11",
"fs-extra": "^9.1.0"
"fs-extra": "^9.1.0",
"@aws-amplify/cli-overrides-helper": "1.1.0-ext9.0"
},
"devDependencies": {
"typescript": "^4.2.4"
Expand Down
7 changes: 4 additions & 3 deletions packages/amplify-category-custom/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"types": "lib/index.d.ts",
"scripts": {
"build": "tsc",
"test": "jest",
"clean": "rimraf lib tsconfig.tsbuildinfo",
"watch": "tsc -w"
},
Expand Down Expand Up @@ -51,9 +52,9 @@
"node"
],
"collectCoverage": true,
"coverageReporters": [
"json",
"html"
"collectCoverageFrom": [
"src/**/*.ts",
"!**/*.d.ts"
]
}
}
6 changes: 6 additions & 0 deletions packages/amplify-category-custom/resources/cdk-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ export class cdkStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

/* Do not remove - Amplify CLI automatically injects the current deployment environment in this input paramater */
new cdk.CfnParameter(this, 'env', {
type: 'String',
description: 'Current Amplify CLI env name',
});

/* AWS CDK Code goes here - Learn more: https://docs.aws.amazon.com/cdk/latest/guide/home.html */

/* Example 1: Set up an SQS queue with an SNS topic
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { $TSContext } from 'amplify-cli-core';
import { run } from '../../commands/custom/add';
import { customDeploymentOptionsQuestion } from '../../utils/common-questions';
import { CDK_DEPLOYMENT_NAME, CFN_DEPLOYMENT_NAME } from '../../utils/constants';
import { addCDKWalkthrough } from '../../walkthroughs/cdk-walkthrough';
import { addCloudFormationWalkthrough } from '../../walkthroughs/cloudformation-walkthrough';

jest.mock('../../utils/common-questions');
jest.mock('../../walkthroughs/cloudformation-walkthrough');
jest.mock('../../walkthroughs/cdk-walkthrough');

const addCloudFormationWalkthrough_mock = addCloudFormationWalkthrough as jest.MockedFunction<typeof addCloudFormationWalkthrough>;
const addCDKWalkthrough_mock = addCDKWalkthrough as jest.MockedFunction<typeof addCDKWalkthrough>;
const customDeploymentOptionsQuestion_mock = customDeploymentOptionsQuestion as jest.MockedFunction<typeof customDeploymentOptionsQuestion>;

describe('add custom flow', () => {
let mockContext: $TSContext;

beforeEach(() => {
jest.clearAllMocks();
mockContext = {
amplify: {},
} as unknown as $TSContext;
});

it('add custom workflow is invoked for CDK', async () => {
customDeploymentOptionsQuestion_mock.mockResolvedValueOnce(CDK_DEPLOYMENT_NAME);

await run(mockContext);
expect(addCDKWalkthrough_mock).toHaveBeenCalledTimes(1);
});

it('add custom workflow is invoked for CFN', async () => {
customDeploymentOptionsQuestion_mock.mockResolvedValueOnce(CFN_DEPLOYMENT_NAME);

await run(mockContext);
expect(addCloudFormationWalkthrough_mock).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { $TSContext } from 'amplify-cli-core';
import { run } from '../../commands/custom/build';
import { buildCustomResources } from '../../utils/build-custom-resources';

jest.mock('../../utils/build-custom-resources');
const buildCustomResources_mock = buildCustomResources as jest.MockedFunction<typeof buildCustomResources>;

describe('build custom resources flow', () => {
let mockContext: $TSContext;

beforeEach(() => {
jest.clearAllMocks();
mockContext = {
amplify: {},
parameters: {},
} as unknown as $TSContext;
});

it('build all custom resources', async () => {
await run(mockContext);

expect(buildCustomResources_mock).toHaveBeenCalledWith(mockContext, undefined);
});

it('build one custom resource', async () => {
const mockResourceName = 'mockresourcename';
mockContext.parameters.first = mockResourceName;

await run(mockContext);
expect(buildCustomResources_mock).toHaveBeenCalledWith(mockContext, mockResourceName);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { $TSContext } from 'amplify-cli-core';
import { run } from '../../commands/custom/remove';

jest.mock('amplify-cli-core');

describe('remove custom resource command tests', () => {
let mockContext: $TSContext;

beforeEach(() => {
jest.clearAllMocks();
mockContext = {
amplify: {},
parameters: {},
} as unknown as $TSContext;
});

it('remove resource workflow is invoked for custom resources with no params', async () => {
mockContext.amplify.removeResource = jest.fn().mockImplementation(async () => {
return;
});

await run(mockContext);

expect(mockContext.amplify.removeResource).toHaveBeenCalledWith(mockContext, 'custom', undefined);
});

it('remove resource workflow is invoked for custom resource with params as resourceName', async () => {
const mockResourceName = 'mockResourceName';
mockContext.parameters.first = mockResourceName;
mockContext.amplify.removeResource = jest.fn().mockImplementation(async () => {
return;
});

await run(mockContext);

expect(mockContext.amplify.removeResource).toHaveBeenCalledWith(mockContext, 'custom', mockResourceName);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { $TSContext, pathManager, stateManager } from 'amplify-cli-core';
import { prompter } from 'amplify-prompts';
import { run } from '../../commands/custom/update';
import { CDK_SERVICE_NAME, CFN_SERVICE_NAME } from '../../utils/constants';
import { updateCloudFormationWalkthrough } from '../../walkthroughs/cloudformation-walkthrough';

jest.mock('../../walkthroughs/cloudformation-walkthrough');
jest.mock('amplify-cli-core');
jest.mock('amplify-prompts');

let mockAmplifyMeta = {
custom: {
mockcdkresourcename: {
service: CDK_SERVICE_NAME,
providerPlugin: 'awscloudformation',
},
mockcfnresourcename: {
service: CFN_SERVICE_NAME,
providerPlugin: 'awscloudformation',
},
},
};

stateManager.getMeta = jest.fn().mockReturnValue(mockAmplifyMeta);
pathManager.getBackendDirPath = jest.fn().mockReturnValue('mockTargetDir');

const updateCloudFormationWalkthrough_mock = updateCloudFormationWalkthrough as jest.MockedFunction<typeof updateCloudFormationWalkthrough>;

describe('update custom flow', () => {
let mockContext: $TSContext;

beforeEach(() => {
jest.clearAllMocks();
mockContext = {
amplify: {
openEditor: jest.fn(),
},
} as unknown as $TSContext;
});

it('update custom workflow is invoked for a CFN resource', async () => {
prompter.pick = jest.fn().mockReturnValueOnce('mockcfnresourcename');

await run(mockContext);
expect(updateCloudFormationWalkthrough_mock).toHaveBeenCalledWith(mockContext, 'mockcfnresourcename');
});

it('update custom workflow is invoked for a CDK resource', async () => {
prompter.pick = jest.fn().mockReturnValueOnce('mockcdkresourcename');

prompter.yesOrNo = jest.fn().mockReturnValueOnce(true);

await run(mockContext);

expect(mockContext.amplify.openEditor).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { $TSContext } from 'amplify-cli-core';
import execa from 'execa';
import { buildCustomResources } from '../../utils/build-custom-resources';

jest.mock('amplify-cli-core');
jest.mock('amplify-prompts');
jest.mock('../../utils/dependency-management-utils');
jest.mock('../../utils/generate-cfn-from-cdk');
jest.mock('execa');
jest.mock('ora');

jest.mock('fs-extra', () => ({
readFileSync: jest.fn().mockReturnValue('mockCode'),
existsSync: jest.fn().mockReturnValue(true),
ensureDirSync: jest.fn().mockReturnValue(true),
writeFileSync: jest.fn().mockReturnValue(true),
}));

jest.mock('ora', () => {
return () => ({
start: jest.fn(),
fail: jest.fn(),
succeed: jest.fn(),
stop: jest.fn(),
});
});

jest.mock('../../utils/dependency-management-utils', () => ({
getAllResources: jest.fn().mockResolvedValue({ mockedvalue: 'mockedkey' }),
}));

jest.mock('../../utils/generate-cfn-from-cdk', () => ({
generateCloudFormationFromCDK: jest.fn(),
}));

jest.mock('amplify-cli-core', () => ({
getPackageManager: jest.fn().mockResolvedValue('npm'),
pathManager: {
getBackendDirPath: jest.fn().mockReturnValue('mockTargetDir'),
},
JSONUtilities: {
writeJson: jest.fn(),
readJson: jest.fn(),
},
}));

describe('build custom resources scenarios', () => {
let mockContext: $TSContext;

beforeEach(() => {
jest.clearAllMocks();
mockContext = {
amplify: {
openEditor: jest.fn(),
updateamplifyMetaAfterResourceAdd: jest.fn(),
copyBatch: jest.fn(),
getResourceStatus: jest.fn().mockResolvedValue({
allResources: [
{
resourceName: 'mockresource1',
service: 'customCDK',
},
{
resourceName: 'mockresource2',
service: 'customCDK',
},
],
}),
},
} as unknown as $TSContext;
});

it('build all resources', async () => {
await buildCustomResources(mockContext);

// 2 for npm install and 2 for tsc build (1 per resource)
expect(execa.sync).toBeCalledTimes(4);
});
});
Loading

0 comments on commit bfaa35e

Please sign in to comment.