@friggframework/devtools 2.0.0--canary.474.d64c550.0 → 2.0.0--canary.474.082077e.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/health/infrastructure/adapters/aws-resource-detector.js +471 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +497 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +386 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.test.js +580 -0
- package/package.json +6 -6
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for AWSResourceDetector Adapter
|
|
3
|
+
*
|
|
4
|
+
* Tests AWS resource discovery for EC2, RDS, and KMS resources
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const AWSResourceDetector = require('./aws-resource-detector');
|
|
8
|
+
|
|
9
|
+
// Mock AWS SDK
|
|
10
|
+
jest.mock('@aws-sdk/client-ec2', () => ({
|
|
11
|
+
EC2Client: jest.fn(),
|
|
12
|
+
DescribeVpcsCommand: jest.fn(),
|
|
13
|
+
DescribeSubnetsCommand: jest.fn(),
|
|
14
|
+
DescribeSecurityGroupsCommand: jest.fn(),
|
|
15
|
+
DescribeRouteTablesCommand: jest.fn(),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
jest.mock('@aws-sdk/client-rds', () => ({
|
|
19
|
+
RDSClient: jest.fn(),
|
|
20
|
+
DescribeDBClustersCommand: jest.fn(),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
jest.mock('@aws-sdk/client-kms', () => ({
|
|
24
|
+
KMSClient: jest.fn(),
|
|
25
|
+
ListKeysCommand: jest.fn(),
|
|
26
|
+
DescribeKeyCommand: jest.fn(),
|
|
27
|
+
ListAliasesCommand: jest.fn(),
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
describe('AWSResourceDetector', () => {
|
|
31
|
+
let detector;
|
|
32
|
+
let mockEC2Send;
|
|
33
|
+
let mockRDSSend;
|
|
34
|
+
let mockKMSSend;
|
|
35
|
+
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
jest.clearAllMocks();
|
|
38
|
+
|
|
39
|
+
// Mock EC2 client
|
|
40
|
+
mockEC2Send = jest.fn();
|
|
41
|
+
const { EC2Client } = require('@aws-sdk/client-ec2');
|
|
42
|
+
EC2Client.mockImplementation(() => ({ send: mockEC2Send }));
|
|
43
|
+
|
|
44
|
+
// Mock RDS client
|
|
45
|
+
mockRDSSend = jest.fn();
|
|
46
|
+
const { RDSClient } = require('@aws-sdk/client-rds');
|
|
47
|
+
RDSClient.mockImplementation(() => ({ send: mockRDSSend }));
|
|
48
|
+
|
|
49
|
+
// Mock KMS client
|
|
50
|
+
mockKMSSend = jest.fn();
|
|
51
|
+
const { KMSClient } = require('@aws-sdk/client-kms');
|
|
52
|
+
KMSClient.mockImplementation(() => ({ send: mockKMSSend }));
|
|
53
|
+
|
|
54
|
+
detector = new AWSResourceDetector({ region: 'us-east-1' });
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('getSupportedResourceTypes', () => {
|
|
58
|
+
it('should return list of supported resource types', async () => {
|
|
59
|
+
const types = await detector.getSupportedResourceTypes();
|
|
60
|
+
|
|
61
|
+
expect(types).toContain('AWS::EC2::VPC');
|
|
62
|
+
expect(types).toContain('AWS::EC2::Subnet');
|
|
63
|
+
expect(types).toContain('AWS::EC2::SecurityGroup');
|
|
64
|
+
expect(types).toContain('AWS::EC2::RouteTable');
|
|
65
|
+
expect(types).toContain('AWS::RDS::DBCluster');
|
|
66
|
+
expect(types).toContain('AWS::KMS::Key');
|
|
67
|
+
expect(types).toHaveLength(6);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('detectResources - VPC', () => {
|
|
72
|
+
it('should detect VPCs', async () => {
|
|
73
|
+
mockEC2Send.mockResolvedValue({
|
|
74
|
+
Vpcs: [
|
|
75
|
+
{
|
|
76
|
+
VpcId: 'vpc-123',
|
|
77
|
+
CidrBlock: '10.0.0.0/16',
|
|
78
|
+
State: 'available',
|
|
79
|
+
Tags: [{ Key: 'Name', Value: 'Main VPC' }],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
VpcId: 'vpc-456',
|
|
83
|
+
CidrBlock: '10.1.0.0/16',
|
|
84
|
+
State: 'available',
|
|
85
|
+
Tags: [],
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const resources = await detector.detectResources({
|
|
91
|
+
resourceType: 'AWS::EC2::VPC',
|
|
92
|
+
region: 'us-east-1',
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
expect(resources).toHaveLength(2);
|
|
96
|
+
expect(resources[0]).toEqual({
|
|
97
|
+
physicalId: 'vpc-123',
|
|
98
|
+
resourceType: 'AWS::EC2::VPC',
|
|
99
|
+
properties: {
|
|
100
|
+
VpcId: 'vpc-123',
|
|
101
|
+
CidrBlock: '10.0.0.0/16',
|
|
102
|
+
State: 'available',
|
|
103
|
+
},
|
|
104
|
+
tags: { Name: 'Main VPC' },
|
|
105
|
+
createdTime: expect.any(Date),
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should filter VPCs by tags', async () => {
|
|
110
|
+
mockEC2Send.mockResolvedValue({
|
|
111
|
+
Vpcs: [
|
|
112
|
+
{
|
|
113
|
+
VpcId: 'vpc-123',
|
|
114
|
+
CidrBlock: '10.0.0.0/16',
|
|
115
|
+
State: 'available',
|
|
116
|
+
Tags: [{ Key: 'Environment', Value: 'production' }],
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const resources = await detector.detectResources({
|
|
122
|
+
resourceType: 'AWS::EC2::VPC',
|
|
123
|
+
region: 'us-east-1',
|
|
124
|
+
filters: { tags: { Environment: 'production' } },
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
expect(resources).toHaveLength(1);
|
|
128
|
+
expect(resources[0].tags).toEqual({ Environment: 'production' });
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('detectResources - Subnet', () => {
|
|
133
|
+
it('should detect Subnets', async () => {
|
|
134
|
+
mockEC2Send.mockResolvedValue({
|
|
135
|
+
Subnets: [
|
|
136
|
+
{
|
|
137
|
+
SubnetId: 'subnet-123',
|
|
138
|
+
VpcId: 'vpc-123',
|
|
139
|
+
CidrBlock: '10.0.1.0/24',
|
|
140
|
+
AvailabilityZone: 'us-east-1a',
|
|
141
|
+
State: 'available',
|
|
142
|
+
Tags: [{ Key: 'Name', Value: 'Private Subnet' }],
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const resources = await detector.detectResources({
|
|
148
|
+
resourceType: 'AWS::EC2::Subnet',
|
|
149
|
+
region: 'us-east-1',
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
expect(resources).toHaveLength(1);
|
|
153
|
+
expect(resources[0].physicalId).toBe('subnet-123');
|
|
154
|
+
expect(resources[0].properties.VpcId).toBe('vpc-123');
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe('detectResources - SecurityGroup', () => {
|
|
159
|
+
it('should detect SecurityGroups', async () => {
|
|
160
|
+
mockEC2Send.mockResolvedValue({
|
|
161
|
+
SecurityGroups: [
|
|
162
|
+
{
|
|
163
|
+
GroupId: 'sg-123',
|
|
164
|
+
GroupName: 'default',
|
|
165
|
+
Description: 'Default security group',
|
|
166
|
+
VpcId: 'vpc-123',
|
|
167
|
+
Tags: [],
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const resources = await detector.detectResources({
|
|
173
|
+
resourceType: 'AWS::EC2::SecurityGroup',
|
|
174
|
+
region: 'us-east-1',
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
expect(resources).toHaveLength(1);
|
|
178
|
+
expect(resources[0].physicalId).toBe('sg-123');
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('detectResources - RouteTable', () => {
|
|
183
|
+
it('should detect RouteTables', async () => {
|
|
184
|
+
mockEC2Send.mockResolvedValue({
|
|
185
|
+
RouteTables: [
|
|
186
|
+
{
|
|
187
|
+
RouteTableId: 'rtb-123',
|
|
188
|
+
VpcId: 'vpc-123',
|
|
189
|
+
Routes: [],
|
|
190
|
+
Associations: [],
|
|
191
|
+
Tags: [{ Key: 'Name', Value: 'Main Route Table' }],
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const resources = await detector.detectResources({
|
|
197
|
+
resourceType: 'AWS::EC2::RouteTable',
|
|
198
|
+
region: 'us-east-1',
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
expect(resources).toHaveLength(1);
|
|
202
|
+
expect(resources[0].physicalId).toBe('rtb-123');
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe('detectResources - RDS DBCluster', () => {
|
|
207
|
+
it('should detect RDS DBClusters', async () => {
|
|
208
|
+
mockRDSSend.mockResolvedValue({
|
|
209
|
+
DBClusters: [
|
|
210
|
+
{
|
|
211
|
+
DBClusterIdentifier: 'my-aurora-cluster',
|
|
212
|
+
DBClusterArn: 'arn:aws:rds:us-east-1:123456789012:cluster:my-aurora-cluster',
|
|
213
|
+
Engine: 'aurora-postgresql',
|
|
214
|
+
EngineVersion: '13.7',
|
|
215
|
+
Status: 'available',
|
|
216
|
+
ClusterCreateTime: new Date('2024-01-01T00:00:00Z'),
|
|
217
|
+
TagList: [{ Key: 'Environment', Value: 'production' }],
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const resources = await detector.detectResources({
|
|
223
|
+
resourceType: 'AWS::RDS::DBCluster',
|
|
224
|
+
region: 'us-east-1',
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
expect(resources).toHaveLength(1);
|
|
228
|
+
expect(resources[0]).toEqual({
|
|
229
|
+
physicalId: 'my-aurora-cluster',
|
|
230
|
+
resourceType: 'AWS::RDS::DBCluster',
|
|
231
|
+
properties: {
|
|
232
|
+
DBClusterIdentifier: 'my-aurora-cluster',
|
|
233
|
+
DBClusterArn: 'arn:aws:rds:us-east-1:123456789012:cluster:my-aurora-cluster',
|
|
234
|
+
Engine: 'aurora-postgresql',
|
|
235
|
+
EngineVersion: '13.7',
|
|
236
|
+
Status: 'available',
|
|
237
|
+
},
|
|
238
|
+
tags: { Environment: 'production' },
|
|
239
|
+
createdTime: new Date('2024-01-01T00:00:00Z'),
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
describe('detectResources - KMS Key', () => {
|
|
245
|
+
it('should detect KMS Keys', async () => {
|
|
246
|
+
// First call: ListKeys
|
|
247
|
+
mockKMSSend.mockResolvedValueOnce({
|
|
248
|
+
Keys: [
|
|
249
|
+
{ KeyId: 'key-123', KeyArn: 'arn:aws:kms:us-east-1:123456789012:key/key-123' },
|
|
250
|
+
],
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Second call: DescribeKey for key-123
|
|
254
|
+
mockKMSSend.mockResolvedValueOnce({
|
|
255
|
+
KeyMetadata: {
|
|
256
|
+
KeyId: 'key-123',
|
|
257
|
+
Arn: 'arn:aws:kms:us-east-1:123456789012:key/key-123',
|
|
258
|
+
CreationDate: new Date('2024-01-01T00:00:00Z'),
|
|
259
|
+
Enabled: true,
|
|
260
|
+
KeyState: 'Enabled',
|
|
261
|
+
KeyManager: 'CUSTOMER',
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Third call: ListAliases for key-123
|
|
266
|
+
mockKMSSend.mockResolvedValueOnce({
|
|
267
|
+
Aliases: [{ AliasName: 'alias/my-key', TargetKeyId: 'key-123' }],
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const resources = await detector.detectResources({
|
|
271
|
+
resourceType: 'AWS::KMS::Key',
|
|
272
|
+
region: 'us-east-1',
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
expect(resources).toHaveLength(1);
|
|
276
|
+
expect(resources[0].physicalId).toBe('key-123');
|
|
277
|
+
expect(resources[0].properties.KeyState).toBe('Enabled');
|
|
278
|
+
expect(mockKMSSend).toHaveBeenCalledTimes(3);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
describe('detectResources - unsupported type', () => {
|
|
283
|
+
it('should throw error for unsupported resource type', async () => {
|
|
284
|
+
await expect(
|
|
285
|
+
detector.detectResources({
|
|
286
|
+
resourceType: 'AWS::Lambda::Function',
|
|
287
|
+
region: 'us-east-1',
|
|
288
|
+
})
|
|
289
|
+
).rejects.toThrow('Resource type AWS::Lambda::Function is not supported');
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
describe('getResourceDetails', () => {
|
|
294
|
+
it('should get VPC details', async () => {
|
|
295
|
+
mockEC2Send.mockResolvedValue({
|
|
296
|
+
Vpcs: [
|
|
297
|
+
{
|
|
298
|
+
VpcId: 'vpc-123',
|
|
299
|
+
CidrBlock: '10.0.0.0/16',
|
|
300
|
+
State: 'available',
|
|
301
|
+
EnableDnsHostnames: true,
|
|
302
|
+
EnableDnsSupport: true,
|
|
303
|
+
Tags: [{ Key: 'Name', Value: 'Main VPC' }],
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const resource = await detector.getResourceDetails({
|
|
309
|
+
resourceType: 'AWS::EC2::VPC',
|
|
310
|
+
physicalId: 'vpc-123',
|
|
311
|
+
region: 'us-east-1',
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
expect(resource.physicalId).toBe('vpc-123');
|
|
315
|
+
expect(resource.properties.EnableDnsHostnames).toBe(true);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('should get RDS DBCluster details', async () => {
|
|
319
|
+
mockRDSSend.mockResolvedValue({
|
|
320
|
+
DBClusters: [
|
|
321
|
+
{
|
|
322
|
+
DBClusterIdentifier: 'my-cluster',
|
|
323
|
+
Engine: 'aurora-postgresql',
|
|
324
|
+
EngineVersion: '13.7',
|
|
325
|
+
Status: 'available',
|
|
326
|
+
ClusterCreateTime: new Date('2024-01-01T00:00:00Z'),
|
|
327
|
+
},
|
|
328
|
+
],
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
const resource = await detector.getResourceDetails({
|
|
332
|
+
resourceType: 'AWS::RDS::DBCluster',
|
|
333
|
+
physicalId: 'my-cluster',
|
|
334
|
+
region: 'us-east-1',
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
expect(resource.physicalId).toBe('my-cluster');
|
|
338
|
+
expect(resource.properties.Engine).toBe('aurora-postgresql');
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('should throw error if resource not found', async () => {
|
|
342
|
+
mockEC2Send.mockResolvedValue({ Vpcs: [] });
|
|
343
|
+
|
|
344
|
+
await expect(
|
|
345
|
+
detector.getResourceDetails({
|
|
346
|
+
resourceType: 'AWS::EC2::VPC',
|
|
347
|
+
physicalId: 'vpc-nonexistent',
|
|
348
|
+
region: 'us-east-1',
|
|
349
|
+
})
|
|
350
|
+
).rejects.toThrow('Resource vpc-nonexistent not found');
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
describe('resourceExists', () => {
|
|
355
|
+
it('should return true if VPC exists', async () => {
|
|
356
|
+
mockEC2Send.mockResolvedValue({
|
|
357
|
+
Vpcs: [{ VpcId: 'vpc-123' }],
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
const exists = await detector.resourceExists({
|
|
361
|
+
resourceType: 'AWS::EC2::VPC',
|
|
362
|
+
physicalId: 'vpc-123',
|
|
363
|
+
region: 'us-east-1',
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
expect(exists).toBe(true);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('should return false if VPC does not exist', async () => {
|
|
370
|
+
mockEC2Send.mockResolvedValue({ Vpcs: [] });
|
|
371
|
+
|
|
372
|
+
const exists = await detector.resourceExists({
|
|
373
|
+
resourceType: 'AWS::EC2::VPC',
|
|
374
|
+
physicalId: 'vpc-nonexistent',
|
|
375
|
+
region: 'us-east-1',
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
expect(exists).toBe(false);
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
describe('detectResourcesByTags', () => {
|
|
383
|
+
it('should detect resources matching tags', async () => {
|
|
384
|
+
// VPCs
|
|
385
|
+
mockEC2Send.mockResolvedValueOnce({
|
|
386
|
+
Vpcs: [
|
|
387
|
+
{
|
|
388
|
+
VpcId: 'vpc-123',
|
|
389
|
+
CidrBlock: '10.0.0.0/16',
|
|
390
|
+
State: 'available',
|
|
391
|
+
Tags: [{ Key: 'Environment', Value: 'production' }],
|
|
392
|
+
},
|
|
393
|
+
],
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// Subnets
|
|
397
|
+
mockEC2Send.mockResolvedValueOnce({
|
|
398
|
+
Subnets: [
|
|
399
|
+
{
|
|
400
|
+
SubnetId: 'subnet-123',
|
|
401
|
+
VpcId: 'vpc-123',
|
|
402
|
+
CidrBlock: '10.0.1.0/24',
|
|
403
|
+
State: 'available',
|
|
404
|
+
Tags: [{ Key: 'Environment', Value: 'production' }],
|
|
405
|
+
},
|
|
406
|
+
],
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
const resources = await detector.detectResourcesByTags({
|
|
410
|
+
tags: { Environment: 'production' },
|
|
411
|
+
region: 'us-east-1',
|
|
412
|
+
resourceTypes: ['AWS::EC2::VPC', 'AWS::EC2::Subnet'],
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
expect(resources).toHaveLength(2);
|
|
416
|
+
expect(resources[0].physicalId).toBe('vpc-123');
|
|
417
|
+
expect(resources[1].physicalId).toBe('subnet-123');
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('should detect all supported types if resourceTypes not specified', async () => {
|
|
421
|
+
// Mock responses for all supported types
|
|
422
|
+
mockEC2Send.mockResolvedValueOnce({ Vpcs: [] });
|
|
423
|
+
mockEC2Send.mockResolvedValueOnce({ Subnets: [] });
|
|
424
|
+
mockEC2Send.mockResolvedValueOnce({ SecurityGroups: [] });
|
|
425
|
+
mockEC2Send.mockResolvedValueOnce({ RouteTables: [] });
|
|
426
|
+
mockRDSSend.mockResolvedValueOnce({ DBClusters: [] });
|
|
427
|
+
mockKMSSend.mockResolvedValueOnce({ Keys: [] });
|
|
428
|
+
|
|
429
|
+
const resources = await detector.detectResourcesByTags({
|
|
430
|
+
tags: { Team: 'platform' },
|
|
431
|
+
region: 'us-east-1',
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
expect(resources).toEqual([]);
|
|
435
|
+
expect(mockEC2Send).toHaveBeenCalledTimes(4);
|
|
436
|
+
expect(mockRDSSend).toHaveBeenCalledTimes(1);
|
|
437
|
+
expect(mockKMSSend).toHaveBeenCalledTimes(1);
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
describe('findOrphanedResources', () => {
|
|
442
|
+
it('should find orphaned RDS DBCluster', async () => {
|
|
443
|
+
mockRDSSend.mockResolvedValue({
|
|
444
|
+
DBClusters: [
|
|
445
|
+
{
|
|
446
|
+
DBClusterIdentifier: 'orphan-cluster',
|
|
447
|
+
DBClusterArn: 'arn:aws:rds:us-east-1:123456789012:cluster:orphan-cluster',
|
|
448
|
+
Engine: 'aurora-postgresql',
|
|
449
|
+
Status: 'available',
|
|
450
|
+
ClusterCreateTime: new Date('2024-01-01T00:00:00Z'),
|
|
451
|
+
TagList: [],
|
|
452
|
+
},
|
|
453
|
+
],
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
const orphans = await detector.findOrphanedResources({
|
|
457
|
+
region: 'us-east-1',
|
|
458
|
+
resourceTypes: ['AWS::RDS::DBCluster'],
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
expect(orphans).toHaveLength(1);
|
|
462
|
+
expect(orphans[0].physicalId).toBe('orphan-cluster');
|
|
463
|
+
expect(orphans[0].isOrphaned).toBe(true);
|
|
464
|
+
expect(orphans[0].reason).toContain('not managed by CloudFormation');
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('should exclude specified physical IDs', async () => {
|
|
468
|
+
mockEC2Send.mockResolvedValue({
|
|
469
|
+
Vpcs: [
|
|
470
|
+
{ VpcId: 'vpc-123', CidrBlock: '10.0.0.0/16', State: 'available', Tags: [] },
|
|
471
|
+
{ VpcId: 'vpc-456', CidrBlock: '10.1.0.0/16', State: 'available', Tags: [] },
|
|
472
|
+
],
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
const orphans = await detector.findOrphanedResources({
|
|
476
|
+
region: 'us-east-1',
|
|
477
|
+
resourceTypes: ['AWS::EC2::VPC'],
|
|
478
|
+
excludePhysicalIds: ['vpc-123'],
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
expect(orphans).toHaveLength(1);
|
|
482
|
+
expect(orphans[0].physicalId).toBe('vpc-456');
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
describe('constructor', () => {
|
|
487
|
+
it('should create instance with default region', () => {
|
|
488
|
+
const det = new AWSResourceDetector();
|
|
489
|
+
expect(det).toBeInstanceOf(AWSResourceDetector);
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it('should create instance with custom region', () => {
|
|
493
|
+
const det = new AWSResourceDetector({ region: 'eu-west-1' });
|
|
494
|
+
expect(det).toBeInstanceOf(AWSResourceDetector);
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
});
|