runbooks 0.7.7__py3-none-any.whl → 0.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. runbooks/__init__.py +1 -1
  2. runbooks/base.py +2 -2
  3. runbooks/cfat/README.md +12 -1
  4. runbooks/cfat/__init__.py +8 -4
  5. runbooks/cfat/assessment/collectors.py +171 -14
  6. runbooks/cfat/assessment/compliance.py +546 -522
  7. runbooks/cfat/assessment/runner.py +129 -10
  8. runbooks/cfat/models.py +6 -2
  9. runbooks/common/__init__.py +152 -0
  10. runbooks/common/accuracy_validator.py +1039 -0
  11. runbooks/common/context_logger.py +440 -0
  12. runbooks/common/cross_module_integration.py +594 -0
  13. runbooks/common/enhanced_exception_handler.py +1108 -0
  14. runbooks/common/enterprise_audit_integration.py +634 -0
  15. runbooks/common/logger.py +14 -0
  16. runbooks/common/mcp_integration.py +539 -0
  17. runbooks/common/performance_monitor.py +387 -0
  18. runbooks/common/profile_utils.py +216 -0
  19. runbooks/common/rich_utils.py +622 -0
  20. runbooks/enterprise/__init__.py +68 -0
  21. runbooks/enterprise/error_handling.py +411 -0
  22. runbooks/enterprise/logging.py +439 -0
  23. runbooks/enterprise/multi_tenant.py +583 -0
  24. runbooks/feedback/user_feedback_collector.py +440 -0
  25. runbooks/finops/README.md +129 -14
  26. runbooks/finops/__init__.py +22 -3
  27. runbooks/finops/account_resolver.py +279 -0
  28. runbooks/finops/accuracy_cross_validator.py +638 -0
  29. runbooks/finops/aws_client.py +721 -36
  30. runbooks/finops/budget_integration.py +313 -0
  31. runbooks/finops/cli.py +90 -33
  32. runbooks/finops/cost_processor.py +211 -37
  33. runbooks/finops/dashboard_router.py +900 -0
  34. runbooks/finops/dashboard_runner.py +1334 -399
  35. runbooks/finops/embedded_mcp_validator.py +288 -0
  36. runbooks/finops/enhanced_dashboard_runner.py +526 -0
  37. runbooks/finops/enhanced_progress.py +327 -0
  38. runbooks/finops/enhanced_trend_visualization.py +423 -0
  39. runbooks/finops/finops_dashboard.py +41 -0
  40. runbooks/finops/helpers.py +639 -323
  41. runbooks/finops/iam_guidance.py +400 -0
  42. runbooks/finops/markdown_exporter.py +466 -0
  43. runbooks/finops/multi_dashboard.py +1502 -0
  44. runbooks/finops/optimizer.py +396 -395
  45. runbooks/finops/profile_processor.py +2 -2
  46. runbooks/finops/runbooks.inventory.organizations_discovery.log +0 -0
  47. runbooks/finops/runbooks.security.report_generator.log +0 -0
  48. runbooks/finops/runbooks.security.run_script.log +0 -0
  49. runbooks/finops/runbooks.security.security_export.log +0 -0
  50. runbooks/finops/service_mapping.py +195 -0
  51. runbooks/finops/single_dashboard.py +710 -0
  52. runbooks/finops/tests/__init__.py +19 -0
  53. runbooks/finops/tests/results_test_finops_dashboard.xml +1 -0
  54. runbooks/finops/tests/run_comprehensive_tests.py +421 -0
  55. runbooks/finops/tests/run_tests.py +305 -0
  56. runbooks/finops/tests/test_finops_dashboard.py +705 -0
  57. runbooks/finops/tests/test_integration.py +477 -0
  58. runbooks/finops/tests/test_performance.py +380 -0
  59. runbooks/finops/tests/test_performance_benchmarks.py +500 -0
  60. runbooks/finops/tests/test_reference_images_validation.py +867 -0
  61. runbooks/finops/tests/test_single_account_features.py +715 -0
  62. runbooks/finops/tests/validate_test_suite.py +220 -0
  63. runbooks/finops/types.py +1 -1
  64. runbooks/hitl/enhanced_workflow_engine.py +725 -0
  65. runbooks/inventory/README.md +12 -1
  66. runbooks/inventory/artifacts/scale-optimize-status.txt +12 -0
  67. runbooks/inventory/collectors/aws_comprehensive.py +192 -185
  68. runbooks/inventory/collectors/enterprise_scale.py +281 -0
  69. runbooks/inventory/core/collector.py +299 -12
  70. runbooks/inventory/list_ec2_instances.py +21 -20
  71. runbooks/inventory/list_ssm_parameters.py +31 -3
  72. runbooks/inventory/organizations_discovery.py +1315 -0
  73. runbooks/inventory/rich_inventory_display.py +360 -0
  74. runbooks/inventory/run_on_multi_accounts.py +32 -16
  75. runbooks/inventory/runbooks.security.report_generator.log +0 -0
  76. runbooks/inventory/runbooks.security.run_script.log +0 -0
  77. runbooks/inventory/vpc_flow_analyzer.py +1030 -0
  78. runbooks/main.py +4171 -1615
  79. runbooks/metrics/dora_metrics_engine.py +1293 -0
  80. runbooks/monitoring/performance_monitor.py +433 -0
  81. runbooks/operate/README.md +394 -0
  82. runbooks/operate/__init__.py +2 -2
  83. runbooks/operate/base.py +291 -11
  84. runbooks/operate/deployment_framework.py +1032 -0
  85. runbooks/operate/deployment_validator.py +853 -0
  86. runbooks/operate/dynamodb_operations.py +10 -6
  87. runbooks/operate/ec2_operations.py +321 -11
  88. runbooks/operate/executive_dashboard.py +779 -0
  89. runbooks/operate/mcp_integration.py +750 -0
  90. runbooks/operate/nat_gateway_operations.py +1120 -0
  91. runbooks/operate/networking_cost_heatmap.py +685 -0
  92. runbooks/operate/privatelink_operations.py +940 -0
  93. runbooks/operate/s3_operations.py +10 -6
  94. runbooks/operate/vpc_endpoints.py +644 -0
  95. runbooks/operate/vpc_operations.py +1038 -0
  96. runbooks/remediation/README.md +489 -13
  97. runbooks/remediation/__init__.py +2 -2
  98. runbooks/remediation/acm_remediation.py +1 -1
  99. runbooks/remediation/base.py +1 -1
  100. runbooks/remediation/cloudtrail_remediation.py +1 -1
  101. runbooks/remediation/cognito_remediation.py +1 -1
  102. runbooks/remediation/commons.py +8 -4
  103. runbooks/remediation/dynamodb_remediation.py +1 -1
  104. runbooks/remediation/ec2_remediation.py +1 -1
  105. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -1
  106. runbooks/remediation/kms_enable_key_rotation.py +1 -1
  107. runbooks/remediation/kms_remediation.py +1 -1
  108. runbooks/remediation/lambda_remediation.py +1 -1
  109. runbooks/remediation/multi_account.py +1 -1
  110. runbooks/remediation/rds_remediation.py +1 -1
  111. runbooks/remediation/s3_block_public_access.py +1 -1
  112. runbooks/remediation/s3_enable_access_logging.py +1 -1
  113. runbooks/remediation/s3_encryption.py +1 -1
  114. runbooks/remediation/s3_remediation.py +1 -1
  115. runbooks/remediation/vpc_remediation.py +475 -0
  116. runbooks/security/ENTERPRISE_SECURITY_FRAMEWORK.md +506 -0
  117. runbooks/security/README.md +12 -1
  118. runbooks/security/__init__.py +166 -33
  119. runbooks/security/compliance_automation.py +634 -0
  120. runbooks/security/compliance_automation_engine.py +1021 -0
  121. runbooks/security/enterprise_security_framework.py +931 -0
  122. runbooks/security/enterprise_security_policies.json +293 -0
  123. runbooks/security/integration_test_enterprise_security.py +879 -0
  124. runbooks/security/module_security_integrator.py +641 -0
  125. runbooks/security/report_generator.py +10 -0
  126. runbooks/security/run_script.py +27 -5
  127. runbooks/security/security_baseline_tester.py +153 -27
  128. runbooks/security/security_export.py +456 -0
  129. runbooks/sre/README.md +472 -0
  130. runbooks/sre/__init__.py +33 -0
  131. runbooks/sre/mcp_reliability_engine.py +1049 -0
  132. runbooks/sre/performance_optimization_engine.py +1032 -0
  133. runbooks/sre/reliability_monitoring_framework.py +1011 -0
  134. runbooks/validation/__init__.py +10 -0
  135. runbooks/validation/benchmark.py +489 -0
  136. runbooks/validation/cli.py +368 -0
  137. runbooks/validation/mcp_validator.py +797 -0
  138. runbooks/vpc/README.md +478 -0
  139. runbooks/vpc/__init__.py +38 -0
  140. runbooks/vpc/config.py +212 -0
  141. runbooks/vpc/cost_engine.py +347 -0
  142. runbooks/vpc/heatmap_engine.py +605 -0
  143. runbooks/vpc/manager_interface.py +649 -0
  144. runbooks/vpc/networking_wrapper.py +1289 -0
  145. runbooks/vpc/rich_formatters.py +693 -0
  146. runbooks/vpc/tests/__init__.py +5 -0
  147. runbooks/vpc/tests/conftest.py +356 -0
  148. runbooks/vpc/tests/test_cli_integration.py +530 -0
  149. runbooks/vpc/tests/test_config.py +458 -0
  150. runbooks/vpc/tests/test_cost_engine.py +479 -0
  151. runbooks/vpc/tests/test_networking_wrapper.py +512 -0
  152. {runbooks-0.7.7.dist-info → runbooks-0.9.0.dist-info}/METADATA +175 -65
  153. {runbooks-0.7.7.dist-info → runbooks-0.9.0.dist-info}/RECORD +157 -60
  154. {runbooks-0.7.7.dist-info → runbooks-0.9.0.dist-info}/entry_points.txt +1 -1
  155. {runbooks-0.7.7.dist-info → runbooks-0.9.0.dist-info}/WHEEL +0 -0
  156. {runbooks-0.7.7.dist-info → runbooks-0.9.0.dist-info}/licenses/LICENSE +0 -0
  157. {runbooks-0.7.7.dist-info → runbooks-0.9.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,512 @@
1
+ """
2
+ Comprehensive tests for VPC Networking Wrapper
3
+
4
+ Tests the main VPCNetworkingWrapper class with comprehensive coverage
5
+ of all networking analysis and optimization functionality.
6
+ """
7
+
8
+ import json
9
+ import time
10
+ from datetime import datetime
11
+ from pathlib import Path
12
+ from unittest.mock import MagicMock, Mock, patch
13
+
14
+ import pytest
15
+ from rich.console import Console
16
+
17
+ from runbooks.vpc.networking_wrapper import VPCNetworkingWrapper
18
+
19
+
20
+ @pytest.mark.unit
21
+ class TestVPCNetworkingWrapper:
22
+ """Test VPC Networking Wrapper functionality."""
23
+
24
+ def test_initialization_default(self):
25
+ """Test VPC wrapper initialization with default parameters."""
26
+ wrapper = VPCNetworkingWrapper()
27
+
28
+ assert wrapper.profile is None
29
+ assert wrapper.region == "us-east-1"
30
+ assert wrapper.billing_profile is None
31
+ assert wrapper.output_format == "rich"
32
+ assert isinstance(wrapper.console, Console)
33
+ assert wrapper.session is None
34
+ assert wrapper.last_results == {}
35
+
36
+ def test_initialization_with_parameters(self, mock_console):
37
+ """Test VPC wrapper initialization with custom parameters."""
38
+ wrapper = VPCNetworkingWrapper(
39
+ profile="test-profile",
40
+ region="us-west-2",
41
+ billing_profile="billing-profile",
42
+ output_format="json",
43
+ console=mock_console,
44
+ )
45
+
46
+ assert wrapper.profile == "test-profile"
47
+ assert wrapper.region == "us-west-2"
48
+ assert wrapper.billing_profile == "billing-profile"
49
+ assert wrapper.output_format == "json"
50
+ assert wrapper.console == mock_console
51
+
52
+ def test_initialization_with_aws_session(self, mock_console):
53
+ """Test VPC wrapper initialization with valid AWS session."""
54
+ with patch("runbooks.vpc.networking_wrapper.boto3.Session") as mock_session:
55
+ mock_session_instance = Mock()
56
+ mock_session.return_value = mock_session_instance
57
+
58
+ wrapper = VPCNetworkingWrapper(profile="test-profile", console=mock_console)
59
+
60
+ assert wrapper.session == mock_session_instance
61
+ mock_console.print.assert_called_with("✅ Connected to AWS profile: test-profile", style="green")
62
+
63
+ def test_initialization_with_invalid_aws_session(self, mock_console):
64
+ """Test VPC wrapper initialization with invalid AWS session."""
65
+ with patch("runbooks.vpc.networking_wrapper.boto3.Session") as mock_session:
66
+ mock_session.side_effect = Exception("Invalid credentials")
67
+
68
+ wrapper = VPCNetworkingWrapper(profile="invalid-profile", console=mock_console)
69
+
70
+ assert wrapper.session is None
71
+ mock_console.print.assert_called_with("⚠️ Failed to connect to AWS: Invalid credentials", style="yellow")
72
+
73
+ @pytest.mark.performance
74
+ def test_analyze_nat_gateways_performance(
75
+ self, vpc_networking_wrapper, performance_benchmarks, assert_performance_benchmark
76
+ ):
77
+ """Test NAT Gateway analysis performance benchmark."""
78
+ # Mock AWS clients and responses
79
+ mock_ec2_client = Mock()
80
+ mock_cloudwatch_client = Mock()
81
+
82
+ # Mock NAT Gateway response
83
+ mock_ec2_client.describe_nat_gateways.return_value = {
84
+ "NatGateways": [
85
+ {
86
+ "NatGatewayId": "nat-0123456789abcdef0",
87
+ "State": "available",
88
+ "VpcId": "vpc-0123456789abcdef0",
89
+ "SubnetId": "subnet-0123456789abcdef0",
90
+ }
91
+ ]
92
+ }
93
+
94
+ # Mock CloudWatch response
95
+ mock_cloudwatch_client.get_metric_statistics.return_value = {
96
+ "Datapoints": [
97
+ {
98
+ "Timestamp": datetime.now(),
99
+ "Average": 50.0,
100
+ "Maximum": 100.0,
101
+ "Sum": 1073741824.0, # 1 GB in bytes
102
+ "Unit": "Count",
103
+ }
104
+ ]
105
+ }
106
+
107
+ # Configure mock session to return our mocked clients
108
+ vpc_networking_wrapper.session.client.side_effect = lambda service: {
109
+ "ec2": mock_ec2_client,
110
+ "cloudwatch": mock_cloudwatch_client,
111
+ }.get(service)
112
+
113
+ # Measure execution time
114
+ start_time = time.time()
115
+ result = vpc_networking_wrapper.analyze_nat_gateways(days=30)
116
+ execution_time = time.time() - start_time
117
+
118
+ # Assert performance benchmark
119
+ assert_performance_benchmark(execution_time, "nat_gateway_analysis_max_time", performance_benchmarks)
120
+
121
+ # Validate result structure
122
+ assert isinstance(result, dict)
123
+ assert "nat_gateways" in result
124
+ assert "total_cost" in result
125
+ assert "optimization_potential" in result
126
+ assert "recommendations" in result
127
+
128
+ def test_analyze_nat_gateways_no_session(self, vpc_networking_wrapper, validate_vpc_structure):
129
+ """Test NAT Gateway analysis without AWS session."""
130
+ # Ensure no session
131
+ vpc_networking_wrapper.session = None
132
+
133
+ result = vpc_networking_wrapper.analyze_nat_gateways()
134
+
135
+ # Validate structure
136
+ expected_keys = ["nat_gateways", "total_cost", "optimization_potential", "recommendations"]
137
+ validate_vpc_structure(result, expected_keys)
138
+
139
+ # Should return empty results
140
+ assert len(result["nat_gateways"]) == 0
141
+ assert result["total_cost"] == 0
142
+ assert result["optimization_potential"] == 0
143
+
144
+ def test_analyze_nat_gateways_with_data(self, vpc_networking_wrapper, sample_nat_gateways):
145
+ """Test NAT Gateway analysis with sample data."""
146
+ mock_ec2_client = Mock()
147
+ mock_cloudwatch_client = Mock()
148
+
149
+ # Mock responses
150
+ mock_ec2_client.describe_nat_gateways.return_value = {"NatGateways": sample_nat_gateways}
151
+
152
+ mock_cloudwatch_client.get_metric_statistics.return_value = {
153
+ "Datapoints": [
154
+ {
155
+ "Sum": 5368709120.0, # 5 GB
156
+ "Average": 150.0,
157
+ "Maximum": 200.0,
158
+ }
159
+ ]
160
+ }
161
+
162
+ vpc_networking_wrapper.session.client.side_effect = lambda service: {
163
+ "ec2": mock_ec2_client,
164
+ "cloudwatch": mock_cloudwatch_client,
165
+ }.get(service)
166
+
167
+ result = vpc_networking_wrapper.analyze_nat_gateways()
168
+
169
+ # Validate results
170
+ assert len(result["nat_gateways"]) == 2
171
+ assert result["total_cost"] > 0
172
+
173
+ # Validate individual NAT Gateway analysis
174
+ for ng in result["nat_gateways"]:
175
+ assert "id" in ng
176
+ assert "monthly_cost" in ng
177
+ assert "usage" in ng
178
+ assert "optimization" in ng
179
+
180
+ def test_analyze_vpc_endpoints_performance(
181
+ self, vpc_networking_wrapper, performance_benchmarks, assert_performance_benchmark
182
+ ):
183
+ """Test VPC Endpoint analysis performance benchmark."""
184
+ mock_ec2_client = Mock()
185
+ mock_ec2_client.describe_vpc_endpoints.return_value = {
186
+ "VpcEndpoints": [
187
+ {
188
+ "VpcEndpointId": "vpce-0123456789abcdef0",
189
+ "VpcEndpointType": "Interface",
190
+ "ServiceName": "com.amazonaws.us-east-1.s3",
191
+ "VpcId": "vpc-0123456789abcdef0",
192
+ "State": "available",
193
+ "SubnetIds": ["subnet-1", "subnet-2"],
194
+ }
195
+ ]
196
+ }
197
+
198
+ vpc_networking_wrapper.session.client.return_value = mock_ec2_client
199
+
200
+ start_time = time.time()
201
+ result = vpc_networking_wrapper.analyze_vpc_endpoints()
202
+ execution_time = time.time() - start_time
203
+
204
+ # Assert performance benchmark
205
+ assert_performance_benchmark(execution_time, "vpc_endpoint_analysis_max_time", performance_benchmarks)
206
+
207
+ # Validate result
208
+ assert isinstance(result, dict)
209
+ assert "vpc_endpoints" in result
210
+
211
+ def test_analyze_vpc_endpoints_with_data(self, vpc_networking_wrapper, sample_vpc_endpoints):
212
+ """Test VPC Endpoint analysis with sample data."""
213
+ mock_ec2_client = Mock()
214
+ mock_ec2_client.describe_vpc_endpoints.return_value = {"VpcEndpoints": sample_vpc_endpoints}
215
+
216
+ vpc_networking_wrapper.session.client.return_value = mock_ec2_client
217
+
218
+ result = vpc_networking_wrapper.analyze_vpc_endpoints()
219
+
220
+ # Validate results
221
+ assert len(result["vpc_endpoints"]) == 2
222
+
223
+ # Check Interface endpoint cost calculation
224
+ interface_endpoint = next(ep for ep in result["vpc_endpoints"] if ep["type"] == "Interface")
225
+ assert interface_endpoint["monthly_cost"] > 0 # Interface endpoints have costs
226
+
227
+ # Check Gateway endpoint cost calculation
228
+ gateway_endpoint = next(ep for ep in result["vpc_endpoints"] if ep["type"] == "Gateway")
229
+ assert gateway_endpoint["monthly_cost"] == 0 # Gateway endpoints are free
230
+
231
+ def test_generate_cost_heatmaps(self, vpc_networking_wrapper):
232
+ """Test cost heatmap generation."""
233
+ # Mock heatmap engine
234
+ mock_heatmap_engine = Mock()
235
+ mock_heatmap_data = {
236
+ "heatmap_data": "sample_data",
237
+ "regions": ["us-east-1", "us-west-2"],
238
+ "cost_breakdown": {"nat_gateways": 100, "vpc_endpoints": 50},
239
+ }
240
+ mock_heatmap_engine.generate_comprehensive_heat_maps.return_value = mock_heatmap_data
241
+
242
+ with patch("runbooks.vpc.networking_wrapper.NetworkingCostHeatMapEngine") as mock_engine_class:
243
+ mock_engine_class.return_value = mock_heatmap_engine
244
+
245
+ result = vpc_networking_wrapper.generate_cost_heatmaps()
246
+
247
+ assert result == mock_heatmap_data
248
+ assert vpc_networking_wrapper.last_results["heat_maps"] == mock_heatmap_data
249
+
250
+ def test_optimize_networking_costs(self, vpc_networking_wrapper):
251
+ """Test networking cost optimization recommendations."""
252
+ # Mock analyze methods to return sample data
253
+ vpc_networking_wrapper.analyze_nat_gateways = Mock(
254
+ return_value={
255
+ "total_cost": 100.0,
256
+ "optimization_potential": 30.0,
257
+ "recommendations": [
258
+ {
259
+ "type": "NAT Gateway",
260
+ "potential_savings": 30.0,
261
+ "risk_level": "low",
262
+ "action": "Remove unused NAT Gateway",
263
+ "resource_id": "nat-123456",
264
+ "implementation_effort": "low",
265
+ }
266
+ ],
267
+ }
268
+ )
269
+
270
+ vpc_networking_wrapper.analyze_vpc_endpoints = Mock(
271
+ return_value={
272
+ "total_cost": 50.0,
273
+ "optimization_potential": 10.0,
274
+ "recommendations": [
275
+ {
276
+ "type": "VPC Endpoint",
277
+ "potential_savings": 10.0,
278
+ "risk_level": "low",
279
+ "action": "Optimize endpoint configuration",
280
+ "resource_id": "vpce-123456",
281
+ "implementation_effort": "medium",
282
+ }
283
+ ],
284
+ }
285
+ )
286
+
287
+ result = vpc_networking_wrapper.optimize_networking_costs(target_reduction=30.0)
288
+
289
+ # Validate optimization results
290
+ assert result["current_monthly_cost"] == 150.0
291
+ assert result["potential_savings"] == 40.0
292
+ assert result["projected_monthly_cost"] == 110.0
293
+ assert len(result["recommendations"]) == 2
294
+ assert len(result["implementation_plan"]) > 0
295
+
296
+ def test_export_results(self, vpc_networking_wrapper, temp_output_directory):
297
+ """Test exporting analysis results to files."""
298
+ # Set up sample results
299
+ vpc_networking_wrapper.last_results = {
300
+ "nat_gateways": {"nat_gateways": [{"id": "nat-123", "monthly_cost": 45.0}], "total_cost": 45.0},
301
+ "vpc_endpoints": {"vpc_endpoints": [{"id": "vpce-123", "monthly_cost": 10.0}], "total_cost": 10.0},
302
+ }
303
+
304
+ exported_files = vpc_networking_wrapper.export_results(str(temp_output_directory))
305
+
306
+ # Validate exported files
307
+ assert len(exported_files) > 0
308
+
309
+ # Check JSON files exist
310
+ assert any("nat_gateways_json" in key for key in exported_files.keys())
311
+ assert any("vpc_endpoints_json" in key for key in exported_files.keys())
312
+
313
+ # Validate file contents
314
+ for file_path in exported_files.values():
315
+ file = Path(file_path)
316
+ assert file.exists()
317
+
318
+ if file.suffix == ".json":
319
+ with open(file, "r") as f:
320
+ data = json.load(f)
321
+ assert isinstance(data, dict)
322
+
323
+ def test_private_analyze_nat_gateway_usage(self, vpc_networking_wrapper, sample_cloudwatch_metrics):
324
+ """Test private method for analyzing NAT Gateway usage."""
325
+ mock_cloudwatch = Mock()
326
+
327
+ # Configure mock responses
328
+ def mock_get_metric_statistics(Namespace, MetricName, **kwargs):
329
+ if MetricName == "ActiveConnectionCount":
330
+ return {"Datapoints": sample_cloudwatch_metrics["ActiveConnectionCount"]}
331
+ elif MetricName == "BytesOutToDestination":
332
+ return {"Datapoints": sample_cloudwatch_metrics["BytesOutToDestination"]}
333
+ return {"Datapoints": []}
334
+
335
+ mock_cloudwatch.get_metric_statistics.side_effect = mock_get_metric_statistics
336
+
337
+ result = vpc_networking_wrapper._analyze_nat_gateway_usage(mock_cloudwatch, "nat-0123456789abcdef0", 30)
338
+
339
+ # Validate usage analysis
340
+ assert "active_connections" in result
341
+ assert "bytes_processed_gb" in result
342
+ assert "is_idle" in result
343
+ assert result["active_connections"] > 0
344
+ assert result["bytes_processed_gb"] > 0
345
+
346
+ def test_private_get_nat_gateway_optimization(self, vpc_networking_wrapper):
347
+ """Test private method for NAT Gateway optimization recommendations."""
348
+ # Test idle NAT Gateway
349
+ idle_usage = {"is_idle": True, "bytes_processed_gb": 0.5, "active_connections": 5}
350
+
351
+ result = vpc_networking_wrapper._get_nat_gateway_optimization(idle_usage)
352
+
353
+ assert result["recommendation"] == "Remove unused NAT Gateway"
354
+ assert result["potential_savings"] == 45.0
355
+ assert result["risk_level"] == "medium"
356
+
357
+ # Test low usage NAT Gateway
358
+ low_usage = {"is_idle": False, "bytes_processed_gb": 50.0, "active_connections": 80}
359
+
360
+ result = vpc_networking_wrapper._get_nat_gateway_optimization(low_usage)
361
+
362
+ assert "VPC Endpoints" in result["recommendation"]
363
+ assert result["potential_savings"] == 20.0
364
+ assert result["risk_level"] == "low"
365
+
366
+ def test_private_get_vpc_endpoint_optimization(self, vpc_networking_wrapper):
367
+ """Test private method for VPC Endpoint optimization recommendations."""
368
+ # Test Interface endpoint with multiple AZs
369
+ interface_endpoint = {
370
+ "VpcEndpointType": "Interface",
371
+ "SubnetIds": ["subnet-1", "subnet-2", "subnet-3", "subnet-4"], # 4 AZs
372
+ }
373
+
374
+ result = vpc_networking_wrapper._get_vpc_endpoint_optimization(interface_endpoint)
375
+
376
+ assert "Reduce AZ coverage" in result["recommendation"]
377
+ assert result["potential_savings"] == 20.0 # (4-2) * 10.0
378
+ assert result["risk_level"] == "low"
379
+
380
+ # Test Gateway endpoint
381
+ gateway_endpoint = {"VpcEndpointType": "Gateway", "SubnetIds": []}
382
+
383
+ result = vpc_networking_wrapper._get_vpc_endpoint_optimization(gateway_endpoint)
384
+
385
+ assert result["potential_savings"] == 0
386
+ assert result["recommendation"] == ""
387
+
388
+ def test_display_methods_with_rich_output(self, vpc_networking_wrapper):
389
+ """Test display methods with Rich output format."""
390
+ # Set output format to rich
391
+ vpc_networking_wrapper.output_format = "rich"
392
+
393
+ # Test NAT Gateway display
394
+ sample_results = {
395
+ "nat_gateways": [
396
+ {
397
+ "id": "nat-123",
398
+ "vpc_id": "vpc-123",
399
+ "state": "available",
400
+ "monthly_cost": 45.0,
401
+ "usage": {"is_idle": False, "bytes_processed_gb": 100.0},
402
+ "optimization": {"recommendation": "Optimize usage", "potential_savings": 15.0},
403
+ }
404
+ ],
405
+ "total_cost": 45.0,
406
+ "optimization_potential": 15.0,
407
+ "recommendations": [],
408
+ }
409
+
410
+ # Should not raise exception
411
+ vpc_networking_wrapper._display_nat_gateway_results(sample_results)
412
+
413
+ # Verify console.print was called
414
+ assert vpc_networking_wrapper.console.print.called
415
+
416
+ def test_error_handling_in_analysis(self, vpc_networking_wrapper):
417
+ """Test error handling during analysis operations."""
418
+ # Mock EC2 client to raise exception
419
+ mock_ec2_client = Mock()
420
+ mock_ec2_client.describe_nat_gateways.side_effect = Exception("API Error")
421
+
422
+ vpc_networking_wrapper.session.client.return_value = mock_ec2_client
423
+
424
+ result = vpc_networking_wrapper.analyze_nat_gateways()
425
+
426
+ # Should return empty results on error
427
+ assert len(result["nat_gateways"]) == 0
428
+ assert result["total_cost"] == 0
429
+
430
+ # Should log error message
431
+ vpc_networking_wrapper.console.print.assert_called_with(
432
+ "❌ Error analyzing NAT Gateways: API Error", style="red"
433
+ )
434
+
435
+ @pytest.mark.security
436
+ def test_security_credential_handling(self, vpc_networking_wrapper, security_test_validator):
437
+ """Test that no credentials are exposed in outputs."""
438
+ # Test with mock data that might contain sensitive info
439
+ vpc_networking_wrapper.last_results = {
440
+ "test_data": {
441
+ "access_key": "TESTKEY123", # Avoid AKIA pattern
442
+ "secret": "hidden_value",
443
+ "normal_data": "safe_value",
444
+ }
445
+ }
446
+
447
+ # Get string representation of results
448
+ result_output = str(vpc_networking_wrapper.last_results)
449
+
450
+ # Validate no sensitive patterns (exclude our test data)
451
+ security_test_validator(result_output, ["PASSWORD", "TOKEN"])
452
+
453
+ @pytest.mark.integration
454
+ def test_full_workflow_integration(self, vpc_networking_wrapper, temp_output_directory):
455
+ """Test complete workflow integration."""
456
+ # Mock all required AWS services
457
+ mock_ec2_client = Mock()
458
+ mock_cloudwatch_client = Mock()
459
+
460
+ # Configure NAT Gateway mock
461
+ mock_ec2_client.describe_nat_gateways.return_value = {
462
+ "NatGateways": [
463
+ {"NatGatewayId": "nat-test", "State": "available", "VpcId": "vpc-test", "SubnetId": "subnet-test"}
464
+ ]
465
+ }
466
+
467
+ # Configure VPC Endpoints mock
468
+ mock_ec2_client.describe_vpc_endpoints.return_value = {
469
+ "VpcEndpoints": [
470
+ {
471
+ "VpcEndpointId": "vpce-test",
472
+ "VpcEndpointType": "Interface",
473
+ "ServiceName": "com.amazonaws.us-east-1.s3",
474
+ "VpcId": "vpc-test",
475
+ "State": "available",
476
+ "SubnetIds": ["subnet-1", "subnet-2"],
477
+ }
478
+ ]
479
+ }
480
+
481
+ # Configure CloudWatch mock
482
+ mock_cloudwatch_client.get_metric_statistics.return_value = {
483
+ "Datapoints": [
484
+ {
485
+ "Sum": 1073741824.0, # 1 GB
486
+ "Average": 100.0,
487
+ "Maximum": 150.0,
488
+ }
489
+ ]
490
+ }
491
+
492
+ vpc_networking_wrapper.session.client.side_effect = lambda service: {
493
+ "ec2": mock_ec2_client,
494
+ "cloudwatch": mock_cloudwatch_client,
495
+ }.get(service)
496
+
497
+ # Execute full workflow
498
+ nat_results = vpc_networking_wrapper.analyze_nat_gateways()
499
+ vpc_results = vpc_networking_wrapper.analyze_vpc_endpoints()
500
+ optimization_results = vpc_networking_wrapper.optimize_networking_costs()
501
+ export_results = vpc_networking_wrapper.export_results(str(temp_output_directory))
502
+
503
+ # Validate complete workflow
504
+ assert len(nat_results["nat_gateways"]) > 0
505
+ assert len(vpc_results["vpc_endpoints"]) > 0
506
+ assert optimization_results["current_monthly_cost"] > 0
507
+ assert len(export_results) > 0
508
+
509
+ # Validate all results stored
510
+ assert "nat_gateways" in vpc_networking_wrapper.last_results
511
+ assert "vpc_endpoints" in vpc_networking_wrapper.last_results
512
+ assert "optimization" in vpc_networking_wrapper.last_results