|
| 1 | +import cxapi = require('@aws-cdk/cx-api'); |
| 2 | +import nodeunit = require('nodeunit'); |
| 3 | +import { AppStacks, Tag } from '../lib/api/cxapp/stacks'; |
| 4 | +import { DeployStackResult } from '../lib/api/deploy-stack'; |
| 5 | +import { DeployStackOptions, IDeploymentTarget, Template } from '../lib/api/deployment-target'; |
| 6 | +import { CdkToolkit } from '../lib/cdk-toolkit'; |
| 7 | + |
| 8 | +export = nodeunit.testCase({ |
| 9 | + deploy: { |
| 10 | + 'makes correct CloudFormation calls': { |
| 11 | + 'without options'(test: nodeunit.Test) { |
| 12 | + // GIVEN |
| 13 | + const toolkit = new CdkToolkit({ |
| 14 | + appStacks: new TestAppStacks(test), |
| 15 | + provisioner: new TestProvisioner(test, { |
| 16 | + 'Test-Stack-A': { Foo: 'Bar' }, |
| 17 | + 'Test-Stack-B': { Baz: 'Zinga!' }, |
| 18 | + }), |
| 19 | + }); |
| 20 | + |
| 21 | + // WHEN |
| 22 | + toolkit.deploy({ stackNames: ['Test-Stack-A', 'Test-Stack-B'] }); |
| 23 | + |
| 24 | + // THEN |
| 25 | + test.done(); |
| 26 | + }, |
| 27 | + }, |
| 28 | + }, |
| 29 | +}); |
| 30 | + |
| 31 | +class MockStack { |
| 32 | + constructor( |
| 33 | + public readonly name: string, |
| 34 | + public readonly originalName: string = name, |
| 35 | + public readonly template: any = { Resources: { TempalteName: name } }, |
| 36 | + public readonly templateFile: string = `fake/stack/${name}.json`, |
| 37 | + public readonly assets: cxapi.AssetMetadataEntry[] = [], |
| 38 | + public readonly parameters: { [id: string]: string } = {}, |
| 39 | + public readonly environment: cxapi.Environment = { name: 'MockEnv', account: '123456789012', region: 'bermuda-triangle-1' }, |
| 40 | + ) {} |
| 41 | +} |
| 42 | + |
| 43 | +class TestAppStacks extends AppStacks { |
| 44 | + public static readonly MOCK_STACK_A = new MockStack('Test-Stack-A'); |
| 45 | + public static readonly MOCK_STACK_B = new MockStack('Test-Stack-B'); |
| 46 | + |
| 47 | + constructor(private readonly test: nodeunit.Test) { |
| 48 | + super(undefined as any); |
| 49 | + } |
| 50 | + |
| 51 | + public getTagsFromStackMetadata(stack: cxapi.CloudFormationStackArtifact): Tag[] { |
| 52 | + switch (stack.name) { |
| 53 | + case TestAppStacks.MOCK_STACK_A.name: |
| 54 | + return [{ Key: 'Foo', Value: 'Bar' }]; |
| 55 | + case TestAppStacks.MOCK_STACK_B.name: |
| 56 | + return [{ Key: 'Baz', Value: 'Zinga!' }]; |
| 57 | + default: |
| 58 | + throw new Error(`Not an expected mock stack: ${stack.name}`); |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + public selectStacks(selectors: string[]): Promise<cxapi.CloudFormationStackArtifact[]> { |
| 63 | + this.test.deepEqual(selectors, ['Test-Stack-A', 'Test-Stack-B']); |
| 64 | + return Promise.resolve([ |
| 65 | + // Cheating the type system here (intentionally, so we have to stub less!) |
| 66 | + TestAppStacks.MOCK_STACK_A as cxapi.CloudFormationStackArtifact, |
| 67 | + TestAppStacks.MOCK_STACK_B as cxapi.CloudFormationStackArtifact, |
| 68 | + ]); |
| 69 | + } |
| 70 | + |
| 71 | + public processMetadata(stacks: cxapi.CloudFormationStackArtifact[]): void { |
| 72 | + stacks.forEach(stack => |
| 73 | + this.test.ok(stack === TestAppStacks.MOCK_STACK_A || stack === TestAppStacks.MOCK_STACK_B, |
| 74 | + `Not an expected mock stack: ${stack.name}`)); |
| 75 | + } |
| 76 | + |
| 77 | + public listStacks(): never { |
| 78 | + throw new Error('Not Implemented'); |
| 79 | + } |
| 80 | + |
| 81 | + public synthesizeStack(): never { |
| 82 | + throw new Error('Not Implemented'); |
| 83 | + } |
| 84 | + |
| 85 | + public synthesizeStacks(): never { |
| 86 | + throw new Error('Not Implemented'); |
| 87 | + } |
| 88 | +} |
| 89 | + |
| 90 | +class TestProvisioner implements IDeploymentTarget { |
| 91 | + private readonly expectedTags: { [sytackName: string]: Tag[] } = {}; |
| 92 | + |
| 93 | + constructor( |
| 94 | + private readonly test: nodeunit.Test, |
| 95 | + expectedTags: { [sytackName: string]: { [kay: string]: string } } = {}, |
| 96 | + ) { |
| 97 | + for (const [stackName, tags] of Object.entries(expectedTags)) { |
| 98 | + this.expectedTags[stackName] = |
| 99 | + Object.entries(tags).map(([Key, Value]) => ({ Key, Value })) |
| 100 | + .sort((l, r) => l.Key.localeCompare(r.Key)); |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + public deployStack(options: DeployStackOptions): Promise<DeployStackResult> { |
| 105 | + this.test.ok( |
| 106 | + options.stack.name === TestAppStacks.MOCK_STACK_A.name || options.stack.name === TestAppStacks.MOCK_STACK_B.name, |
| 107 | + `Not an expected mock stack: ${options.stack.name}` |
| 108 | + ); |
| 109 | + this.test.deepEqual(options.tags, this.expectedTags[options.stack.name]); |
| 110 | + return Promise.resolve({ |
| 111 | + stackArn: `arn:aws:cloudformation:::stack/${options.stack.name}/MockedOut`, |
| 112 | + noOp: false, |
| 113 | + outputs: { StackName: options.stack.name }, |
| 114 | + }); |
| 115 | + } |
| 116 | + |
| 117 | + public readCurrentTemplate(stack: cxapi.CloudFormationStackArtifact): Promise<Template> { |
| 118 | + switch (stack.name) { |
| 119 | + case TestAppStacks.MOCK_STACK_A.name: |
| 120 | + return Promise.resolve({}); |
| 121 | + case TestAppStacks.MOCK_STACK_B.name: |
| 122 | + return Promise.resolve({}); |
| 123 | + default: |
| 124 | + return Promise.reject(`Not an expected mock stack: ${stack.name}`); |
| 125 | + } |
| 126 | + } |
| 127 | +} |
0 commit comments