Skip to content

Commit

Permalink
test: add e2e tests and change location of raisePrePushEvent firing o…
Browse files Browse the repository at this point in the history
…n init
  • Loading branch information
edwardfoyle committed Mar 25, 2023
1 parent adf9d34 commit 692362b
Show file tree
Hide file tree
Showing 11 changed files with 146 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,9 @@ const askForEnvironmentVariableValue = async (
*/
export const ensureEnvironmentVariableValues = async (context: $TSContext): Promise<void> => {
const yesFlagSet = context?.exeInfo?.inputParams?.yes || context?.input?.options?.yes;
const currentEnvName = context?.exeInfo?.inputParams?.amplify?.envName || stateManager.getLocalEnvInfo()?.envName;
const currentEnvName = stateManager.localEnvInfoExists()
? stateManager.getLocalEnvInfo()?.envName
: context?.exeInfo?.inputParams?.amplify?.envName;
await ensureEnvParamManager(currentEnvName);
const functionNames = Object.keys(stateManager.getBackendConfig()?.function || {});
if (functionNames.length === 0) {
Expand All @@ -180,7 +182,7 @@ export const ensureEnvironmentVariableValues = async (context: $TSContext): Prom
const functionConfigMissingEnvVars = functionNames
.map((funcName) => {
const storedList = getStoredList(funcName);
const keyValues = getStoredKeyValue(funcName);
const keyValues = getStoredKeyValue(funcName, currentEnvName);
return {
funcName,
existingKeyValues: keyValues,
Expand Down Expand Up @@ -218,7 +220,7 @@ export const ensureEnvironmentVariableValues = async (context: $TSContext): Prom
});
keyValues[cfnName] = newValue;
}
setStoredKeyValue(funcName, keyValues);
setStoredKeyValue(funcName, keyValues, currentEnvName);
}
};

Expand Down Expand Up @@ -317,6 +319,6 @@ const setStoredParameters = (resourceName: string, newParameters: $TSAny): void
const getStoredKeyValue = (resourceName: string, envName?: string): Record<string, string> =>
getEnvParamManager(envName).getResourceParamManager(categoryName, resourceName).getAllParams();

const setStoredKeyValue = (resourceName: string, newKeyValue: $TSAny): void => {
getEnvParamManager().getResourceParamManager(categoryName, resourceName).setAllParams(newKeyValue);
const setStoredKeyValue = (resourceName: string, newKeyValue: $TSAny, envName?: string): void => {
getEnvParamManager(envName).getResourceParamManager(categoryName, resourceName).setAllParams(newKeyValue);
};
1 change: 1 addition & 0 deletions packages/amplify-cli/src/amplify-exception-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const init = (_context: Context): void => {
* Handle exceptions
*/
export const handleException = async (exception: unknown): Promise<void> => {
process.exitCode = 1;
let amplifyException: AmplifyException;

if (exception instanceof AmplifyException) {
Expand Down
17 changes: 15 additions & 2 deletions packages/amplify-cli/src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const run = async (context: $TSContext): Promise<void> => {

try {
await generateDependentResourcesType();
const resourcesToBuild: IAmplifyResource[] = await getResources(context);
const resourcesToBuild: IAmplifyResource[] = await getChangedResources(context);
let filteredResources: IAmplifyResource[] = resourcesToBuild;
if (categoryName) {
filteredResources = filteredResources.filter((resource) => resource.category === categoryName);
Expand Down Expand Up @@ -44,7 +44,7 @@ export const run = async (context: $TSContext): Promise<void> => {
/**
* Returns resources in create or update state
*/
export const getResources = async (context: $TSContext): Promise<IAmplifyResource[]> => {
export const getChangedResources = async (context: $TSContext): Promise<IAmplifyResource[]> => {
const resources: IAmplifyResource[] = [];
const { resourcesToBeCreated, resourcesToBeUpdated } = await context.amplify.getResourceStatus();
resourcesToBeCreated.forEach((resourceCreated: IAmplifyResource) => {
Expand All @@ -64,3 +64,16 @@ export const getResources = async (context: $TSContext): Promise<IAmplifyResourc
});
return resources;
};

export const getAllResources = async (context: $TSContext): Promise<IAmplifyResource[]> => {
const resources: IAmplifyResource[] = [];
const { allResources } = await context.amplify.getResourceStatus();
allResources.forEach((resource: IAmplifyResource) => {
resources.push({
service: resource.service,
category: resource.category,
resourceName: resource.resourceName,
});
});
return resources;
};
4 changes: 2 additions & 2 deletions packages/amplify-cli/src/commands/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { printer } from '@aws-amplify/amplify-prompts';
import chalk from 'chalk';
import { getResourceOutputs } from '../extensions/amplify-helpers/get-resource-outputs';
import Ora from 'ora';
import { getResources } from './build';
import { getChangedResources } from './build';
import * as _ from 'lodash';

export const run = async (context: $TSContext) => {
Expand Down Expand Up @@ -77,7 +77,7 @@ async function exportBackend(context: $TSContext, exportPath: string) {
}

async function buildAllResources(context: $TSContext) {
const resourcesToBuild: IAmplifyResource[] = await getResources(context);
const resourcesToBuild: IAmplifyResource[] = await getChangedResources(context);
await context.amplify.executeProviderUtils(context, 'awscloudformation', 'buildOverrides', { resourcesToBuild, forceCompile: true });
}

Expand Down
8 changes: 7 additions & 1 deletion packages/amplify-cli/src/commands/init.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { $TSContext, LocalEnvInfo } from 'amplify-cli-core';
import { constructInputParams } from '../amplify-service-helper';
import { Context } from '../domain/context';
import { raisePostEnvAddEvent } from '../execution-manager';
import { raisePostEnvAddEvent, raisePrePushEvent } from '../execution-manager';
import { postInitSetup } from '../init-steps/postInitSetup';
import { preInitSetup } from '../init-steps/preInitSetup';
import { analyzeProject, analyzeProjectHeadless } from '../init-steps/s0-analyzeProject';
import { initFrontend } from '../init-steps/s1-initFrontend';
import { initProviders } from '../init-steps/s2-initProviders';
import { scaffoldProjectHeadless } from '../init-steps/s8-scaffoldHeadless';
import { onHeadlessSuccess, onSuccess } from '../init-steps/s9-onSuccess';
import { verifyExpectedEnvParams } from '../utils/verify-expected-env-params';
import { checkForNestedProject } from './helpers/projectUtils';

const constructExeInfo = (context: $TSContext): void => {
Expand All @@ -30,6 +31,11 @@ const runStrategy = (quickstart: boolean) =>
export const run = async (context: $TSContext): Promise<void> => {
constructExeInfo(context);
checkForNestedProject();
if (context?.input?.options?.forcePush === true) {
await verifyExpectedEnvParams(context);
// raising PrePush event here because init with --forcePush will do a push after initializing
await raisePrePushEvent(context as unknown as Context);
}

const steps = runStrategy(!!context?.parameters?.options?.quickstart);
for (const step of steps) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import { generateDependentResourcesType } from '@aws-amplify/amplify-category-custom';
import { ensureEnvParamManager, IEnvironmentParameterManager } from '@aws-amplify/amplify-environment-parameters';
import { printer, prompter } from '@aws-amplify/amplify-prompts';
import { getResources } from '../../commands/build';
import { getChangedResources } from '../../commands/build';
import { initializeEnv } from '../../initialize-env';
import { getEnvInfo } from './get-env-info';
import { getProjectConfig } from './get-project-config';
Expand Down Expand Up @@ -74,7 +74,7 @@ export const pushResources = async (

// building all CFN stacks here to get the resource Changes
await generateDependentResourcesType();
const resourcesToBuild: IAmplifyResource[] = await getResources(context);
const resourcesToBuild: IAmplifyResource[] = await getChangedResources(context);
await context.amplify.executeProviderUtils(context, 'awscloudformation', 'buildOverrides', {
resourcesToBuild,
forceCompile: true,
Expand Down
2 changes: 1 addition & 1 deletion packages/amplify-cli/src/initialize-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export const initializeEnv = async (

if (context.exeInfo.forcePush) {
// context has type $TSContext here but to avoid retyping all of the raise*Event functions, we're coercing to Context here
await raisePrePushEvent(context as unknown as Context);
// await raisePrePushEvent(context as unknown as Context);
await verifyExpectedEnvParams(context);
for (const provider of context.exeInfo.projectConfig.providers) {
const providerModule = await import(providerPlugins[provider]);
Expand Down
13 changes: 8 additions & 5 deletions packages/amplify-cli/src/utils/verify-expected-env-params.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { $TSContext, IAmplifyResource } from 'amplify-cli-core';
import { $TSContext, IAmplifyResource, stateManager } from 'amplify-cli-core';
import { ensureEnvParamManager, IEnvironmentParameterManager } from '@aws-amplify/amplify-environment-parameters';
import { printer, prompter } from '@aws-amplify/amplify-prompts';
import { getResources } from '../commands/build';
import { getChangedResources, getAllResources } from '../commands/build';

export const verifyExpectedEnvParams = async (context: $TSContext, category?: string, resourceName?: string) => {
const envParamManager = (await ensureEnvParamManager()).instance;
const resourcesToBuild: IAmplifyResource[] = await getResources(context);
const envParamManager = stateManager.localEnvInfoExists()
? (await ensureEnvParamManager()).instance
: (await ensureEnvParamManager(context?.exeInfo?.inputParams?.amplify?.envName)).instance;
const getResources = context?.input?.options?.forcePush === true ? getAllResources : getChangedResources;
const resources: IAmplifyResource[] = await getResources(context);

const parametersToCheck = resourcesToBuild.filter(({ category: c, resourceName: r }) => {
const parametersToCheck = resources.filter(({ category: c, resourceName: r }) => {
// Filter based on optional parameters
if ((category && c !== category) || (resourceName && r !== resourceName)) {
return false;
Expand Down
7 changes: 4 additions & 3 deletions packages/amplify-e2e-core/src/init/non-interactive-init.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import execa from 'execa';
import execa, { ExecaReturnValue } from 'execa';
// eslint-disable-next-line import/no-cycle
import { getCLIPath, TEST_PROFILE_NAME } from '..';
import { CategoriesConfig, AwsProviderConfig } from './headless-types';
Expand Down Expand Up @@ -37,7 +37,8 @@ export const nonInteractiveInitWithForcePushAttach = async (
categoriesConfig?: CategoriesConfig,
testingWithLatestCodebase = false,
awsProviderConfig = getAwsProviderConfig(),
): Promise<void> => {
rejectOnFailure = true,
): Promise<ExecaReturnValue<string>> => {
const args = [
'init',
'--yes',
Expand All @@ -53,7 +54,7 @@ export const nonInteractiveInitWithForcePushAttach = async (
if (categoriesConfig) {
args.push('--categories', JSON.stringify(categoriesConfig));
}
await execa(getCLIPath(testingWithLatestCodebase), args, { cwd: projRoot });
return execa(getCLIPath(testingWithLatestCodebase), args, { cwd: projRoot, reject: rejectOnFailure });
};

/**
Expand Down
16 changes: 16 additions & 0 deletions packages/amplify-e2e-core/src/utils/sdk-calls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,22 @@ export const getSSMParameters = async (region: string, appId: string, envName: s
.promise();
};

export const deleteSSMParameter = async (
region: string,
appId: string,
envName: string,
category: string,
funcName: string,
parameterName: string,
) => {
const ssmClient = new SSM({ region });
return await ssmClient
.deleteParameter({
Name: path.posix.join('/amplify', appId, envName, `AMPLIFY_${category}_${funcName}_${parameterName}`),
})
.promise();
};

export const getSSMParametersCategoryPrefix = async (
region: string,
appId: string,
Expand Down
83 changes: 83 additions & 0 deletions packages/amplify-e2e-tests/src/__tests__/init-force-push.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {
addFunction,
amplifyPushAuth,
createNewProjectDir,
deleteProject,
deleteProjectDir,
deleteSSMParameter,
generateRandomShortId,
getAmplifyInitConfig,
getProjectConfig,
getProjectMeta,
getTeamProviderInfo,
initJSProjectWithProfile,
nonInteractiveInitWithForcePushAttach,
setTeamProviderInfo,
} from '@aws-amplify/amplify-e2e-core';

describe('init --forcePush', () => {
const envName = 'testtest';
let projRoot: string;
let funcName: string;
beforeEach(async () => {
projRoot = await createNewProjectDir('original');
await initJSProjectWithProfile(projRoot, { envName, disableAmplifyAppCreation: false });
funcName = `testfunction${generateRandomShortId()}`;
await addFunction(
projRoot,
{
functionTemplate: 'Hello World',
name: funcName,
environmentVariables: {
key: 'FOO_BAR',
value: 'fooBar',
},
},
'nodejs',
);
await amplifyPushAuth(projRoot);
});

afterEach(async () => {
await deleteProject(projRoot);
deleteProjectDir(projRoot);
});
it('fails fast on missing env parameters', async () => {
const { projectName } = getProjectConfig(projRoot);

// remove env param from TPI
const tpi = getTeamProviderInfo(projRoot);
delete tpi?.[envName]?.categories?.function?.[funcName]?.fooBar;
setTeamProviderInfo(projRoot, tpi);

// remote env param from ParameterStore
const meta = getProjectMeta(projRoot);
const { AmplifyAppId: appId, Region: region } = meta?.providers?.awscloudformation;
await deleteSSMParameter(region, appId, 'testtest', 'function', funcName, 'fooBar');

// init --forcePush should fail due to missing param
const result = await nonInteractiveInitWithForcePushAttach(
projRoot,
getAmplifyInitConfig(projectName, 'newenv'),
undefined,
true,
undefined,
false, // don't reject on failure
);
expect(result.exitCode).toBe(1);
});

it('restores missing param from ParameterStore', async () => {
const { projectName } = getProjectConfig(projRoot);

// remove env param from TPI
const tpi = getTeamProviderInfo(projRoot);
delete tpi?.[envName]?.categories?.function?.[funcName]?.fooBar;
setTeamProviderInfo(projRoot, tpi);

await nonInteractiveInitWithForcePushAttach(projRoot, getAmplifyInitConfig(projectName, envName), undefined, true);

const tpiAfter = getTeamProviderInfo(projRoot);
expect(tpiAfter?.[envName]?.categories?.function?.[funcName]?.fooBar).toBe('fooBar');
});
});

0 comments on commit 692362b

Please sign in to comment.