@friggframework/devtools 2.0.0--canary.474.86c5119.0 → 2.0.0--canary.474.6a0bba7.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/infrastructure/domains/database/migration-builder.js +199 -1
- package/infrastructure/domains/database/migration-builder.test.js +73 -0
- package/infrastructure/domains/health/application/use-cases/__tests__/mismatch-analyzer-method-name.test.js +167 -0
- package/infrastructure/domains/health/application/use-cases/__tests__/repair-via-import-use-case.test.js +1130 -0
- package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.js +6 -0
- package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.js +307 -1
- package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.js +38 -5
- package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.test.js +3 -3
- package/infrastructure/domains/health/docs/ACME-DEV-DRIFT-ANALYSIS.md +267 -0
- package/infrastructure/domains/health/docs/BUILD-VS-DEPLOYED-TEMPLATE-ANALYSIS.md +324 -0
- package/infrastructure/domains/health/docs/ORPHAN-DETECTION-ANALYSIS.md +386 -0
- package/infrastructure/domains/health/docs/SPEC-CLEANUP-COMMAND.md +1419 -0
- package/infrastructure/domains/health/docs/TDD-IMPLEMENTATION-SUMMARY.md +391 -0
- package/infrastructure/domains/health/docs/TEMPLATE-COMPARISON-IMPLEMENTATION.md +551 -0
- package/infrastructure/domains/health/domain/entities/issue.js +50 -1
- package/infrastructure/domains/health/domain/entities/issue.test.js +111 -0
- package/infrastructure/domains/health/domain/services/__tests__/health-score-percentage-based.test.js +380 -0
- package/infrastructure/domains/health/domain/services/__tests__/logical-id-mapper.test.js +672 -0
- package/infrastructure/domains/health/domain/services/__tests__/template-parser.test.js +496 -0
- package/infrastructure/domains/health/domain/services/health-score-calculator.js +174 -91
- package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +332 -228
- package/infrastructure/domains/health/domain/services/logical-id-mapper.js +345 -0
- package/infrastructure/domains/health/domain/services/template-parser.js +245 -0
- package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-cfn-tagged.test.js +312 -0
- package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-multi-stack.test.js +367 -0
- package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-relationship-analysis.test.js +432 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.js +407 -20
- package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.test.js +698 -26
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.js +108 -14
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +69 -12
- package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +392 -1
- package/package.json +6 -6
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TemplateParser Tests
|
|
3
|
+
*
|
|
4
|
+
* TDD tests for CloudFormation template parsing functionality
|
|
5
|
+
* Domain Layer - Service Tests
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { TemplateParser } = require('../template-parser');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
describe('TemplateParser', () => {
|
|
13
|
+
let parser;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
parser = new TemplateParser();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('parseTemplate', () => {
|
|
20
|
+
it('should parse template from file path', () => {
|
|
21
|
+
// Arrange
|
|
22
|
+
const mockTemplate = {
|
|
23
|
+
AWSTemplateFormatVersion: '2010-09-09',
|
|
24
|
+
Description: 'Test template',
|
|
25
|
+
Resources: {
|
|
26
|
+
FriggVPC: { Type: 'AWS::EC2::VPC' },
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const tempFile = path.join(__dirname, 'temp-template.json');
|
|
31
|
+
fs.writeFileSync(tempFile, JSON.stringify(mockTemplate));
|
|
32
|
+
|
|
33
|
+
// Act
|
|
34
|
+
const result = parser.parseTemplate(tempFile);
|
|
35
|
+
|
|
36
|
+
// Assert
|
|
37
|
+
expect(result.resources).toEqual(mockTemplate.Resources);
|
|
38
|
+
expect(result.version).toBe('2010-09-09');
|
|
39
|
+
expect(result.description).toBe('Test template');
|
|
40
|
+
|
|
41
|
+
// Cleanup
|
|
42
|
+
fs.unlinkSync(tempFile);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should parse template from object', () => {
|
|
46
|
+
// Arrange
|
|
47
|
+
const mockTemplate = {
|
|
48
|
+
AWSTemplateFormatVersion: '2010-09-09',
|
|
49
|
+
Resources: {
|
|
50
|
+
FriggVPC: { Type: 'AWS::EC2::VPC' },
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Act
|
|
55
|
+
const result = parser.parseTemplate(mockTemplate);
|
|
56
|
+
|
|
57
|
+
// Assert
|
|
58
|
+
expect(result.resources).toEqual(mockTemplate.Resources);
|
|
59
|
+
expect(result.version).toBe('2010-09-09');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should throw error if template file not found', () => {
|
|
63
|
+
// Arrange
|
|
64
|
+
const nonExistentPath = '/path/to/nonexistent/template.json';
|
|
65
|
+
|
|
66
|
+
// Act & Assert
|
|
67
|
+
expect(() => parser.parseTemplate(nonExistentPath)).toThrow(
|
|
68
|
+
'Template not found at path'
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should return empty resources if Resources key missing', () => {
|
|
73
|
+
// Arrange
|
|
74
|
+
const mockTemplate = {
|
|
75
|
+
AWSTemplateFormatVersion: '2010-09-09',
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Act
|
|
79
|
+
const result = parser.parseTemplate(mockTemplate);
|
|
80
|
+
|
|
81
|
+
// Assert
|
|
82
|
+
expect(result.resources).toEqual({});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('getVpcResources', () => {
|
|
87
|
+
it('should extract VPC resources from template', () => {
|
|
88
|
+
// Arrange
|
|
89
|
+
const template = {
|
|
90
|
+
resources: {
|
|
91
|
+
FriggVPC: {
|
|
92
|
+
Type: 'AWS::EC2::VPC',
|
|
93
|
+
Properties: { CidrBlock: '10.0.0.0/16' },
|
|
94
|
+
},
|
|
95
|
+
FriggPrivateSubnet1: {
|
|
96
|
+
Type: 'AWS::EC2::Subnet',
|
|
97
|
+
Properties: { CidrBlock: '10.0.0.0/24' },
|
|
98
|
+
},
|
|
99
|
+
FriggLambdaSecurityGroup: {
|
|
100
|
+
Type: 'AWS::EC2::SecurityGroup',
|
|
101
|
+
Properties: { GroupDescription: 'Lambda SG' },
|
|
102
|
+
},
|
|
103
|
+
SomeOtherResource: {
|
|
104
|
+
Type: 'AWS::Lambda::Function',
|
|
105
|
+
Properties: {},
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Act
|
|
111
|
+
const result = parser.getVpcResources(template);
|
|
112
|
+
|
|
113
|
+
// Assert
|
|
114
|
+
expect(result).toHaveLength(3);
|
|
115
|
+
expect(result[0]).toEqual({
|
|
116
|
+
logicalId: 'FriggVPC',
|
|
117
|
+
resourceType: 'AWS::EC2::VPC',
|
|
118
|
+
properties: { CidrBlock: '10.0.0.0/16' },
|
|
119
|
+
});
|
|
120
|
+
expect(result[1]).toEqual({
|
|
121
|
+
logicalId: 'FriggPrivateSubnet1',
|
|
122
|
+
resourceType: 'AWS::EC2::Subnet',
|
|
123
|
+
properties: { CidrBlock: '10.0.0.0/24' },
|
|
124
|
+
});
|
|
125
|
+
expect(result[2]).toEqual({
|
|
126
|
+
logicalId: 'FriggLambdaSecurityGroup',
|
|
127
|
+
resourceType: 'AWS::EC2::SecurityGroup',
|
|
128
|
+
properties: { GroupDescription: 'Lambda SG' },
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should return empty array if no VPC resources', () => {
|
|
133
|
+
// Arrange
|
|
134
|
+
const template = {
|
|
135
|
+
resources: {
|
|
136
|
+
MyLambda: { Type: 'AWS::Lambda::Function' },
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// Act
|
|
141
|
+
const result = parser.getVpcResources(template);
|
|
142
|
+
|
|
143
|
+
// Assert
|
|
144
|
+
expect(result).toEqual([]);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should include all VPC-related resource types', () => {
|
|
148
|
+
// Arrange
|
|
149
|
+
const template = {
|
|
150
|
+
resources: {
|
|
151
|
+
MyVPC: { Type: 'AWS::EC2::VPC', Properties: {} },
|
|
152
|
+
MySubnet: { Type: 'AWS::EC2::Subnet', Properties: {} },
|
|
153
|
+
MySG: { Type: 'AWS::EC2::SecurityGroup', Properties: {} },
|
|
154
|
+
MyIGW: { Type: 'AWS::EC2::InternetGateway', Properties: {} },
|
|
155
|
+
MyNAT: { Type: 'AWS::EC2::NatGateway', Properties: {} },
|
|
156
|
+
MyRT: { Type: 'AWS::EC2::RouteTable', Properties: {} },
|
|
157
|
+
MyEndpoint: { Type: 'AWS::EC2::VPCEndpoint', Properties: {} },
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Act
|
|
162
|
+
const result = parser.getVpcResources(template);
|
|
163
|
+
|
|
164
|
+
// Assert
|
|
165
|
+
expect(result).toHaveLength(7);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('extractHardcodedIds', () => {
|
|
170
|
+
it('should extract hardcoded VPC IDs from deployed template', () => {
|
|
171
|
+
// Arrange
|
|
172
|
+
const template = {
|
|
173
|
+
resources: {
|
|
174
|
+
MyLambda: {
|
|
175
|
+
Type: 'AWS::Lambda::Function',
|
|
176
|
+
Properties: {
|
|
177
|
+
VpcConfig: {
|
|
178
|
+
VpcId: 'vpc-0eadd96976d29ede7',
|
|
179
|
+
SubnetIds: ['subnet-00ab9e0502e66aac3', 'subnet-00d085a52937aaf91'],
|
|
180
|
+
SecurityGroupIds: ['sg-07c01370e830b6ad6'],
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// Act
|
|
188
|
+
const result = parser.extractHardcodedIds(template);
|
|
189
|
+
|
|
190
|
+
// Assert
|
|
191
|
+
expect(result.vpcIds).toEqual(['vpc-0eadd96976d29ede7']);
|
|
192
|
+
expect(result.subnetIds).toEqual([
|
|
193
|
+
'subnet-00ab9e0502e66aac3',
|
|
194
|
+
'subnet-00d085a52937aaf91',
|
|
195
|
+
]);
|
|
196
|
+
expect(result.securityGroupIds).toEqual(['sg-07c01370e830b6ad6']);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should handle nested VPC configurations', () => {
|
|
200
|
+
// Arrange
|
|
201
|
+
const template = {
|
|
202
|
+
resources: {
|
|
203
|
+
Lambda1: {
|
|
204
|
+
Type: 'AWS::Lambda::Function',
|
|
205
|
+
Properties: {
|
|
206
|
+
VpcConfig: {
|
|
207
|
+
SubnetIds: ['subnet-111', 'subnet-222'],
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
Lambda2: {
|
|
212
|
+
Type: 'AWS::Lambda::Function',
|
|
213
|
+
Properties: {
|
|
214
|
+
VpcConfig: {
|
|
215
|
+
SubnetIds: ['subnet-333'],
|
|
216
|
+
SecurityGroupIds: ['sg-444'],
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// Act
|
|
224
|
+
const result = parser.extractHardcodedIds(template);
|
|
225
|
+
|
|
226
|
+
// Assert
|
|
227
|
+
expect(result.subnetIds).toEqual(['subnet-111', 'subnet-222', 'subnet-333']);
|
|
228
|
+
expect(result.securityGroupIds).toEqual(['sg-444']);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('should return empty arrays if no hardcoded IDs found', () => {
|
|
232
|
+
// Arrange
|
|
233
|
+
const template = {
|
|
234
|
+
resources: {
|
|
235
|
+
MyLambda: {
|
|
236
|
+
Type: 'AWS::Lambda::Function',
|
|
237
|
+
Properties: {},
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// Act
|
|
243
|
+
const result = parser.extractHardcodedIds(template);
|
|
244
|
+
|
|
245
|
+
// Assert
|
|
246
|
+
expect(result.vpcIds).toEqual([]);
|
|
247
|
+
expect(result.subnetIds).toEqual([]);
|
|
248
|
+
expect(result.securityGroupIds).toEqual([]);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should deduplicate hardcoded IDs', () => {
|
|
252
|
+
// Arrange
|
|
253
|
+
const template = {
|
|
254
|
+
resources: {
|
|
255
|
+
Lambda1: {
|
|
256
|
+
Type: 'AWS::Lambda::Function',
|
|
257
|
+
Properties: {
|
|
258
|
+
VpcConfig: {
|
|
259
|
+
SubnetIds: ['subnet-111', 'subnet-222'],
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
Lambda2: {
|
|
264
|
+
Type: 'AWS::Lambda::Function',
|
|
265
|
+
Properties: {
|
|
266
|
+
VpcConfig: {
|
|
267
|
+
SubnetIds: ['subnet-111', 'subnet-333'],
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// Act
|
|
275
|
+
const result = parser.extractHardcodedIds(template);
|
|
276
|
+
|
|
277
|
+
// Assert
|
|
278
|
+
expect(result.subnetIds).toEqual(['subnet-111', 'subnet-222', 'subnet-333']);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
describe('extractRefs', () => {
|
|
283
|
+
it('should extract Refs from build template', () => {
|
|
284
|
+
// Arrange
|
|
285
|
+
const template = {
|
|
286
|
+
resources: {
|
|
287
|
+
FriggVPC: { Type: 'AWS::EC2::VPC' },
|
|
288
|
+
FriggPrivateSubnet1: { Type: 'AWS::EC2::Subnet' },
|
|
289
|
+
MyLambda: {
|
|
290
|
+
Type: 'AWS::Lambda::Function',
|
|
291
|
+
Properties: {
|
|
292
|
+
VpcConfig: {
|
|
293
|
+
SubnetIds: [
|
|
294
|
+
{ Ref: 'FriggPrivateSubnet1' },
|
|
295
|
+
{ Ref: 'FriggPrivateSubnet2' },
|
|
296
|
+
],
|
|
297
|
+
SecurityGroupIds: [{ Ref: 'FriggLambdaSecurityGroup' }],
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
// Act
|
|
305
|
+
const result = parser.extractRefs(template);
|
|
306
|
+
|
|
307
|
+
// Assert
|
|
308
|
+
expect(result.subnetRefs).toEqual([
|
|
309
|
+
'FriggPrivateSubnet1',
|
|
310
|
+
'FriggPrivateSubnet2',
|
|
311
|
+
]);
|
|
312
|
+
expect(result.securityGroupRefs).toEqual(['FriggLambdaSecurityGroup']);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('should identify VPC Refs correctly', () => {
|
|
316
|
+
// Arrange
|
|
317
|
+
const template = {
|
|
318
|
+
resources: {
|
|
319
|
+
MySubnet: {
|
|
320
|
+
Type: 'AWS::EC2::Subnet',
|
|
321
|
+
Properties: {
|
|
322
|
+
VpcId: { Ref: 'FriggVPC' },
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// Act
|
|
329
|
+
const result = parser.extractRefs(template);
|
|
330
|
+
|
|
331
|
+
// Assert
|
|
332
|
+
expect(result.vpcRefs).toEqual(['FriggVPC']);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should not confuse VPCEndpoint with VPC refs', () => {
|
|
336
|
+
// Arrange
|
|
337
|
+
const template = {
|
|
338
|
+
resources: {
|
|
339
|
+
MyEndpoint: {
|
|
340
|
+
Type: 'AWS::EC2::VPCEndpoint',
|
|
341
|
+
Properties: {
|
|
342
|
+
VpcId: { Ref: 'FriggVPCEndpoint' },
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
// Act
|
|
349
|
+
const result = parser.extractRefs(template);
|
|
350
|
+
|
|
351
|
+
// Assert
|
|
352
|
+
expect(result.vpcRefs).toEqual([]); // VPCEndpoint should be excluded
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('should return empty arrays if no Refs found', () => {
|
|
356
|
+
// Arrange
|
|
357
|
+
const template = {
|
|
358
|
+
resources: {
|
|
359
|
+
MyLambda: {
|
|
360
|
+
Type: 'AWS::Lambda::Function',
|
|
361
|
+
Properties: {
|
|
362
|
+
VpcConfig: {
|
|
363
|
+
SubnetIds: ['subnet-111'],
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
// Act
|
|
371
|
+
const result = parser.extractRefs(template);
|
|
372
|
+
|
|
373
|
+
// Assert
|
|
374
|
+
expect(result.vpcRefs).toEqual([]);
|
|
375
|
+
expect(result.subnetRefs).toEqual([]);
|
|
376
|
+
expect(result.securityGroupRefs).toEqual([]);
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
describe('findLogicalIdForPhysicalId', () => {
|
|
381
|
+
it('should match physical ID to logical ID via template comparison', () => {
|
|
382
|
+
// Arrange
|
|
383
|
+
const deployedTemplate = {
|
|
384
|
+
resources: {
|
|
385
|
+
MyLambda: {
|
|
386
|
+
Type: 'AWS::Lambda::Function',
|
|
387
|
+
Properties: {
|
|
388
|
+
VpcConfig: {
|
|
389
|
+
SubnetIds: ['subnet-00ab9e0502e66aac3'],
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
const buildTemplate = {
|
|
397
|
+
resources: {
|
|
398
|
+
FriggPrivateSubnet1: { Type: 'AWS::EC2::Subnet' },
|
|
399
|
+
MyLambda: {
|
|
400
|
+
Type: 'AWS::Lambda::Function',
|
|
401
|
+
Properties: {
|
|
402
|
+
VpcConfig: {
|
|
403
|
+
SubnetIds: [{ Ref: 'FriggPrivateSubnet1' }],
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// Act
|
|
411
|
+
const result = parser.findLogicalIdForPhysicalId(
|
|
412
|
+
'subnet-00ab9e0502e66aac3',
|
|
413
|
+
deployedTemplate,
|
|
414
|
+
buildTemplate
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
// Assert
|
|
418
|
+
expect(result).toBe('FriggPrivateSubnet1');
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('should return null if no match found', () => {
|
|
422
|
+
// Arrange
|
|
423
|
+
const deployedTemplate = { resources: {} };
|
|
424
|
+
const buildTemplate = { resources: {} };
|
|
425
|
+
|
|
426
|
+
// Act
|
|
427
|
+
const result = parser.findLogicalIdForPhysicalId(
|
|
428
|
+
'subnet-unknown',
|
|
429
|
+
deployedTemplate,
|
|
430
|
+
buildTemplate
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
// Assert
|
|
434
|
+
expect(result).toBeNull();
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
describe('static methods', () => {
|
|
439
|
+
describe('getBuildTemplatePath', () => {
|
|
440
|
+
it('should return correct path to build template', () => {
|
|
441
|
+
// Act
|
|
442
|
+
const result = TemplateParser.getBuildTemplatePath('/project/root');
|
|
443
|
+
|
|
444
|
+
// Assert
|
|
445
|
+
expect(result).toBe(
|
|
446
|
+
'/project/root/.serverless/cloudformation-template-update-stack.json'
|
|
447
|
+
);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it('should use current directory if no path provided', () => {
|
|
451
|
+
// Act
|
|
452
|
+
const result = TemplateParser.getBuildTemplatePath();
|
|
453
|
+
|
|
454
|
+
// Assert
|
|
455
|
+
expect(result).toContain('.serverless/cloudformation-template-update-stack.json');
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
describe('buildTemplateExists', () => {
|
|
460
|
+
it('should return true if build template exists', () => {
|
|
461
|
+
// Arrange
|
|
462
|
+
const tempDir = path.join(__dirname, 'temp-project');
|
|
463
|
+
const serverlessDir = path.join(tempDir, '.serverless');
|
|
464
|
+
const templatePath = path.join(
|
|
465
|
+
serverlessDir,
|
|
466
|
+
'cloudformation-template-update-stack.json'
|
|
467
|
+
);
|
|
468
|
+
|
|
469
|
+
fs.mkdirSync(serverlessDir, { recursive: true });
|
|
470
|
+
fs.writeFileSync(templatePath, '{}');
|
|
471
|
+
|
|
472
|
+
// Act
|
|
473
|
+
const result = TemplateParser.buildTemplateExists(tempDir);
|
|
474
|
+
|
|
475
|
+
// Assert
|
|
476
|
+
expect(result).toBe(true);
|
|
477
|
+
|
|
478
|
+
// Cleanup
|
|
479
|
+
fs.unlinkSync(templatePath);
|
|
480
|
+
fs.rmdirSync(serverlessDir);
|
|
481
|
+
fs.rmdirSync(tempDir);
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it('should return false if build template does not exist', () => {
|
|
485
|
+
// Arrange
|
|
486
|
+
const tempDir = path.join(__dirname, 'nonexistent-project');
|
|
487
|
+
|
|
488
|
+
// Act
|
|
489
|
+
const result = TemplateParser.buildTemplateExists(tempDir);
|
|
490
|
+
|
|
491
|
+
// Assert
|
|
492
|
+
expect(result).toBe(false);
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
});
|