1 | 'use strict';
|
2 |
|
3 | const test = require('tape');
|
4 | const cloudfriend = require('..');
|
5 | const path = require('path');
|
6 |
|
7 | const expectedTemplate = require('./fixtures/static.json');
|
8 | const fixtures = path.resolve(__dirname, 'fixtures');
|
9 |
|
10 | test('intrinsic functions', (assert) => {
|
11 | assert.deepEqual(cloudfriend.base64('secret'), { 'Fn::Base64': 'secret' }, 'base64');
|
12 | assert.deepEqual(cloudfriend.findInMap('mapping', 'key', 'value'), { 'Fn::FindInMap': ['mapping', 'key', 'value'] }, 'lookup');
|
13 | assert.deepEqual(cloudfriend.getAtt('obj', 'key'), { 'Fn::GetAtt': ['obj', 'key'] }, 'attr');
|
14 | assert.deepEqual(cloudfriend.getAzs(), { 'Fn::GetAZs': '' }, 'azs (no value specified)');
|
15 | assert.deepEqual(cloudfriend.getAzs('us-east-1'), { 'Fn::GetAZs': 'us-east-1' }, 'azs (value specified)');
|
16 | assert.deepEqual(cloudfriend.join(['abra', 'cadabra']), { 'Fn::Join': ['', ['abra', 'cadabra']] }, 'join (no delimeter specified)');
|
17 | assert.deepEqual(cloudfriend.join('-', ['abra', 'cadabra']), { 'Fn::Join': ['-', ['abra', 'cadabra']] }, 'join (delimeter specified)');
|
18 | assert.deepEqual(cloudfriend.select(1, ['abra', 'cadabra']), { 'Fn::Select': ['1', ['abra', 'cadabra']] }, '');
|
19 | assert.deepEqual(cloudfriend.ref('something'), { Ref: 'something' }, 'ref');
|
20 | assert.deepEqual(cloudfriend.split(',', 'a,b,c,d'), { 'Fn::Split': [',', 'a,b,c,d'] }, 'split');
|
21 | assert.deepEqual(cloudfriend.split(',', cloudfriend.ref('Id')), { 'Fn::Split': [',', { Ref: 'Id' }] }, 'split');
|
22 | assert.deepEqual(cloudfriend.userData(['#!/usr/bin/env bash', 'set -e']), { 'Fn::Base64': { 'Fn::Join': ['\n', ['#!/usr/bin/env bash', 'set -e']] } }, 'userData');
|
23 | assert.deepEqual(cloudfriend.sub('my ${thing}'), { 'Fn::Sub': 'my ${thing}' }, 'sub without variables');
|
24 | assert.deepEqual(cloudfriend.sub('my ${thing}', { thing: 'stuff' }), { 'Fn::Sub': ['my ${thing}', { thing: 'stuff' }] }, 'sub with variables');
|
25 | assert.deepEqual(cloudfriend.importValue('id'), { 'Fn::ImportValue': 'id' }, 'import value with string');
|
26 | assert.deepEqual(cloudfriend.importValue(cloudfriend.ref('Id')), { 'Fn::ImportValue': cloudfriend.ref('Id') }, 'import value with string');
|
27 | assert.deepEqual(cloudfriend.arn('s3', 'my-bucket/*'), { 'Fn::Sub': ['arn:${AWS::Partition}:${service}:::${suffix}', { service: 's3', suffix: 'my-bucket/*' }] }, 's3 arn');
|
28 | assert.deepEqual(cloudfriend.arn('cloudformation', 'stack/my-stack/*'), { 'Fn::Sub': ['arn:${AWS::Partition}:${service}:${AWS::Region}:${AWS::AccountId}:${suffix}', { service: 'cloudformation', suffix: 'stack/my-stack/*' }] }, 'non-s3 arn');
|
29 | assert.end();
|
30 | });
|
31 |
|
32 | test('conditions', (assert) => {
|
33 | assert.deepEqual(cloudfriend.and(['a', 'b']), { 'Fn::And': ['a', 'b'] }, 'and');
|
34 | assert.deepEqual(cloudfriend.equals('a', 'b'), { 'Fn::Equals': ['a', 'b'] }, 'equal');
|
35 | assert.deepEqual(cloudfriend.if('condition', 'a', 'b'), { 'Fn::If': ['condition', 'a', 'b'] }, 'if');
|
36 | assert.deepEqual(cloudfriend.not('condition'), { 'Fn::Not': ['condition'] }, 'not');
|
37 | assert.deepEqual(cloudfriend.or(['a', 'b']), { 'Fn::Or': ['a', 'b'] }, 'or');
|
38 | assert.deepEqual(cloudfriend.notEquals('a', 'b'), { 'Fn::Not': [{ 'Fn::Equals': ['a', 'b'] }] }, 'notEqual');
|
39 | assert.end();
|
40 | });
|
41 |
|
42 | test('pseudo', (assert) => {
|
43 | assert.deepEqual(cloudfriend.accountId, { Ref: 'AWS::AccountId' }, 'account');
|
44 | assert.deepEqual(cloudfriend.notificationArns, { Ref: 'AWS::NotificationARNs' }, 'notificationArns');
|
45 | assert.deepEqual(cloudfriend.noValue, { Ref: 'AWS::NoValue' }, 'noValue');
|
46 | assert.deepEqual(cloudfriend.region, { Ref: 'AWS::Region' }, 'region');
|
47 | assert.deepEqual(cloudfriend.stackId, { Ref: 'AWS::StackId' }, 'stackId');
|
48 | assert.deepEqual(cloudfriend.stackName, { Ref: 'AWS::StackName' }, 'stack');
|
49 | assert.deepEqual(cloudfriend.partition, { Ref: 'AWS::Partition' }, 'stack');
|
50 | assert.deepEqual(cloudfriend.urlSuffix, { Ref: 'AWS::URLSuffix' }, 'stack');
|
51 | assert.end();
|
52 | });
|
53 |
|
54 | test('build', (assert) => {
|
55 | assert.plan(8);
|
56 |
|
57 | cloudfriend.build(path.join(fixtures, 'static.json'))
|
58 | .then((template) => {
|
59 | assert.deepEqual(template, expectedTemplate, 'static.json');
|
60 | return cloudfriend.build(path.join(fixtures, 'static.js'));
|
61 | })
|
62 | .then((template) => {
|
63 | assert.deepEqual(template, expectedTemplate, 'static.js');
|
64 | return cloudfriend.build(path.join(fixtures, 'sync.js'));
|
65 | })
|
66 | .then((template) => {
|
67 | assert.deepEqual(template, expectedTemplate, 'sync.js');
|
68 | return cloudfriend.build(path.join(fixtures, 'async.js'));
|
69 | })
|
70 | .then((template) => {
|
71 | assert.deepEqual(template, expectedTemplate, 'async.js (success)');
|
72 | return cloudfriend.build(path.join(fixtures, 'async-error.js'))
|
73 | .catch((err) => {
|
74 | assert.ok(err, 'async.js (error)');
|
75 | });
|
76 | })
|
77 | .then(() => {
|
78 | return cloudfriend.build(path.join(fixtures, 'sync-args.js'), { some: 'options' });
|
79 | })
|
80 | .then((template) => {
|
81 | assert.deepEqual(template, { some: 'options' }, 'passes args (sync)');
|
82 | return cloudfriend.build(path.join(fixtures, 'async-args.js'), { some: 'options' });
|
83 | })
|
84 | .then((template) => {
|
85 | assert.deepEqual(template, { some: 'options' }, 'passes args (async)');
|
86 | return cloudfriend.build(path.join(fixtures, 'malformed.json'))
|
87 | .catch((err) => {
|
88 | assert.ok(err, 'malformed JSON (error)');
|
89 | });
|
90 | });
|
91 | });
|
92 |
|
93 | test('validate', (assert) => {
|
94 | assert.plan(2);
|
95 |
|
96 | cloudfriend.validate(path.join(fixtures, 'static.json'))
|
97 | .then(() => {
|
98 | assert.ok(true, 'valid');
|
99 | return cloudfriend.validate(path.join(fixtures, 'invalid.json'));
|
100 | })
|
101 | .catch((err) => {
|
102 | assert.ok(/Template format error: Unrecognized resource type/.test(err.message), 'invalid');
|
103 | });
|
104 | });
|
105 |
|
106 | test('merge', (assert) => {
|
107 | const a = {
|
108 | Metadata: { Instances: { Description: 'Information about the instances' } },
|
109 | Parameters: { InstanceCount: { Type: 'Number' } },
|
110 | Mappings: { Region: { 'us-east-1': { AMI: 'ami-123456' } } },
|
111 | Conditions: { WouldYouLikeBaconWithThat: cloudfriend.equals(cloudfriend.ref('InstanceCount'), 999) },
|
112 | Resources: { Instance: { Type: 'AWS::EC2::Instance', Properties: { ImageId: cloudfriend.findInMap('Region', cloudfriend.region, 'AMI') } } },
|
113 | Outputs: { Breakfast: { Condition: 'WouldYouLikeBaconWithThat', Value: cloudfriend.ref('Instance') } }
|
114 | };
|
115 |
|
116 | let b = {
|
117 | Metadata: { Databases: { Description: 'Information about the databases' } },
|
118 | Parameters: { DatabasePrefix: { Type: 'String' } },
|
119 | Mappings: { Prefix: { eggs: { Name: 'bananas' } } },
|
120 | Conditions: { TooMuch: cloudfriend.equals(cloudfriend.ref('DatabasePrefix'), 'bacon') },
|
121 | Resources: { Database: { Type: 'AWS::DynamoDB::Table', Properties: { Name: cloudfriend.findInMap('Prefix', cloudfriend.ref('DatabasePrefix'), 'Name') } } },
|
122 | Outputs: { GoSomewhereElse: { Condition: 'TooMuch', Value: cloudfriend.ref('Database') } }
|
123 | };
|
124 |
|
125 | const c = {
|
126 | Parameters: { NoConsequence: { Type: 'String' } }
|
127 | };
|
128 |
|
129 | assert.deepEqual(cloudfriend.merge(a, b, c), {
|
130 | AWSTemplateFormatVersion: '2010-09-09',
|
131 | Metadata: {
|
132 | Instances: { Description: 'Information about the instances' },
|
133 | Databases: { Description: 'Information about the databases' }
|
134 | },
|
135 | Parameters: {
|
136 | InstanceCount: { Type: 'Number' },
|
137 | DatabasePrefix: { Type: 'String' },
|
138 | NoConsequence: { Type: 'String' }
|
139 | },
|
140 | Mappings: {
|
141 | Region: { 'us-east-1': { AMI: 'ami-123456' } },
|
142 | Prefix: { eggs: { Name: 'bananas' } }
|
143 | },
|
144 | Conditions: {
|
145 | WouldYouLikeBaconWithThat: cloudfriend.equals(cloudfriend.ref('InstanceCount'), 999),
|
146 | TooMuch: cloudfriend.equals(cloudfriend.ref('DatabasePrefix'), 'bacon')
|
147 | },
|
148 | Resources: {
|
149 | Instance: { Type: 'AWS::EC2::Instance', Properties: { ImageId: cloudfriend.findInMap('Region', cloudfriend.region, 'AMI') } },
|
150 | Database: { Type: 'AWS::DynamoDB::Table', Properties: { Name: cloudfriend.findInMap('Prefix', cloudfriend.ref('DatabasePrefix'), 'Name') } }
|
151 | },
|
152 | Outputs: {
|
153 | Breakfast: { Condition: 'WouldYouLikeBaconWithThat', Value: cloudfriend.ref('Instance') },
|
154 | GoSomewhereElse: { Condition: 'TooMuch', Value: cloudfriend.ref('Database') }
|
155 | }
|
156 | }, 'merge without overlap');
|
157 |
|
158 | assert.throws(() => {
|
159 | b = { Metadata: { Instances: { Description: 'Information about the instances different' } } };
|
160 | cloudfriend.merge(a, b);
|
161 | }, /Metadata name used more than once: Instances/, 'throws on .Metadata overlap');
|
162 |
|
163 | assert.doesNotThrow(() => {
|
164 | b = { Metadata: { Instances: { Description: 'Information about the instances' } } };
|
165 | cloudfriend.merge(a, b);
|
166 | }, 'allows identical .Metadata overlap');
|
167 |
|
168 | assert.throws(() => {
|
169 | b = { Parameters: { InstanceCount: { Type: 'Number', Description: 'Different' } } };
|
170 | cloudfriend.merge(a, b);
|
171 | }, /Parameters name used more than once: InstanceCount/, 'throws on .Parameters overlap');
|
172 |
|
173 | assert.doesNotThrow(() => {
|
174 | b = { Parameters: { InstanceCount: { Type: 'Number' } } };
|
175 | cloudfriend.merge(a, b);
|
176 | }, 'allows identical .Parameters overlap');
|
177 |
|
178 | assert.throws(() => {
|
179 | b = { Mappings: { Region: { 'us-east-1': { AMI: 'ami-123456' }, 'us-east-4': { AMI: 'ami-123456' } } } };
|
180 | cloudfriend.merge(a, b);
|
181 | }, /Mappings name used more than once: Region/, 'throws on .Mappings overlap');
|
182 |
|
183 | assert.doesNotThrow(() => {
|
184 | b = { Mappings: { Region: { 'us-east-1': { AMI: 'ami-123456' } } } };
|
185 | cloudfriend.merge(a, b);
|
186 | }, 'allows identical .Mappings overlap');
|
187 |
|
188 | assert.throws(() => {
|
189 | b = { Conditions: { WouldYouLikeBaconWithThat: cloudfriend.equals(cloudfriend.ref('InstanceCount'), 998) } };
|
190 | cloudfriend.merge(a, b);
|
191 | }, /Conditions name used more than once: WouldYouLikeBaconWithThat/, 'throws on .Conditions overlap');
|
192 |
|
193 | assert.doesNotThrow(() => {
|
194 | b = { Conditions: { WouldYouLikeBaconWithThat: cloudfriend.equals(cloudfriend.ref('InstanceCount'), 999) } };
|
195 | cloudfriend.merge(a, b);
|
196 | }, 'allows identical .Conditions overlap');
|
197 |
|
198 | assert.throws(() => {
|
199 | b = { Resources: { Instance: { Type: 'AWS::EC2::Instance', Properties: { ImageId: cloudfriend.findInMap('Region', cloudfriend.region, 'AMIz') } } } };
|
200 | cloudfriend.merge(a, b);
|
201 | }, /Resources name used more than once: Instance/, 'throws on .Resources overlap');
|
202 |
|
203 | assert.doesNotThrow(() => {
|
204 | b = { Resources: { Instance: { Type: 'AWS::EC2::Instance', Properties: { ImageId: cloudfriend.findInMap('Region', cloudfriend.region, 'AMI') } } } };
|
205 | cloudfriend.merge(a, b);
|
206 | }, 'allows identical .Resources overlap');
|
207 |
|
208 | assert.throws(() => {
|
209 | b = { Outputs: { Breakfast: { Condition: 'WouldYouLikeBaconWithThat', Value: cloudfriend.ref('Instancez') } } };
|
210 | cloudfriend.merge(a, b);
|
211 | }, /Outputs name used more than once: Breakfast/, 'throws on .Outputs overlap');
|
212 |
|
213 | assert.doesNotThrow(() => {
|
214 | b = { Outputs: { Breakfast: { Condition: 'WouldYouLikeBaconWithThat', Value: cloudfriend.ref('Instance') } } };
|
215 | cloudfriend.merge(a, b);
|
216 | }, 'allows identical .Outputs overlap');
|
217 |
|
218 | assert.doesNotThrow(() => {
|
219 | b = { Mappings: { Instance: { 'us-east-1': { AMI: 'ami-123456' } } } };
|
220 | cloudfriend.merge(a, b);
|
221 | }, 'does not throw on cross-property name overlap');
|
222 |
|
223 | assert.end();
|
224 | });
|