From 8833955e586914a56eda47ce76d47c4884a9b685 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Mon, 2 Jul 2018 23:17:27 -0700 Subject: [PATCH] Context providers: return dummy values if env is undefined Throwing if "env" is undefined makes it hard to unit test stacks that use context providers. It requires dummy values for account and region, which is boilerplate and non-intuitive. Since we already have a concept of dummy context values, this change short-circuits context resolution and returns the dummy values in case the stack's env is undefined. --- packages/@aws-cdk/core/lib/context.ts | 22 +++++++++++++++------ packages/@aws-cdk/core/test/test.context.ts | 9 ++++++++- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/core/lib/context.ts b/packages/@aws-cdk/core/lib/context.ts index 783623d4e4b3c..8d1efe83cd6ee 100644 --- a/packages/@aws-cdk/core/lib/context.ts +++ b/packages/@aws-cdk/core/lib/context.ts @@ -24,7 +24,12 @@ export class ContextProvider { /** * Read a provider value, verifying it's a string */ - public getStringValue(provider: string, scope: string[], args: string[]): string { + public getStringValue(provider: string, scope: undefined | string[], args: string[], defaultValue = ''): string { + // if scope is undefined, this is probably a test mode, so we just + // return the default value + if (!scope) { + return defaultValue; + } const key = colonQuote([provider].concat(scope).concat(args)).join(':'); const value = this.context.getContext(key); if (value != null) { @@ -35,13 +40,19 @@ export class ContextProvider { } this.stack.reportMissingContext(key, { provider, scope, args }); - return ''; + return defaultValue; } /** * Read a provider value, verifying it's a list */ - public getStringListValue(provider: string, scope: string[], args: string[], defaultValue = ['']): string[] { + public getStringListValue(provider: string, scope: undefined | string[], args: string[], defaultValue = ['']): string[] { + // if scope is undefined, this is probably a test mode, so we just + // return the default value + if (!scope) { + return defaultValue; + } + const key = colonQuote([provider].concat(scope).concat(args)).join(':'); const value = this.context.getContext(key); @@ -59,7 +70,7 @@ export class ContextProvider { /** * Helper function to wrap up account and region into a scope tuple */ - public accountRegionScope(providerDescription: string): string[] { + public accountRegionScope(providerDescription: string): undefined | string[] { const stack = Stack.find(this.context); if (!stack) { throw new Error(`${providerDescription}: construct must be in a stack`); @@ -69,8 +80,7 @@ export class ContextProvider { const region = stack.env.region; if (account == null || region == null) { - // tslint:disable-next-line:max-line-length - throw new Error(`${providerDescription}: requires account and region information, but ${stack.name} doesn't have an "env" defined`); + return undefined; } return [account, region]; diff --git a/packages/@aws-cdk/core/test/test.context.ts b/packages/@aws-cdk/core/test/test.context.ts index af525033a3716..7e94cf809c636 100644 --- a/packages/@aws-cdk/core/test/test.context.ts +++ b/packages/@aws-cdk/core/test/test.context.ts @@ -50,7 +50,14 @@ export = { test.deepEqual(azs, 'abc'); test.done(); - } + }, + + 'Return default values if "env" is undefined to facilitate unit tests'(test: Test) { + const stack = new Stack(); + test.deepEqual(new AvailabilityZoneProvider(stack).availabilityZones, [ 'dummy1a', 'dummy1b', 'dummy1c' ]); + test.deepEqual(new SSMParameterProvider(stack).getString('foo'), ''); + test.done(); + }, }; function firstKey(obj: any): string {