@friggframework/devtools 2.0.0--canary.406.78e2685.0 → 2.0.0--canary.398.24926ac.0
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.
- package/frigg-cli/build-command/index.js +4 -2
- package/frigg-cli/deploy-command/index.js +5 -2
- package/frigg-cli/generate-iam-command.js +115 -0
- package/frigg-cli/index.js +11 -1
- package/infrastructure/AWS-DISCOVERY-TROUBLESHOOTING.md +245 -0
- package/infrastructure/AWS-IAM-CREDENTIAL-NEEDS.md +596 -0
- package/infrastructure/DEPLOYMENT-INSTRUCTIONS.md +268 -0
- package/infrastructure/GENERATE-IAM-DOCS.md +253 -0
- package/infrastructure/IAM-POLICY-TEMPLATES.md +176 -0
- package/infrastructure/README-TESTING.md +332 -0
- package/infrastructure/README.md +421 -0
- package/infrastructure/WEBSOCKET-CONFIGURATION.md +105 -0
- package/infrastructure/__tests__/fixtures/mock-aws-resources.js +391 -0
- package/infrastructure/__tests__/helpers/test-utils.js +277 -0
- package/infrastructure/aws-discovery.js +568 -0
- package/infrastructure/aws-discovery.test.js +373 -0
- package/infrastructure/build-time-discovery.js +206 -0
- package/infrastructure/build-time-discovery.test.js +375 -0
- package/infrastructure/create-frigg-infrastructure.js +2 -2
- package/infrastructure/frigg-deployment-iam-stack.yaml +379 -0
- package/infrastructure/iam-generator.js +687 -0
- package/infrastructure/iam-generator.test.js +169 -0
- package/infrastructure/iam-policy-basic.json +212 -0
- package/infrastructure/iam-policy-full.json +282 -0
- package/infrastructure/integration.test.js +383 -0
- package/infrastructure/run-discovery.js +110 -0
- package/infrastructure/serverless-template.js +514 -167
- package/infrastructure/serverless-template.test.js +541 -0
- package/management-ui/dist/assets/FriggLogo-B7Xx8ZW1.svg +1 -0
- package/management-ui/dist/assets/index-BA21WgFa.js +1221 -0
- package/management-ui/dist/assets/index-CbM64Oba.js +1221 -0
- package/management-ui/dist/assets/index-CkvseXTC.css +1 -0
- package/management-ui/dist/index.html +14 -0
- package/package.json +9 -5
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock AWS resources for consistent testing across all VPC/KMS/SSM tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const mockVpc = {
|
|
6
|
+
VpcId: 'vpc-12345678',
|
|
7
|
+
IsDefault: true,
|
|
8
|
+
State: 'available',
|
|
9
|
+
CidrBlock: '10.0.0.0/16',
|
|
10
|
+
DhcpOptionsId: 'dopt-12345678',
|
|
11
|
+
InstanceTenancy: 'default'
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const mockSubnets = [
|
|
15
|
+
{
|
|
16
|
+
SubnetId: 'subnet-private-1',
|
|
17
|
+
VpcId: 'vpc-12345678',
|
|
18
|
+
CidrBlock: '10.0.1.0/24',
|
|
19
|
+
AvailabilityZone: 'us-east-1a',
|
|
20
|
+
State: 'available',
|
|
21
|
+
MapPublicIpOnLaunch: false
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
SubnetId: 'subnet-private-2',
|
|
25
|
+
VpcId: 'vpc-12345678',
|
|
26
|
+
CidrBlock: '10.0.2.0/24',
|
|
27
|
+
AvailabilityZone: 'us-east-1b',
|
|
28
|
+
State: 'available',
|
|
29
|
+
MapPublicIpOnLaunch: false
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
SubnetId: 'subnet-public-1',
|
|
33
|
+
VpcId: 'vpc-12345678',
|
|
34
|
+
CidrBlock: '10.0.3.0/24',
|
|
35
|
+
AvailabilityZone: 'us-east-1a',
|
|
36
|
+
State: 'available',
|
|
37
|
+
MapPublicIpOnLaunch: true
|
|
38
|
+
}
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const mockSecurityGroups = [
|
|
42
|
+
{
|
|
43
|
+
GroupId: 'sg-frigg-12345678',
|
|
44
|
+
GroupName: 'frigg-lambda-sg',
|
|
45
|
+
Description: 'Security group for Frigg Lambda functions',
|
|
46
|
+
VpcId: 'vpc-12345678'
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
GroupId: 'sg-default-12345678',
|
|
50
|
+
GroupName: 'default',
|
|
51
|
+
Description: 'Default security group',
|
|
52
|
+
VpcId: 'vpc-12345678'
|
|
53
|
+
}
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const mockRouteTables = [
|
|
57
|
+
{
|
|
58
|
+
RouteTableId: 'rtb-private-12345678',
|
|
59
|
+
VpcId: 'vpc-12345678',
|
|
60
|
+
Routes: [
|
|
61
|
+
{
|
|
62
|
+
DestinationCidrBlock: '10.0.0.0/16',
|
|
63
|
+
GatewayId: 'local',
|
|
64
|
+
State: 'active'
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
DestinationCidrBlock: '0.0.0.0/0',
|
|
68
|
+
NatGatewayId: 'nat-12345678',
|
|
69
|
+
State: 'active'
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
Associations: [
|
|
73
|
+
{
|
|
74
|
+
RouteTableAssociationId: 'rtbassoc-12345678',
|
|
75
|
+
RouteTableId: 'rtb-private-12345678',
|
|
76
|
+
SubnetId: 'subnet-private-1'
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
RouteTableId: 'rtb-public-12345678',
|
|
82
|
+
VpcId: 'vpc-12345678',
|
|
83
|
+
Routes: [
|
|
84
|
+
{
|
|
85
|
+
DestinationCidrBlock: '10.0.0.0/16',
|
|
86
|
+
GatewayId: 'local',
|
|
87
|
+
State: 'active'
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
DestinationCidrBlock: '0.0.0.0/0',
|
|
91
|
+
GatewayId: 'igw-12345678',
|
|
92
|
+
State: 'active'
|
|
93
|
+
}
|
|
94
|
+
],
|
|
95
|
+
Associations: [
|
|
96
|
+
{
|
|
97
|
+
RouteTableAssociationId: 'rtbassoc-87654321',
|
|
98
|
+
RouteTableId: 'rtb-public-12345678',
|
|
99
|
+
SubnetId: 'subnet-public-1'
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
const mockKmsKeys = [
|
|
106
|
+
{
|
|
107
|
+
KeyId: '12345678-1234-1234-1234-123456789012',
|
|
108
|
+
Arn: 'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012'
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
KeyId: '87654321-8765-4321-8765-876543218765',
|
|
112
|
+
Arn: 'arn:aws:kms:us-east-1:123456789012:key/87654321-8765-4321-8765-876543218765'
|
|
113
|
+
}
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
const mockKmsKeyMetadata = {
|
|
117
|
+
KeyId: '12345678-1234-1234-1234-123456789012',
|
|
118
|
+
Arn: 'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012',
|
|
119
|
+
CreationDate: new Date('2023-01-01'),
|
|
120
|
+
Enabled: true,
|
|
121
|
+
Description: 'Default KMS key for Frigg encryption',
|
|
122
|
+
KeyUsage: 'ENCRYPT_DECRYPT',
|
|
123
|
+
KeyState: 'Enabled',
|
|
124
|
+
Origin: 'AWS_KMS',
|
|
125
|
+
KeyManager: 'CUSTOMER',
|
|
126
|
+
CustomerMasterKeySpec: 'SYMMETRIC_DEFAULT'
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const mockStsCallerIdentity = {
|
|
130
|
+
UserId: 'AIDACKCEVSQ6C2EXAMPLE',
|
|
131
|
+
Account: '123456789012',
|
|
132
|
+
Arn: 'arn:aws:iam::123456789012:user/test-user'
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const mockDiscoveredResources = {
|
|
136
|
+
defaultVpcId: mockVpc.VpcId,
|
|
137
|
+
defaultSecurityGroupId: mockSecurityGroups[0].GroupId,
|
|
138
|
+
privateSubnetId1: mockSubnets[0].SubnetId,
|
|
139
|
+
privateSubnetId2: mockSubnets[1].SubnetId,
|
|
140
|
+
privateRouteTableId: mockRouteTables[0].RouteTableId,
|
|
141
|
+
defaultKmsKeyId: mockKmsKeyMetadata.Arn
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// App definitions for testing different scenarios
|
|
145
|
+
const mockAppDefinitions = {
|
|
146
|
+
vpcOnly: {
|
|
147
|
+
name: 'vpc-test-app',
|
|
148
|
+
vpc: { enable: true },
|
|
149
|
+
integrations: []
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
kmsOnly: {
|
|
153
|
+
name: 'kms-test-app',
|
|
154
|
+
encryption: { useDefaultKMSForFieldLevelEncryption: true },
|
|
155
|
+
integrations: []
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
ssmOnly: {
|
|
159
|
+
name: 'ssm-test-app',
|
|
160
|
+
ssm: { enable: true },
|
|
161
|
+
integrations: []
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
allFeatures: {
|
|
165
|
+
name: 'full-feature-app',
|
|
166
|
+
vpc: { enable: true },
|
|
167
|
+
encryption: { useDefaultKMSForFieldLevelEncryption: true },
|
|
168
|
+
ssm: { enable: true },
|
|
169
|
+
integrations: [{
|
|
170
|
+
Definition: {
|
|
171
|
+
name: 'testIntegration'
|
|
172
|
+
}
|
|
173
|
+
}]
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
noFeatures: {
|
|
177
|
+
name: 'minimal-app',
|
|
178
|
+
integrations: []
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
multipleIntegrations: {
|
|
182
|
+
name: 'multi-integration-app',
|
|
183
|
+
vpc: { enable: true },
|
|
184
|
+
integrations: [
|
|
185
|
+
{
|
|
186
|
+
Definition: {
|
|
187
|
+
name: 'hubspot'
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
Definition: {
|
|
192
|
+
name: 'salesforce'
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// Mock serverless service configurations
|
|
200
|
+
const mockServerlessServices = {
|
|
201
|
+
withVpc: {
|
|
202
|
+
provider: {
|
|
203
|
+
name: 'aws',
|
|
204
|
+
region: 'us-east-1',
|
|
205
|
+
vpc: '${self:custom.vpc.${self:provider.stage}}'
|
|
206
|
+
},
|
|
207
|
+
plugins: [],
|
|
208
|
+
custom: {
|
|
209
|
+
vpc: {
|
|
210
|
+
'${self:provider.stage}': {
|
|
211
|
+
securityGroupIds: ['${env:AWS_DISCOVERY_SECURITY_GROUP_ID}'],
|
|
212
|
+
subnetIds: [
|
|
213
|
+
'${env:AWS_DISCOVERY_SUBNET_ID_1}',
|
|
214
|
+
'${env:AWS_DISCOVERY_SUBNET_ID_2}'
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
functions: {}
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
withKms: {
|
|
223
|
+
provider: {
|
|
224
|
+
name: 'aws',
|
|
225
|
+
region: 'us-east-1'
|
|
226
|
+
},
|
|
227
|
+
plugins: ['serverless-kms-grants'],
|
|
228
|
+
custom: {
|
|
229
|
+
kmsGrants: {
|
|
230
|
+
kmsKeyId: '${env:AWS_DISCOVERY_KMS_KEY_ID}'
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
functions: {}
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
withSsm: {
|
|
237
|
+
provider: {
|
|
238
|
+
name: 'aws',
|
|
239
|
+
region: 'us-east-1',
|
|
240
|
+
layers: [
|
|
241
|
+
'arn:aws:lambda:us-east-1:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension:11'
|
|
242
|
+
]
|
|
243
|
+
},
|
|
244
|
+
plugins: [],
|
|
245
|
+
custom: {},
|
|
246
|
+
functions: {}
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
withAll: {
|
|
250
|
+
provider: {
|
|
251
|
+
name: 'aws',
|
|
252
|
+
region: 'us-east-1',
|
|
253
|
+
vpc: '${self:custom.vpc.${self:provider.stage}}',
|
|
254
|
+
layers: [
|
|
255
|
+
'arn:aws:lambda:us-east-1:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension:11'
|
|
256
|
+
]
|
|
257
|
+
},
|
|
258
|
+
plugins: ['serverless-kms-grants'],
|
|
259
|
+
custom: {
|
|
260
|
+
vpc: {
|
|
261
|
+
'${self:provider.stage}': {
|
|
262
|
+
securityGroupIds: ['${env:AWS_DISCOVERY_SECURITY_GROUP_ID}'],
|
|
263
|
+
subnetIds: [
|
|
264
|
+
'${env:AWS_DISCOVERY_SUBNET_ID_1}',
|
|
265
|
+
'${env:AWS_DISCOVERY_SUBNET_ID_2}'
|
|
266
|
+
]
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
kmsGrants: {
|
|
270
|
+
kmsKeyId: '${env:AWS_DISCOVERY_KMS_KEY_ID}'
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
functions: {}
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// Environment variables for testing
|
|
278
|
+
const mockEnvironmentVariables = {
|
|
279
|
+
AWS_DISCOVERY_VPC_ID: mockVpc.VpcId,
|
|
280
|
+
AWS_DISCOVERY_SECURITY_GROUP_ID: mockSecurityGroups[0].GroupId,
|
|
281
|
+
AWS_DISCOVERY_SUBNET_ID_1: mockSubnets[0].SubnetId,
|
|
282
|
+
AWS_DISCOVERY_SUBNET_ID_2: mockSubnets[1].SubnetId,
|
|
283
|
+
AWS_DISCOVERY_ROUTE_TABLE_ID: mockRouteTables[0].RouteTableId,
|
|
284
|
+
AWS_DISCOVERY_KMS_KEY_ID: mockKmsKeyMetadata.Arn
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// Fallback environment variables for error scenarios
|
|
288
|
+
const mockFallbackEnvironmentVariables = {
|
|
289
|
+
AWS_DISCOVERY_VPC_ID: 'vpc-fallback',
|
|
290
|
+
AWS_DISCOVERY_SECURITY_GROUP_ID: 'sg-fallback',
|
|
291
|
+
AWS_DISCOVERY_SUBNET_ID_1: 'subnet-fallback-1',
|
|
292
|
+
AWS_DISCOVERY_SUBNET_ID_2: 'subnet-fallback-2',
|
|
293
|
+
AWS_DISCOVERY_ROUTE_TABLE_ID: 'rtb-fallback',
|
|
294
|
+
AWS_DISCOVERY_KMS_KEY_ID: 'arn:aws:kms:*:*:key/*'
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// Mock AWS SDK responses
|
|
298
|
+
const mockAwsSdkResponses = {
|
|
299
|
+
ec2: {
|
|
300
|
+
describeVpcs: {
|
|
301
|
+
default: {
|
|
302
|
+
Vpcs: [mockVpc]
|
|
303
|
+
},
|
|
304
|
+
empty: {
|
|
305
|
+
Vpcs: []
|
|
306
|
+
},
|
|
307
|
+
multiple: {
|
|
308
|
+
Vpcs: [mockVpc, { ...mockVpc, VpcId: 'vpc-87654321', IsDefault: false }]
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
describeSubnets: {
|
|
313
|
+
private: {
|
|
314
|
+
Subnets: mockSubnets.slice(0, 2) // Only private subnets
|
|
315
|
+
},
|
|
316
|
+
mixed: {
|
|
317
|
+
Subnets: mockSubnets // All subnets
|
|
318
|
+
},
|
|
319
|
+
empty: {
|
|
320
|
+
Subnets: []
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
describeSecurityGroups: {
|
|
325
|
+
frigg: {
|
|
326
|
+
SecurityGroups: [mockSecurityGroups[0]]
|
|
327
|
+
},
|
|
328
|
+
default: {
|
|
329
|
+
SecurityGroups: [mockSecurityGroups[1]]
|
|
330
|
+
},
|
|
331
|
+
empty: {
|
|
332
|
+
SecurityGroups: []
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
describeRouteTables: {
|
|
337
|
+
private: {
|
|
338
|
+
RouteTables: [mockRouteTables[0]]
|
|
339
|
+
},
|
|
340
|
+
public: {
|
|
341
|
+
RouteTables: [mockRouteTables[1]]
|
|
342
|
+
},
|
|
343
|
+
mixed: {
|
|
344
|
+
RouteTables: mockRouteTables
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
|
|
349
|
+
kms: {
|
|
350
|
+
listKeys: {
|
|
351
|
+
withKeys: {
|
|
352
|
+
Keys: mockKmsKeys
|
|
353
|
+
},
|
|
354
|
+
empty: {
|
|
355
|
+
Keys: []
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
describeKey: {
|
|
360
|
+
customer: {
|
|
361
|
+
KeyMetadata: mockKmsKeyMetadata
|
|
362
|
+
},
|
|
363
|
+
aws: {
|
|
364
|
+
KeyMetadata: {
|
|
365
|
+
...mockKmsKeyMetadata,
|
|
366
|
+
KeyManager: 'AWS'
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
|
|
372
|
+
sts: {
|
|
373
|
+
getCallerIdentity: mockStsCallerIdentity
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
module.exports = {
|
|
378
|
+
mockVpc,
|
|
379
|
+
mockSubnets,
|
|
380
|
+
mockSecurityGroups,
|
|
381
|
+
mockRouteTables,
|
|
382
|
+
mockKmsKeys,
|
|
383
|
+
mockKmsKeyMetadata,
|
|
384
|
+
mockStsCallerIdentity,
|
|
385
|
+
mockDiscoveredResources,
|
|
386
|
+
mockAppDefinitions,
|
|
387
|
+
mockServerlessServices,
|
|
388
|
+
mockEnvironmentVariables,
|
|
389
|
+
mockFallbackEnvironmentVariables,
|
|
390
|
+
mockAwsSdkResponses
|
|
391
|
+
};
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test utilities for VPC/KMS/SSM testing
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { mockEnvironmentVariables, mockFallbackEnvironmentVariables } = require('../fixtures/mock-aws-resources');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Set up environment variables for testing
|
|
9
|
+
* @param {Object} envVars - Environment variables to set
|
|
10
|
+
*/
|
|
11
|
+
function setTestEnvironmentVariables(envVars = mockEnvironmentVariables) {
|
|
12
|
+
Object.keys(envVars).forEach(key => {
|
|
13
|
+
process.env[key] = envVars[key];
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Clean up environment variables after testing
|
|
19
|
+
* @param {Object} envVars - Environment variables to clean up
|
|
20
|
+
*/
|
|
21
|
+
function cleanupTestEnvironmentVariables(envVars = mockEnvironmentVariables) {
|
|
22
|
+
Object.keys(envVars).forEach(key => {
|
|
23
|
+
delete process.env[key];
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Set up fallback environment variables for error testing
|
|
29
|
+
*/
|
|
30
|
+
function setFallbackEnvironmentVariables() {
|
|
31
|
+
setTestEnvironmentVariables(mockFallbackEnvironmentVariables);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Create a mock AWS SDK client send function
|
|
36
|
+
* @param {Array} responses - Array of responses to return in order
|
|
37
|
+
* @returns {Function} Mock send function
|
|
38
|
+
*/
|
|
39
|
+
function createMockSendFunction(responses) {
|
|
40
|
+
let callCount = 0;
|
|
41
|
+
return jest.fn().mockImplementation(() => {
|
|
42
|
+
const response = responses[callCount] || responses[responses.length - 1];
|
|
43
|
+
callCount++;
|
|
44
|
+
return Promise.resolve(response);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create a mock serverless object for plugin testing
|
|
50
|
+
* @param {Object} serviceConfig - Serverless service configuration
|
|
51
|
+
* @param {Array} commands - Commands in processedInput
|
|
52
|
+
* @returns {Object} Mock serverless object
|
|
53
|
+
*/
|
|
54
|
+
function createMockServerless(serviceConfig = {}, commands = []) {
|
|
55
|
+
return {
|
|
56
|
+
cli: {
|
|
57
|
+
log: jest.fn()
|
|
58
|
+
},
|
|
59
|
+
service: {
|
|
60
|
+
provider: {
|
|
61
|
+
name: 'aws',
|
|
62
|
+
region: 'us-east-1',
|
|
63
|
+
...serviceConfig.provider
|
|
64
|
+
},
|
|
65
|
+
plugins: serviceConfig.plugins || [],
|
|
66
|
+
custom: serviceConfig.custom || {},
|
|
67
|
+
functions: serviceConfig.functions || {},
|
|
68
|
+
...serviceConfig
|
|
69
|
+
},
|
|
70
|
+
processedInput: {
|
|
71
|
+
commands: commands
|
|
72
|
+
},
|
|
73
|
+
getProvider: jest.fn(() => ({})),
|
|
74
|
+
extendConfiguration: jest.fn()
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Verify that environment variables are set correctly
|
|
80
|
+
* @param {Object} expectedVars - Expected environment variables
|
|
81
|
+
*/
|
|
82
|
+
function verifyEnvironmentVariables(expectedVars) {
|
|
83
|
+
Object.keys(expectedVars).forEach(key => {
|
|
84
|
+
expect(process.env[key]).toBe(expectedVars[key]);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Create a mock integration definition
|
|
90
|
+
* @param {string} name - Integration name
|
|
91
|
+
* @returns {Object} Mock integration
|
|
92
|
+
*/
|
|
93
|
+
function createMockIntegration(name = 'testIntegration') {
|
|
94
|
+
return {
|
|
95
|
+
Definition: {
|
|
96
|
+
name: name
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Create a mock app definition with specified features
|
|
103
|
+
* @param {Object} features - Features to enable (vpc, kms, ssm)
|
|
104
|
+
* @param {Array} integrations - Integration definitions
|
|
105
|
+
* @returns {Object} Mock app definition
|
|
106
|
+
*/
|
|
107
|
+
function createMockAppDefinition(features = {}, integrations = []) {
|
|
108
|
+
const appDefinition = {
|
|
109
|
+
name: 'test-app',
|
|
110
|
+
integrations: integrations
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
if (features.vpc) {
|
|
114
|
+
appDefinition.vpc = { enable: true };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (features.kms) {
|
|
118
|
+
appDefinition.encryption = { useDefaultKMSForFieldLevelEncryption: true };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (features.ssm) {
|
|
122
|
+
appDefinition.ssm = { enable: true };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return appDefinition;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Verify serverless configuration contains expected VPC settings
|
|
130
|
+
* @param {Object} config - Serverless configuration
|
|
131
|
+
*/
|
|
132
|
+
function verifyVpcConfiguration(config) {
|
|
133
|
+
expect(config.provider.vpc).toBe('${self:custom.vpc.${self:provider.stage}}');
|
|
134
|
+
expect(config.custom.vpc).toEqual({
|
|
135
|
+
'${self:provider.stage}': {
|
|
136
|
+
securityGroupIds: ['${env:AWS_DISCOVERY_SECURITY_GROUP_ID}'],
|
|
137
|
+
subnetIds: [
|
|
138
|
+
'${env:AWS_DISCOVERY_SUBNET_ID_1}',
|
|
139
|
+
'${env:AWS_DISCOVERY_SUBNET_ID_2}'
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
expect(config.resources.Resources.VPCEndpointS3).toBeDefined();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Verify serverless configuration contains expected KMS settings
|
|
148
|
+
* @param {Object} config - Serverless configuration
|
|
149
|
+
*/
|
|
150
|
+
function verifyKmsConfiguration(config) {
|
|
151
|
+
expect(config.plugins).toContain('serverless-kms-grants');
|
|
152
|
+
expect(config.provider.environment.KMS_KEY_ARN).toBe('${self:custom.kmsGrants.kmsKeyId}');
|
|
153
|
+
expect(config.custom.kmsGrants).toEqual({
|
|
154
|
+
kmsKeyId: '${env:AWS_DISCOVERY_KMS_KEY_ID}'
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Verify KMS IAM permissions
|
|
158
|
+
const kmsPermission = config.provider.iamRoleStatements.find(
|
|
159
|
+
statement => statement.Action.includes('kms:GenerateDataKey')
|
|
160
|
+
);
|
|
161
|
+
expect(kmsPermission).toBeDefined();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Verify serverless configuration contains expected SSM settings
|
|
166
|
+
* @param {Object} config - Serverless configuration
|
|
167
|
+
*/
|
|
168
|
+
function verifySsmConfiguration(config) {
|
|
169
|
+
expect(config.provider.layers).toEqual([
|
|
170
|
+
'arn:aws:lambda:${self:provider.region}:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension:11'
|
|
171
|
+
]);
|
|
172
|
+
expect(config.provider.environment.SSM_PARAMETER_PREFIX).toBe('/${self:service}/${self:provider.stage}');
|
|
173
|
+
|
|
174
|
+
// Verify SSM IAM permissions
|
|
175
|
+
const ssmPermission = config.provider.iamRoleStatements.find(
|
|
176
|
+
statement => statement.Action.includes('ssm:GetParameter')
|
|
177
|
+
);
|
|
178
|
+
expect(ssmPermission).toBeDefined();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Verify integration-specific resources are created
|
|
183
|
+
* @param {Object} config - Serverless configuration
|
|
184
|
+
* @param {string} integrationName - Name of the integration
|
|
185
|
+
*/
|
|
186
|
+
function verifyIntegrationConfiguration(config, integrationName) {
|
|
187
|
+
const capitalizedName = integrationName.charAt(0).toUpperCase() + integrationName.slice(1);
|
|
188
|
+
|
|
189
|
+
// Verify integration function
|
|
190
|
+
expect(config.functions[integrationName]).toBeDefined();
|
|
191
|
+
|
|
192
|
+
// Verify queue worker function
|
|
193
|
+
expect(config.functions[`${integrationName}QueueWorker`]).toBeDefined();
|
|
194
|
+
|
|
195
|
+
// Verify SQS queue resource
|
|
196
|
+
expect(config.resources.Resources[`${capitalizedName}Queue`]).toBeDefined();
|
|
197
|
+
|
|
198
|
+
// Verify environment variable
|
|
199
|
+
expect(config.provider.environment[`${integrationName.toUpperCase()}_QUEUE_URL`]).toBeDefined();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Wait for async operations to complete
|
|
204
|
+
* @param {number} ms - Milliseconds to wait
|
|
205
|
+
*/
|
|
206
|
+
function wait(ms = 0) {
|
|
207
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Capture console output for testing
|
|
212
|
+
*/
|
|
213
|
+
function captureConsoleOutput() {
|
|
214
|
+
const originalLog = console.log;
|
|
215
|
+
const originalError = console.error;
|
|
216
|
+
const originalWarn = console.warn;
|
|
217
|
+
|
|
218
|
+
const logs = [];
|
|
219
|
+
const errors = [];
|
|
220
|
+
const warnings = [];
|
|
221
|
+
|
|
222
|
+
console.log = (...args) => {
|
|
223
|
+
logs.push(args.join(' '));
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
console.error = (...args) => {
|
|
227
|
+
errors.push(args.join(' '));
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
console.warn = (...args) => {
|
|
231
|
+
warnings.push(args.join(' '));
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
logs,
|
|
236
|
+
errors,
|
|
237
|
+
warnings,
|
|
238
|
+
restore: () => {
|
|
239
|
+
console.log = originalLog;
|
|
240
|
+
console.error = originalError;
|
|
241
|
+
console.warn = originalWarn;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Mock process.argv for testing
|
|
248
|
+
* @param {Array} argv - Arguments to set
|
|
249
|
+
*/
|
|
250
|
+
function mockProcessArgv(argv = ['node', 'test']) {
|
|
251
|
+
const originalArgv = process.argv;
|
|
252
|
+
jest.spyOn(process, 'argv', 'get').mockReturnValue(argv);
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
restore: () => {
|
|
256
|
+
process.argv = originalArgv;
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
module.exports = {
|
|
262
|
+
setTestEnvironmentVariables,
|
|
263
|
+
cleanupTestEnvironmentVariables,
|
|
264
|
+
setFallbackEnvironmentVariables,
|
|
265
|
+
createMockSendFunction,
|
|
266
|
+
createMockServerless,
|
|
267
|
+
verifyEnvironmentVariables,
|
|
268
|
+
createMockIntegration,
|
|
269
|
+
createMockAppDefinition,
|
|
270
|
+
verifyVpcConfiguration,
|
|
271
|
+
verifyKmsConfiguration,
|
|
272
|
+
verifySsmConfiguration,
|
|
273
|
+
verifyIntegrationConfiguration,
|
|
274
|
+
wait,
|
|
275
|
+
captureConsoleOutput,
|
|
276
|
+
mockProcessArgv
|
|
277
|
+
};
|