@friggframework/devtools 2.0.0--canary.490.feacde9.0 → 2.0.0--canary.497.a3f25f9.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/deploy-command/index.js +3 -9
- package/infrastructure/README.md +0 -28
- package/infrastructure/domains/database/migration-builder.js +13 -19
- package/infrastructure/domains/database/migration-builder.test.js +0 -57
- package/infrastructure/domains/integration/integration-builder.js +14 -19
- package/infrastructure/domains/integration/integration-builder.test.js +74 -0
- package/infrastructure/domains/networking/vpc-builder.js +18 -240
- package/infrastructure/domains/networking/vpc-builder.test.js +13 -711
- package/infrastructure/domains/networking/vpc-resolver.js +40 -221
- package/infrastructure/domains/networking/vpc-resolver.test.js +18 -318
- package/infrastructure/domains/security/kms-builder.js +6 -55
- package/infrastructure/domains/security/kms-builder.test.js +1 -19
- package/infrastructure/domains/shared/cloudformation-discovery.js +13 -310
- package/infrastructure/domains/shared/cloudformation-discovery.test.js +0 -395
- package/infrastructure/domains/shared/providers/aws-provider-adapter.js +6 -41
- package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +0 -39
- package/infrastructure/domains/shared/resource-discovery.js +5 -17
- package/infrastructure/domains/shared/resource-discovery.test.js +0 -36
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +17 -27
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +0 -73
- package/infrastructure/infrastructure-composer.js +3 -11
- package/infrastructure/scripts/build-prisma-layer.js +81 -8
- package/infrastructure/scripts/build-prisma-layer.test.js +53 -1
- package/infrastructure/scripts/verify-prisma-layer.js +72 -0
- package/package.json +7 -7
- package/layers/prisma/.build-complete +0 -3
|
@@ -398,93 +398,19 @@ describe('VpcBuilder', () => {
|
|
|
398
398
|
expect(result.resources.FriggS3VPCEndpoint.Properties.VpcId).toBe('vpc-123');
|
|
399
399
|
});
|
|
400
400
|
|
|
401
|
-
it('should
|
|
401
|
+
it('should reuse stack-managed VPC endpoints without creating CloudFormation resources', async () => {
|
|
402
402
|
const appDefinition = {
|
|
403
|
-
vpc: { enable: true },
|
|
404
|
-
};
|
|
405
|
-
|
|
406
|
-
const discoveredResources = {
|
|
407
|
-
fromCloudFormationStack: true,
|
|
408
|
-
stackName: 'test-stack',
|
|
409
|
-
existingLogicalIds: ['FriggLambdaSecurityGroup'],
|
|
410
|
-
defaultVpcId: 'vpc-123',
|
|
411
|
-
privateSubnetId1: 'subnet-1',
|
|
412
|
-
privateSubnetId2: 'subnet-2',
|
|
413
|
-
lambdaSecurityGroupId: 'sg-existing-stack', // Existing in stack
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
const result = await vpcBuilder.build(appDefinition, discoveredResources);
|
|
417
|
-
|
|
418
|
-
// CRITICAL: Must RE-ADD stack-managed SG to template or CloudFormation will DELETE it
|
|
419
|
-
expect(result.resources.FriggLambdaSecurityGroup).toBeDefined();
|
|
420
|
-
expect(result.resources.FriggLambdaSecurityGroup.Type).toBe('AWS::EC2::SecurityGroup');
|
|
421
|
-
expect(result.resources.FriggLambdaSecurityGroup.Properties.VpcId).toBe('vpc-123');
|
|
422
|
-
expect(result.resources.FriggLambdaSecurityGroup.Properties.GroupDescription).toBeDefined();
|
|
423
|
-
|
|
424
|
-
// Should use Ref in Lambda config (not recreating)
|
|
425
|
-
expect(result.vpcConfig.securityGroupIds).toContainEqual({ Ref: 'FriggLambdaSecurityGroup' });
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
it('should add stack-managed subnets back to template to prevent deletion', async () => {
|
|
429
|
-
const appDefinition = {
|
|
430
|
-
vpc: { enable: true },
|
|
431
|
-
};
|
|
432
|
-
|
|
433
|
-
const discoveredResources = {
|
|
434
|
-
fromCloudFormationStack: true,
|
|
435
|
-
stackName: 'test-stack',
|
|
436
|
-
existingLogicalIds: ['FriggPrivateSubnet1', 'FriggPrivateSubnet2'],
|
|
437
|
-
defaultVpcId: 'vpc-123',
|
|
438
|
-
// Subnets exist in stack with specific IDs
|
|
439
|
-
privateSubnetId1: 'subnet-existing-1',
|
|
440
|
-
privateSubnetId2: 'subnet-existing-2',
|
|
441
|
-
};
|
|
442
|
-
|
|
443
|
-
const result = await vpcBuilder.build(appDefinition, discoveredResources);
|
|
444
|
-
|
|
445
|
-
// CRITICAL: Must RE-ADD stack-managed subnets to template or CloudFormation will DELETE them
|
|
446
|
-
expect(result.resources.FriggPrivateSubnet1).toBeDefined();
|
|
447
|
-
expect(result.resources.FriggPrivateSubnet1.Type).toBe('AWS::EC2::Subnet');
|
|
448
|
-
expect(result.resources.FriggPrivateSubnet1.Properties.VpcId).toBe('vpc-123');
|
|
449
|
-
|
|
450
|
-
expect(result.resources.FriggPrivateSubnet2).toBeDefined();
|
|
451
|
-
expect(result.resources.FriggPrivateSubnet2.Type).toBe('AWS::EC2::Subnet');
|
|
452
|
-
expect(result.resources.FriggPrivateSubnet2.Properties.VpcId).toBe('vpc-123');
|
|
453
|
-
|
|
454
|
-
// Should use Refs (not external IDs)
|
|
455
|
-
expect(result.vpcConfig.subnetIds).toEqual([
|
|
456
|
-
{ Ref: 'FriggPrivateSubnet1' },
|
|
457
|
-
{ Ref: 'FriggPrivateSubnet2' }
|
|
458
|
-
]);
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
it('should add stack-managed VPC endpoints back to template to prevent deletion', async () => {
|
|
462
|
-
const appDefinition = {
|
|
463
|
-
vpc: { enable: true },
|
|
403
|
+
vpc: { enable: true, enableVPCEndpoints: true },
|
|
464
404
|
encryption: { fieldLevelEncryptionMethod: 'kms' },
|
|
405
|
+
database: { postgres: { enable: true } },
|
|
465
406
|
};
|
|
466
|
-
|
|
467
|
-
// Structured discovery from CloudFormation
|
|
468
407
|
const discoveredResources = {
|
|
469
|
-
fromCloudFormationStack: true,
|
|
470
|
-
stackName: 'test-stack',
|
|
471
|
-
existingLogicalIds: [
|
|
472
|
-
'FriggLambdaSecurityGroup',
|
|
473
|
-
'FriggLambdaRouteTable',
|
|
474
|
-
'FriggS3VPCEndpoint',
|
|
475
|
-
'FriggDynamoDBVPCEndpoint',
|
|
476
|
-
'FriggKMSVPCEndpoint',
|
|
477
|
-
'FriggSecretsManagerVPCEndpoint',
|
|
478
|
-
'FriggSQSVPCEndpoint'
|
|
479
|
-
],
|
|
480
408
|
defaultVpcId: 'vpc-123',
|
|
481
409
|
privateSubnetId1: 'subnet-1',
|
|
482
410
|
privateSubnetId2: 'subnet-2',
|
|
483
|
-
|
|
484
|
-
lambdaSecurityGroupId: 'sg-123',
|
|
485
|
-
// VPC endpoints discovered in stack
|
|
411
|
+
// VPC endpoints from CloudFormation stack (string IDs)
|
|
486
412
|
s3VpcEndpointId: 'vpce-s3-stack',
|
|
487
|
-
|
|
413
|
+
dynamoDbVpcEndpointId: 'vpce-ddb-stack',
|
|
488
414
|
kmsVpcEndpointId: 'vpce-kms-stack',
|
|
489
415
|
secretsManagerVpcEndpointId: 'vpce-sm-stack',
|
|
490
416
|
sqsVpcEndpointId: 'vpce-sqs-stack',
|
|
@@ -492,30 +418,15 @@ describe('VpcBuilder', () => {
|
|
|
492
418
|
|
|
493
419
|
const result = await vpcBuilder.build(appDefinition, discoveredResources);
|
|
494
420
|
|
|
495
|
-
//
|
|
496
|
-
expect(result.resources.FriggS3VPCEndpoint).
|
|
497
|
-
expect(result.resources.
|
|
498
|
-
expect(result.resources.
|
|
499
|
-
|
|
500
|
-
expect(result.resources.
|
|
501
|
-
expect(result.resources.FriggDynamoDBVPCEndpoint.Type).toBe('AWS::EC2::VPCEndpoint');
|
|
502
|
-
expect(result.resources.FriggDynamoDBVPCEndpoint.Properties.VpcEndpointType).toBe('Gateway');
|
|
503
|
-
|
|
504
|
-
expect(result.resources.FriggKMSVPCEndpoint).toBeDefined();
|
|
505
|
-
expect(result.resources.FriggKMSVPCEndpoint.Type).toBe('AWS::EC2::VPCEndpoint');
|
|
506
|
-
expect(result.resources.FriggKMSVPCEndpoint.Properties.VpcEndpointType).toBe('Interface');
|
|
507
|
-
|
|
508
|
-
expect(result.resources.FriggSecretsManagerVPCEndpoint).toBeDefined();
|
|
509
|
-
expect(result.resources.FriggSecretsManagerVPCEndpoint.Type).toBe('AWS::EC2::VPCEndpoint');
|
|
510
|
-
expect(result.resources.FriggSecretsManagerVPCEndpoint.Properties.VpcEndpointType).toBe('Interface');
|
|
511
|
-
|
|
512
|
-
expect(result.resources.FriggSQSVPCEndpoint).toBeDefined();
|
|
513
|
-
expect(result.resources.FriggSQSVPCEndpoint.Type).toBe('AWS::EC2::VPCEndpoint');
|
|
514
|
-
expect(result.resources.FriggSQSVPCEndpoint.Properties.VpcEndpointType).toBe('Interface');
|
|
421
|
+
// Should NOT create CloudFormation resources (reuse stack endpoints)
|
|
422
|
+
expect(result.resources.FriggS3VPCEndpoint).toBeUndefined();
|
|
423
|
+
expect(result.resources.FriggDynamoDBVPCEndpoint).toBeUndefined();
|
|
424
|
+
expect(result.resources.FriggKMSVPCEndpoint).toBeUndefined();
|
|
425
|
+
expect(result.resources.FriggSecretsManagerVPCEndpoint).toBeUndefined();
|
|
426
|
+
expect(result.resources.FriggSQSVPCEndpoint).toBeUndefined();
|
|
515
427
|
|
|
516
|
-
// Should create VPC Endpoint Security Group
|
|
517
|
-
expect(result.resources.FriggVPCEndpointSecurityGroup).
|
|
518
|
-
expect(result.resources.FriggVPCEndpointSecurityGroup.Type).toBe('AWS::EC2::SecurityGroup');
|
|
428
|
+
// Should still NOT create VPC Endpoint Security Group
|
|
429
|
+
expect(result.resources.FriggVPCEndpointSecurityGroup).toBeUndefined();
|
|
519
430
|
});
|
|
520
431
|
|
|
521
432
|
it('should create VPC endpoints when discovered from AWS but not stack', async () => {
|
|
@@ -804,35 +715,6 @@ describe('VpcBuilder', () => {
|
|
|
804
715
|
expect(result.resources.FriggPrivateSubnet2RouteTableAssociation.Properties.RouteTableId).toEqual({ Ref: 'FriggLambdaRouteTable' });
|
|
805
716
|
});
|
|
806
717
|
|
|
807
|
-
it('should add UpdateReplacePolicy to force association recreation on updates', async () => {
|
|
808
|
-
const appDefinition = {
|
|
809
|
-
vpc: {
|
|
810
|
-
enable: true,
|
|
811
|
-
management: 'discover',
|
|
812
|
-
subnets: { management: 'discover' },
|
|
813
|
-
natGateway: { management: 'discover' },
|
|
814
|
-
},
|
|
815
|
-
};
|
|
816
|
-
|
|
817
|
-
const discoveredResources = {
|
|
818
|
-
vpcId: 'vpc-123',
|
|
819
|
-
privateSubnetId1: 'subnet-existing-1',
|
|
820
|
-
privateSubnetId2: 'subnet-existing-2',
|
|
821
|
-
natGatewayId: 'nat-existing',
|
|
822
|
-
routeTableId: 'rtb-old',
|
|
823
|
-
existingLogicalIds: ['FriggSubnet1RouteAssociation', 'FriggSubnet2RouteAssociation'],
|
|
824
|
-
};
|
|
825
|
-
|
|
826
|
-
const result = await vpcBuilder.build(appDefinition, discoveredResources);
|
|
827
|
-
|
|
828
|
-
// Verify associations have UpdateReplacePolicy to force recreation
|
|
829
|
-
expect(result.resources.FriggSubnet1RouteAssociation.UpdateReplacePolicy).toBe('Delete');
|
|
830
|
-
expect(result.resources.FriggSubnet2RouteAssociation.UpdateReplacePolicy).toBe('Delete');
|
|
831
|
-
|
|
832
|
-
// This forces CloudFormation to delete old associations and create new ones
|
|
833
|
-
// instead of trying to update them in-place (which doesn't work)
|
|
834
|
-
});
|
|
835
|
-
|
|
836
718
|
it('should not create NAT when existing NAT is properly placed', async () => {
|
|
837
719
|
const appDefinition = {
|
|
838
720
|
vpc: {
|
|
@@ -1376,585 +1258,5 @@ describe('VpcBuilder', () => {
|
|
|
1376
1258
|
expect(result.outputs.PrivateSubnet2Id).toBeDefined();
|
|
1377
1259
|
});
|
|
1378
1260
|
});
|
|
1379
|
-
|
|
1380
|
-
describe('External VPC with stack-managed routing infrastructure pattern', () => {
|
|
1381
|
-
it('should correctly handle external VPC with NEW logical IDs (FriggPrivateRoute pattern)', async () => {
|
|
1382
|
-
// This pattern occurs when VPC/subnets/NAT are external but routing (route tables,
|
|
1383
|
-
// VPC endpoints, security groups) are managed by CloudFormation stack
|
|
1384
|
-
// This tests the NEWER naming convention
|
|
1385
|
-
const appDefinition = {
|
|
1386
|
-
vpc: { enable: true },
|
|
1387
|
-
encryption: { fieldLevelEncryptionMethod: 'kms' },
|
|
1388
|
-
database: {
|
|
1389
|
-
dynamodb: { enable: true } // Enable DynamoDB to create DynamoDB VPC endpoint
|
|
1390
|
-
}
|
|
1391
|
-
};
|
|
1392
|
-
|
|
1393
|
-
// Discovery results from real-world production scenario (newer stack)
|
|
1394
|
-
const discoveredResources = {
|
|
1395
|
-
fromCloudFormationStack: true,
|
|
1396
|
-
stackName: 'test-production-stack',
|
|
1397
|
-
existingLogicalIds: [
|
|
1398
|
-
'FriggLambdaSecurityGroup',
|
|
1399
|
-
'FriggLambdaRouteTable',
|
|
1400
|
-
'FriggPrivateRoute', // NEW naming
|
|
1401
|
-
'FriggPrivateSubnet1RouteTableAssociation', // NEW naming
|
|
1402
|
-
'FriggPrivateSubnet2RouteTableAssociation', // NEW naming
|
|
1403
|
-
'FriggS3VPCEndpoint', // NEW naming
|
|
1404
|
-
'FriggDynamoDBVPCEndpoint', // NEW naming
|
|
1405
|
-
'FriggKMSVPCEndpoint' // NEW naming
|
|
1406
|
-
],
|
|
1407
|
-
// Stack resources (from CloudFormation)
|
|
1408
|
-
lambdaSecurityGroupId: 'sg-01002240c6a446202',
|
|
1409
|
-
routeTableId: 'rtb-08af43bbf0775602d',
|
|
1410
|
-
s3VpcEndpointId: 'vpce-0d1ecb2c53ce9b4b8',
|
|
1411
|
-
dynamodbVpcEndpointId: 'vpce-0fb749b207f1020b0',
|
|
1412
|
-
kmsVpcEndpointId: 'vpce-0e38c25155b86de22',
|
|
1413
|
-
// External resources (discovered via queries)
|
|
1414
|
-
defaultVpcId: 'vpc-0cd17c0e06cb28b28',
|
|
1415
|
-
privateSubnetId1: 'subnet-034f6562dbbc16348',
|
|
1416
|
-
privateSubnetId2: 'subnet-0b8be2b82aeb5cdec',
|
|
1417
|
-
existingNatGatewayId: 'nat-022660c36a47e2d79'
|
|
1418
|
-
};
|
|
1419
|
-
|
|
1420
|
-
const result = await vpcBuilder.build(appDefinition, discoveredResources);
|
|
1421
|
-
|
|
1422
|
-
// === ASSERTIONS: Template Structure ===
|
|
1423
|
-
|
|
1424
|
-
// 1. VPC should be external (not in template)
|
|
1425
|
-
expect(result.resources.FriggVPC).toBeUndefined();
|
|
1426
|
-
expect(result.vpcId).toBe('vpc-0cd17c0e06cb28b28');
|
|
1427
|
-
|
|
1428
|
-
// 2. Security Group MUST be in template (stack-managed)
|
|
1429
|
-
expect(result.resources.FriggLambdaSecurityGroup).toBeDefined();
|
|
1430
|
-
expect(result.resources.FriggLambdaSecurityGroup.Type).toBe('AWS::EC2::SecurityGroup');
|
|
1431
|
-
expect(result.vpcConfig.securityGroupIds).toEqual([{ Ref: 'FriggLambdaSecurityGroup' }]);
|
|
1432
|
-
|
|
1433
|
-
// 3. Subnets should be external (use hardcoded IDs, not in template)
|
|
1434
|
-
expect(result.resources.FriggPrivateSubnet1).toBeUndefined();
|
|
1435
|
-
expect(result.resources.FriggPrivateSubnet2).toBeUndefined();
|
|
1436
|
-
expect(result.vpcConfig.subnetIds).toEqual([
|
|
1437
|
-
'subnet-034f6562dbbc16348',
|
|
1438
|
-
'subnet-0b8be2b82aeb5cdec'
|
|
1439
|
-
]);
|
|
1440
|
-
|
|
1441
|
-
// 4. NAT Gateway should be external (not in template)
|
|
1442
|
-
expect(result.resources.FriggNATGateway).toBeUndefined();
|
|
1443
|
-
expect(result.resources.FriggNATGatewayEIP).toBeUndefined();
|
|
1444
|
-
expect(result.natGatewayId).toBe('nat-022660c36a47e2d79');
|
|
1445
|
-
|
|
1446
|
-
// 5. Route table MUST be in template (stack-managed)
|
|
1447
|
-
expect(result.resources.FriggLambdaRouteTable).toBeDefined();
|
|
1448
|
-
expect(result.resources.FriggLambdaRouteTable.Type).toBe('AWS::EC2::RouteTable');
|
|
1449
|
-
|
|
1450
|
-
// 6. Route table associations MUST be in template
|
|
1451
|
-
expect(result.resources.FriggPrivateSubnet1RouteTableAssociation).toBeDefined();
|
|
1452
|
-
expect(result.resources.FriggPrivateSubnet2RouteTableAssociation).toBeDefined();
|
|
1453
|
-
|
|
1454
|
-
// 7. VPC Endpoints MUST be in template (stack-managed, prevents deletion)
|
|
1455
|
-
expect(result.resources.FriggS3VPCEndpoint).toBeDefined();
|
|
1456
|
-
expect(result.resources.FriggS3VPCEndpoint.Properties.VpcEndpointType).toBe('Gateway');
|
|
1457
|
-
|
|
1458
|
-
expect(result.resources.FriggDynamoDBVPCEndpoint).toBeDefined();
|
|
1459
|
-
expect(result.resources.FriggDynamoDBVPCEndpoint.Properties.VpcEndpointType).toBe('Gateway');
|
|
1460
|
-
|
|
1461
|
-
expect(result.resources.FriggKMSVPCEndpoint).toBeDefined();
|
|
1462
|
-
expect(result.resources.FriggKMSVPCEndpoint.Properties.VpcEndpointType).toBe('Interface');
|
|
1463
|
-
|
|
1464
|
-
// 8. VPC Endpoint Security Group needed for interface endpoints
|
|
1465
|
-
expect(result.resources.FriggVPCEndpointSecurityGroup).toBeDefined();
|
|
1466
|
-
|
|
1467
|
-
// === ASSERTIONS: Resource Count ===
|
|
1468
|
-
const resourceKeys = Object.keys(result.resources);
|
|
1469
|
-
const friggResources = resourceKeys.filter(k => k.startsWith('Frigg') || k.startsWith('VPC'));
|
|
1470
|
-
|
|
1471
|
-
// Should have routing infrastructure + endpoints + security groups
|
|
1472
|
-
// NOT full VPC (no FriggVPC, FriggPrivateSubnet1/2, FriggNATGateway)
|
|
1473
|
-
expect(friggResources).toContain('FriggLambdaSecurityGroup');
|
|
1474
|
-
expect(friggResources).toContain('FriggLambdaRouteTable');
|
|
1475
|
-
expect(friggResources).toContain('FriggS3VPCEndpoint');
|
|
1476
|
-
expect(friggResources).toContain('FriggDynamoDBVPCEndpoint');
|
|
1477
|
-
expect(friggResources).toContain('FriggKMSVPCEndpoint');
|
|
1478
|
-
expect(friggResources).not.toContain('FriggVPC');
|
|
1479
|
-
expect(friggResources).not.toContain('FriggPrivateSubnet1');
|
|
1480
|
-
expect(friggResources).not.toContain('FriggNATGateway');
|
|
1481
|
-
});
|
|
1482
|
-
|
|
1483
|
-
it('should use OLD logical IDs for backwards compatibility (FriggNATRoute, VPCEndpointS3 pattern)', async () => {
|
|
1484
|
-
// CRITICAL TEST: Real Frontify production stack uses OLD naming convention
|
|
1485
|
-
// Stack currently has: FriggNATRoute, VPCEndpointS3, VPCEndpointDynamoDB
|
|
1486
|
-
// We MUST use these same logical IDs to avoid AlreadyExists errors
|
|
1487
|
-
const appDefinition = {
|
|
1488
|
-
vpc: {
|
|
1489
|
-
enable: true,
|
|
1490
|
-
ownership: {
|
|
1491
|
-
securityGroup: 'external'
|
|
1492
|
-
},
|
|
1493
|
-
external: {
|
|
1494
|
-
securityGroupIds: ['sg-0c5e0d0e4a2f5efcf']
|
|
1495
|
-
}
|
|
1496
|
-
},
|
|
1497
|
-
encryption: { fieldLevelEncryptionMethod: 'kms' },
|
|
1498
|
-
database: {
|
|
1499
|
-
dynamodb: { enable: true }
|
|
1500
|
-
}
|
|
1501
|
-
};
|
|
1502
|
-
|
|
1503
|
-
// Discovery results matching ACTUAL Frontify production stack
|
|
1504
|
-
const discoveredResources = {
|
|
1505
|
-
fromCloudFormationStack: true,
|
|
1506
|
-
stackName: 'create-frigg-app-production',
|
|
1507
|
-
existingLogicalIds: [
|
|
1508
|
-
'FriggLambdaRouteTable',
|
|
1509
|
-
'FriggNATRoute', // OLD naming
|
|
1510
|
-
'FriggSubnet1RouteAssociation', // OLD naming
|
|
1511
|
-
'FriggSubnet2RouteAssociation', // OLD naming
|
|
1512
|
-
'VPCEndpointS3', // OLD naming
|
|
1513
|
-
'VPCEndpointDynamoDB' // OLD naming
|
|
1514
|
-
],
|
|
1515
|
-
// Structured discovery (what resolver needs)
|
|
1516
|
-
_structured: {
|
|
1517
|
-
stackManaged: [
|
|
1518
|
-
{ logicalId: 'FriggLambdaRouteTable', physicalId: 'rtb-08af43bbf0775602d', resourceType: 'AWS::EC2::RouteTable' },
|
|
1519
|
-
{ logicalId: 'FriggNATRoute', physicalId: 'rtb-08af43bbf0775602d|0.0.0.0/0', resourceType: 'AWS::EC2::Route' },
|
|
1520
|
-
{ logicalId: 'VPCEndpointS3', physicalId: 'vpce-0352ceac2124c14be', resourceType: 'AWS::EC2::VPCEndpoint' },
|
|
1521
|
-
{ logicalId: 'VPCEndpointDynamoDB', physicalId: 'vpce-0b06c4f631199ea68', resourceType: 'AWS::EC2::VPCEndpoint' }
|
|
1522
|
-
],
|
|
1523
|
-
external: [
|
|
1524
|
-
{ physicalId: 'vpc-01cd124575c683a17', resourceType: 'AWS::EC2::VPC' },
|
|
1525
|
-
{ physicalId: 'sg-0c5e0d0e4a2f5efcf', resourceType: 'AWS::EC2::SecurityGroup' },
|
|
1526
|
-
{ physicalId: 'subnet-0bbca02e9981df72c', resourceType: 'AWS::EC2::Subnet' },
|
|
1527
|
-
{ physicalId: 'subnet-005f7092b91efaaeb', resourceType: 'AWS::EC2::Subnet' },
|
|
1528
|
-
{ physicalId: 'nat-05a536cbe7056325f', resourceType: 'AWS::EC2::NatGateway' }
|
|
1529
|
-
]
|
|
1530
|
-
},
|
|
1531
|
-
// Flat discovery (for backwards compatibility)
|
|
1532
|
-
routeTableId: 'rtb-08af43bbf0775602d',
|
|
1533
|
-
natRoute: 'rtb-08af43bbf0775602d|0.0.0.0/0',
|
|
1534
|
-
s3VpcEndpointId: 'vpce-0352ceac2124c14be',
|
|
1535
|
-
dynamodbVpcEndpointId: 'vpce-0b06c4f631199ea68',
|
|
1536
|
-
// External resources
|
|
1537
|
-
defaultVpcId: 'vpc-01cd124575c683a17',
|
|
1538
|
-
defaultSecurityGroupId: 'sg-0c5e0d0e4a2f5efcf',
|
|
1539
|
-
privateSubnetId1: 'subnet-0bbca02e9981df72c',
|
|
1540
|
-
privateSubnetId2: 'subnet-005f7092b91efaaeb',
|
|
1541
|
-
existingNatGatewayId: 'nat-05a536cbe7056325f'
|
|
1542
|
-
};
|
|
1543
|
-
|
|
1544
|
-
const result = await vpcBuilder.build(appDefinition, discoveredResources);
|
|
1545
|
-
|
|
1546
|
-
// CRITICAL: Must use OLD logical IDs to match existing stack
|
|
1547
|
-
expect(result.resources.FriggNATRoute).toBeDefined();
|
|
1548
|
-
expect(result.resources.FriggNATRoute.Type).toBe('AWS::EC2::Route');
|
|
1549
|
-
expect(result.resources.FriggPrivateRoute).toBeUndefined(); // Should NOT create new ID
|
|
1550
|
-
|
|
1551
|
-
expect(result.resources.FriggSubnet1RouteAssociation).toBeDefined();
|
|
1552
|
-
expect(result.resources.FriggSubnet2RouteAssociation).toBeDefined();
|
|
1553
|
-
expect(result.resources.FriggPrivateSubnet1RouteTableAssociation).toBeUndefined();
|
|
1554
|
-
expect(result.resources.FriggPrivateSubnet2RouteTableAssociation).toBeUndefined();
|
|
1555
|
-
|
|
1556
|
-
expect(result.resources.VPCEndpointS3).toBeDefined();
|
|
1557
|
-
expect(result.resources.VPCEndpointS3.Type).toBe('AWS::EC2::VPCEndpoint');
|
|
1558
|
-
expect(result.resources.FriggS3VPCEndpoint).toBeUndefined(); // Should NOT create new ID
|
|
1559
|
-
|
|
1560
|
-
expect(result.resources.VPCEndpointDynamoDB).toBeDefined();
|
|
1561
|
-
expect(result.resources.VPCEndpointDynamoDB.Type).toBe('AWS::EC2::VPCEndpoint');
|
|
1562
|
-
expect(result.resources.FriggDynamoDBVPCEndpoint).toBeUndefined(); // Should NOT create new ID
|
|
1563
|
-
|
|
1564
|
-
// Route table should still be created
|
|
1565
|
-
expect(result.resources.FriggLambdaRouteTable).toBeDefined();
|
|
1566
|
-
});
|
|
1567
|
-
|
|
1568
|
-
it('should convert OLD logical IDs to structured discovery stackManaged array', () => {
|
|
1569
|
-
// TDD test: Verify that VPCEndpointS3 in existingLogicalIds gets added to stackManaged
|
|
1570
|
-
const flatDiscovery = {
|
|
1571
|
-
fromCloudFormationStack: true,
|
|
1572
|
-
stackName: 'create-frigg-app-production',
|
|
1573
|
-
existingLogicalIds: [
|
|
1574
|
-
'VPCEndpointS3', // OLD naming
|
|
1575
|
-
'VPCEndpointDynamoDB', // OLD naming
|
|
1576
|
-
'FriggNATRoute' // OLD naming
|
|
1577
|
-
],
|
|
1578
|
-
s3VpcEndpointId: 'vpce-0352ceac2124c14be',
|
|
1579
|
-
dynamodbVpcEndpointId: 'vpce-0b06c4f631199ea68',
|
|
1580
|
-
natRoute: 'rtb-xxx|0.0.0.0/0'
|
|
1581
|
-
};
|
|
1582
|
-
|
|
1583
|
-
const structured = vpcBuilder.convertFlatDiscoveryToStructured(flatDiscovery);
|
|
1584
|
-
|
|
1585
|
-
// CRITICAL: Old logical IDs should be in stackManaged array
|
|
1586
|
-
expect(structured.stackManaged).toContainEqual(
|
|
1587
|
-
expect.objectContaining({
|
|
1588
|
-
logicalId: 'VPCEndpointS3',
|
|
1589
|
-
physicalId: 'vpce-0352ceac2124c14be',
|
|
1590
|
-
resourceType: 'AWS::EC2::VPCEndpoint'
|
|
1591
|
-
})
|
|
1592
|
-
);
|
|
1593
|
-
expect(structured.stackManaged).toContainEqual(
|
|
1594
|
-
expect.objectContaining({
|
|
1595
|
-
logicalId: 'VPCEndpointDynamoDB',
|
|
1596
|
-
physicalId: 'vpce-0b06c4f631199ea68',
|
|
1597
|
-
resourceType: 'AWS::EC2::VPCEndpoint'
|
|
1598
|
-
})
|
|
1599
|
-
);
|
|
1600
|
-
expect(structured.stackManaged).toContainEqual(
|
|
1601
|
-
expect.objectContaining({
|
|
1602
|
-
logicalId: 'FriggNATRoute',
|
|
1603
|
-
physicalId: 'rtb-xxx|0.0.0.0/0',
|
|
1604
|
-
resourceType: 'AWS::EC2::Route'
|
|
1605
|
-
})
|
|
1606
|
-
);
|
|
1607
|
-
});
|
|
1608
|
-
});
|
|
1609
|
-
|
|
1610
|
-
describe('convertFlatDiscoveryToStructured - Direct Properties', () => {
|
|
1611
|
-
it('should copy flat discovery properties to structured discovery for resolver access', () => {
|
|
1612
|
-
const flatDiscovery = {
|
|
1613
|
-
fromCloudFormationStack: true,
|
|
1614
|
-
defaultVpcId: 'vpc-123',
|
|
1615
|
-
defaultSecurityGroupId: 'sg-default-456',
|
|
1616
|
-
lambdaSecurityGroupId: 'sg-lambda-789',
|
|
1617
|
-
privateSubnetId1: 'subnet-1',
|
|
1618
|
-
privateSubnetId2: 'subnet-2',
|
|
1619
|
-
natGatewayId: 'nat-123'
|
|
1620
|
-
};
|
|
1621
|
-
|
|
1622
|
-
const result = vpcBuilder.convertFlatDiscoveryToStructured(flatDiscovery);
|
|
1623
|
-
|
|
1624
|
-
// Direct properties should be copied for resolver access
|
|
1625
|
-
expect(result.defaultVpcId).toBe('vpc-123');
|
|
1626
|
-
expect(result.defaultSecurityGroupId).toBe('sg-default-456');
|
|
1627
|
-
expect(result.lambdaSecurityGroupId).toBe('sg-lambda-789');
|
|
1628
|
-
expect(result.privateSubnetId1).toBe('subnet-1');
|
|
1629
|
-
expect(result.privateSubnetId2).toBe('subnet-2');
|
|
1630
|
-
expect(result.natGatewayId).toBe('nat-123');
|
|
1631
|
-
});
|
|
1632
|
-
});
|
|
1633
|
-
|
|
1634
|
-
describe('VPC Endpoint Security Group with External Lambda SG', () => {
|
|
1635
|
-
it('should use external Lambda SG ID (not Ref) for VPC endpoint SG when Lambda SG is external', async () => {
|
|
1636
|
-
const appDefinition = {
|
|
1637
|
-
vpc: {
|
|
1638
|
-
enable: true,
|
|
1639
|
-
enableVPCEndpoints: true,
|
|
1640
|
-
ownership: {
|
|
1641
|
-
securityGroup: 'external' // External Lambda SG
|
|
1642
|
-
}
|
|
1643
|
-
},
|
|
1644
|
-
encryption: { fieldLevelEncryptionMethod: 'kms' }
|
|
1645
|
-
};
|
|
1646
|
-
const discoveredResources = {
|
|
1647
|
-
fromCloudFormationStack: true,
|
|
1648
|
-
defaultVpcId: 'vpc-123',
|
|
1649
|
-
defaultSecurityGroupId: 'sg-default-456', // Default VPC SG
|
|
1650
|
-
lambdaSecurityGroupId: 'sg-stack-789', // Stack-managed SG (will be ignored)
|
|
1651
|
-
privateSubnetId1: 'subnet-1',
|
|
1652
|
-
privateSubnetId2: 'subnet-2',
|
|
1653
|
-
natGatewayId: 'nat-123',
|
|
1654
|
-
existingLogicalIds: ['FriggS3VPCEndpoint', 'FriggKMSVPCEndpoint'],
|
|
1655
|
-
s3VpcEndpointId: 'vpce-s3-stack',
|
|
1656
|
-
kmsVpcEndpointId: 'vpce-kms-stack'
|
|
1657
|
-
};
|
|
1658
|
-
|
|
1659
|
-
const result = await vpcBuilder.build(appDefinition, discoveredResources);
|
|
1660
|
-
|
|
1661
|
-
// VPC Endpoint SG should be created
|
|
1662
|
-
expect(result.resources.FriggVPCEndpointSecurityGroup).toBeDefined();
|
|
1663
|
-
|
|
1664
|
-
// CRITICAL: Should use external Lambda SG ID directly, NOT a CloudFormation Ref
|
|
1665
|
-
const ingressRule = result.resources.FriggVPCEndpointSecurityGroup.Properties.SecurityGroupIngress[0];
|
|
1666
|
-
expect(ingressRule.SourceSecurityGroupId).toBe('sg-default-456'); // Direct ID, not { Ref: 'FriggLambdaSecurityGroup' }
|
|
1667
|
-
expect(typeof ingressRule.SourceSecurityGroupId).toBe('string');
|
|
1668
|
-
|
|
1669
|
-
// Verify FriggLambdaSecurityGroup is NOT in the template
|
|
1670
|
-
expect(result.resources.FriggLambdaSecurityGroup).toBeUndefined();
|
|
1671
|
-
});
|
|
1672
|
-
|
|
1673
|
-
it('should use CloudFormation Ref when Lambda SG is stack-managed', async () => {
|
|
1674
|
-
const appDefinition = {
|
|
1675
|
-
vpc: {
|
|
1676
|
-
enable: true,
|
|
1677
|
-
enableVPCEndpoints: true,
|
|
1678
|
-
ownership: {
|
|
1679
|
-
securityGroup: 'stack' // Stack-managed Lambda SG
|
|
1680
|
-
}
|
|
1681
|
-
},
|
|
1682
|
-
encryption: { fieldLevelEncryptionMethod: 'kms' }
|
|
1683
|
-
};
|
|
1684
|
-
const discoveredResources = {
|
|
1685
|
-
defaultVpcId: 'vpc-123',
|
|
1686
|
-
privateSubnetId1: 'subnet-1',
|
|
1687
|
-
privateSubnetId2: 'subnet-2'
|
|
1688
|
-
};
|
|
1689
|
-
|
|
1690
|
-
const result = await vpcBuilder.build(appDefinition, discoveredResources);
|
|
1691
|
-
|
|
1692
|
-
// VPC Endpoint SG should be created
|
|
1693
|
-
expect(result.resources.FriggVPCEndpointSecurityGroup).toBeDefined();
|
|
1694
|
-
|
|
1695
|
-
// Should use CloudFormation Ref when Lambda SG is in stack
|
|
1696
|
-
const ingressRule = result.resources.FriggVPCEndpointSecurityGroup.Properties.SecurityGroupIngress[0];
|
|
1697
|
-
expect(ingressRule.SourceSecurityGroupId).toEqual({ Ref: 'FriggLambdaSecurityGroup' });
|
|
1698
|
-
|
|
1699
|
-
// Verify FriggLambdaSecurityGroup IS in the template
|
|
1700
|
-
expect(result.resources.FriggLambdaSecurityGroup).toBeDefined();
|
|
1701
|
-
});
|
|
1702
|
-
});
|
|
1703
|
-
|
|
1704
|
-
describe('convertFlatDiscoveryToStructured - VPC Endpoints from CloudFormation', () => {
|
|
1705
|
-
it('should add VPC endpoints to stackManaged when in existingLogicalIds', () => {
|
|
1706
|
-
const flatDiscovery = {
|
|
1707
|
-
fromCloudFormationStack: true,
|
|
1708
|
-
stackName: 'test-stack',
|
|
1709
|
-
existingLogicalIds: [
|
|
1710
|
-
'FriggS3VPCEndpoint',
|
|
1711
|
-
'FriggDynamoDBVPCEndpoint',
|
|
1712
|
-
'FriggKMSVPCEndpoint'
|
|
1713
|
-
],
|
|
1714
|
-
s3VpcEndpointId: 'vpce-s3-stack',
|
|
1715
|
-
dynamodbVpcEndpointId: 'vpce-ddb-stack',
|
|
1716
|
-
kmsVpcEndpointId: 'vpce-kms-stack'
|
|
1717
|
-
};
|
|
1718
|
-
|
|
1719
|
-
const result = vpcBuilder.convertFlatDiscoveryToStructured(flatDiscovery);
|
|
1720
|
-
|
|
1721
|
-
// VPC endpoints should be in stackManaged (not external)
|
|
1722
|
-
expect(result.stackManaged).toContainEqual(
|
|
1723
|
-
expect.objectContaining({
|
|
1724
|
-
logicalId: 'FriggS3VPCEndpoint',
|
|
1725
|
-
physicalId: 'vpce-s3-stack',
|
|
1726
|
-
resourceType: 'AWS::EC2::VPCEndpoint'
|
|
1727
|
-
})
|
|
1728
|
-
);
|
|
1729
|
-
expect(result.stackManaged).toContainEqual(
|
|
1730
|
-
expect.objectContaining({
|
|
1731
|
-
logicalId: 'FriggDynamoDBVPCEndpoint',
|
|
1732
|
-
physicalId: 'vpce-ddb-stack',
|
|
1733
|
-
resourceType: 'AWS::EC2::VPCEndpoint'
|
|
1734
|
-
})
|
|
1735
|
-
);
|
|
1736
|
-
expect(result.stackManaged).toContainEqual(
|
|
1737
|
-
expect.objectContaining({
|
|
1738
|
-
logicalId: 'FriggKMSVPCEndpoint',
|
|
1739
|
-
physicalId: 'vpce-kms-stack',
|
|
1740
|
-
resourceType: 'AWS::EC2::VPCEndpoint'
|
|
1741
|
-
})
|
|
1742
|
-
);
|
|
1743
|
-
|
|
1744
|
-
// Should NOT be in external array
|
|
1745
|
-
expect(result.external.some(r => r.physicalId === 'vpce-s3-stack')).toBe(false);
|
|
1746
|
-
expect(result.external.some(r => r.physicalId === 'vpce-ddb-stack')).toBe(false);
|
|
1747
|
-
expect(result.external.some(r => r.physicalId === 'vpce-kms-stack')).toBe(false);
|
|
1748
|
-
});
|
|
1749
|
-
|
|
1750
|
-
it('should add VPC endpoints to external when NOT in existingLogicalIds', () => {
|
|
1751
|
-
const flatDiscovery = {
|
|
1752
|
-
fromCloudFormationStack: false, // AWS API discovery
|
|
1753
|
-
s3VpcEndpointId: 'vpce-s3-external',
|
|
1754
|
-
dynamodbVpcEndpointId: 'vpce-ddb-external'
|
|
1755
|
-
};
|
|
1756
|
-
|
|
1757
|
-
const result = vpcBuilder.convertFlatDiscoveryToStructured(flatDiscovery);
|
|
1758
|
-
|
|
1759
|
-
// Should be in external (AWS discovery)
|
|
1760
|
-
expect(result.external).toContainEqual(
|
|
1761
|
-
expect.objectContaining({
|
|
1762
|
-
physicalId: 'vpce-s3-external',
|
|
1763
|
-
resourceType: 'AWS::EC2::VPCEndpoint',
|
|
1764
|
-
source: 'aws-discovery'
|
|
1765
|
-
})
|
|
1766
|
-
);
|
|
1767
|
-
|
|
1768
|
-
// Should NOT be in stackManaged
|
|
1769
|
-
expect(result.stackManaged.some(r => r.physicalId === 'vpce-s3-external')).toBe(false);
|
|
1770
|
-
});
|
|
1771
|
-
|
|
1772
|
-
it('should preserve existing VPC endpoints and only create missing ones', async () => {
|
|
1773
|
-
const appDefinition = {
|
|
1774
|
-
vpc: { enable: true },
|
|
1775
|
-
encryption: { fieldLevelEncryptionMethod: 'kms' },
|
|
1776
|
-
};
|
|
1777
|
-
|
|
1778
|
-
const discoveredResources = {
|
|
1779
|
-
fromCloudFormationStack: true,
|
|
1780
|
-
stackName: 'test-stack',
|
|
1781
|
-
existingLogicalIds: [
|
|
1782
|
-
'FriggS3VPCEndpoint', // In stack
|
|
1783
|
-
'FriggDynamoDBVPCEndpoint', // In stack
|
|
1784
|
-
'FriggKMSVPCEndpoint' // In stack
|
|
1785
|
-
// SecretsManager and SQS NOT in stack (were deleted)
|
|
1786
|
-
],
|
|
1787
|
-
defaultVpcId: 'vpc-123',
|
|
1788
|
-
privateSubnetId1: 'subnet-1',
|
|
1789
|
-
privateSubnetId2: 'subnet-2',
|
|
1790
|
-
lambdaSecurityGroupId: 'sg-123',
|
|
1791
|
-
routeTableId: 'rtb-123',
|
|
1792
|
-
// Endpoints in stack
|
|
1793
|
-
s3VpcEndpointId: 'vpce-s3-existing',
|
|
1794
|
-
dynamodbVpcEndpointId: 'vpce-ddb-existing',
|
|
1795
|
-
kmsVpcEndpointId: 'vpce-kms-existing'
|
|
1796
|
-
// secretsManagerVpcEndpointId and sqsVpcEndpointId NOT present
|
|
1797
|
-
};
|
|
1798
|
-
|
|
1799
|
-
const result = await vpcBuilder.build(appDefinition, discoveredResources);
|
|
1800
|
-
|
|
1801
|
-
// Existing endpoints MUST be in template (re-added)
|
|
1802
|
-
expect(result.resources.FriggS3VPCEndpoint).toBeDefined();
|
|
1803
|
-
expect(result.resources.FriggS3VPCEndpoint.Properties.VpcId).toBe('vpc-123');
|
|
1804
|
-
|
|
1805
|
-
expect(result.resources.FriggDynamoDBVPCEndpoint).toBeDefined();
|
|
1806
|
-
expect(result.resources.FriggDynamoDBVPCEndpoint.Properties.VpcId).toBe('vpc-123');
|
|
1807
|
-
|
|
1808
|
-
expect(result.resources.FriggKMSVPCEndpoint).toBeDefined();
|
|
1809
|
-
expect(result.resources.FriggKMSVPCEndpoint.Properties.VpcId).toBe('vpc-123');
|
|
1810
|
-
|
|
1811
|
-
// Missing endpoints should also be created
|
|
1812
|
-
expect(result.resources.FriggSecretsManagerVPCEndpoint).toBeDefined();
|
|
1813
|
-
expect(result.resources.FriggSQSVPCEndpoint).toBeDefined();
|
|
1814
|
-
|
|
1815
|
-
// VPC Endpoint Security Group should be created
|
|
1816
|
-
expect(result.resources.FriggVPCEndpointSecurityGroup).toBeDefined();
|
|
1817
|
-
});
|
|
1818
|
-
});
|
|
1819
|
-
|
|
1820
|
-
describe('convertFlatDiscoveryToStructured - CloudFormation query results', () => {
|
|
1821
|
-
it('should add VPC from CloudFormation query to external array', () => {
|
|
1822
|
-
const flatDiscovery = {
|
|
1823
|
-
fromCloudFormationStack: true,
|
|
1824
|
-
stackName: 'test-stack',
|
|
1825
|
-
existingLogicalIds: ['FriggLambdaRouteTable', 'FriggLambdaSecurityGroup'],
|
|
1826
|
-
// VPC ID was extracted from security group query (NOT a stack resource)
|
|
1827
|
-
defaultVpcId: 'vpc-extracted-from-sg',
|
|
1828
|
-
lambdaSecurityGroupId: 'sg-123',
|
|
1829
|
-
routeTableId: 'rtb-123'
|
|
1830
|
-
};
|
|
1831
|
-
|
|
1832
|
-
const result = vpcBuilder.convertFlatDiscoveryToStructured(flatDiscovery);
|
|
1833
|
-
|
|
1834
|
-
// VPC should be in external array (discovered via query, not in stack)
|
|
1835
|
-
const vpcExternal = result.external.find(r => r.resourceType === 'AWS::EC2::VPC');
|
|
1836
|
-
expect(vpcExternal).toBeDefined();
|
|
1837
|
-
expect(vpcExternal.physicalId).toBe('vpc-extracted-from-sg');
|
|
1838
|
-
expect(vpcExternal.source).toBe('cloudformation-query');
|
|
1839
|
-
|
|
1840
|
-
// Security group SHOULD be in stackManaged (is in stack)
|
|
1841
|
-
const sgStack = result.stackManaged.find(r => r.logicalId === 'FriggLambdaSecurityGroup');
|
|
1842
|
-
expect(sgStack).toBeDefined();
|
|
1843
|
-
expect(sgStack.physicalId).toBe('sg-123');
|
|
1844
|
-
});
|
|
1845
|
-
|
|
1846
|
-
it('should add subnets from route table associations to external array', () => {
|
|
1847
|
-
const flatDiscovery = {
|
|
1848
|
-
fromCloudFormationStack: true,
|
|
1849
|
-
stackName: 'test-stack',
|
|
1850
|
-
existingLogicalIds: ['FriggLambdaRouteTable'],
|
|
1851
|
-
routeTableId: 'rtb-123',
|
|
1852
|
-
// Subnets extracted from route table associations (NOT stack resources)
|
|
1853
|
-
privateSubnetId1: 'subnet-1',
|
|
1854
|
-
privateSubnetId2: 'subnet-2'
|
|
1855
|
-
};
|
|
1856
|
-
|
|
1857
|
-
const result = vpcBuilder.convertFlatDiscoveryToStructured(flatDiscovery);
|
|
1858
|
-
|
|
1859
|
-
// Subnets should be in external array
|
|
1860
|
-
const subnet1 = result.external.find(r => r.physicalId === 'subnet-1');
|
|
1861
|
-
const subnet2 = result.external.find(r => r.physicalId === 'subnet-2');
|
|
1862
|
-
|
|
1863
|
-
expect(subnet1).toBeDefined();
|
|
1864
|
-
expect(subnet1.resourceType).toBe('AWS::EC2::Subnet');
|
|
1865
|
-
expect(subnet1.source).toBe('cloudformation-query');
|
|
1866
|
-
|
|
1867
|
-
expect(subnet2).toBeDefined();
|
|
1868
|
-
expect(subnet2.resourceType).toBe('AWS::EC2::Subnet');
|
|
1869
|
-
expect(subnet2.source).toBe('cloudformation-query');
|
|
1870
|
-
});
|
|
1871
|
-
|
|
1872
|
-
it('should add NAT Gateway from route table queries to external array', () => {
|
|
1873
|
-
const flatDiscovery = {
|
|
1874
|
-
fromCloudFormationStack: true,
|
|
1875
|
-
stackName: 'test-stack',
|
|
1876
|
-
existingLogicalIds: ['FriggLambdaRouteTable', 'FriggPrivateRoute'],
|
|
1877
|
-
routeTableId: 'rtb-123',
|
|
1878
|
-
// NAT Gateway extracted from route table routes (NOT a stack resource)
|
|
1879
|
-
existingNatGatewayId: 'nat-extracted'
|
|
1880
|
-
};
|
|
1881
|
-
|
|
1882
|
-
const result = vpcBuilder.convertFlatDiscoveryToStructured(flatDiscovery);
|
|
1883
|
-
|
|
1884
|
-
// NAT should be in external array
|
|
1885
|
-
const natExternal = result.external.find(r => r.resourceType === 'AWS::EC2::NatGateway');
|
|
1886
|
-
expect(natExternal).toBeDefined();
|
|
1887
|
-
expect(natExternal.physicalId).toBe('nat-extracted');
|
|
1888
|
-
expect(natExternal.source).toBe('cloudformation-query');
|
|
1889
|
-
});
|
|
1890
|
-
|
|
1891
|
-
it('should NOT add resources to external if they are in stack', () => {
|
|
1892
|
-
const flatDiscovery = {
|
|
1893
|
-
fromCloudFormationStack: true,
|
|
1894
|
-
stackName: 'test-stack',
|
|
1895
|
-
existingLogicalIds: ['FriggVPC', 'FriggPrivateSubnet1'],
|
|
1896
|
-
// These ARE in the stack
|
|
1897
|
-
defaultVpcId: 'vpc-in-stack',
|
|
1898
|
-
privateSubnetId1: 'subnet-in-stack'
|
|
1899
|
-
};
|
|
1900
|
-
|
|
1901
|
-
const result = vpcBuilder.convertFlatDiscoveryToStructured(flatDiscovery);
|
|
1902
|
-
|
|
1903
|
-
// Should be in stackManaged, NOT external
|
|
1904
|
-
expect(result.stackManaged.some(r => r.logicalId === 'FriggVPC')).toBe(true);
|
|
1905
|
-
expect(result.stackManaged.some(r => r.logicalId === 'FriggPrivateSubnet1')).toBe(true);
|
|
1906
|
-
|
|
1907
|
-
// Should NOT be in external
|
|
1908
|
-
expect(result.external.some(r => r.physicalId === 'vpc-in-stack')).toBe(false);
|
|
1909
|
-
expect(result.external.some(r => r.physicalId === 'subnet-in-stack')).toBe(false);
|
|
1910
|
-
});
|
|
1911
|
-
|
|
1912
|
-
it('should handle external VPC pattern: stack resources + queried external references', () => {
|
|
1913
|
-
const flatDiscovery = {
|
|
1914
|
-
fromCloudFormationStack: true,
|
|
1915
|
-
stackName: 'test-production-stack',
|
|
1916
|
-
existingLogicalIds: [
|
|
1917
|
-
'FriggLambdaSecurityGroup',
|
|
1918
|
-
'FriggLambdaRouteTable',
|
|
1919
|
-
'FriggPrivateRoute',
|
|
1920
|
-
'FriggPrivateSubnet1RouteTableAssociation',
|
|
1921
|
-
'FriggPrivateSubnet2RouteTableAssociation',
|
|
1922
|
-
'FriggS3VPCEndpoint',
|
|
1923
|
-
'FriggDynamoDBVPCEndpoint',
|
|
1924
|
-
'FriggKMSVPCEndpoint'
|
|
1925
|
-
],
|
|
1926
|
-
// Stack resources
|
|
1927
|
-
lambdaSecurityGroupId: 'sg-stack-123',
|
|
1928
|
-
routeTableId: 'rtb-stack-456',
|
|
1929
|
-
s3VpcEndpointId: 'vpce-s3-stack',
|
|
1930
|
-
// External resources (discovered via queries)
|
|
1931
|
-
defaultVpcId: 'vpc-external-123',
|
|
1932
|
-
privateSubnetId1: 'subnet-external-1',
|
|
1933
|
-
privateSubnetId2: 'subnet-external-2',
|
|
1934
|
-
existingNatGatewayId: 'nat-external-789'
|
|
1935
|
-
};
|
|
1936
|
-
|
|
1937
|
-
const result = vpcBuilder.convertFlatDiscoveryToStructured(flatDiscovery);
|
|
1938
|
-
|
|
1939
|
-
// Stack resources should be in stackManaged
|
|
1940
|
-
expect(result.stackManaged).toEqual(
|
|
1941
|
-
expect.arrayContaining([
|
|
1942
|
-
expect.objectContaining({ logicalId: 'FriggLambdaSecurityGroup', physicalId: 'sg-stack-123' }),
|
|
1943
|
-
expect.objectContaining({ logicalId: 'FriggLambdaRouteTable', physicalId: 'rtb-stack-456' }),
|
|
1944
|
-
expect.objectContaining({ logicalId: 'FriggS3VPCEndpoint', physicalId: 'vpce-s3-stack' })
|
|
1945
|
-
])
|
|
1946
|
-
);
|
|
1947
|
-
|
|
1948
|
-
// External resources should be in external array
|
|
1949
|
-
expect(result.external).toEqual(
|
|
1950
|
-
expect.arrayContaining([
|
|
1951
|
-
expect.objectContaining({ physicalId: 'vpc-external-123', resourceType: 'AWS::EC2::VPC', source: 'cloudformation-query' }),
|
|
1952
|
-
expect.objectContaining({ physicalId: 'subnet-external-1', resourceType: 'AWS::EC2::Subnet', source: 'cloudformation-query' }),
|
|
1953
|
-
expect.objectContaining({ physicalId: 'subnet-external-2', resourceType: 'AWS::EC2::Subnet', source: 'cloudformation-query' }),
|
|
1954
|
-
expect.objectContaining({ physicalId: 'nat-external-789', resourceType: 'AWS::EC2::NatGateway', source: 'cloudformation-query' })
|
|
1955
|
-
])
|
|
1956
|
-
);
|
|
1957
|
-
});
|
|
1958
|
-
});
|
|
1959
1261
|
});
|
|
1960
1262
|
|