@mapbox/cloudfriend 9.2.0 → 9.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/.idea/cloudfriend.iml +9 -0
  3. package/.idea/codeStyles/Project.xml +7 -0
  4. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  5. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  6. package/.idea/misc.xml +6 -0
  7. package/.idea/modules.xml +8 -0
  8. package/.idea/vcs.xml +6 -0
  9. package/.nyc_output/6686e513-03b6-415d-9b8b-0fcde7a4e430.json +1 -0
  10. package/.nyc_output/f58a557c-a1c0-4dcc-83c5-bbca4d01baf9.json +1 -0
  11. package/.nyc_output/processinfo/6686e513-03b6-415d-9b8b-0fcde7a4e430.json +1 -0
  12. package/.nyc_output/processinfo/f58a557c-a1c0-4dcc-83c5-bbca4d01baf9.json +1 -0
  13. package/.nyc_output/processinfo/index.json +1 -1
  14. package/cfniceberg_createtable.yaml +71 -0
  15. package/changelog.md +8 -0
  16. package/coverage/clover.xml +630 -0
  17. package/coverage/coverage-final.json +33 -0
  18. package/coverage/lcov-report/base.css +224 -0
  19. package/coverage/lcov-report/block-navigation.js +87 -0
  20. package/coverage/lcov-report/cloudfriend/bin/build-template.js.html +130 -0
  21. package/coverage/lcov-report/cloudfriend/bin/index.html +131 -0
  22. package/coverage/lcov-report/cloudfriend/bin/validate-template.js.html +142 -0
  23. package/coverage/lcov-report/cloudfriend/index.html +116 -0
  24. package/coverage/lcov-report/cloudfriend/index.js.html +307 -0
  25. package/coverage/lcov-report/cloudfriend/lib/build.js.html +217 -0
  26. package/coverage/lcov-report/cloudfriend/lib/conditions.js.html +430 -0
  27. package/coverage/lcov-report/cloudfriend/lib/index.html +206 -0
  28. package/coverage/lcov-report/cloudfriend/lib/intrinsic.js.html +979 -0
  29. package/coverage/lcov-report/cloudfriend/lib/merge.js.html +478 -0
  30. package/coverage/lcov-report/cloudfriend/lib/pseudo.js.html +370 -0
  31. package/coverage/lcov-report/cloudfriend/lib/rules.js.html +349 -0
  32. package/coverage/lcov-report/cloudfriend/lib/shortcuts/cross-account-role.js.html +244 -0
  33. package/coverage/lcov-report/cloudfriend/lib/shortcuts/event-lambda.js.html +367 -0
  34. package/coverage/lcov-report/cloudfriend/lib/shortcuts/glue-database.js.html +286 -0
  35. package/coverage/lcov-report/cloudfriend/lib/shortcuts/glue-iceberg-table.js.html +646 -0
  36. package/coverage/lcov-report/cloudfriend/lib/shortcuts/glue-json-table.js.html +235 -0
  37. package/coverage/lcov-report/cloudfriend/lib/shortcuts/glue-orc-table.js.html +226 -0
  38. package/coverage/lcov-report/cloudfriend/lib/shortcuts/glue-parquet-table.js.html +268 -0
  39. package/coverage/lcov-report/cloudfriend/lib/shortcuts/glue-presto-view.js.html +358 -0
  40. package/coverage/lcov-report/cloudfriend/lib/shortcuts/glue-spark-view.js.html +241 -0
  41. package/coverage/lcov-report/cloudfriend/lib/shortcuts/glue-table.js.html +481 -0
  42. package/coverage/lcov-report/cloudfriend/lib/shortcuts/hookshot.js.html +1504 -0
  43. package/coverage/lcov-report/cloudfriend/lib/shortcuts/index.html +431 -0
  44. package/coverage/lcov-report/cloudfriend/lib/shortcuts/index.js.html +157 -0
  45. package/coverage/lcov-report/cloudfriend/lib/shortcuts/kinesis-firehose-base.js.html +418 -0
  46. package/coverage/lcov-report/cloudfriend/lib/shortcuts/lambda.js.html +832 -0
  47. package/coverage/lcov-report/cloudfriend/lib/shortcuts/log-subscription-lambda.js.html +310 -0
  48. package/coverage/lcov-report/cloudfriend/lib/shortcuts/queue-lambda.js.html +364 -0
  49. package/coverage/lcov-report/cloudfriend/lib/shortcuts/queue.js.html +619 -0
  50. package/coverage/lcov-report/cloudfriend/lib/shortcuts/role.js.html +382 -0
  51. package/coverage/lcov-report/cloudfriend/lib/shortcuts/s3-kinesis-firehose.js.html +568 -0
  52. package/coverage/lcov-report/cloudfriend/lib/shortcuts/scheduled-lambda.js.html +490 -0
  53. package/coverage/lcov-report/cloudfriend/lib/shortcuts/service-role.js.html +307 -0
  54. package/coverage/lcov-report/cloudfriend/lib/shortcuts/stream-lambda.js.html +493 -0
  55. package/coverage/lcov-report/cloudfriend/lib/validate.js.html +154 -0
  56. package/coverage/lcov-report/favicon.png +0 -0
  57. package/coverage/lcov-report/index.html +161 -0
  58. package/coverage/lcov-report/prettify.css +1 -0
  59. package/coverage/lcov-report/prettify.js +2 -0
  60. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  61. package/coverage/lcov-report/sorter.js +210 -0
  62. package/coverage/lcov.info +1319 -0
  63. package/jest.config.js +14 -0
  64. package/lib/shortcuts/api.md +37 -0
  65. package/lib/shortcuts/glue-iceberg-table.js +187 -0
  66. package/lib/shortcuts/index.js +1 -0
  67. package/package.json +15 -13
  68. package/readme.md +1 -1
  69. package/requirements.dev.txt +2 -2
  70. package/test/bin.test.js +18 -14
  71. package/test/fixtures/shortcuts/cross-account-role-defaults.json +1 -1
  72. package/test/fixtures/shortcuts/cross-account-role-no-defaults.json +1 -1
  73. package/test/fixtures/shortcuts/event-lambda-defaults.json +1 -1
  74. package/test/fixtures/shortcuts/event-lambda-full.json +1 -1
  75. package/test/fixtures/shortcuts/firehose-defaults.json +1 -1
  76. package/test/fixtures/shortcuts/firehose-with-stream.json +1 -1
  77. package/test/fixtures/shortcuts/glue-database-defaults.json +1 -1
  78. package/test/fixtures/shortcuts/glue-database-no-defaults.json +1 -1
  79. package/test/fixtures/shortcuts/glue-iceberg-table-defaults.json +41 -0
  80. package/test/fixtures/shortcuts/glue-iceberg-table-no-defaults.json +39 -0
  81. package/test/fixtures/shortcuts/glue-iceberg-table-with-all-optimizers.json +101 -0
  82. package/test/fixtures/shortcuts/glue-iceberg-table-with-both-optimizers.json +80 -0
  83. package/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-custom.json +68 -0
  84. package/test/fixtures/shortcuts/glue-iceberg-table-with-compaction-defaults.json +57 -0
  85. package/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-custom.json +75 -0
  86. package/test/fixtures/shortcuts/glue-iceberg-table-with-optimizer-defaults.json +64 -0
  87. package/test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-custom.json +74 -0
  88. package/test/fixtures/shortcuts/glue-iceberg-table-with-orphan-deletion-defaults.json +62 -0
  89. package/test/fixtures/shortcuts/glue-json-table-defaults.json +1 -1
  90. package/test/fixtures/shortcuts/glue-json-table-no-defaults.json +1 -1
  91. package/test/fixtures/shortcuts/glue-orc-table-defaults.json +1 -1
  92. package/test/fixtures/shortcuts/glue-orc-table-no-defaults.json +1 -1
  93. package/test/fixtures/shortcuts/glue-parquet-table-defaults.json +1 -1
  94. package/test/fixtures/shortcuts/glue-parquet-table-no-defaults.json +1 -1
  95. package/test/fixtures/shortcuts/glue-table-defaults.json +1 -1
  96. package/test/fixtures/shortcuts/glue-table-no-defaults.json +1 -1
  97. package/test/fixtures/shortcuts/glue-view-defaults.json +1 -1
  98. package/test/fixtures/shortcuts/glue-view-no-defaults.json +1 -1
  99. package/test/fixtures/shortcuts/hookshot-github-secret-ref.json +1 -1
  100. package/test/fixtures/shortcuts/hookshot-github-secret-string.json +1 -1
  101. package/test/fixtures/shortcuts/hookshot-github.json +1 -1
  102. package/test/fixtures/shortcuts/hookshot-passthrough-access-log-format.json +1 -1
  103. package/test/fixtures/shortcuts/hookshot-passthrough-alarms.json +1 -1
  104. package/test/fixtures/shortcuts/hookshot-passthrough-enhanced-logging.json +1 -1
  105. package/test/fixtures/shortcuts/hookshot-passthrough-full-blown-logging.json +1 -1
  106. package/test/fixtures/shortcuts/hookshot-passthrough-logging.json +1 -1
  107. package/test/fixtures/shortcuts/hookshot-passthrough.json +1 -1
  108. package/test/fixtures/shortcuts/lambda-defaults.json +1 -1
  109. package/test/fixtures/shortcuts/lambda-docker.json +1 -1
  110. package/test/fixtures/shortcuts/lambda-full.json +1 -1
  111. package/test/fixtures/shortcuts/lambda-provided-role.json +1 -1
  112. package/test/fixtures/shortcuts/lambda-zipfile.json +1 -1
  113. package/test/fixtures/shortcuts/log-subscription-lambda-defaults.json +1 -1
  114. package/test/fixtures/shortcuts/log-subscription-lambda-no-defaults.json +1 -1
  115. package/test/fixtures/shortcuts/queue-defaults.json +1 -1
  116. package/test/fixtures/shortcuts/queue-external-topic-ref.json +1 -1
  117. package/test/fixtures/shortcuts/queue-external-topic.json +1 -1
  118. package/test/fixtures/shortcuts/queue-fifo-queuename.json +1 -1
  119. package/test/fixtures/shortcuts/queue-fifo.json +1 -1
  120. package/test/fixtures/shortcuts/queue-full.json +1 -1
  121. package/test/fixtures/shortcuts/queue-lambda-zero.json +1 -1
  122. package/test/fixtures/shortcuts/queue-lambda.json +1 -1
  123. package/test/fixtures/shortcuts/role-defaults.json +1 -1
  124. package/test/fixtures/shortcuts/role-no-defaults.json +1 -1
  125. package/test/fixtures/shortcuts/scheduled-lambda-defaults.json +1 -1
  126. package/test/fixtures/shortcuts/scheduled-lambda-full.json +1 -1
  127. package/test/fixtures/shortcuts/service-role-defaults.json +1 -1
  128. package/test/fixtures/shortcuts/service-role-no-defaults.json +1 -1
  129. package/test/fixtures/shortcuts/service-role-no-url-suffix.json +1 -1
  130. package/test/fixtures/shortcuts/service-role-url-suffix-with-replacement.json +1 -1
  131. package/test/fixtures/shortcuts/service-role-url-suffix.json +1 -1
  132. package/test/fixtures/shortcuts/stream-lambda-defaults.json +1 -1
  133. package/test/fixtures/shortcuts/stream-lambda-no-defaults.json +1 -1
  134. package/test/index.test.js +383 -235
  135. package/test/shortcuts.test.js +1523 -1483
  136. package/.nyc_output/2b544c0b-2830-4ad0-97cd-8e661710b0bb.json +0 -1
  137. package/.nyc_output/65e58b48-bf1c-412d-8dd4-d7b564b12d76.json +0 -1
  138. package/.nyc_output/processinfo/2b544c0b-2830-4ad0-97cd-8e661710b0bb.json +0 -1
  139. package/.nyc_output/processinfo/65e58b48-bf1c-412d-8dd4-d7b564b12d76.json +0 -1
@@ -3,7 +3,6 @@
3
3
  const cp = require('child_process');
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
- const test = require('tape');
7
6
  const cf = require('..');
8
7
  const fixtures = require('./fixtures/shortcuts');
9
8
  const util = require('util');
@@ -13,11 +12,16 @@ const update = !!process.env.UPDATE;
13
12
 
14
13
  const noUndefined = (template) => JSON.parse(JSON.stringify(template));
15
14
 
16
- test('[shortcuts] fixture validation', async (assert) => {
15
+ describe('[shortcuts] fixture validation', () => {
17
16
  // Runs cfn-lint, ignoring "warnings". Install via pip or Homebrew to run these
18
17
  // tests locally.
19
- const cfnLint = (filepath) => new Promise((resolve, reject) => {
20
- cp.exec(`cfn-lint ${filepath} --ignore-checks W`, (err, stdout) => {
18
+ const cfnLint = (filepath, filename) => new Promise((resolve, reject) => {
19
+ // Ignore E3003 (missing TableInput) and E3002 (unexpected properties) for Iceberg tables only
20
+ // cfn-lint doesn't yet support OpenTableFormatInput (Iceberg table format)
21
+ const isIcebergTable = filename.includes('glue-iceberg-table');
22
+ const ignoreChecks = isIcebergTable ? 'W,E3003,E3002' : 'W';
23
+
24
+ cp.execFile('cfn-lint', [filepath, '--ignore-checks', ignoreChecks], (err, stdout) => {
21
25
  if (err) return reject(new Error(stdout));
22
26
  return resolve();
23
27
  });
@@ -27,36 +31,25 @@ test('[shortcuts] fixture validation', async (assert) => {
27
31
  .readdirSync(path.join(__dirname, 'fixtures', 'shortcuts'))
28
32
  .filter((filename) => path.extname(filename) === '.json');
29
33
 
30
- while (toValidate.length) {
31
- const filename = toValidate.shift();
34
+ test.each(toValidate)('%s fixture passes validation', async (filename) => {
32
35
  await Promise.all([
33
- cfnLint(path.join(__dirname, 'fixtures', 'shortcuts', filename))
34
- .then(() => assert.pass(`${filename} fixture passed validation`))
35
- .catch((err) => {
36
- assert.fail(`${filename} fixture fails validation`);
37
- console.log(err.message);
38
- }),
36
+ cfnLint(path.join(__dirname, 'fixtures', 'shortcuts', filename), filename),
39
37
  sleep(1000)
40
38
  ]);
41
- }
42
-
43
- assert.end();
39
+ });
44
40
  });
45
41
 
46
- test('[shortcuts] lambda', (assert) => {
47
- assert.throws(
48
- () => new cf.shortcuts.Lambda(),
49
- 'Options required',
50
- 'throws without options'
51
- );
52
- assert.throws(
53
- () => new cf.shortcuts.Lambda({}),
54
- /You must provide a LogicalName, and Code/,
55
- 'throws without required parameters'
56
- );
42
+ describe('[shortcuts] lambda', () => {
43
+ test('throws without options', () => {
44
+ expect(() => new cf.shortcuts.Lambda()).toThrow('Options required');
45
+ });
57
46
 
58
- assert.throws(
59
- () => new cf.shortcuts.Lambda({
47
+ test('throws without required parameters', () => {
48
+ expect(() => new cf.shortcuts.Lambda({})).toThrow(/You must provide a LogicalName, and Code/);
49
+ });
50
+
51
+ test('throws for RoleArn and Statements both provided', () => {
52
+ expect(() => new cf.shortcuts.Lambda({
60
53
  LogicalName: 'MyLambda',
61
54
  Code: {
62
55
  S3Bucket: 'my-code-bucket',
@@ -68,469 +61,400 @@ test('[shortcuts] lambda', (assert) => {
68
61
  Action: 's3:GetObject',
69
62
  Resource: 'arn:aws:s3:::my-bucket/*'
70
63
  }]
71
- }),
72
- /You cannot specify both Statements and a RoleArn/,
73
- 'throws for RoleArn and Statements both provided'
74
- );
75
-
76
- let lambda = new cf.shortcuts.Lambda({
77
- LogicalName: 'MyLambda',
78
- Code: {
79
- S3Bucket: 'my-code-bucket',
80
- S3Key: 'path/to/code.zip'
81
- }
64
+ })).toThrow(/You cannot specify both Statements and a RoleArn/);
82
65
  });
83
66
 
84
- let template = cf.merge(lambda);
85
- if (update) fixtures.update('lambda-defaults', template);
86
- assert.deepEqual(
87
- noUndefined(template),
88
- fixtures.get('lambda-defaults'),
89
- 'expected resources generated using all default values'
90
- );
67
+ test('expected resources generated using all default values', () => {
68
+ const lambda = new cf.shortcuts.Lambda({
69
+ LogicalName: 'MyLambda',
70
+ Code: {
71
+ S3Bucket: 'my-code-bucket',
72
+ S3Key: 'path/to/code.zip'
73
+ }
74
+ });
91
75
 
92
- const lambdaWithRetain = new cf.shortcuts.Lambda({
93
- LogicalName: 'MyLambda',
94
- Code: { S3Bucket: 'my-code-bucket', S3Key: 'path/to/code.zip' },
95
- LogPolicyDeletionPolicy: 'Retain'
76
+ const template = cf.merge(lambda);
77
+ if (update) fixtures.update('lambda-defaults', template);
78
+ expect(noUndefined(template)).toEqual(fixtures.get('lambda-defaults'));
96
79
  });
97
- const templateWithRetain = cf.merge(lambdaWithRetain);
98
- assert.equal(
99
- templateWithRetain.Resources.MyLambdaLogPolicy.DeletionPolicy,
100
- 'Retain',
101
- 'LogPolicyDeletionPolicy=Retain sets DeletionPolicy on IAM Policy resource'
102
- );
103
80
 
104
- lambda = new cf.shortcuts.Lambda({
105
- LogicalName: 'MyLambda',
106
- Code: {
107
- ImageUri: 'MyImage'
108
- }
81
+ test('LogPolicyDeletionPolicy=Retain sets DeletionPolicy on IAM Policy resource', () => {
82
+ const lambdaWithRetain = new cf.shortcuts.Lambda({
83
+ LogicalName: 'MyLambda',
84
+ Code: { S3Bucket: 'my-code-bucket', S3Key: 'path/to/code.zip' },
85
+ LogPolicyDeletionPolicy: 'Retain'
86
+ });
87
+ const templateWithRetain = cf.merge(lambdaWithRetain);
88
+ expect(templateWithRetain.Resources.MyLambdaLogPolicy.DeletionPolicy).toBe('Retain');
109
89
  });
110
90
 
111
- template = cf.merge(lambda);
112
- if (update) fixtures.update('lambda-docker', template);
113
- assert.deepEqual(
114
- noUndefined(template),
115
- fixtures.get('lambda-docker'),
116
- 'expected resources generated using all default values and a docker image'
117
- );
91
+ test('expected resources generated using all default values and a docker image', () => {
92
+ const lambda = new cf.shortcuts.Lambda({
93
+ LogicalName: 'MyLambda',
94
+ Code: {
95
+ ImageUri: 'MyImage'
96
+ }
97
+ });
118
98
 
119
- lambda = new cf.shortcuts.Lambda({
120
- LogicalName: 'MyLambda',
121
- Code: {
122
- ZipFile: 'fake code'
123
- }
99
+ const template = cf.merge(lambda);
100
+ if (update) fixtures.update('lambda-docker', template);
101
+ expect(noUndefined(template)).toEqual(fixtures.get('lambda-docker'));
124
102
  });
125
103
 
126
- template = cf.merge(lambda);
127
- if (update) fixtures.update('lambda-zipfile', template);
128
- assert.deepEqual(
129
- noUndefined(template),
130
- fixtures.get('lambda-zipfile'),
131
- 'expected resources generated using all default values and inline code'
132
- );
104
+ test('expected resources generated using all default values and inline code', () => {
105
+ const lambda = new cf.shortcuts.Lambda({
106
+ LogicalName: 'MyLambda',
107
+ Code: {
108
+ ZipFile: 'fake code'
109
+ }
110
+ });
133
111
 
134
- lambda = new cf.shortcuts.Lambda({
135
- LogicalName: 'MyLambda',
136
- Code: {
137
- S3Bucket: 'my-code-bucket',
138
- S3Key: 'path/to/code.zip'
139
- },
140
- RoleArn: cf.getAtt('CustomLambdaRole', 'Arn')
141
- });
142
-
143
- template = cf.merge(lambda, {
144
- Resources: {
145
- 'CustomLambdaRole': {
146
- Type: 'AWS::IAM::Role',
147
- Properties: {
148
- AssumeRolePolicyDocument: {}
112
+ const template = cf.merge(lambda);
113
+ if (update) fixtures.update('lambda-zipfile', template);
114
+ expect(noUndefined(template)).toEqual(fixtures.get('lambda-zipfile'));
115
+ });
116
+
117
+ test('expected resources generated if RoleArn provided', () => {
118
+ const lambda = new cf.shortcuts.Lambda({
119
+ LogicalName: 'MyLambda',
120
+ Code: {
121
+ S3Bucket: 'my-code-bucket',
122
+ S3Key: 'path/to/code.zip'
123
+ },
124
+ RoleArn: cf.getAtt('CustomLambdaRole', 'Arn')
125
+ });
126
+
127
+ const template = cf.merge(lambda, {
128
+ Resources: {
129
+ 'CustomLambdaRole': {
130
+ Type: 'AWS::IAM::Role',
131
+ Properties: {
132
+ AssumeRolePolicyDocument: {}
133
+ }
149
134
  }
150
135
  }
151
- }
136
+ });
137
+ if (update) fixtures.update('lambda-provided-role', template);
138
+ expect(noUndefined(template)).toEqual(fixtures.get('lambda-provided-role'));
152
139
  });
153
- if (update) fixtures.update('lambda-provided-role', template);
154
- assert.deepEqual(
155
- noUndefined(template),
156
- fixtures.get('lambda-provided-role'),
157
- 'expected resources generated if RoleArn provided'
158
- );
159
140
 
160
- lambda = new cf.shortcuts.Lambda({
161
- LogicalName: 'MyLambda',
162
- Code: {
163
- S3Bucket: 'my-code-bucket',
164
- S3Key: 'path/to/code.zip'
165
- },
166
- DeadLetterConfig: {
167
- TargetArn: 'arn:aws:sqs:us-east-1:123456789012:queue/fake'
168
- },
169
- Description: 'my description',
170
- Environment: { Variables: { MyCoolEnv: 'a' } },
171
- FunctionName: 'my-function',
172
- Handler: 'index.something',
173
- KmsKeyArn: 'arn:aws:kms:us-east-1:123456789012:key/fake',
174
- Layers: ['arn:aws:lambda:us-east-2:590474943231:layer:AWS-Parameters-and-Secrets-Lambda-Extension:4'],
175
- MemorySize: 512,
176
- ReservedConcurrentExecutions: 10,
177
- Runtime: 'nodejs22.x',
178
- Tags: [{ Key: 'a', Value: 'b' }],
179
- Timeout: 30,
180
- TracingConfig: { Mode: 'Active' },
181
- VpcConfig: {
182
- SecurityGroupIds: ['sg-12345678'],
183
- SubnetIds: ['fake']
184
- },
185
- Condition: 'Always',
186
- DependsOn: 'AnotherThing',
187
- Statement: [
188
- {
189
- Effect: 'Allow',
190
- Action: 's3:GetObject',
191
- Resource: 'arn:aws:s3:::fake/data'
192
- }
193
- ],
194
- AlarmName: 'my-alarm',
195
- AlarmDescription: 'some alarm',
196
- AlarmActions: ['devnull@mapbox.com'],
197
- Period: 120,
198
- EvaluationPeriods: 2,
199
- Statistic: 'Minimum',
200
- Threshold: 10,
201
- ComparisonOperator: 'LessThanThreshold',
202
- TreatMissingData: 'breaching',
203
- EvaluateLowSampleCountPercentile: 'ignore',
204
- ExtendedStatistics: 'p100',
205
- OKActions: ['devnull@mapbox.com'],
206
- LogRetentionInDays: 30
207
- });
208
-
209
- template = cf.merge(
210
- { Conditions: { Always: cf.equals('1', '1') } },
211
- { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
212
- lambda
213
- );
214
- if (update) fixtures.update('lambda-full', template);
215
- assert.deepEqual(
216
- noUndefined(template),
217
- fixtures.get('lambda-full'),
218
- 'expected resources generated using no default values'
219
- );
141
+ test('expected resources generated using no default values', () => {
142
+ const lambda = new cf.shortcuts.Lambda({
143
+ LogicalName: 'MyLambda',
144
+ Code: {
145
+ S3Bucket: 'my-code-bucket',
146
+ S3Key: 'path/to/code.zip'
147
+ },
148
+ DeadLetterConfig: {
149
+ TargetArn: 'arn:aws:sqs:us-east-1:123456789012:queue/fake'
150
+ },
151
+ Description: 'my description',
152
+ Environment: { Variables: { MyCoolEnv: 'a' } },
153
+ FunctionName: 'my-function',
154
+ Handler: 'index.something',
155
+ KmsKeyArn: 'arn:aws:kms:us-east-1:123456789012:key/fake',
156
+ Layers: ['arn:aws:lambda:us-east-2:590474943231:layer:AWS-Parameters-and-Secrets-Lambda-Extension:4'],
157
+ MemorySize: 512,
158
+ ReservedConcurrentExecutions: 10,
159
+ Runtime: 'nodejs22.x',
160
+ Tags: [{ Key: 'a', Value: 'b' }],
161
+ Timeout: 30,
162
+ TracingConfig: { Mode: 'Active' },
163
+ VpcConfig: {
164
+ SecurityGroupIds: ['sg-12345678'],
165
+ SubnetIds: ['fake']
166
+ },
167
+ Condition: 'Always',
168
+ DependsOn: 'AnotherThing',
169
+ Statement: [
170
+ {
171
+ Effect: 'Allow',
172
+ Action: 's3:GetObject',
173
+ Resource: 'arn:aws:s3:::fake/data'
174
+ }
175
+ ],
176
+ AlarmName: 'my-alarm',
177
+ AlarmDescription: 'some alarm',
178
+ AlarmActions: ['devnull@mapbox.com'],
179
+ Period: 120,
180
+ EvaluationPeriods: 2,
181
+ Statistic: 'Minimum',
182
+ Threshold: 10,
183
+ ComparisonOperator: 'LessThanThreshold',
184
+ TreatMissingData: 'breaching',
185
+ EvaluateLowSampleCountPercentile: 'ignore',
186
+ ExtendedStatistics: 'p100',
187
+ OKActions: ['devnull@mapbox.com'],
188
+ LogRetentionInDays: 30
189
+ });
220
190
 
221
- lambda = new cf.shortcuts.Lambda({
222
- LogicalName: 'MyLambda',
223
- Code: {
224
- S3Bucket: 'my-code-bucket',
225
- S3Key: 'path/to/code.zip'
226
- },
227
- LogPolicyName: 'CustomLogPolicyName'
191
+ const template = cf.merge(
192
+ { Conditions: { Always: cf.equals('1', '1') } },
193
+ { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
194
+ lambda
195
+ );
196
+ if (update) fixtures.update('lambda-full', template);
197
+ expect(noUndefined(template)).toEqual(fixtures.get('lambda-full'));
228
198
  });
229
199
 
230
- template = cf.merge(lambda);
231
- assert.equal(
232
- template.Resources.MyLambdaLogPolicy.Properties.PolicyName,
233
- 'CustomLogPolicyName',
234
- 'LogPolicyName parameter correctly overrides the default policy name'
235
- );
200
+ test('LogPolicyName parameter correctly overrides the default policy name', () => {
201
+ const lambda = new cf.shortcuts.Lambda({
202
+ LogicalName: 'MyLambda',
203
+ Code: {
204
+ S3Bucket: 'my-code-bucket',
205
+ S3Key: 'path/to/code.zip'
206
+ },
207
+ LogPolicyName: 'CustomLogPolicyName'
208
+ });
236
209
 
237
- assert.end();
210
+ const template = cf.merge(lambda);
211
+ expect(template.Resources.MyLambdaLogPolicy.Properties.PolicyName).toBe('CustomLogPolicyName');
212
+ });
238
213
  });
239
214
 
240
- test('[shortcuts] queue-lambda', (assert) => {
241
- assert.throws(
242
- () => new cf.shortcuts.QueueLambda(),
243
- 'Options required',
244
- 'throws without options'
245
- );
246
- assert.throws(
247
- () => new cf.shortcuts.QueueLambda({}),
248
- /You must provide a LogicalName, and Code/,
249
- 'throws without basic lambda required parameters'
250
- );
215
+ describe('[shortcuts] queue-lambda', () => {
216
+ test('throws without options', () => {
217
+ expect(() => new cf.shortcuts.QueueLambda()).toThrow('Options required');
218
+ });
251
219
 
252
- assert.throws(
253
- () =>
254
- new cf.shortcuts.QueueLambda({
255
- LogicalName: 'MyLambda',
256
- Code: {
257
- S3Bucket: 'my-code-bucket',
258
- S3Key: 'path/to/code.zip'
259
- }
260
- }),
261
- /You must provide an EventSourceArn and ReservedConcurrentExecutions/,
262
- 'throws without queue-lambda required parameters'
263
- );
220
+ test('throws without basic lambda required parameters', () => {
221
+ expect(() => new cf.shortcuts.QueueLambda({})).toThrow(/You must provide a LogicalName, and Code/);
222
+ });
264
223
 
265
- assert.throws(
266
- () =>
267
- new cf.shortcuts.QueueLambda({
268
- LogicalName: 'MyLambda',
269
- Code: {
270
- S3Bucket: 'my-code-bucket',
271
- S3Key: 'path/to/code.zip'
272
- },
273
- EventSourceArn: 'arn:aws:sqs:us-east-1:123456789012:queue/fake',
274
- ReservedConcurrentExecutions: -1
275
- }),
276
- /ReservedConcurrentExecutions must be greater than or equal to 0/,
277
- 'throws when ReservedConcurrentExecutions is a negative number'
278
- );
224
+ test('throws without queue-lambda required parameters', () => {
225
+ expect(() => new cf.shortcuts.QueueLambda({
226
+ LogicalName: 'MyLambda',
227
+ Code: {
228
+ S3Bucket: 'my-code-bucket',
229
+ S3Key: 'path/to/code.zip'
230
+ }
231
+ })).toThrow(/You must provide an EventSourceArn and ReservedConcurrentExecutions/);
232
+ });
279
233
 
280
- const zeroLambda = new cf.shortcuts.QueueLambda({
281
- LogicalName: 'MyLambda',
282
- Code: {
283
- S3Bucket: 'my-code-bucket',
284
- S3Key: 'path/to/code.zip'
285
- },
286
- EventSourceArn: 'arn:aws:sqs:us-east-1:123456789012:queue/fake',
287
- ReservedConcurrentExecutions: 0
288
- });
289
- const zeroTemplate = cf.merge(zeroLambda);
290
- if (update) fixtures.update('queue-lambda-zero', zeroTemplate);
291
- assert.deepEqual(
292
- noUndefined(zeroTemplate),
293
- fixtures.get('queue-lambda-zero'),
294
- 'expected resources generated'
295
- );
234
+ test('throws when ReservedConcurrentExecutions is a negative number', () => {
235
+ expect(() => new cf.shortcuts.QueueLambda({
236
+ LogicalName: 'MyLambda',
237
+ Code: {
238
+ S3Bucket: 'my-code-bucket',
239
+ S3Key: 'path/to/code.zip'
240
+ },
241
+ EventSourceArn: 'arn:aws:sqs:us-east-1:123456789012:queue/fake',
242
+ ReservedConcurrentExecutions: -1
243
+ })).toThrow(/ReservedConcurrentExecutions must be greater than or equal to 0/);
244
+ });
296
245
 
297
- const lambda = new cf.shortcuts.QueueLambda({
298
- LogicalName: 'MyLambda',
299
- Code: {
300
- S3Bucket: 'my-code-bucket',
301
- S3Key: 'path/to/code.zip'
302
- },
303
- EventSourceArn: 'arn:aws:sqs:us-east-1:123456789012:queue/fake',
304
- ReservedConcurrentExecutions: 10
305
- });
306
-
307
- const template = cf.merge(lambda);
308
- if (update) fixtures.update('queue-lambda', template);
309
- assert.deepEqual(
310
- noUndefined(template),
311
- fixtures.get('queue-lambda'),
312
- 'expected resources generated'
313
- );
246
+ test('expected resources generated with zero concurrency', () => {
247
+ const zeroLambda = new cf.shortcuts.QueueLambda({
248
+ LogicalName: 'MyLambda',
249
+ Code: {
250
+ S3Bucket: 'my-code-bucket',
251
+ S3Key: 'path/to/code.zip'
252
+ },
253
+ EventSourceArn: 'arn:aws:sqs:us-east-1:123456789012:queue/fake',
254
+ ReservedConcurrentExecutions: 0
255
+ });
256
+ const zeroTemplate = cf.merge(zeroLambda);
257
+ if (update) fixtures.update('queue-lambda-zero', zeroTemplate);
258
+ expect(noUndefined(zeroTemplate)).toEqual(fixtures.get('queue-lambda-zero'));
259
+ });
260
+
261
+ test('expected resources generated', () => {
262
+ const lambda = new cf.shortcuts.QueueLambda({
263
+ LogicalName: 'MyLambda',
264
+ Code: {
265
+ S3Bucket: 'my-code-bucket',
266
+ S3Key: 'path/to/code.zip'
267
+ },
268
+ EventSourceArn: 'arn:aws:sqs:us-east-1:123456789012:queue/fake',
269
+ ReservedConcurrentExecutions: 10
270
+ });
314
271
 
315
- assert.end();
272
+ const template = cf.merge(lambda);
273
+ if (update) fixtures.update('queue-lambda', template);
274
+ expect(noUndefined(template)).toEqual(fixtures.get('queue-lambda'));
275
+ });
316
276
  });
317
277
 
318
- test('[shortcuts] scheduled-lambda', (assert) => {
319
- assert.throws(
320
- () => new cf.shortcuts.ScheduledLambda(),
321
- 'Options required',
322
- 'throws without options'
323
- );
324
- assert.throws(
325
- () => new cf.shortcuts.ScheduledLambda({}),
326
- /You must provide a LogicalName, and Code/,
327
- 'throws without basic lambda required parameters'
328
- );
278
+ describe('[shortcuts] scheduled-lambda', () => {
279
+ test('throws without options', () => {
280
+ expect(() => new cf.shortcuts.ScheduledLambda()).toThrow('Options required');
281
+ });
329
282
 
330
- assert.throws(
331
- () =>
332
- new cf.shortcuts.ScheduledLambda({
333
- LogicalName: 'MyLambda',
334
- Code: {
335
- S3Bucket: 'my-code-bucket',
336
- S3Key: 'path/to/code.zip'
337
- }
338
- }),
339
- /You must provide a ScheduleExpression/,
340
- 'throws without scheduled-lambda required parameters'
341
- );
283
+ test('throws without basic lambda required parameters', () => {
284
+ expect(() => new cf.shortcuts.ScheduledLambda({})).toThrow(/You must provide a LogicalName, and Code/);
285
+ });
342
286
 
343
- let lambda = new cf.shortcuts.ScheduledLambda({
344
- LogicalName: 'MyLambda',
345
- Code: {
346
- S3Bucket: 'my-code-bucket',
347
- S3Key: 'path/to/code.zip'
348
- },
349
- ScheduleExpression: 'rate(1 hour)'
350
- });
351
-
352
- let template = cf.merge(lambda);
353
- if (update) fixtures.update('scheduled-lambda-defaults', template);
354
- assert.deepEqual(
355
- noUndefined(template),
356
- fixtures.get('scheduled-lambda-defaults'),
357
- 'expected resources generated with defaults'
358
- );
287
+ test('throws without scheduled-lambda required parameters', () => {
288
+ expect(() => new cf.shortcuts.ScheduledLambda({
289
+ LogicalName: 'MyLambda',
290
+ Code: {
291
+ S3Bucket: 'my-code-bucket',
292
+ S3Key: 'path/to/code.zip'
293
+ }
294
+ })).toThrow(/You must provide a ScheduleExpression/);
295
+ });
359
296
 
360
- lambda = new cf.shortcuts.ScheduledLambda({
361
- LogicalName: 'MyLambda',
362
- Code: {
363
- S3Bucket: 'my-code-bucket',
364
- S3Key: 'path/to/code.zip'
365
- },
366
- ScheduleRoleArn: 'arn:aws:iam::012345678901:role/MyCoolRole',
367
- ScheduleGroupName: 'my-cool-stack',
368
- ScheduleExpression: 'rate(1 hour)',
369
- State: 'DISABLED'
370
- });
371
-
372
- template = cf.merge(lambda);
373
- if (update) fixtures.update('scheduled-lambda-full', template);
374
- assert.deepEqual(
375
- noUndefined(template),
376
- fixtures.get('scheduled-lambda-full'),
377
- 'expected resources generated without defaults'
378
- );
297
+ test('expected resources generated with defaults', () => {
298
+ const lambda = new cf.shortcuts.ScheduledLambda({
299
+ LogicalName: 'MyLambda',
300
+ Code: {
301
+ S3Bucket: 'my-code-bucket',
302
+ S3Key: 'path/to/code.zip'
303
+ },
304
+ ScheduleExpression: 'rate(1 hour)'
305
+ });
306
+
307
+ const template = cf.merge(lambda);
308
+ if (update) fixtures.update('scheduled-lambda-defaults', template);
309
+ expect(noUndefined(template)).toEqual(fixtures.get('scheduled-lambda-defaults'));
310
+ });
311
+
312
+ test('expected resources generated without defaults', () => {
313
+ const lambda = new cf.shortcuts.ScheduledLambda({
314
+ LogicalName: 'MyLambda',
315
+ Code: {
316
+ S3Bucket: 'my-code-bucket',
317
+ S3Key: 'path/to/code.zip'
318
+ },
319
+ ScheduleRoleArn: 'arn:aws:iam::012345678901:role/MyCoolRole',
320
+ ScheduleGroupName: 'my-cool-stack',
321
+ ScheduleExpression: 'rate(1 hour)',
322
+ State: 'DISABLED'
323
+ });
379
324
 
380
- assert.end();
325
+ const template = cf.merge(lambda);
326
+ if (update) fixtures.update('scheduled-lambda-full', template);
327
+ expect(noUndefined(template)).toEqual(fixtures.get('scheduled-lambda-full'));
328
+ });
381
329
  });
382
330
 
383
- test('[shortcuts] event-lambda', (assert) => {
384
- assert.throws(
385
- () => new cf.shortcuts.EventLambda(),
386
- 'Options required',
387
- 'throws without options'
388
- );
389
- assert.throws(
390
- () => new cf.shortcuts.EventLambda({}),
391
- /You must provide a LogicalName, and Code/,
392
- 'throws without basic lambda required parameters'
393
- );
331
+ describe('[shortcuts] event-lambda', () => {
332
+ test('throws without options', () => {
333
+ expect(() => new cf.shortcuts.EventLambda()).toThrow('Options required');
334
+ });
394
335
 
395
- assert.throws(
396
- () =>
397
- new cf.shortcuts.EventLambda({
398
- LogicalName: 'MyLambda',
399
- Code: {
400
- S3Bucket: 'my-code-bucket',
401
- S3Key: 'path/to/code.zip'
402
- }
403
- }),
404
- /You must provide an EventPattern/,
405
- 'throws without event-lambda required parameters'
406
- );
336
+ test('throws without basic lambda required parameters', () => {
337
+ expect(() => new cf.shortcuts.EventLambda({})).toThrow(/You must provide a LogicalName, and Code/);
338
+ });
407
339
 
408
- let lambda = new cf.shortcuts.EventLambda({
409
- LogicalName: 'MyLambda',
410
- Code: {
411
- S3Bucket: 'my-code-bucket',
412
- S3Key: 'path/to/code.zip'
413
- },
414
- EventPattern: {
415
- source: ['aws.ec2'],
416
- 'detail-type': ['EC2 Instance State-change Notification'],
417
- detail: {
418
- state: ['running']
340
+ test('throws without event-lambda required parameters', () => {
341
+ expect(() => new cf.shortcuts.EventLambda({
342
+ LogicalName: 'MyLambda',
343
+ Code: {
344
+ S3Bucket: 'my-code-bucket',
345
+ S3Key: 'path/to/code.zip'
419
346
  }
420
- }
347
+ })).toThrow(/You must provide an EventPattern/);
421
348
  });
422
349
 
423
- let template = cf.merge(lambda);
424
- if (update) fixtures.update('event-lambda-defaults', template);
425
- assert.deepEqual(
426
- noUndefined(template),
427
- fixtures.get('event-lambda-defaults'),
428
- 'expected resources generated with defaults'
429
- );
430
-
431
- lambda = new cf.shortcuts.EventLambda({
432
- LogicalName: 'MyLambda',
433
- Code: {
434
- S3Bucket: 'my-code-bucket',
435
- S3Key: 'path/to/code.zip'
436
- },
437
- EventPattern: {
438
- source: ['aws.ec2'],
439
- 'detail-type': ['EC2 Instance State-change Notification'],
440
- detail: {
441
- state: ['running']
350
+ test('expected resources generated with defaults', () => {
351
+ const lambda = new cf.shortcuts.EventLambda({
352
+ LogicalName: 'MyLambda',
353
+ Code: {
354
+ S3Bucket: 'my-code-bucket',
355
+ S3Key: 'path/to/code.zip'
356
+ },
357
+ EventPattern: {
358
+ source: ['aws.ec2'],
359
+ 'detail-type': ['EC2 Instance State-change Notification'],
360
+ detail: {
361
+ state: ['running']
362
+ }
442
363
  }
443
- },
444
- State: 'DISABLED'
364
+ });
365
+
366
+ const template = cf.merge(lambda);
367
+ if (update) fixtures.update('event-lambda-defaults', template);
368
+ expect(noUndefined(template)).toEqual(fixtures.get('event-lambda-defaults'));
445
369
  });
446
370
 
447
- template = cf.merge(lambda);
448
- if (update) fixtures.update('event-lambda-full', template);
449
- assert.deepEqual(
450
- noUndefined(template),
451
- fixtures.get('event-lambda-full'),
452
- 'expected resources generated without defaults'
453
- );
371
+ test('expected resources generated without defaults', () => {
372
+ const lambda = new cf.shortcuts.EventLambda({
373
+ LogicalName: 'MyLambda',
374
+ Code: {
375
+ S3Bucket: 'my-code-bucket',
376
+ S3Key: 'path/to/code.zip'
377
+ },
378
+ EventPattern: {
379
+ source: ['aws.ec2'],
380
+ 'detail-type': ['EC2 Instance State-change Notification'],
381
+ detail: {
382
+ state: ['running']
383
+ }
384
+ },
385
+ State: 'DISABLED'
386
+ });
454
387
 
455
- assert.end();
388
+ const template = cf.merge(lambda);
389
+ if (update) fixtures.update('event-lambda-full', template);
390
+ expect(noUndefined(template)).toEqual(fixtures.get('event-lambda-full'));
391
+ });
456
392
  });
457
393
 
458
- test('[shortcuts] stream-lambda', (assert) => {
459
- assert.throws(
460
- () => new cf.shortcuts.StreamLambda(),
461
- 'Options required',
462
- 'throws without options'
463
- );
464
- assert.throws(
465
- () => new cf.shortcuts.StreamLambda({}),
466
- /You must provide a LogicalName, and Code/,
467
- 'throws without basic lambda required parameters'
468
- );
394
+ describe('[shortcuts] stream-lambda', () => {
395
+ test('throws without options', () => {
396
+ expect(() => new cf.shortcuts.StreamLambda()).toThrow('Options required');
397
+ });
469
398
 
470
- assert.throws(
471
- () =>
472
- new cf.shortcuts.StreamLambda({
473
- LogicalName: 'MyLambda',
474
- Code: {
475
- S3Bucket: 'my-code-bucket',
476
- S3Key: 'path/to/code.zip'
477
- }
478
- }),
479
- /You must provide an EventSourceArn/,
480
- 'throws without stream-lambda required parameters'
481
- );
399
+ test('throws without basic lambda required parameters', () => {
400
+ expect(() => new cf.shortcuts.StreamLambda({})).toThrow(/You must provide a LogicalName, and Code/);
401
+ });
482
402
 
483
- let lambda = new cf.shortcuts.StreamLambda({
484
- LogicalName: 'MyLambda',
485
- Code: {
486
- S3Bucket: 'my-code-bucket',
487
- S3Key: 'path/to/code.zip'
488
- },
489
- EventSourceArn: 'arn:aws:kinesis:us-east-1:123456789012:stream/fake'
490
- });
491
-
492
- let template = cf.merge(lambda);
493
- if (update) fixtures.update('stream-lambda-defaults', template);
494
- assert.deepEqual(
495
- noUndefined(template),
496
- fixtures.get('stream-lambda-defaults'),
497
- 'expected resources generated via defaults'
498
- );
403
+ test('throws without stream-lambda required parameters', () => {
404
+ expect(() => new cf.shortcuts.StreamLambda({
405
+ LogicalName: 'MyLambda',
406
+ Code: {
407
+ S3Bucket: 'my-code-bucket',
408
+ S3Key: 'path/to/code.zip'
409
+ }
410
+ })).toThrow(/You must provide an EventSourceArn/);
411
+ });
499
412
 
500
- lambda = new cf.shortcuts.StreamLambda({
501
- LogicalName: 'MyLambda',
502
- Code: {
503
- S3Bucket: 'my-code-bucket',
504
- S3Key: 'path/to/code.zip'
505
- },
506
- EventSourceArn: 'arn:aws:kinesis:us-east-1:123456789012:stream/fake',
507
- FilterCriteria: {
508
- Filters: [
509
- {
510
- Pattern: JSON.stringify({ eventName: ['INSERT', 'MODIFY'] })
511
- }
512
- ]
513
- },
514
- BatchSize: 10000,
515
- MaximumBatchingWindowInSeconds: 300,
516
- Enabled: false,
517
- StartingPosition: 'TRIM_HORIZON'
518
- });
519
-
520
- template = cf.merge(lambda);
521
- if (update) fixtures.update('stream-lambda-no-defaults', template);
522
- assert.deepEqual(
523
- noUndefined(template),
524
- fixtures.get('stream-lambda-no-defaults'),
525
- 'expected resources generated without defaults'
526
- );
413
+ test('expected resources generated via defaults', () => {
414
+ const lambda = new cf.shortcuts.StreamLambda({
415
+ LogicalName: 'MyLambda',
416
+ Code: {
417
+ S3Bucket: 'my-code-bucket',
418
+ S3Key: 'path/to/code.zip'
419
+ },
420
+ EventSourceArn: 'arn:aws:kinesis:us-east-1:123456789012:stream/fake'
421
+ });
422
+
423
+ const template = cf.merge(lambda);
424
+ if (update) fixtures.update('stream-lambda-defaults', template);
425
+ expect(noUndefined(template)).toEqual(fixtures.get('stream-lambda-defaults'));
426
+ });
427
+
428
+ test('expected resources generated without defaults', () => {
429
+ const lambda = new cf.shortcuts.StreamLambda({
430
+ LogicalName: 'MyLambda',
431
+ Code: {
432
+ S3Bucket: 'my-code-bucket',
433
+ S3Key: 'path/to/code.zip'
434
+ },
435
+ EventSourceArn: 'arn:aws:kinesis:us-east-1:123456789012:stream/fake',
436
+ FilterCriteria: {
437
+ Filters: [
438
+ {
439
+ Pattern: JSON.stringify({ eventName: ['INSERT', 'MODIFY'] })
440
+ }
441
+ ]
442
+ },
443
+ BatchSize: 10000,
444
+ MaximumBatchingWindowInSeconds: 300,
445
+ Enabled: false,
446
+ StartingPosition: 'TRIM_HORIZON'
447
+ });
527
448
 
528
- assert.end();
449
+ const template = cf.merge(lambda);
450
+ if (update) fixtures.update('stream-lambda-no-defaults', template);
451
+ expect(noUndefined(template)).toEqual(fixtures.get('stream-lambda-no-defaults'));
452
+ });
529
453
  });
530
454
 
531
- test('[shortcuts] StreamLambda FilterCriteria', (assert) => {
532
- assert.throws(
533
- () => new cf.shortcuts.StreamLambda({
455
+ describe('[shortcuts] StreamLambda FilterCriteria', () => {
456
+ test('FilterCriteria must be a JSON-like object', () => {
457
+ expect(() => new cf.shortcuts.StreamLambda({
534
458
  LogicalName: 'MyLambda',
535
459
  Code: {
536
460
  S3Bucket: 'my-code-bucket',
@@ -538,11 +462,11 @@ test('[shortcuts] StreamLambda FilterCriteria', (assert) => {
538
462
  },
539
463
  EventSourceArn: 'arn:aws:kinesis:us-east-1:123456789012:stream/fake',
540
464
  FilterCriteria: ['test']
541
- }),
542
- '`FilterCriteria` must be a JSON-like object',
543
- );
544
- assert.throws(
545
- () => new cf.shortcuts.StreamLambda({
465
+ })).toThrow();
466
+ });
467
+
468
+ test('FilterCriteria must contain property Filter of type array', () => {
469
+ expect(() => new cf.shortcuts.StreamLambda({
546
470
  LogicalName: 'MyLambda',
547
471
  Code: {
548
472
  S3Bucket: 'my-code-bucket',
@@ -550,11 +474,11 @@ test('[shortcuts] StreamLambda FilterCriteria', (assert) => {
550
474
  },
551
475
  EventSourceArn: 'arn:aws:kinesis:us-east-1:123456789012:stream/fake',
552
476
  FilterCriteria: {}
553
- }),
554
- '`FilterCriteria` must contain property `Filter` of type array',
555
- );
556
- assert.throws(
557
- () => new cf.shortcuts.StreamLambda({
477
+ })).toThrow();
478
+ });
479
+
480
+ test('FilterCriteria.Filter must be an array', () => {
481
+ expect(() => new cf.shortcuts.StreamLambda({
558
482
  LogicalName: 'MyLambda',
559
483
  Code: {
560
484
  S3Bucket: 'my-code-bucket',
@@ -564,11 +488,11 @@ test('[shortcuts] StreamLambda FilterCriteria', (assert) => {
564
488
  FilterCriteria: {
565
489
  Filter: 613
566
490
  }
567
- }),
568
- '`FilterCriteria` must contain property `Filter` of type array',
569
- );
570
- assert.throws(
571
- () => new cf.shortcuts.StreamLambda({
491
+ })).toThrow();
492
+ });
493
+
494
+ test('FilterCriteria.Filter objects must have Pattern property', () => {
495
+ expect(() => new cf.shortcuts.StreamLambda({
572
496
  LogicalName: 'MyLambda',
573
497
  Code: {
574
498
  S3Bucket: 'my-code-bucket',
@@ -585,11 +509,11 @@ test('[shortcuts] StreamLambda FilterCriteria', (assert) => {
585
509
  }
586
510
  ]
587
511
  }
588
- }),
589
- 'An object in `FilterCriteria.Filter` was missing the required property `Pattern`',
590
- );
591
- assert.throws(
592
- () => new cf.shortcuts.StreamLambda({
512
+ })).toThrow();
513
+ });
514
+
515
+ test('FilterCriteria.Filter Pattern must be JSON parseable string', () => {
516
+ expect(() => new cf.shortcuts.StreamLambda({
593
517
  LogicalName: 'MyLambda',
594
518
  Code: {
595
519
  S3Bucket: 'my-code-bucket',
@@ -606,930 +530,1084 @@ test('[shortcuts] StreamLambda FilterCriteria', (assert) => {
606
530
  }
607
531
  ]
608
532
  }
609
- }),
610
- 'An object in `FilterCriteria.Filter` contains a `Pattern` property that is not a JSON parseable string',
611
- );
612
- assert.end();
533
+ })).toThrow();
534
+ });
535
+ });
536
+
537
+ describe('[shortcuts] log-subscription-lambda', () => {
538
+ test('throws without options', () => {
539
+ expect(() => new cf.shortcuts.LogSubscriptionLambda()).toThrow('Options required');
540
+ });
541
+
542
+ test('throws without basic lambda required parameters', () => {
543
+ expect(() => new cf.shortcuts.LogSubscriptionLambda({})).toThrow(/You must provide a LogicalName, and Code/);
544
+ });
545
+
546
+ test('throws without log-subscription-lambda required parameters', () => {
547
+ expect(() => new cf.shortcuts.LogSubscriptionLambda({
548
+ LogicalName: 'MyLambda',
549
+ Code: {
550
+ S3Bucket: 'my-code-bucket',
551
+ S3Key: 'path/to/code.zip'
552
+ }
553
+ })).toThrow(/You must provide a LogGroupName/);
554
+ });
555
+
556
+ test('expected resources generated via defaults', () => {
557
+ const lambda = new cf.shortcuts.LogSubscriptionLambda({
558
+ LogicalName: 'MyLambda',
559
+ Code: {
560
+ S3Bucket: 'my-code-bucket',
561
+ S3Key: 'path/to/code.zip'
562
+ },
563
+ LogGroupName: 'my-log-group'
564
+ });
565
+
566
+ const template = cf.merge(lambda);
567
+ if (update) fixtures.update('log-subscription-lambda-defaults', template);
568
+ expect(noUndefined(template)).toEqual(fixtures.get('log-subscription-lambda-defaults'));
569
+ });
570
+
571
+ test('expected resources generated without defaults', () => {
572
+ const lambda = new cf.shortcuts.LogSubscriptionLambda({
573
+ LogicalName: 'MyLambda',
574
+ Code: {
575
+ S3Bucket: 'my-code-bucket',
576
+ S3Key: 'path/to/code.zip'
577
+ },
578
+ FilterPattern: '{ $.errorCode = 400 }',
579
+ LogGroupName: 'my-log-group'
580
+ });
581
+
582
+ const template = cf.merge(lambda);
583
+ if (update) fixtures.update('log-subscription-lambda-no-defaults', template);
584
+ expect(noUndefined(template)).toEqual(fixtures.get('log-subscription-lambda-no-defaults'));
585
+ });
586
+ });
587
+
588
+ describe('[shortcuts] queue', () => {
589
+ test('throws without options', () => {
590
+ expect(() => new cf.shortcuts.Queue()).toThrow('Options required');
591
+ });
592
+
593
+ test('throws without required parameters', () => {
594
+ expect(() => new cf.shortcuts.Queue({})).toThrow(/You must provide a LogicalName/);
595
+ });
596
+
597
+ test('expected resources generated for full defaults', () => {
598
+ const queue = new cf.shortcuts.Queue({
599
+ LogicalName: 'MyQueue'
600
+ });
601
+
602
+ const template = cf.merge(queue);
603
+ if (update) fixtures.update('queue-defaults', template);
604
+ expect(noUndefined(template)).toEqual(fixtures.get('queue-defaults'));
605
+ });
606
+
607
+ test('expected resources generated no defaults', () => {
608
+ const queue = new cf.shortcuts.Queue({
609
+ LogicalName: 'MyQueue',
610
+ VisibilityTimeout: 60,
611
+ maxReceiveCount: 100,
612
+ DelaySeconds: 60,
613
+ KmsMasterKeyId: 'alias/my-key',
614
+ KmsDataKeyReusePeriondSeconds: 86400,
615
+ MaximumMessageSize: 1024,
616
+ MessageRetentionPeriod: 60,
617
+ QueueName: 'my-queue',
618
+ ReceiveMessageWaitTimeSeconds: 20,
619
+ Condition: 'Always',
620
+ DependsOn: 'AnotherThing',
621
+ TopicName: 'my-topic',
622
+ DisplayName: 'topic-display-name',
623
+ DeadLetterVisibilityTimeout: 60
624
+ });
625
+
626
+ const template = cf.merge(
627
+ { Conditions: { Always: cf.equals('1', '1') } },
628
+ { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
629
+ queue
630
+ );
631
+ if (update) fixtures.update('queue-full', template);
632
+ expect(noUndefined(template)).toEqual(fixtures.get('queue-full'));
633
+ });
634
+
635
+ test('expected resources generated for external topic', () => {
636
+ const queue = new cf.shortcuts.Queue({
637
+ LogicalName: 'MyQueue',
638
+ ExistingTopicArn: 'arn:aws:sns:us-east-1:111122223333:MyTopic'
639
+ });
640
+ const template = cf.merge(queue);
641
+ if (update) fixtures.update('queue-external-topic', template);
642
+ expect(noUndefined(template)).toEqual(fixtures.get('queue-external-topic'));
643
+ });
644
+
645
+ test('expected resources generated for external topic identified by ref', () => {
646
+ const queue = new cf.shortcuts.Queue({
647
+ LogicalName: 'MyQueue',
648
+ ExistingTopicArn: { Ref: 'TopicForOtherThing' }
649
+ });
650
+ const template = cf.merge(
651
+ { Resources: { TopicForOtherThing: { Type: 'AWS::SNS::Topic' } } },
652
+ queue
653
+ );
654
+ if (update) fixtures.update('queue-external-topic-ref', template);
655
+ expect(noUndefined(template)).toEqual(fixtures.get('queue-external-topic-ref'));
656
+ });
657
+
658
+ test('expected resources generated for FIFO queue', () => {
659
+ const queue = new cf.shortcuts.Queue({
660
+ LogicalName: 'MyFifoQueue',
661
+ FifoQueue: true
662
+ });
663
+ const template = cf.merge(queue);
664
+ if (update) fixtures.update('queue-fifo', template);
665
+ expect(noUndefined(template)).toEqual(fixtures.get('queue-fifo'));
666
+ });
667
+
668
+ test('expected resources generated for FIFO queue with specified QueueName', () => {
669
+ const queue = new cf.shortcuts.Queue({
670
+ LogicalName: 'MyFifoQueue',
671
+ QueueName: 'custom-and-fancy',
672
+ FifoQueue: true
673
+ });
674
+ const template = cf.merge(queue);
675
+ if (update) fixtures.update('queue-fifo-queuename', template);
676
+ expect(noUndefined(template)).toEqual(fixtures.get('queue-fifo-queuename'));
677
+ });
678
+
679
+ test('the FifoQueue value false is converted to undefined, to pass CFN validation', () => {
680
+ const queue = new cf.shortcuts.Queue({
681
+ LogicalName: 'MyFifoFalseQueue',
682
+ FifoQueue: false
683
+ });
684
+ const template = cf.merge(queue);
685
+ expect(template.Resources.MyFifoFalseQueue.Properties.FifoQueue).toBeUndefined();
686
+ });
687
+ });
688
+
689
+ describe('[shortcuts] s3 kinesis firehose', () => {
690
+ test('throws without options', () => {
691
+ expect(() => new cf.shortcuts.S3KinesisFirehose()).toThrow('Options required');
692
+ });
693
+
694
+ test('throws without required LogicalName parameter', () => {
695
+ expect(() => new cf.shortcuts.S3KinesisFirehose({})).toThrow(/You must provide a LogicalName/);
696
+ });
697
+
698
+ test('throws without required DestinationBucket parameter', () => {
699
+ expect(() => new cf.shortcuts.S3KinesisFirehose({
700
+ LogicalName: 'MyKinesisFirehose'
701
+ })).toThrow(/You must provide a DestinationBucket/);
702
+ });
703
+
704
+ test('expected resources generated for full defaults', () => {
705
+ const firehose = new cf.shortcuts.S3KinesisFirehose({
706
+ LogicalName: 'MyKinesisFirehose',
707
+ DestinationBucket: 'mah-bukkit'
708
+ });
709
+
710
+ const template = cf.merge(firehose);
711
+ if (update) fixtures.update('firehose-defaults', template);
712
+ expect(noUndefined(template)).toEqual(fixtures.get('firehose-defaults'));
713
+ });
714
+
715
+ test('expected resources generated with stream', () => {
716
+ const firehose = new cf.shortcuts.S3KinesisFirehose({
717
+ LogicalName: 'MyKinesisFirehose',
718
+ DestinationBucket: 'mah-bukkit',
719
+ KinesisStreamARN: 'arn:aws:kinesis:us-east-1:111122223333:stream/my-stream'
720
+ });
721
+
722
+ const template = cf.merge(
723
+ { Conditions: { Always: cf.equals('1', '1') } },
724
+ { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
725
+ firehose
726
+ );
727
+ if (update) fixtures.update('firehose-with-stream', template);
728
+ expect(noUndefined(template)).toEqual(fixtures.get('firehose-with-stream'));
729
+ });
730
+ });
731
+
732
+ describe('[shortcuts] role', () => {
733
+ test('throws without options', () => {
734
+ expect(() => new cf.shortcuts.Role()).toThrow('Options required');
735
+ });
736
+
737
+ test('throws without required parameters', () => {
738
+ expect(() => new cf.shortcuts.Role({})).toThrow(/You must provide a LogicalName and AssumeRolePrincipals/);
739
+ });
740
+
741
+ test('expected resources generated with defaults', () => {
742
+ const role = new cf.shortcuts.Role({
743
+ LogicalName: 'MyRole',
744
+ AssumeRolePrincipals: [{ Service: 'ec2.amazonaws.com' }]
745
+ });
746
+
747
+ const template = cf.merge(role);
748
+ if (update) fixtures.update('role-defaults', template);
749
+ expect(noUndefined(template)).toEqual(fixtures.get('role-defaults'));
750
+ });
751
+
752
+ test('expected resources generated without defaults', () => {
753
+ const role = new cf.shortcuts.Role({
754
+ LogicalName: 'MyRole',
755
+ AssumeRolePrincipals: [{ Service: 'ec2.amazonaws.com' }],
756
+ Statement: [
757
+ {
758
+ Effect: 'Allow',
759
+ Action: 's3:GetObject',
760
+ Resource: 'arn:aws:s3:::fake/data'
761
+ }
762
+ ],
763
+ ManagedPolicyArns: ['arn:aws:iam::123456789012:policy/fake'],
764
+ MaxSessionDuration: 3600,
765
+ Path: '/fake/',
766
+ RoleName: 'my-role',
767
+ Tags: [{ Key: 'pipeline-name', Value: 'test' }],
768
+ Condition: 'Always',
769
+ DependsOn: 'AnotherThing'
770
+ });
771
+
772
+ const template = cf.merge(
773
+ { Conditions: { Always: cf.equals('1', '1') } },
774
+ { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
775
+ role
776
+ );
777
+ if (update) fixtures.update('role-no-defaults', template);
778
+ expect(noUndefined(template)).toEqual(fixtures.get('role-no-defaults'));
779
+ });
613
780
  });
614
781
 
615
- test('[shortcuts] log-subscription-lambda', (assert) => {
616
- assert.throws(
617
- () => new cf.shortcuts.LogSubscriptionLambda(),
618
- 'Options required',
619
- 'throws without options'
620
- );
621
- assert.throws(
622
- () => new cf.shortcuts.LogSubscriptionLambda({}),
623
- /You must provide a LogicalName, and Code/,
624
- 'throws without basic lambda required parameters'
625
- );
782
+ describe('[shortcuts] cross-account role', () => {
783
+ test('throws without options', () => {
784
+ expect(() => new cf.shortcuts.CrossAccountRole()).toThrow('Options required');
785
+ });
626
786
 
627
- assert.throws(
628
- () =>
629
- new cf.shortcuts.LogSubscriptionLambda({
630
- LogicalName: 'MyLambda',
631
- Code: {
632
- S3Bucket: 'my-code-bucket',
633
- S3Key: 'path/to/code.zip'
634
- }
635
- }),
636
- /You must provide a LogGroupName/,
637
- 'throws without log-subscription-lambda required parameters'
638
- );
787
+ test('throws without required parameters', () => {
788
+ expect(() => new cf.shortcuts.CrossAccountRole({})).toThrow(/You must provide a LogicalName and Accounts/);
789
+ });
639
790
 
640
- let lambda = new cf.shortcuts.LogSubscriptionLambda({
641
- LogicalName: 'MyLambda',
642
- Code: {
643
- S3Bucket: 'my-code-bucket',
644
- S3Key: 'path/to/code.zip'
645
- },
646
- LogGroupName: 'my-log-group'
647
- });
648
-
649
- let template = cf.merge(lambda);
650
- if (update) fixtures.update('log-subscription-lambda-defaults', template);
651
- assert.deepEqual(
652
- noUndefined(template),
653
- fixtures.get('log-subscription-lambda-defaults'),
654
- 'expected resources generated via defaults'
655
- );
791
+ test('expected resources generated with defaults', () => {
792
+ const role = new cf.shortcuts.CrossAccountRole({
793
+ LogicalName: 'MyRole',
794
+ Accounts: [
795
+ '123456789012',
796
+ 'arn:aws:iam::123456789012:root',
797
+ { 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:root' }
798
+ ]
799
+ });
656
800
 
657
- lambda = new cf.shortcuts.LogSubscriptionLambda({
658
- LogicalName: 'MyLambda',
659
- Code: {
660
- S3Bucket: 'my-code-bucket',
661
- S3Key: 'path/to/code.zip'
662
- },
663
- FilterPattern: '{ $.errorCode = 400 }',
664
- LogGroupName: 'my-log-group'
665
- });
666
-
667
- template = cf.merge(lambda);
668
- if (update) fixtures.update('log-subscription-lambda-no-defaults', template);
669
- assert.deepEqual(
670
- noUndefined(template),
671
- fixtures.get('log-subscription-lambda-no-defaults'),
672
- 'expected resources generated without defaults'
673
- );
801
+ const template = cf.merge(role);
802
+ if (update) fixtures.update('cross-account-role-defaults', template);
803
+ expect(noUndefined(template)).toEqual(fixtures.get('cross-account-role-defaults'));
804
+ });
674
805
 
675
- assert.end();
676
- });
806
+ test('expected resources generated without defaults', () => {
807
+ const role = new cf.shortcuts.CrossAccountRole({
808
+ LogicalName: 'MyRole',
809
+ Accounts: [
810
+ '123456789012',
811
+ 'arn:aws:iam::123456789012:root',
812
+ { 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:root' }
813
+ ],
814
+ Statement: [
815
+ {
816
+ Effect: 'Allow',
817
+ Action: 's3:GetObject',
818
+ Resource: 'arn:aws:s3:::fake/data'
819
+ }
820
+ ],
821
+ ManagedPolicyArns: ['arn:aws:iam::123456789012:policy/fake'],
822
+ MaxSessionDuration: 3600,
823
+ Path: '/fake/',
824
+ RoleName: 'my-role',
825
+ Condition: 'Always',
826
+ DependsOn: 'AnotherThing'
827
+ });
677
828
 
678
- test('[shortcuts] queue', (assert) => {
679
- assert.throws(
680
- () => new cf.shortcuts.Queue(),
681
- 'Options required',
682
- 'throws without options'
683
- );
684
- assert.throws(
685
- () => new cf.shortcuts.Queue({}),
686
- /You must provide a LogicalName/,
687
- 'throws without required parameters'
688
- );
829
+ const template = cf.merge(
830
+ { Conditions: { Always: cf.equals('1', '1') } },
831
+ { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
832
+ role
833
+ );
834
+ if (update) fixtures.update('cross-account-role-no-defaults', template);
835
+ expect(noUndefined(template)).toEqual(fixtures.get('cross-account-role-no-defaults'));
836
+ });
837
+ });
689
838
 
690
- let queue = new cf.shortcuts.Queue({
691
- LogicalName: 'MyQueue'
839
+ describe('[shortcuts] service role', () => {
840
+ test('throws without options', () => {
841
+ expect(() => new cf.shortcuts.ServiceRole()).toThrow('Options required');
692
842
  });
693
843
 
694
- let template = cf.merge(queue);
695
- if (update) fixtures.update('queue-defaults', template);
696
- assert.deepEqual(
697
- noUndefined(template),
698
- fixtures.get('queue-defaults'),
699
- 'expected resources generated for full defaults'
700
- );
844
+ test('throws without required parameters', () => {
845
+ expect(() => new cf.shortcuts.ServiceRole({})).toThrow(/You must provide a LogicalName and Service/);
846
+ });
701
847
 
702
- queue = new cf.shortcuts.Queue({
703
- LogicalName: 'MyQueue',
704
- VisibilityTimeout: 60,
705
- maxReceiveCount: 100,
706
- DelaySeconds: 60,
707
- KmsMasterKeyId: 'alias/my-key',
708
- KmsDataKeyReusePeriondSeconds: 86400,
709
- MaximumMessageSize: 1024,
710
- MessageRetentionPeriod: 60,
711
- QueueName: 'my-queue',
712
- ReceiveMessageWaitTimeSeconds: 20,
713
- Condition: 'Always',
714
- DependsOn: 'AnotherThing',
715
- TopicName: 'my-topic',
716
- DisplayName: 'topic-display-name',
717
- DeadLetterVisibilityTimeout: 60
718
- });
719
-
720
- template = cf.merge(
721
- { Conditions: { Always: cf.equals('1', '1') } },
722
- { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
723
- queue
724
- );
725
- if (update) fixtures.update('queue-full', template);
726
- assert.deepEqual(
727
- noUndefined(template),
728
- fixtures.get('queue-full'),
729
- 'expected resources generated no defaults'
730
- );
848
+ test('expected resources generated with defaults', () => {
849
+ const role = new cf.shortcuts.ServiceRole({
850
+ LogicalName: 'MyRole',
851
+ Service: 'lambda'
852
+ });
731
853
 
732
- queue = new cf.shortcuts.Queue({
733
- LogicalName: 'MyQueue',
734
- ExistingTopicArn: 'arn:aws:sns:us-east-1:111122223333:MyTopic'
854
+ const template = cf.merge(role);
855
+ if (update) fixtures.update('service-role-defaults', template);
856
+ expect(noUndefined(template)).toEqual(fixtures.get('service-role-defaults'));
735
857
  });
736
- template = cf.merge(queue);
737
- if (update) fixtures.update('queue-external-topic', template);
738
- assert.deepEqual(
739
- noUndefined(template),
740
- fixtures.get('queue-external-topic'),
741
- 'expected resources generated for external topic'
742
- );
743
858
 
744
- queue = new cf.shortcuts.Queue({
745
- LogicalName: 'MyQueue',
746
- ExistingTopicArn: { Ref: 'TopicForOtherThing' }
859
+ test('expected resources generated, service for which AWS::URLSuffix is invalid', () => {
860
+ const role = new cf.shortcuts.ServiceRole({
861
+ LogicalName: 'MyRole',
862
+ Service: 'lambda.amazonaws.com'
863
+ });
864
+
865
+ const template = cf.merge(role);
866
+ if (update) fixtures.update('service-role-no-url-suffix', template);
867
+ expect(noUndefined(template)).toEqual(fixtures.get('service-role-no-url-suffix'));
747
868
  });
748
- template = cf.merge(
749
- { Resources: { TopicForOtherThing: { Type: 'AWS::SNS::Topic' } } },
750
- queue
751
- );
752
- if (update) fixtures.update('queue-external-topic-ref', template);
753
- assert.deepEqual(
754
- noUndefined(template),
755
- fixtures.get('queue-external-topic-ref'),
756
- 'expected resources generated for external topic identified by ref'
757
- );
758
869
 
759
- queue = new cf.shortcuts.Queue({
760
- LogicalName: 'MyFifoQueue',
761
- FifoQueue: true
870
+ test('expected resources generated, service for which AWS::URLSuffix is valid', () => {
871
+ const role = new cf.shortcuts.ServiceRole({
872
+ LogicalName: 'MyRole',
873
+ Service: 'ec2'
874
+ });
875
+
876
+ const template = cf.merge(role);
877
+ if (update) fixtures.update('service-role-url-suffix', template);
878
+ expect(noUndefined(template)).toEqual(fixtures.get('service-role-url-suffix'));
762
879
  });
763
- template = cf.merge(queue);
764
- if (update) fixtures.update('queue-fifo', template);
765
- assert.deepEqual(
766
- noUndefined(template),
767
- fixtures.get('queue-fifo'),
768
- 'expected resources generated for FIFO queue'
769
- );
770
880
 
771
- queue = new cf.shortcuts.Queue({
772
- LogicalName: 'MyFifoQueue',
773
- QueueName: 'custom-and-fancy',
774
- FifoQueue: true
775
- });
776
- template = cf.merge(queue);
777
- if (update) fixtures.update('queue-fifo-queuename', template);
778
- assert.deepEqual(
779
- noUndefined(template),
780
- fixtures.get('queue-fifo-queuename'),
781
- 'expected resources generated for FIFO queue with specified QueueName'
782
- );
881
+ test('expected resources generated, service for which AWS::URLSuffix is invalid specified with a suffix', () => {
882
+ const role = new cf.shortcuts.ServiceRole({
883
+ LogicalName: 'MyRole',
884
+ Service: 'ec2.amazonaws.com'
885
+ });
783
886
 
784
- queue = new cf.shortcuts.Queue({
785
- LogicalName: 'MyFifoFalseQueue',
786
- FifoQueue: false
887
+ const template = cf.merge(role);
888
+ if (update) fixtures.update('service-role-url-suffix-with-replacement', template);
889
+ expect(noUndefined(template)).toEqual(fixtures.get('service-role-url-suffix-with-replacement'));
787
890
  });
788
- template = cf.merge(queue);
789
- assert.equal(
790
- template.Resources.MyFifoFalseQueue.Properties.FifoQueue,
791
- undefined,
792
- 'the FifoQueue value false is converted to undefined, to pass CFN validation'
793
- );
794
891
 
795
- assert.end();
796
- });
892
+ test('expected resources generated without defaults', () => {
893
+ const role = new cf.shortcuts.ServiceRole({
894
+ LogicalName: 'MyRole',
895
+ Service: 'lambda.amazonaws.com',
896
+ Statement: [
897
+ {
898
+ Effect: 'Allow',
899
+ Action: 's3:GetObject',
900
+ Resource: 'arn:aws:s3:::fake/data'
901
+ }
902
+ ],
903
+ ManagedPolicyArns: ['arn:aws:iam::123456789012:policy/fake'],
904
+ MaxSessionDuration: 3600,
905
+ Path: '/fake/',
906
+ RoleName: 'my-role',
907
+ Condition: 'Always',
908
+ DependsOn: 'AnotherThing'
909
+ });
797
910
 
798
- test('[shortcuts] s3 kinesis firehose', (assert) => {
799
- assert.throws(
800
- () => new cf.shortcuts.S3KinesisFirehose(),
801
- 'Options required',
802
- 'throws without options'
803
- );
804
- assert.throws(
805
- () => new cf.shortcuts.S3KinesisFirehose({}),
806
- /You must provide a LogicalName/,
807
- 'throws without required LogicalName parameter'
808
- );
911
+ const template = cf.merge(
912
+ { Conditions: { Always: cf.equals('1', '1') } },
913
+ { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
914
+ role
915
+ );
916
+ if (update) fixtures.update('service-role-no-defaults', template);
917
+ expect(noUndefined(template)).toEqual(fixtures.get('service-role-no-defaults'));
918
+ });
919
+ });
809
920
 
810
- assert.throws(
811
- () => new cf.shortcuts.S3KinesisFirehose({
812
- LogicalName: 'MyKinesisFirehose'
813
- }),
814
- /You must provide a DestinationBucket/,
815
- 'throws without required DestinationBucket parameter'
816
- );
921
+ describe('[shortcuts] glue database', () => {
922
+ test('throws without options', () => {
923
+ expect(() => new cf.shortcuts.GlueDatabase()).toThrow('Options required');
924
+ });
817
925
 
818
- let firehose = new cf.shortcuts.S3KinesisFirehose({
819
- LogicalName: 'MyKinesisFirehose',
820
- DestinationBucket: 'mah-bukkit'
926
+ test('throws without required parameters', () => {
927
+ expect(() => new cf.shortcuts.GlueDatabase({})).toThrow(/You must provide a LogicalName and Name/);
821
928
  });
822
929
 
823
- let template = cf.merge(firehose);
824
- if (update) fixtures.update('firehose-defaults', template);
825
- assert.deepEqual(
826
- noUndefined(template),
827
- fixtures.get('firehose-defaults'),
828
- 'expected resources generated for full defaults'
829
- );
930
+ test('expected resources generated with defaults', () => {
931
+ const db = new cf.shortcuts.GlueDatabase({
932
+ LogicalName: 'MyDatabase',
933
+ Name: 'my_database'
934
+ });
830
935
 
831
- firehose = new cf.shortcuts.S3KinesisFirehose({
832
- LogicalName: 'MyKinesisFirehose',
833
- DestinationBucket: 'mah-bukkit',
834
- KinesisStreamARN: 'arn:aws:kinesis:us-east-1:111122223333:stream/my-stream'
936
+ const template = cf.merge(db);
937
+ if (update) fixtures.update('glue-database-defaults', template);
938
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-database-defaults'));
835
939
  });
836
940
 
837
- template = cf.merge(
838
- { Conditions: { Always: cf.equals('1', '1') } },
839
- { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
840
- firehose
841
- );
842
- if (update) fixtures.update('firehose-with-stream', template);
843
- assert.deepEqual(
844
- noUndefined(template),
845
- fixtures.get('firehose-with-stream'),
846
- 'expected resources generated with stream'
847
- );
941
+ test('expected resources generated without defaults', () => {
942
+ const db = new cf.shortcuts.GlueDatabase({
943
+ LogicalName: 'MyDatabase',
944
+ Name: 'my_database',
945
+ CatalogId: '123456',
946
+ Description: 'my_database description',
947
+ LocationUri: 'fakeuri',
948
+ Parameters: { thing: 'a' },
949
+ Condition: 'Always',
950
+ DependsOn: 'AnotherThing'
951
+ });
848
952
 
849
- assert.end();
953
+ const template = cf.merge(
954
+ { Conditions: { Always: cf.equals('1', '1') } },
955
+ { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
956
+ db
957
+ );
958
+ if (update) fixtures.update('glue-database-no-defaults', template);
959
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-database-no-defaults'));
960
+ });
850
961
  });
851
962
 
852
- test('[shortcuts] role', (assert) => {
853
- assert.throws(
854
- () => new cf.shortcuts.Role(),
855
- 'Options required',
856
- 'throws without options'
857
- );
858
- assert.throws(
859
- () => new cf.shortcuts.Role({}),
860
- /You must provide a LogicalName and AssumeRolePrincipals/,
861
- 'throws without required parameters'
862
- );
963
+ describe('[shortcuts] glue table', () => {
964
+ test('throws without options', () => {
965
+ expect(() => new cf.shortcuts.GlueTable()).toThrow('Options required');
966
+ });
863
967
 
864
- let role = new cf.shortcuts.Role({
865
- LogicalName: 'MyRole',
866
- AssumeRolePrincipals: [{ Service: 'ec2.amazonaws.com' }]
968
+ test('throws without required parameters', () => {
969
+ expect(() => new cf.shortcuts.GlueTable({})).toThrow(/You must provide a LogicalName, Name, DatabaseName, and Columns/);
867
970
  });
868
971
 
869
- let template = cf.merge(role);
870
- if (update) fixtures.update('role-defaults', template);
871
- assert.deepEqual(
872
- noUndefined(template),
873
- fixtures.get('role-defaults'),
874
- 'expected resources generated with defaults'
875
- );
972
+ test('expected resources generated with defaults', () => {
973
+ const db = new cf.shortcuts.GlueTable({
974
+ LogicalName: 'MyTable',
975
+ DatabaseName: 'my_database',
976
+ Name: 'my_table',
977
+ Columns: [
978
+ { Name: 'column', Type: 'string' }
979
+ ]
980
+ });
876
981
 
877
- role = new cf.shortcuts.Role({
878
- LogicalName: 'MyRole',
879
- AssumeRolePrincipals: [{ Service: 'ec2.amazonaws.com' }],
880
- Statement: [
881
- {
882
- Effect: 'Allow',
883
- Action: 's3:GetObject',
884
- Resource: 'arn:aws:s3:::fake/data'
885
- }
886
- ],
887
- ManagedPolicyArns: ['arn:aws:iam::123456789012:policy/fake'],
888
- MaxSessionDuration: 3600,
889
- Path: '/fake/',
890
- RoleName: 'my-role',
891
- Tags: [{ Key: 'pipeline-name', Value: 'test' }],
892
- Condition: 'Always',
893
- DependsOn: 'AnotherThing'
894
- });
895
-
896
- template = cf.merge(
897
- { Conditions: { Always: cf.equals('1', '1') } },
898
- { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
899
- role
900
- );
901
- if (update) fixtures.update('role-no-defaults', template);
902
- assert.deepEqual(
903
- noUndefined(template),
904
- fixtures.get('role-no-defaults'),
905
- 'expected resources generated without defaults'
906
- );
982
+ const template = cf.merge(db);
983
+ if (update) fixtures.update('glue-table-defaults', template);
984
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-table-defaults'));
985
+ });
986
+
987
+ test('expected resources generated without defaults', () => {
988
+ const db = new cf.shortcuts.GlueTable({
989
+ LogicalName: 'MyTable',
990
+ DatabaseName: 'my_database',
991
+ Name: 'my_table',
992
+ Columns: [
993
+ { Name: 'column', Type: 'string' }
994
+ ],
995
+ CatalogId: '1234',
996
+ Owner: 'Team',
997
+ Parameters: { table: 'params' },
998
+ Description: 'my_table description',
999
+ Retention: 12,
1000
+ TableType: 'EXTERNAL_TABLE',
1001
+ ViewExpandedText: '/* Presto View */',
1002
+ ViewOriginalText: '/* Presto View: abc123= */',
1003
+ BucketColumns: ['column'],
1004
+ Compressed: true,
1005
+ InputFormat: 'fake.input.format',
1006
+ Location: 's3://fake/location',
1007
+ OutputFormat: 'fake.output.format',
1008
+ StorageParameters: { storage: 'parameters' },
1009
+ SerdeInfo: {
1010
+ SerializationLibrary: 'fake.serde'
1011
+ },
1012
+ SkewedColumns: {
1013
+ SkewedColumnNames: ['column'],
1014
+ SkewedColumnValueLocationMap: { fake: 'map' },
1015
+ SkewedColumnValues: ['value']
1016
+ },
1017
+ SortColumns: [
1018
+ { Column: 'column', SortOrder: 0 }
1019
+ ],
1020
+ StoredAsSubdirectory: true,
1021
+ Condition: 'Always',
1022
+ DependsOn: 'AnotherThing'
1023
+ });
907
1024
 
908
- assert.end();
1025
+ const template = cf.merge(
1026
+ { Conditions: { Always: cf.equals('1', '1') } },
1027
+ { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
1028
+ db
1029
+ );
1030
+ if (update) fixtures.update('glue-table-no-defaults', template);
1031
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-table-no-defaults'));
1032
+ });
909
1033
  });
910
1034
 
911
- test('[shortcuts] cross-account role', (assert) => {
912
- assert.throws(
913
- () => new cf.shortcuts.CrossAccountRole(),
914
- 'Options required',
915
- 'throws without options'
916
- );
917
- assert.throws(
918
- () => new cf.shortcuts.CrossAccountRole({}),
919
- /You must provide a LogicalName and Accounts/,
920
- 'throws without required parameters'
921
- );
1035
+ describe('[shortcuts] glue json table', () => {
1036
+ test('throws without options', () => {
1037
+ expect(() => new cf.shortcuts.GlueJsonTable()).toThrow('Options required');
1038
+ });
922
1039
 
923
- let role = new cf.shortcuts.CrossAccountRole({
924
- LogicalName: 'MyRole',
925
- Accounts: [
926
- '123456789012',
927
- 'arn:aws:iam::123456789012:root',
928
- { 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:root' }
929
- ]
930
- });
931
-
932
- let template = cf.merge(role);
933
- if (update) fixtures.update('cross-account-role-defaults', template);
934
- assert.deepEqual(
935
- noUndefined(template),
936
- fixtures.get('cross-account-role-defaults'),
937
- 'expected resources generated with defaults'
938
- );
1040
+ test('throws without required parameters', () => {
1041
+ expect(() => new cf.shortcuts.GlueJsonTable({})).toThrow(/You must provide a Location/);
1042
+ });
939
1043
 
940
- role = new cf.shortcuts.CrossAccountRole({
941
- LogicalName: 'MyRole',
942
- Accounts: [
943
- '123456789012',
944
- 'arn:aws:iam::123456789012:root',
945
- { 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:root' }
946
- ],
947
- Statement: [
948
- {
949
- Effect: 'Allow',
950
- Action: 's3:GetObject',
951
- Resource: 'arn:aws:s3:::fake/data'
952
- }
953
- ],
954
- ManagedPolicyArns: ['arn:aws:iam::123456789012:policy/fake'],
955
- MaxSessionDuration: 3600,
956
- Path: '/fake/',
957
- RoleName: 'my-role',
958
- Condition: 'Always',
959
- DependsOn: 'AnotherThing'
960
- });
961
-
962
- template = cf.merge(
963
- { Conditions: { Always: cf.equals('1', '1') } },
964
- { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
965
- role
966
- );
967
- if (update) fixtures.update('cross-account-role-no-defaults', template);
968
- assert.deepEqual(
969
- noUndefined(template),
970
- fixtures.get('cross-account-role-no-defaults'),
971
- 'expected resources generated without defaults'
972
- );
1044
+ test('expected resources generated with defaults', () => {
1045
+ const db = new cf.shortcuts.GlueJsonTable({
1046
+ LogicalName: 'MyTable',
1047
+ DatabaseName: 'my_database',
1048
+ Name: 'my_table',
1049
+ Columns: [
1050
+ { Name: 'column', Type: 'string' }
1051
+ ],
1052
+ Location: 's3://fake/location'
1053
+ });
1054
+
1055
+ const template = cf.merge(db);
1056
+ if (update) fixtures.update('glue-json-table-defaults', template);
1057
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-json-table-defaults'));
1058
+ });
1059
+
1060
+ test('expected resources generated without defaults', () => {
1061
+ const db = new cf.shortcuts.GlueJsonTable({
1062
+ LogicalName: 'MyTable',
1063
+ DatabaseName: 'my_database',
1064
+ Name: 'my_table',
1065
+ Columns: [
1066
+ { Name: 'column', Type: 'string' }
1067
+ ],
1068
+ CatalogId: '1234',
1069
+ Owner: 'Team',
1070
+ Parameters: { table: 'params' },
1071
+ Description: 'my_table description',
1072
+ Retention: 12,
1073
+ TableType: 'EXTERNAL_TABLE',
1074
+ ViewExpandedText: '/* Presto View */',
1075
+ ViewOriginalText: '/* Presto View: abc123= */',
1076
+ BucketColumns: ['column'],
1077
+ Compressed: true,
1078
+ Location: 's3://fake/location',
1079
+ InputFormat: 'fake.input.format',
1080
+ OutputFormat: 'fake.output.format',
1081
+ StorageParameters: { storage: 'parameters' },
1082
+ SerdeInfo: {
1083
+ SerializationLibrary: 'fake.serde'
1084
+ },
1085
+ SkewedColumns: {
1086
+ SkewedColumnNames: ['column'],
1087
+ SkewedColumnValueLocationMap: { fake: 'map' },
1088
+ SkewedColumnValues: ['value']
1089
+ },
1090
+ SortColumns: [
1091
+ { Column: 'column', SortOrder: 0 }
1092
+ ],
1093
+ StoredAsSubdirectory: true,
1094
+ Condition: 'Always',
1095
+ DependsOn: 'AnotherThing'
1096
+ });
973
1097
 
974
- assert.end();
1098
+ const template = cf.merge(
1099
+ { Conditions: { Always: cf.equals('1', '1') } },
1100
+ { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
1101
+ db
1102
+ );
1103
+ if (update) fixtures.update('glue-json-table-no-defaults', template);
1104
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-json-table-no-defaults'));
1105
+ });
975
1106
  });
976
1107
 
977
- test('[shortcuts] service role', (assert) => {
978
- assert.throws(
979
- () => new cf.shortcuts.ServiceRole(),
980
- 'Options required',
981
- 'throws without options'
982
- );
983
- assert.throws(
984
- () => new cf.shortcuts.ServiceRole({}),
985
- /You must provide a LogicalName and Service/,
986
- 'throws without required parameters'
987
- );
1108
+ describe('[shortcuts] glue orc table', () => {
1109
+ test('throws without options', () => {
1110
+ expect(() => new cf.shortcuts.GlueOrcTable()).toThrow('Options required');
1111
+ });
988
1112
 
989
- let role = new cf.shortcuts.ServiceRole({
990
- LogicalName: 'MyRole',
991
- Service: 'lambda'
1113
+ test('throws without required parameters', () => {
1114
+ expect(() => new cf.shortcuts.GlueOrcTable({})).toThrow(/You must provide a Location/);
992
1115
  });
993
1116
 
994
- let template = cf.merge(role);
995
- if (update) fixtures.update('service-role-defaults', template);
996
- assert.deepEqual(
997
- noUndefined(template),
998
- fixtures.get('service-role-defaults'),
999
- 'expected resources generated with defaults'
1000
- );
1117
+ test('expected resources generated with defaults', () => {
1118
+ const db = new cf.shortcuts.GlueOrcTable({
1119
+ LogicalName: 'MyTable',
1120
+ DatabaseName: 'my_database',
1121
+ Name: 'my_table',
1122
+ Columns: [
1123
+ { Name: 'column', Type: 'string' }
1124
+ ],
1125
+ Location: 's3://fake/location'
1126
+ });
1001
1127
 
1002
- role = new cf.shortcuts.ServiceRole({
1003
- LogicalName: 'MyRole',
1004
- Service: 'lambda.amazonaws.com'
1128
+ const template = cf.merge(db);
1129
+ if (update) fixtures.update('glue-orc-table-defaults', template);
1130
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-orc-table-defaults'));
1005
1131
  });
1006
1132
 
1007
- template = cf.merge(role);
1008
- if (update) fixtures.update('service-role-no-url-suffix', template);
1009
- assert.deepEqual(
1010
- noUndefined(template),
1011
- fixtures.get('service-role-no-url-suffix'),
1012
- 'expected resources generated, service for which AWS::URLSuffix is invalid'
1013
- );
1133
+ test('expected resources generated without defaults', () => {
1134
+ const db = new cf.shortcuts.GlueOrcTable({
1135
+ LogicalName: 'MyTable',
1136
+ DatabaseName: 'my_database',
1137
+ Name: 'my_table',
1138
+ Columns: [
1139
+ { Name: 'column', Type: 'string' }
1140
+ ],
1141
+ CatalogId: '1234',
1142
+ Owner: 'Team',
1143
+ Parameters: { table: 'params' },
1144
+ Description: 'my_table description',
1145
+ Retention: 12,
1146
+ TableType: 'EXTERNAL_TABLE',
1147
+ ViewExpandedText: '/* Presto View */',
1148
+ ViewOriginalText: '/* Presto View: abc123= */',
1149
+ BucketColumns: ['column'],
1150
+ Compressed: true,
1151
+ Location: 's3://fake/location',
1152
+ InputFormat: 'fake.input.format',
1153
+ OutputFormat: 'fake.output.format',
1154
+ StorageParameters: { storage: 'parameters' },
1155
+ SerdeInfo: {
1156
+ SerializationLibrary: 'fake.serde'
1157
+ },
1158
+ SkewedColumns: {
1159
+ SkewedColumnNames: ['column'],
1160
+ SkewedColumnValueLocationMap: { fake: 'map' },
1161
+ SkewedColumnValues: ['value']
1162
+ },
1163
+ SortColumns: [
1164
+ { Column: 'column', SortOrder: 0 }
1165
+ ],
1166
+ StoredAsSubdirectory: true,
1167
+ Condition: 'Always',
1168
+ DependsOn: 'AnotherThing'
1169
+ });
1014
1170
 
1015
- role = new cf.shortcuts.ServiceRole({
1016
- LogicalName: 'MyRole',
1017
- Service: 'ec2'
1171
+ const template = cf.merge(
1172
+ { Conditions: { Always: cf.equals('1', '1') } },
1173
+ { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
1174
+ db
1175
+ );
1176
+ if (update) fixtures.update('glue-orc-table-no-defaults', template);
1177
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-orc-table-no-defaults'));
1018
1178
  });
1179
+ });
1019
1180
 
1020
- template = cf.merge(role);
1021
- if (update) fixtures.update('service-role-url-suffix', template);
1022
- assert.deepEqual(
1023
- noUndefined(template),
1024
- fixtures.get('service-role-url-suffix'),
1025
- 'expected resources generated, service for which AWS::URLSuffix is invalid'
1026
- );
1181
+ describe('[shortcuts] glue parquet table', () => {
1182
+ test('throws without options', () => {
1183
+ expect(() => new cf.shortcuts.GlueParquetTable()).toThrow('Options required');
1184
+ });
1027
1185
 
1028
- role = new cf.shortcuts.ServiceRole({
1029
- LogicalName: 'MyRole',
1030
- Service: 'ec2.amazonaws.com'
1186
+ test('throws without required parameters', () => {
1187
+ expect(() => new cf.shortcuts.GlueParquetTable({})).toThrow(/You must provide a Location/);
1031
1188
  });
1032
1189
 
1033
- template = cf.merge(role);
1034
- if (update)
1035
- fixtures.update('service-role-url-suffix-with-replacement', template);
1036
- assert.deepEqual(
1037
- noUndefined(template),
1038
- fixtures.get('service-role-url-suffix-with-replacement'),
1039
- 'expected resources generated, service for which AWS::URLSuffix is invalid specified with a suffix'
1040
- );
1190
+ test('expected resources generated with defaults', () => {
1191
+ const db = new cf.shortcuts.GlueParquetTable({
1192
+ LogicalName: 'MyTable',
1193
+ DatabaseName: 'my_database',
1194
+ Name: 'my_table',
1195
+ Columns: [
1196
+ { Name: 'column', Type: 'string' }
1197
+ ],
1198
+ Location: 's3://fake/location'
1199
+ });
1041
1200
 
1042
- role = new cf.shortcuts.ServiceRole({
1043
- LogicalName: 'MyRole',
1044
- Service: 'lambda.amazonaws.com',
1045
- Statement: [
1046
- {
1047
- Effect: 'Allow',
1048
- Action: 's3:GetObject',
1049
- Resource: 'arn:aws:s3:::fake/data'
1050
- }
1051
- ],
1052
- ManagedPolicyArns: ['arn:aws:iam::123456789012:policy/fake'],
1053
- MaxSessionDuration: 3600,
1054
- Path: '/fake/',
1055
- RoleName: 'my-role',
1056
- Condition: 'Always',
1057
- DependsOn: 'AnotherThing'
1058
- });
1059
-
1060
- template = cf.merge(
1061
- { Conditions: { Always: cf.equals('1', '1') } },
1062
- { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
1063
- role
1064
- );
1065
- if (update) fixtures.update('service-role-no-defaults', template);
1066
- assert.deepEqual(
1067
- noUndefined(template),
1068
- fixtures.get('service-role-no-defaults'),
1069
- 'expected resources generated without defaults'
1070
- );
1201
+ const template = cf.merge(db);
1202
+ if (update) fixtures.update('glue-parquet-table-defaults', template);
1203
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-parquet-table-defaults'));
1204
+ });
1205
+
1206
+ test('expected resources generated without defaults', () => {
1207
+ const db = new cf.shortcuts.GlueParquetTable({
1208
+ LogicalName: 'MyTable',
1209
+ DatabaseName: 'my_database',
1210
+ Name: 'my_table',
1211
+ Columns: [
1212
+ { Name: 'column', Type: 'string' }
1213
+ ],
1214
+ CatalogId: '1234',
1215
+ Owner: 'Team',
1216
+ Parameters: { table: 'params' },
1217
+ Description: 'my_table description',
1218
+ Retention: 12,
1219
+ TableType: 'EXTERNAL_TABLE',
1220
+ ViewExpandedText: '/* Presto View */',
1221
+ ViewOriginalText: '/* Presto View: abc123= */',
1222
+ BucketColumns: ['column'],
1223
+ Compressed: true,
1224
+ Location: 's3://fake/location',
1225
+ InputFormat: 'fake.input.format',
1226
+ OutputFormat: 'fake.output.format',
1227
+ StorageParameters: { storage: 'parameters' },
1228
+ SerdeInfo: {
1229
+ SerializationLibrary: 'fake.serde'
1230
+ },
1231
+ SkewedColumns: {
1232
+ SkewedColumnNames: ['column'],
1233
+ SkewedColumnValueLocationMap: { fake: 'map' },
1234
+ SkewedColumnValues: ['value']
1235
+ },
1236
+ SortColumns: [
1237
+ { Column: 'column', SortOrder: 0 }
1238
+ ],
1239
+ StoredAsSubdirectory: true,
1240
+ Condition: 'Always',
1241
+ DependsOn: 'AnotherThing'
1242
+ });
1071
1243
 
1072
- assert.end();
1244
+ const template = cf.merge(
1245
+ { Conditions: { Always: cf.equals('1', '1') } },
1246
+ { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
1247
+ db
1248
+ );
1249
+ if (update) fixtures.update('glue-parquet-table-no-defaults', template);
1250
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-parquet-table-no-defaults'));
1251
+ });
1073
1252
  });
1074
1253
 
1075
- test('[shortcuts] glue database', (assert) => {
1076
- assert.throws(
1077
- () => new cf.shortcuts.GlueDatabase(),
1078
- 'Options required',
1079
- 'throws without options'
1080
- );
1081
- assert.throws(
1082
- () => new cf.shortcuts.GlueDatabase({}),
1083
- /You must provide a LogicalName and Name/,
1084
- 'throws without required parameters'
1085
- );
1254
+ describe('[shortcuts] glue iceberg table', () => {
1255
+ test('throws without options', () => {
1256
+ expect(() => new cf.shortcuts.GlueIcebergTable()).toThrow('Options required');
1257
+ });
1086
1258
 
1087
- let db = new cf.shortcuts.GlueDatabase({
1088
- LogicalName: 'MyDatabase',
1089
- Name: 'my_database'
1259
+ test('throws without required parameters', () => {
1260
+ expect(() => new cf.shortcuts.GlueIcebergTable({})).toThrow(/You must provide a LogicalName, Name, DatabaseName, Location, and Schema/);
1090
1261
  });
1091
1262
 
1092
- let template = cf.merge(db);
1093
- if (update) fixtures.update('glue-database-defaults', template);
1094
- assert.deepEqual(
1095
- noUndefined(template),
1096
- fixtures.get('glue-database-defaults'),
1097
- 'expected resources generated with defaults'
1098
- );
1263
+ test('expected resources generated with defaults', () => {
1264
+ const db = new cf.shortcuts.GlueIcebergTable({
1265
+ LogicalName: 'MyTable',
1266
+ DatabaseName: 'my_database',
1267
+ Name: 'my_table',
1268
+ Schema: {
1269
+ Type: 'struct',
1270
+ Fields: [
1271
+ { Name: 'column', Type: 'string', Id: 1, Required: true }
1272
+ ]
1273
+ },
1274
+ Location: 's3://fake/location'
1275
+ });
1099
1276
 
1100
- db = new cf.shortcuts.GlueDatabase({
1101
- LogicalName: 'MyDatabase',
1102
- Name: 'my_database',
1103
- CatalogId: '123456',
1104
- Description: 'my_database description',
1105
- LocationUri: 'fakeuri',
1106
- Parameters: { thing: 'a' },
1107
- Condition: 'Always',
1108
- DependsOn: 'AnotherThing'
1109
- });
1110
-
1111
- template = cf.merge(
1112
- { Conditions: { Always: cf.equals('1', '1') } },
1113
- { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
1114
- db
1115
- );
1116
- if (update) fixtures.update('glue-database-no-defaults', template);
1117
- assert.deepEqual(
1118
- noUndefined(template),
1119
- fixtures.get('glue-database-no-defaults'),
1120
- 'expected resources generated without defaults'
1121
- );
1277
+ const template = cf.merge(db);
1278
+ if (update) fixtures.update('glue-iceberg-table-defaults', template);
1279
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-iceberg-table-defaults'));
1280
+ });
1122
1281
 
1123
- assert.end();
1124
- });
1282
+ test('expected resources generated without defaults', () => {
1283
+ const db = new cf.shortcuts.GlueIcebergTable({
1284
+ LogicalName: 'MyTable',
1285
+ DatabaseName: 'my_database',
1286
+ Name: 'my_table',
1287
+ Schema: {
1288
+ Type: 'struct',
1289
+ Fields: [
1290
+ { Name: 'column', Type: 'string', Id: 1, Required: true }
1291
+ ]
1292
+ },
1293
+ CatalogId: '1234',
1294
+ Location: 's3://fake/location',
1295
+ IcebergVersion: '2'
1296
+ });
1125
1297
 
1126
- test('[shortcuts] glue table', (assert) => {
1127
- assert.throws(
1128
- () => new cf.shortcuts.GlueTable(),
1129
- 'Options required',
1130
- 'throws without options'
1131
- );
1132
- assert.throws(
1133
- () => new cf.shortcuts.GlueTable({}),
1134
- /You must provide a LogicalName, Name, DatabaseName, and Columns/,
1135
- 'throws without required parameters'
1136
- );
1298
+ const template = cf.merge(db);
1299
+ if (update) fixtures.update('glue-iceberg-table-no-defaults', template);
1300
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-iceberg-table-no-defaults'));
1301
+ });
1137
1302
 
1138
- let db = new cf.shortcuts.GlueTable({
1139
- LogicalName: 'MyTable',
1140
- DatabaseName: 'my_database',
1141
- Name: 'my_table',
1142
- Columns: [
1143
- { Name: 'column', Type: 'string' }
1144
- ]
1145
- });
1146
-
1147
- let template = cf.merge(db);
1148
- if (update) fixtures.update('glue-table-defaults', template);
1149
- assert.deepEqual(
1150
- noUndefined(template),
1151
- fixtures.get('glue-table-defaults'),
1152
- 'expected resources generated with defaults'
1153
- );
1303
+ test('throws when EnableOptimizer is true but OptimizerRoleArn is missing', () => {
1304
+ expect(() => new cf.shortcuts.GlueIcebergTable({
1305
+ LogicalName: 'MyTable',
1306
+ DatabaseName: 'my_database',
1307
+ Name: 'my_table',
1308
+ Schema: {
1309
+ Type: 'struct',
1310
+ Fields: [
1311
+ { Name: 'column', Type: 'string', Id: 1, Required: true }
1312
+ ]
1313
+ },
1314
+ Location: 's3://fake/location',
1315
+ EnableOptimizer: true
1316
+ })).toThrow(/You must provide an OptimizerRoleArn when EnableOptimizer is true/);
1317
+ });
1154
1318
 
1155
- db = new cf.shortcuts.GlueTable({
1156
- LogicalName: 'MyTable',
1157
- DatabaseName: 'my_database',
1158
- Name: 'my_table',
1159
- Columns: [
1160
- { Name: 'column', Type: 'string' }
1161
- ],
1162
- CatalogId: '1234',
1163
- Owner: 'Team',
1164
- Parameters: { table: 'params' },
1165
- Description: 'my_table description',
1166
- Retention: 12,
1167
- TableType: 'EXTERNAL_TABLE',
1168
- ViewExpandedText: '/* Presto View */',
1169
- ViewOriginalText: '/* Presto View: abc123= */',
1170
- BucketColumns: ['column'],
1171
- Compressed: true,
1172
- InputFormat: 'fake.input.format',
1173
- Location: 's3://fake/location',
1174
- OutputFormat: 'fake.output.format',
1175
- StorageParameters: { storage: 'parameters' },
1176
- SerdeInfo: {
1177
- SerializationLibrary: 'fake.serde'
1178
- },
1179
- SkewedColumns: {
1180
- SkewedColumnNames: ['column'],
1181
- SkewedColumnValueLocationMap: { fake: 'map' },
1182
- SkewedColumnValues: ['value']
1183
- },
1184
- SortColumns: [
1185
- { Column: 'column', SortOrder: 0 }
1186
- ],
1187
- StoredAsSubdirectory: true,
1188
- Condition: 'Always',
1189
- DependsOn: 'AnotherThing'
1190
- });
1191
-
1192
- template = cf.merge(
1193
- { Conditions: { Always: cf.equals('1', '1') } },
1194
- { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
1195
- db
1196
- );
1197
- if (update) fixtures.update('glue-table-no-defaults', template);
1198
- assert.deepEqual(
1199
- noUndefined(template),
1200
- fixtures.get('glue-table-no-defaults'),
1201
- 'expected resources generated without defaults'
1202
- );
1319
+ test('expected resources generated with optimizer using default retention settings', () => {
1320
+ const db = new cf.shortcuts.GlueIcebergTable({
1321
+ LogicalName: 'MyTable',
1322
+ DatabaseName: 'my_database',
1323
+ Name: 'my_table',
1324
+ Schema: {
1325
+ Type: 'struct',
1326
+ Fields: [
1327
+ { Name: 'column', Type: 'string', Id: 1, Required: true }
1328
+ ]
1329
+ },
1330
+ Location: 's3://fake/location',
1331
+ EnableOptimizer: true,
1332
+ OptimizerRoleArn: 'arn:aws:iam::123456789012:role/OptimizerRole'
1333
+ });
1203
1334
 
1204
- assert.end();
1205
- });
1335
+ const template = cf.merge(db);
1336
+ if (update) fixtures.update('glue-iceberg-table-with-optimizer-defaults', template);
1337
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-iceberg-table-with-optimizer-defaults'));
1338
+ });
1206
1339
 
1207
- test('[shortcuts] glue json table', (assert) => {
1208
- assert.throws(
1209
- () => new cf.shortcuts.GlueJsonTable(),
1210
- 'Options required',
1211
- 'throws without options'
1212
- );
1213
- assert.throws(
1214
- () => new cf.shortcuts.GlueJsonTable({}),
1215
- /You must provide a Location/,
1216
- 'throws without required parameters'
1217
- );
1340
+ test('expected resources generated with optimizer using custom retention settings', () => {
1341
+ const db = new cf.shortcuts.GlueIcebergTable({
1342
+ LogicalName: 'MyTable',
1343
+ DatabaseName: 'my_database',
1344
+ Name: 'my_table',
1345
+ Schema: {
1346
+ Type: 'struct',
1347
+ Fields: [
1348
+ { Name: 'column', Type: 'string', Id: 1, Required: true }
1349
+ ]
1350
+ },
1351
+ Location: 's3://fake/location',
1352
+ EnableOptimizer: true,
1353
+ OptimizerRoleArn: cf.getAtt('OptimizerRole', 'Arn'),
1354
+ SnapshotRetentionPeriodInDays: 7,
1355
+ NumberOfSnapshotsToRetain: 3,
1356
+ CleanExpiredFiles: false
1357
+ });
1218
1358
 
1219
- let db = new cf.shortcuts.GlueJsonTable({
1220
- LogicalName: 'MyTable',
1221
- DatabaseName: 'my_database',
1222
- Name: 'my_table',
1223
- Columns: [
1224
- { Name: 'column', Type: 'string' }
1225
- ],
1226
- Location: 's3://fake/location'
1227
- });
1228
-
1229
- let template = cf.merge(db);
1230
- if (update) fixtures.update('glue-json-table-defaults', template);
1231
- assert.deepEqual(
1232
- noUndefined(template),
1233
- fixtures.get('glue-json-table-defaults'),
1234
- 'expected resources generated with defaults'
1235
- );
1359
+ const template = cf.merge(
1360
+ { Resources: { OptimizerRole: { Type: 'AWS::IAM::Role', Properties: { AssumeRolePolicyDocument: {} } } } },
1361
+ db
1362
+ );
1363
+ if (update) fixtures.update('glue-iceberg-table-with-optimizer-custom', template);
1364
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-iceberg-table-with-optimizer-custom'));
1365
+ });
1236
1366
 
1237
- db = new cf.shortcuts.GlueJsonTable({
1238
- LogicalName: 'MyTable',
1239
- DatabaseName: 'my_database',
1240
- Name: 'my_table',
1241
- Columns: [
1242
- { Name: 'column', Type: 'string' }
1243
- ],
1244
- CatalogId: '1234',
1245
- Owner: 'Team',
1246
- Parameters: { table: 'params' },
1247
- Description: 'my_table description',
1248
- Retention: 12,
1249
- TableType: 'EXTERNAL_TABLE',
1250
- ViewExpandedText: '/* Presto View */',
1251
- ViewOriginalText: '/* Presto View: abc123= */',
1252
- BucketColumns: ['column'],
1253
- Compressed: true,
1254
- Location: 's3://fake/location',
1255
- InputFormat: 'fake.input.format',
1256
- OutputFormat: 'fake.output.format',
1257
- StorageParameters: { storage: 'parameters' },
1258
- SerdeInfo: {
1259
- SerializationLibrary: 'fake.serde'
1260
- },
1261
- SkewedColumns: {
1262
- SkewedColumnNames: ['column'],
1263
- SkewedColumnValueLocationMap: { fake: 'map' },
1264
- SkewedColumnValues: ['value']
1265
- },
1266
- SortColumns: [
1267
- { Column: 'column', SortOrder: 0 }
1268
- ],
1269
- StoredAsSubdirectory: true,
1270
- Condition: 'Always',
1271
- DependsOn: 'AnotherThing'
1272
- });
1273
-
1274
- template = cf.merge(
1275
- { Conditions: { Always: cf.equals('1', '1') } },
1276
- { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
1277
- db
1278
- );
1279
- if (update) fixtures.update('glue-json-table-no-defaults', template);
1280
- assert.deepEqual(
1281
- noUndefined(template),
1282
- fixtures.get('glue-json-table-no-defaults'),
1283
- 'expected resources generated without defaults'
1284
- );
1367
+ test('throws when EnableCompaction is true but CompactionRoleArn is missing', () => {
1368
+ expect(() => new cf.shortcuts.GlueIcebergTable({
1369
+ LogicalName: 'MyTable',
1370
+ DatabaseName: 'my_database',
1371
+ Name: 'my_table',
1372
+ Schema: {
1373
+ Type: 'struct',
1374
+ Fields: [
1375
+ { Name: 'column', Type: 'string', Id: 1, Required: true }
1376
+ ]
1377
+ },
1378
+ Location: 's3://fake/location',
1379
+ EnableCompaction: true
1380
+ })).toThrow(/You must provide a CompactionRoleArn when EnableCompaction is true/);
1381
+ });
1285
1382
 
1286
- assert.end();
1287
- });
1383
+ test('expected resources generated with compaction using default settings', () => {
1384
+ const db = new cf.shortcuts.GlueIcebergTable({
1385
+ LogicalName: 'MyTable',
1386
+ DatabaseName: 'my_database',
1387
+ Name: 'my_table',
1388
+ Schema: {
1389
+ Type: 'struct',
1390
+ Fields: [
1391
+ { Name: 'column', Type: 'string', Id: 1, Required: true }
1392
+ ]
1393
+ },
1394
+ Location: 's3://fake/location',
1395
+ EnableCompaction: true,
1396
+ CompactionRoleArn: 'arn:aws:iam::123456789012:role/CompactionRole'
1397
+ });
1288
1398
 
1289
- test('[shortcuts] glue orc table', (assert) => {
1290
- assert.throws(
1291
- () => new cf.shortcuts.GlueOrcTable(),
1292
- 'Options required',
1293
- 'throws without options'
1294
- );
1295
- assert.throws(
1296
- () => new cf.shortcuts.GlueOrcTable({}),
1297
- /You must provide a Location/,
1298
- 'throws without required parameters'
1299
- );
1399
+ const template = cf.merge(db);
1400
+ if (update) fixtures.update('glue-iceberg-table-with-compaction-defaults', template);
1401
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-iceberg-table-with-compaction-defaults'));
1402
+ });
1300
1403
 
1301
- let db = new cf.shortcuts.GlueOrcTable({
1302
- LogicalName: 'MyTable',
1303
- DatabaseName: 'my_database',
1304
- Name: 'my_table',
1305
- Columns: [
1306
- { Name: 'column', Type: 'string' }
1307
- ],
1308
- Location: 's3://fake/location'
1309
- });
1310
-
1311
- let template = cf.merge(db);
1312
- if (update) fixtures.update('glue-orc-table-defaults', template);
1313
- assert.deepEqual(
1314
- noUndefined(template),
1315
- fixtures.get('glue-orc-table-defaults'),
1316
- 'expected resources generated with defaults'
1317
- );
1404
+ test('expected resources generated with compaction using custom settings', () => {
1405
+ const db = new cf.shortcuts.GlueIcebergTable({
1406
+ LogicalName: 'MyTable',
1407
+ DatabaseName: 'my_database',
1408
+ Name: 'my_table',
1409
+ Schema: {
1410
+ Type: 'struct',
1411
+ Fields: [
1412
+ { Name: 'column', Type: 'string', Id: 1, Required: true }
1413
+ ]
1414
+ },
1415
+ Location: 's3://fake/location',
1416
+ EnableCompaction: true,
1417
+ CompactionRoleArn: cf.getAtt('CompactionRole', 'Arn')
1418
+ });
1318
1419
 
1319
- db = new cf.shortcuts.GlueOrcTable({
1320
- LogicalName: 'MyTable',
1321
- DatabaseName: 'my_database',
1322
- Name: 'my_table',
1323
- Columns: [
1324
- { Name: 'column', Type: 'string' }
1325
- ],
1326
- CatalogId: '1234',
1327
- Owner: 'Team',
1328
- Parameters: { table: 'params' },
1329
- Description: 'my_table description',
1330
- Retention: 12,
1331
- TableType: 'EXTERNAL_TABLE',
1332
- ViewExpandedText: '/* Presto View */',
1333
- ViewOriginalText: '/* Presto View: abc123= */',
1334
- BucketColumns: ['column'],
1335
- Compressed: true,
1336
- Location: 's3://fake/location',
1337
- InputFormat: 'fake.input.format',
1338
- OutputFormat: 'fake.output.format',
1339
- StorageParameters: { storage: 'parameters' },
1340
- SerdeInfo: {
1341
- SerializationLibrary: 'fake.serde'
1342
- },
1343
- SkewedColumns: {
1344
- SkewedColumnNames: ['column'],
1345
- SkewedColumnValueLocationMap: { fake: 'map' },
1346
- SkewedColumnValues: ['value']
1347
- },
1348
- SortColumns: [
1349
- { Column: 'column', SortOrder: 0 }
1350
- ],
1351
- StoredAsSubdirectory: true,
1352
- Condition: 'Always',
1353
- DependsOn: 'AnotherThing'
1354
- });
1355
-
1356
- template = cf.merge(
1357
- { Conditions: { Always: cf.equals('1', '1') } },
1358
- { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
1359
- db
1360
- );
1361
- if (update) fixtures.update('glue-orc-table-no-defaults', template);
1362
- assert.deepEqual(
1363
- noUndefined(template),
1364
- fixtures.get('glue-orc-table-no-defaults'),
1365
- 'expected resources generated without defaults'
1366
- );
1420
+ const template = cf.merge(
1421
+ { Resources: { CompactionRole: { Type: 'AWS::IAM::Role', Properties: { AssumeRolePolicyDocument: {} } } } },
1422
+ db
1423
+ );
1424
+ if (update) fixtures.update('glue-iceberg-table-with-compaction-custom', template);
1425
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-iceberg-table-with-compaction-custom'));
1426
+ });
1367
1427
 
1368
- assert.end();
1369
- });
1428
+ test('expected resources generated with both retention and compaction optimizers', () => {
1429
+ const db = new cf.shortcuts.GlueIcebergTable({
1430
+ LogicalName: 'MyTable',
1431
+ DatabaseName: 'my_database',
1432
+ Name: 'my_table',
1433
+ Schema: {
1434
+ Type: 'struct',
1435
+ Fields: [
1436
+ { Name: 'column', Type: 'string', Id: 1, Required: true }
1437
+ ]
1438
+ },
1439
+ Location: 's3://fake/location',
1440
+ EnableOptimizer: true,
1441
+ OptimizerRoleArn: 'arn:aws:iam::123456789012:role/RetentionRole',
1442
+ EnableCompaction: true,
1443
+ CompactionRoleArn: 'arn:aws:iam::123456789012:role/CompactionRole'
1444
+ });
1370
1445
 
1371
- test('[shortcuts] glue parquet table', (assert) => {
1372
- assert.throws(
1373
- () => new cf.shortcuts.GlueParquetTable(),
1374
- 'Options required',
1375
- 'throws without options'
1376
- );
1377
- assert.throws(
1378
- () => new cf.shortcuts.GlueParquetTable({}),
1379
- /You must provide a Location/,
1380
- 'throws without required parameters'
1381
- );
1446
+ const template = cf.merge(db);
1447
+ if (update) fixtures.update('glue-iceberg-table-with-both-optimizers', template);
1448
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-iceberg-table-with-both-optimizers'));
1449
+ });
1382
1450
 
1383
- let db = new cf.shortcuts.GlueParquetTable({
1384
- LogicalName: 'MyTable',
1385
- DatabaseName: 'my_database',
1386
- Name: 'my_table',
1387
- Columns: [
1388
- { Name: 'column', Type: 'string' }
1389
- ],
1390
- Location: 's3://fake/location'
1391
- });
1392
-
1393
- let template = cf.merge(db);
1394
- if (update) fixtures.update('glue-parquet-table-defaults', template);
1395
- assert.deepEqual(
1396
- noUndefined(template),
1397
- fixtures.get('glue-parquet-table-defaults'),
1398
- 'expected resources generated with defaults'
1399
- );
1451
+ test('throws when EnableOrphanFileDeletion is true but OrphanFileDeletionRoleArn is missing', () => {
1452
+ expect(() => new cf.shortcuts.GlueIcebergTable({
1453
+ LogicalName: 'MyTable',
1454
+ DatabaseName: 'my_database',
1455
+ Name: 'my_table',
1456
+ Schema: {
1457
+ Type: 'struct',
1458
+ Fields: [
1459
+ { Name: 'column', Type: 'string', Id: 1, Required: true }
1460
+ ]
1461
+ },
1462
+ Location: 's3://fake/location',
1463
+ EnableOrphanFileDeletion: true
1464
+ })).toThrow(/You must provide an OrphanFileDeletionRoleArn when EnableOrphanFileDeletion is true/);
1465
+ });
1400
1466
 
1401
- db = new cf.shortcuts.GlueParquetTable({
1402
- LogicalName: 'MyTable',
1403
- DatabaseName: 'my_database',
1404
- Name: 'my_table',
1405
- Columns: [
1406
- { Name: 'column', Type: 'string' }
1407
- ],
1408
- CatalogId: '1234',
1409
- Owner: 'Team',
1410
- Parameters: { table: 'params' },
1411
- Description: 'my_table description',
1412
- Retention: 12,
1413
- TableType: 'EXTERNAL_TABLE',
1414
- ViewExpandedText: '/* Presto View */',
1415
- ViewOriginalText: '/* Presto View: abc123= */',
1416
- BucketColumns: ['column'],
1417
- Compressed: true,
1418
- Location: 's3://fake/location',
1419
- InputFormat: 'fake.input.format',
1420
- OutputFormat: 'fake.output.format',
1421
- StorageParameters: { storage: 'parameters' },
1422
- SerdeInfo: {
1423
- SerializationLibrary: 'fake.serde'
1424
- },
1425
- SkewedColumns: {
1426
- SkewedColumnNames: ['column'],
1427
- SkewedColumnValueLocationMap: { fake: 'map' },
1428
- SkewedColumnValues: ['value']
1429
- },
1430
- SortColumns: [
1431
- { Column: 'column', SortOrder: 0 }
1432
- ],
1433
- StoredAsSubdirectory: true,
1434
- Condition: 'Always',
1435
- DependsOn: 'AnotherThing'
1436
- });
1437
-
1438
- template = cf.merge(
1439
- { Conditions: { Always: cf.equals('1', '1') } },
1440
- { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
1441
- db
1442
- );
1443
- if (update) fixtures.update('glue-parquet-table-no-defaults', template);
1444
- assert.deepEqual(
1445
- noUndefined(template),
1446
- fixtures.get('glue-parquet-table-no-defaults'),
1447
- 'expected resources generated without defaults'
1448
- );
1467
+ test('expected resources generated with orphan file deletion using default settings', () => {
1468
+ const db = new cf.shortcuts.GlueIcebergTable({
1469
+ LogicalName: 'MyTable',
1470
+ DatabaseName: 'my_database',
1471
+ Name: 'my_table',
1472
+ Schema: {
1473
+ Type: 'struct',
1474
+ Fields: [
1475
+ { Name: 'column', Type: 'string', Id: 1, Required: true }
1476
+ ]
1477
+ },
1478
+ Location: 's3://fake/location',
1479
+ EnableOrphanFileDeletion: true,
1480
+ OrphanFileDeletionRoleArn: 'arn:aws:iam::123456789012:role/OrphanFileDeletionRole'
1481
+ });
1482
+
1483
+ const template = cf.merge(db);
1484
+ if (update) fixtures.update('glue-iceberg-table-with-orphan-deletion-defaults', template);
1485
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-iceberg-table-with-orphan-deletion-defaults'));
1486
+ });
1487
+
1488
+ test('expected resources generated with orphan file deletion using custom settings', () => {
1489
+ const db = new cf.shortcuts.GlueIcebergTable({
1490
+ LogicalName: 'MyTable',
1491
+ DatabaseName: 'my_database',
1492
+ Name: 'my_table',
1493
+ Schema: {
1494
+ Type: 'struct',
1495
+ Fields: [
1496
+ { Name: 'column', Type: 'string', Id: 1, Required: true }
1497
+ ]
1498
+ },
1499
+ Location: 's3://fake/location',
1500
+ EnableOrphanFileDeletion: true,
1501
+ OrphanFileDeletionRoleArn: cf.getAtt('OrphanFileDeletionRole', 'Arn'),
1502
+ OrphanFileRetentionPeriodInDays: 7,
1503
+ OrphanFileDeletionLocation: 's3://fake/location/subdir'
1504
+ });
1449
1505
 
1450
- assert.end();
1506
+ const template = cf.merge(
1507
+ { Resources: { OrphanFileDeletionRole: { Type: 'AWS::IAM::Role', Properties: { AssumeRolePolicyDocument: {} } } } },
1508
+ db
1509
+ );
1510
+ if (update) fixtures.update('glue-iceberg-table-with-orphan-deletion-custom', template);
1511
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-iceberg-table-with-orphan-deletion-custom'));
1512
+ });
1513
+
1514
+ test('expected resources generated with all three optimizers using same role', () => {
1515
+ const db = new cf.shortcuts.GlueIcebergTable({
1516
+ LogicalName: 'MyTable',
1517
+ DatabaseName: 'my_database',
1518
+ Name: 'my_table',
1519
+ Schema: {
1520
+ Type: 'struct',
1521
+ Fields: [
1522
+ { Name: 'column', Type: 'string', Id: 1, Required: true }
1523
+ ]
1524
+ },
1525
+ Location: 's3://fake/location',
1526
+ EnableOptimizer: true,
1527
+ OptimizerRoleArn: 'arn:aws:iam::123456789012:role/SharedRole',
1528
+ EnableCompaction: true,
1529
+ CompactionRoleArn: 'arn:aws:iam::123456789012:role/SharedRole',
1530
+ EnableOrphanFileDeletion: true,
1531
+ OrphanFileDeletionRoleArn: 'arn:aws:iam::123456789012:role/SharedRole'
1532
+ });
1533
+
1534
+ const template = cf.merge(db);
1535
+ if (update) fixtures.update('glue-iceberg-table-with-all-optimizers', template);
1536
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-iceberg-table-with-all-optimizers'));
1537
+ });
1451
1538
  });
1452
1539
 
1453
- test('[shortcuts] glue view', (assert) => {
1454
- assert.throws(
1455
- () => new cf.shortcuts.GluePrestoView(),
1456
- 'Options required',
1457
- 'throws without options'
1458
- );
1459
- assert.throws(
1460
- () => new cf.shortcuts.GluePrestoView({}),
1461
- /You must provide a DatabaseName, Columns, and OriginalSql/,
1462
- 'throws without required parameters'
1463
- );
1540
+ describe('[shortcuts] glue view', () => {
1541
+ test('throws without options', () => {
1542
+ expect(() => new cf.shortcuts.GluePrestoView()).toThrow('Options required');
1543
+ });
1464
1544
 
1465
- let db = new cf.shortcuts.GluePrestoView({
1466
- LogicalName: 'MyView',
1467
- DatabaseName: 'my_database',
1468
- Name: 'my_view',
1469
- Columns: [
1470
- { Name: 'column', Type: 'string' }
1471
- ],
1472
- OriginalSql: 'SELECT * FROM another.table'
1473
- });
1474
-
1475
- let template = cf.merge(db);
1476
- if (update) fixtures.update('glue-view-defaults', template);
1477
- assert.deepEqual(
1478
- noUndefined(template),
1479
- fixtures.get('glue-view-defaults'),
1480
- 'expected resources generated with defaults'
1481
- );
1545
+ test('throws without required parameters', () => {
1546
+ expect(() => new cf.shortcuts.GluePrestoView({})).toThrow(/You must provide a DatabaseName, Columns, and OriginalSql/);
1547
+ });
1482
1548
 
1483
- db = new cf.shortcuts.GluePrestoView({
1484
- LogicalName: 'MyTable',
1485
- DatabaseName: 'my_database',
1486
- Name: 'my_view',
1487
- Columns: [
1488
- { Name: 'column', Type: 'string' }
1489
- ],
1490
- OriginalSql: 'SELECT * FROM another.table',
1491
- CatalogId: '1234',
1492
- Owner: 'Team',
1493
- Parameters: { table: 'params' },
1494
- Description: 'my_view description',
1495
- Retention: 12,
1496
- TableType: 'EXTERNAL_TABLE',
1497
- BucketColumns: ['column'],
1498
- Compressed: true,
1499
- Location: 's3://fake/location',
1500
- InputFormat: 'fake.input.format',
1501
- OutputFormat: 'fake.output.format',
1502
- StorageParameters: { storage: 'parameters' },
1503
- SerdeInfo: {
1504
- SerializationLibrary: 'fake.serde'
1505
- },
1506
- SkewedColumns: {
1507
- SkewedColumnNames: ['column'],
1508
- SkewedColumnValueLocationMap: { fake: 'map' },
1509
- SkewedColumnValues: ['value']
1510
- },
1511
- SortColumns: [
1512
- { Column: 'column', SortOrder: 0 }
1513
- ],
1514
- StoredAsSubdirectory: true,
1515
- SqlVariables: { env: { Ref: 'AWS::StackName' } },
1516
- Condition: 'Always',
1517
- DependsOn: 'AnotherThing'
1518
- });
1519
-
1520
- template = cf.merge(
1521
- { Conditions: { Always: cf.equals('1', '1') } },
1522
- { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
1523
- db
1524
- );
1525
- if (update) fixtures.update('glue-view-no-defaults', template);
1526
- assert.deepEqual(
1527
- noUndefined(template),
1528
- fixtures.get('glue-view-no-defaults'),
1529
- 'expected resources generated without defaults'
1530
- );
1549
+ test('expected resources generated with defaults', () => {
1550
+ const db = new cf.shortcuts.GluePrestoView({
1551
+ LogicalName: 'MyView',
1552
+ DatabaseName: 'my_database',
1553
+ Name: 'my_view',
1554
+ Columns: [
1555
+ { Name: 'column', Type: 'string' }
1556
+ ],
1557
+ OriginalSql: 'SELECT * FROM another.table'
1558
+ });
1559
+
1560
+ const template = cf.merge(db);
1561
+ if (update) fixtures.update('glue-view-defaults', template);
1562
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-view-defaults'));
1563
+ });
1564
+
1565
+ test('expected resources generated without defaults', () => {
1566
+ const db = new cf.shortcuts.GluePrestoView({
1567
+ LogicalName: 'MyTable',
1568
+ DatabaseName: 'my_database',
1569
+ Name: 'my_view',
1570
+ Columns: [
1571
+ { Name: 'column', Type: 'string' }
1572
+ ],
1573
+ OriginalSql: 'SELECT * FROM another.table',
1574
+ CatalogId: '1234',
1575
+ Owner: 'Team',
1576
+ Parameters: { table: 'params' },
1577
+ Description: 'my_view description',
1578
+ Retention: 12,
1579
+ TableType: 'EXTERNAL_TABLE',
1580
+ BucketColumns: ['column'],
1581
+ Compressed: true,
1582
+ Location: 's3://fake/location',
1583
+ InputFormat: 'fake.input.format',
1584
+ OutputFormat: 'fake.output.format',
1585
+ StorageParameters: { storage: 'parameters' },
1586
+ SerdeInfo: {
1587
+ SerializationLibrary: 'fake.serde'
1588
+ },
1589
+ SkewedColumns: {
1590
+ SkewedColumnNames: ['column'],
1591
+ SkewedColumnValueLocationMap: { fake: 'map' },
1592
+ SkewedColumnValues: ['value']
1593
+ },
1594
+ SortColumns: [
1595
+ { Column: 'column', SortOrder: 0 }
1596
+ ],
1597
+ StoredAsSubdirectory: true,
1598
+ SqlVariables: { env: { Ref: 'AWS::StackName' } },
1599
+ Condition: 'Always',
1600
+ DependsOn: 'AnotherThing'
1601
+ });
1531
1602
 
1532
- assert.end();
1603
+ const template = cf.merge(
1604
+ { Conditions: { Always: cf.equals('1', '1') } },
1605
+ { Resources: { AnotherThing: { Type: 'AWS::SNS::Topic' } } },
1606
+ db
1607
+ );
1608
+ if (update) fixtures.update('glue-view-no-defaults', template);
1609
+ expect(noUndefined(template)).toEqual(fixtures.get('glue-view-no-defaults'));
1610
+ });
1533
1611
  });
1534
1612
 
1535
1613
  const normalizeDeployment = (template) => {
@@ -1540,230 +1618,192 @@ const normalizeDeployment = (template) => {
1540
1618
  return JSON.parse(str);
1541
1619
  };
1542
1620
 
1543
- test('[shortcuts] hookshot passthrough', (assert) => {
1544
- assert.throws(
1545
- () => new cf.shortcuts.hookshot.Passthrough(),
1546
- 'Options required',
1547
- 'throws without options'
1548
- );
1549
- assert.throws(
1550
- () => new cf.shortcuts.hookshot.Passthrough({}),
1551
- /You must provide a Prefix, and PassthroughTo/,
1552
- 'throws without required parameters'
1553
- );
1621
+ describe('[shortcuts] hookshot passthrough', () => {
1622
+ test('throws without options', () => {
1623
+ expect(() => new cf.shortcuts.hookshot.Passthrough()).toThrow('Options required');
1624
+ });
1554
1625
 
1555
- assert.throws(
1556
- () =>
1557
- new cf.shortcuts.hookshot.Passthrough({
1558
- Prefix: 'Pass',
1559
- PassthroughTo: 'Destination',
1560
- LoggingLevel: 'HAM'
1561
- }),
1562
- /LoggingLevel must be one of OFF, INFO, or ERROR/,
1563
- 'throws with invalid LoggingLevel'
1564
- );
1626
+ test('throws without required parameters', () => {
1627
+ expect(() => new cf.shortcuts.hookshot.Passthrough({})).toThrow(/You must provide a Prefix, and PassthroughTo/);
1628
+ });
1565
1629
 
1566
- assert.throws(
1567
- () =>
1568
- new cf.shortcuts.hookshot.Passthrough({
1569
- Prefix: 'Pass',
1570
- PassthroughTo: 'Destination',
1571
- LoggingLevel: 'INFO',
1572
- Runtime: 'python3.7'
1573
- }),
1574
- /Only valid nodejs runtimes are supported for hookshot lambdas, received: 'python3.7'/,
1575
- 'throws with invalid lambda Runtime python3.7'
1576
- );
1630
+ test('throws with invalid LoggingLevel', () => {
1631
+ expect(() => new cf.shortcuts.hookshot.Passthrough({
1632
+ Prefix: 'Pass',
1633
+ PassthroughTo: 'Destination',
1634
+ LoggingLevel: 'HAM'
1635
+ })).toThrow(/LoggingLevel must be one of OFF, INFO, or ERROR/);
1636
+ });
1577
1637
 
1578
- assert.throws(
1579
- () =>
1580
- new cf.shortcuts.hookshot.Passthrough({
1581
- Prefix: 'Pass',
1582
- PassthroughTo: 'Destination',
1583
- LoggingLevel: 'INFO',
1584
- Runtime: 'nodejs16.x'
1585
- }),
1586
- /Only nodejs runtimes >= 18 are supported for hookshot lambdas, received: 'nodejs16.x'/,
1587
- 'throws with invalid lambda Runtime nodejs16.x'
1588
- );
1638
+ test('throws with invalid lambda Runtime python3.7', () => {
1639
+ expect(() => new cf.shortcuts.hookshot.Passthrough({
1640
+ Prefix: 'Pass',
1641
+ PassthroughTo: 'Destination',
1642
+ LoggingLevel: 'INFO',
1643
+ Runtime: 'python3.7'
1644
+ })).toThrow(/Only valid nodejs runtimes are supported for hookshot lambdas, received: 'python3.7'/);
1645
+ });
1646
+
1647
+ test('throws with invalid lambda Runtime nodejs16.x', () => {
1648
+ expect(() => new cf.shortcuts.hookshot.Passthrough({
1649
+ Prefix: 'Pass',
1650
+ PassthroughTo: 'Destination',
1651
+ LoggingLevel: 'INFO',
1652
+ Runtime: 'nodejs16.x'
1653
+ })).toThrow(/Only nodejs runtimes >= 18 are supported for hookshot lambdas, received: 'nodejs16.x'/);
1654
+ });
1589
1655
 
1590
- const to = new cf.shortcuts.Lambda({
1656
+ const getDestinationLambda = () => new cf.shortcuts.Lambda({
1591
1657
  LogicalName: 'Destination',
1592
1658
  Code: {
1593
1659
  ZipFile: 'module.exports.handler = (e, c, cb) => cb();'
1594
1660
  }
1595
1661
  });
1596
1662
 
1597
- let passthrough = new cf.shortcuts.hookshot.Passthrough({
1598
- Prefix: 'Pass',
1599
- PassthroughTo: 'Destination'
1663
+ test('expected resources generated with defaults', () => {
1664
+ const to = getDestinationLambda();
1665
+ const passthrough = new cf.shortcuts.hookshot.Passthrough({
1666
+ Prefix: 'Pass',
1667
+ PassthroughTo: 'Destination'
1668
+ });
1669
+
1670
+ const template = cf.merge(passthrough, to);
1671
+ if (update) fixtures.update('hookshot-passthrough', template);
1672
+ expect(normalizeDeployment(noUndefined(template))).toEqual(normalizeDeployment(fixtures.get('hookshot-passthrough')));
1600
1673
  });
1601
1674
 
1602
- let template = cf.merge(passthrough, to);
1603
- if (update) fixtures.update('hookshot-passthrough', template);
1604
- assert.deepEqual(
1605
- normalizeDeployment(noUndefined(template)),
1606
- normalizeDeployment(fixtures.get('hookshot-passthrough')),
1607
- 'expected resources generated with defaults'
1608
- );
1675
+ test('expected resources generated with alarm config', () => {
1676
+ const to = getDestinationLambda();
1677
+ const passthrough = new cf.shortcuts.hookshot.Passthrough({
1678
+ Prefix: 'Pass',
1679
+ PassthroughTo: 'Destination',
1680
+ AlarmActions: ['devnull@mapbox.com']
1681
+ });
1609
1682
 
1610
- passthrough = new cf.shortcuts.hookshot.Passthrough({
1611
- Prefix: 'Pass',
1612
- PassthroughTo: 'Destination',
1613
- AlarmActions: ['devnull@mapbox.com']
1683
+ const template = cf.merge(passthrough, to);
1684
+ if (update) fixtures.update('hookshot-passthrough-alarms', template);
1685
+ expect(normalizeDeployment(noUndefined(template))).toEqual(normalizeDeployment(fixtures.get('hookshot-passthrough-alarms')));
1614
1686
  });
1615
1687
 
1616
- template = cf.merge(passthrough, to);
1617
- if (update) fixtures.update('hookshot-passthrough-alarms', template);
1618
- assert.deepEqual(
1619
- normalizeDeployment(noUndefined(template)),
1620
- normalizeDeployment(fixtures.get('hookshot-passthrough-alarms')),
1621
- 'expected resources generated with alarm config'
1622
- );
1688
+ test('expected resources generated with configured LoggingLevel', () => {
1689
+ const to = getDestinationLambda();
1690
+ const passthrough = new cf.shortcuts.hookshot.Passthrough({
1691
+ Prefix: 'Pass',
1692
+ PassthroughTo: 'Destination',
1693
+ LoggingLevel: 'INFO'
1694
+ });
1623
1695
 
1624
- passthrough = new cf.shortcuts.hookshot.Passthrough({
1625
- Prefix: 'Pass',
1626
- PassthroughTo: 'Destination',
1627
- LoggingLevel: 'INFO'
1696
+ const template = cf.merge(passthrough, to);
1697
+ if (update) fixtures.update('hookshot-passthrough-logging', template);
1698
+ expect(normalizeDeployment(noUndefined(template))).toEqual(normalizeDeployment(fixtures.get('hookshot-passthrough-logging')));
1628
1699
  });
1629
1700
 
1630
- template = cf.merge(passthrough, to);
1631
- if (update) fixtures.update('hookshot-passthrough-logging', template);
1632
- assert.deepEqual(
1633
- normalizeDeployment(noUndefined(template)),
1634
- normalizeDeployment(fixtures.get('hookshot-passthrough-logging')),
1635
- 'expected resources generated with configured LoggingLevel'
1636
- );
1701
+ test('expected resources generated with detailed logging and metrics', () => {
1702
+ const to = getDestinationLambda();
1703
+ const passthrough = new cf.shortcuts.hookshot.Passthrough({
1704
+ Prefix: 'Pass',
1705
+ PassthroughTo: 'Destination',
1706
+ DataTraceEnabled: true,
1707
+ MetricsEnabled: true
1708
+ });
1637
1709
 
1638
- passthrough = new cf.shortcuts.hookshot.Passthrough({
1639
- Prefix: 'Pass',
1640
- PassthroughTo: 'Destination',
1641
- DataTraceEnabled: true,
1642
- MetricsEnabled: true
1710
+ const template = cf.merge(passthrough, to);
1711
+ if (update) fixtures.update('hookshot-passthrough-enhanced-logging', template);
1712
+ expect(normalizeDeployment(noUndefined(template))).toEqual(normalizeDeployment(fixtures.get('hookshot-passthrough-enhanced-logging')));
1643
1713
  });
1644
1714
 
1645
- template = cf.merge(passthrough, to);
1646
- if (update)
1647
- fixtures.update('hookshot-passthrough-enhanced-logging', template);
1648
- assert.deepEqual(
1649
- normalizeDeployment(noUndefined(template)),
1650
- normalizeDeployment(fixtures.get('hookshot-passthrough-enhanced-logging')),
1651
- 'expected resources generated with detailed logging and metrics'
1652
- );
1653
-
1654
- passthrough = new cf.shortcuts.hookshot.Passthrough({
1655
- Prefix: 'Pass',
1656
- PassthroughTo: 'Destination',
1657
- DataTraceEnabled: true,
1658
- MetricsEnabled: true,
1659
- LoggingLevel: 'INFO'
1660
- });
1661
-
1662
- template = cf.merge(passthrough, to);
1663
- if (update)
1664
- fixtures.update('hookshot-passthrough-full-blown-logging', template);
1665
- assert.deepEqual(
1666
- normalizeDeployment(noUndefined(template)),
1667
- normalizeDeployment(
1668
- fixtures.get('hookshot-passthrough-full-blown-logging')
1669
- ),
1670
- 'LoggingLevel respected with detailed logging and metrics'
1671
- );
1715
+ test('LoggingLevel respected with detailed logging and metrics', () => {
1716
+ const to = getDestinationLambda();
1717
+ const passthrough = new cf.shortcuts.hookshot.Passthrough({
1718
+ Prefix: 'Pass',
1719
+ PassthroughTo: 'Destination',
1720
+ DataTraceEnabled: true,
1721
+ MetricsEnabled: true,
1722
+ LoggingLevel: 'INFO'
1723
+ });
1672
1724
 
1673
- passthrough = new cf.shortcuts.hookshot.Passthrough({
1674
- Prefix: 'Pass',
1675
- PassthroughTo: 'Destination',
1676
- AccessLogFormat: '{ "requestId":"$context.requestId" }'
1725
+ const template = cf.merge(passthrough, to);
1726
+ if (update) fixtures.update('hookshot-passthrough-full-blown-logging', template);
1727
+ expect(normalizeDeployment(noUndefined(template))).toEqual(normalizeDeployment(fixtures.get('hookshot-passthrough-full-blown-logging')));
1677
1728
  });
1678
1729
 
1679
- template = cf.merge(passthrough, to);
1680
- if (update)
1681
- fixtures.update('hookshot-passthrough-access-log-format', template);
1682
- assert.deepEqual(
1683
- normalizeDeployment(noUndefined(template)),
1684
- normalizeDeployment(fixtures.get('hookshot-passthrough-access-log-format')),
1685
- 'expected resources generated with access logs'
1686
- );
1730
+ test('expected resources generated with access logs', () => {
1731
+ const to = getDestinationLambda();
1732
+ const passthrough = new cf.shortcuts.hookshot.Passthrough({
1733
+ Prefix: 'Pass',
1734
+ PassthroughTo: 'Destination',
1735
+ AccessLogFormat: '{ "requestId":"$context.requestId" }'
1736
+ });
1687
1737
 
1688
- assert.end();
1738
+ const template = cf.merge(passthrough, to);
1739
+ if (update) fixtures.update('hookshot-passthrough-access-log-format', template);
1740
+ expect(normalizeDeployment(noUndefined(template))).toEqual(normalizeDeployment(fixtures.get('hookshot-passthrough-access-log-format')));
1741
+ });
1689
1742
  });
1690
1743
 
1691
- test('[shortcuts] hookshot github', (assert) => {
1692
- assert.throws(
1693
- () => new cf.shortcuts.hookshot.Github(),
1694
- /You must provide a Prefix, and PassthroughTo/,
1695
- 'throws without required parameters'
1696
- );
1744
+ describe('[shortcuts] hookshot github', () => {
1745
+ test('throws without required parameters', () => {
1746
+ expect(() => new cf.shortcuts.hookshot.Github()).toThrow(/You must provide a Prefix, and PassthroughTo/);
1747
+ });
1697
1748
 
1698
- assert.throws(
1699
- () =>
1700
- new cf.shortcuts.hookshot.Github({
1701
- Prefix: 'Pass',
1702
- PassthroughTo: 'Destination',
1703
- Runtime: 'python3.7'
1704
- }),
1705
- /Only valid nodejs runtimes are supported for hookshot lambdas, received: 'python3.7'/,
1706
- 'throws with invalid lambda Runtime python3.7'
1707
- );
1749
+ test('throws with invalid lambda Runtime python3.7', () => {
1750
+ expect(() => new cf.shortcuts.hookshot.Github({
1751
+ Prefix: 'Pass',
1752
+ PassthroughTo: 'Destination',
1753
+ Runtime: 'python3.7'
1754
+ })).toThrow(/Only valid nodejs runtimes are supported for hookshot lambdas, received: 'python3.7'/);
1755
+ });
1708
1756
 
1709
- assert.throws(
1710
- () =>
1711
- new cf.shortcuts.hookshot.Github({
1712
- Prefix: 'Pass',
1713
- PassthroughTo: 'Destination',
1714
- Runtime: 'nodejs16.x'
1715
- }),
1716
- /Only nodejs runtimes >= 18 are supported for hookshot lambdas, received: 'nodejs16.x'/,
1717
- 'throws with invalid lambda Runtime nodejs16.x'
1718
- );
1757
+ test('throws with invalid lambda Runtime nodejs16.x', () => {
1758
+ expect(() => new cf.shortcuts.hookshot.Github({
1759
+ Prefix: 'Pass',
1760
+ PassthroughTo: 'Destination',
1761
+ Runtime: 'nodejs16.x'
1762
+ })).toThrow(/Only nodejs runtimes >= 18 are supported for hookshot lambdas, received: 'nodejs16.x'/);
1763
+ });
1719
1764
 
1720
- const to = new cf.shortcuts.Lambda({
1765
+ const getDestinationLambda = () => new cf.shortcuts.Lambda({
1721
1766
  LogicalName: 'Destination',
1722
1767
  Code: {
1723
1768
  ZipFile: 'module.exports.handler = (e, c, cb) => cb();'
1724
1769
  }
1725
1770
  });
1726
1771
 
1727
- let github = new cf.shortcuts.hookshot.Github({
1728
- Prefix: 'Pass',
1729
- PassthroughTo: 'Destination'
1730
- });
1731
-
1732
- let template = cf.merge(github, to);
1733
- if (update) fixtures.update('hookshot-github', template);
1734
- assert.deepEqual(
1735
- normalizeDeployment(noUndefined(template)),
1736
- normalizeDeployment(fixtures.get('hookshot-github')),
1737
- 'expected resources generated with defaults'
1738
- );
1772
+ test('expected resources generated with defaults', () => {
1773
+ const to = getDestinationLambda();
1774
+ const github = new cf.shortcuts.hookshot.Github({
1775
+ Prefix: 'Pass',
1776
+ PassthroughTo: 'Destination'
1777
+ });
1739
1778
 
1740
- github = new cf.shortcuts.hookshot.Github({
1741
- Prefix: 'Pass',
1742
- PassthroughTo: 'Destination',
1743
- WebhookSecret: 'abc123'
1779
+ const template = cf.merge(github, to);
1780
+ if (update) fixtures.update('hookshot-github', template);
1781
+ expect(normalizeDeployment(noUndefined(template))).toEqual(normalizeDeployment(fixtures.get('hookshot-github')));
1744
1782
  });
1745
1783
 
1746
- template = cf.merge(github, to);
1747
- if (update) fixtures.update('hookshot-github-secret-string', template);
1748
- assert.deepEqual(
1749
- normalizeDeployment(noUndefined(template)),
1750
- normalizeDeployment(fixtures.get('hookshot-github-secret-string')),
1751
- 'expected resources generated when secret passed as string'
1752
- );
1784
+ test('expected resources generated when secret passed as string', () => {
1785
+ const to = getDestinationLambda();
1786
+ const github = new cf.shortcuts.hookshot.Github({
1787
+ Prefix: 'Pass',
1788
+ PassthroughTo: 'Destination',
1789
+ WebhookSecret: 'abc123'
1790
+ });
1753
1791
 
1754
- github = new cf.shortcuts.hookshot.Github({
1755
- Prefix: 'Pass',
1756
- PassthroughTo: 'Destination',
1757
- WebhookSecret: cf.ref('SomeParameter')
1758
- });
1759
- const Parameters = { SomeParameter: { Type: 'String' } };
1760
- template = cf.merge(github, to, { Parameters });
1761
- if (update) fixtures.update('hookshot-github-secret-ref', template);
1762
- assert.deepEqual(
1763
- normalizeDeployment(noUndefined(template)),
1764
- normalizeDeployment(fixtures.get('hookshot-github-secret-ref')),
1765
- 'expected resources generated when secret passed as ref'
1766
- );
1792
+ const template = cf.merge(github, to);
1793
+ if (update) fixtures.update('hookshot-github-secret-string', template);
1794
+ expect(normalizeDeployment(noUndefined(template))).toEqual(normalizeDeployment(fixtures.get('hookshot-github-secret-string')));
1795
+ });
1767
1796
 
1768
- assert.end();
1797
+ test('expected resources generated when secret passed as ref', () => {
1798
+ const to = getDestinationLambda();
1799
+ const github = new cf.shortcuts.hookshot.Github({
1800
+ Prefix: 'Pass',
1801
+ PassthroughTo: 'Destination',
1802
+ WebhookSecret: cf.ref('SomeParameter')
1803
+ });
1804
+ const Parameters = { SomeParameter: { Type: 'String' } };
1805
+ const template = cf.merge(github, to, { Parameters });
1806
+ if (update) fixtures.update('hookshot-github-secret-ref', template);
1807
+ expect(normalizeDeployment(noUndefined(template))).toEqual(normalizeDeployment(fixtures.get('hookshot-github-secret-ref')));
1808
+ });
1769
1809
  });