regscale-cli 6.20.10.0__py3-none-any.whl → 6.21.1.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.

Potentially problematic release.


This version of regscale-cli might be problematic. Click here for more details.

Files changed (64) hide show
  1. regscale/_version.py +1 -1
  2. regscale/core/app/application.py +12 -5
  3. regscale/core/app/internal/set_permissions.py +58 -27
  4. regscale/integrations/commercial/__init__.py +1 -2
  5. regscale/integrations/commercial/amazon/common.py +79 -2
  6. regscale/integrations/commercial/aws/cli.py +183 -9
  7. regscale/integrations/commercial/aws/scanner.py +544 -9
  8. regscale/integrations/commercial/cpe.py +18 -1
  9. regscale/integrations/commercial/nessus/scanner.py +2 -0
  10. regscale/integrations/commercial/sonarcloud.py +35 -36
  11. regscale/integrations/commercial/synqly/ticketing.py +51 -0
  12. regscale/integrations/commercial/tenablev2/jsonl_scanner.py +2 -1
  13. regscale/integrations/commercial/wizv2/async_client.py +10 -3
  14. regscale/integrations/commercial/wizv2/click.py +102 -26
  15. regscale/integrations/commercial/wizv2/constants.py +249 -1
  16. regscale/integrations/commercial/wizv2/issue.py +2 -2
  17. regscale/integrations/commercial/wizv2/parsers.py +3 -2
  18. regscale/integrations/commercial/wizv2/policy_compliance.py +1858 -0
  19. regscale/integrations/commercial/wizv2/scanner.py +15 -21
  20. regscale/integrations/commercial/wizv2/utils.py +258 -85
  21. regscale/integrations/commercial/wizv2/variables.py +4 -3
  22. regscale/integrations/compliance_integration.py +1455 -0
  23. regscale/integrations/integration_override.py +15 -6
  24. regscale/integrations/public/fedramp/fedramp_five.py +1 -1
  25. regscale/integrations/public/fedramp/markdown_parser.py +7 -1
  26. regscale/integrations/scanner_integration.py +193 -37
  27. regscale/models/app_models/__init__.py +1 -0
  28. regscale/models/integration_models/amazon_models/inspector_scan.py +32 -57
  29. regscale/models/integration_models/aqua.py +92 -78
  30. regscale/models/integration_models/cisa_kev_data.json +117 -5
  31. regscale/models/integration_models/defenderimport.py +64 -59
  32. regscale/models/integration_models/ecr_models/ecr.py +100 -147
  33. regscale/models/integration_models/flat_file_importer/__init__.py +52 -38
  34. regscale/models/integration_models/ibm.py +29 -47
  35. regscale/models/integration_models/nexpose.py +156 -68
  36. regscale/models/integration_models/prisma.py +46 -66
  37. regscale/models/integration_models/qualys.py +99 -93
  38. regscale/models/integration_models/snyk.py +229 -158
  39. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  40. regscale/models/integration_models/veracode.py +15 -20
  41. regscale/{integrations/commercial/wizv2/models.py → models/integration_models/wizv2.py} +4 -12
  42. regscale/models/integration_models/xray.py +276 -82
  43. regscale/models/regscale_models/control_implementation.py +14 -12
  44. regscale/models/regscale_models/file.py +4 -0
  45. regscale/models/regscale_models/issue.py +123 -0
  46. regscale/models/regscale_models/milestone.py +1 -1
  47. regscale/models/regscale_models/rbac.py +22 -0
  48. regscale/models/regscale_models/regscale_model.py +4 -2
  49. regscale/models/regscale_models/security_plan.py +1 -1
  50. regscale/utils/graphql_client.py +3 -1
  51. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/METADATA +9 -9
  52. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/RECORD +64 -60
  53. tests/fixtures/test_fixture.py +58 -2
  54. tests/regscale/core/test_app.py +5 -3
  55. tests/regscale/core/test_version_regscale.py +5 -3
  56. tests/regscale/integrations/test_integration_mapping.py +522 -40
  57. tests/regscale/integrations/test_issue_due_date.py +1 -1
  58. tests/regscale/integrations/test_update_finding_dates.py +336 -0
  59. tests/regscale/integrations/test_wiz_policy_compliance_affected_controls.py +154 -0
  60. tests/regscale/models/test_asset.py +406 -50
  61. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/LICENSE +0 -0
  62. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/WHEEL +0 -0
  63. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/entry_points.txt +0 -0
  64. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,12 @@
1
- import sys
2
- from unittest.mock import MagicMock
1
+ """
2
+ Tests for the IntegrationOverride class.
3
+
4
+ This module provides comprehensive test coverage for the IntegrationOverride class,
5
+ which handles custom integration mappings for findings and field validation.
6
+ """
7
+
8
+ from typing import Any, Dict
9
+ from unittest.mock import MagicMock, Mock, patch
3
10
 
4
11
  import pytest
5
12
 
@@ -7,8 +14,9 @@ from regscale.core.app.application import Application
7
14
  from regscale.integrations.integration_override import IntegrationOverride
8
15
 
9
16
 
10
- @pytest.fixture()
11
- def config():
17
+ @pytest.fixture
18
+ def basic_config() -> Dict[str, Any]:
19
+ """Basic configuration fixture with finding mappings."""
12
20
  return {
13
21
  "findingFromMapping": {
14
22
  "tenable_sc": {
@@ -16,45 +24,519 @@ def config():
16
24
  "description": "details",
17
25
  "remediation": "rando",
18
26
  "title": "default",
19
- }
27
+ },
28
+ "qualys": {
29
+ "severity": "cvss_score",
30
+ "description": "summary",
31
+ "solution": "fix",
32
+ },
20
33
  }
21
34
  }
22
35
 
23
36
 
24
- def test_integration_mapping_load(config):
25
- # Mock Application object and its config
37
+ @pytest.fixture
38
+ def config_with_unique_override() -> Dict[str, Any]:
39
+ """Configuration fixture with unique override settings."""
40
+ return {
41
+ "findingFromMapping": {
42
+ "tenable_sc": {
43
+ "severity": "risk_level",
44
+ "description": "details",
45
+ }
46
+ },
47
+ "uniqueOverride": {"asset": ["ipAddress"], "issue": ["title"]},
48
+ }
49
+
50
+
51
+ @pytest.fixture
52
+ def mock_app(basic_config: Dict[str, Any]) -> Application:
53
+ """Mock application fixture with basic configuration."""
26
54
  app = Application()
27
- # Override the config for testing purposes
28
- app.config = config
29
- assert "findingFromMapping" in app.config
30
- # Initialize IntegrationMapping with the mocked app
31
- integration_mapping = IntegrationOverride(app)
32
- # Test default
33
- assert integration_mapping.mapping_exists("tenable_sc", "title") is False
34
-
35
- # Test loading existing mapping
36
- assert integration_mapping.load("tenable_sc", "severity") == "risk_level"
37
- assert integration_mapping.load("tenable_sc", "description") == "details"
38
- assert integration_mapping.load("tenable_sc", "remediation") == "rando"
39
-
40
- # Test loading non-existing mapping
41
- assert integration_mapping.load("tenable_sc", "title") is None # default is None
42
- assert integration_mapping.load("tenable_sc", "non_existing_field") is None
43
- assert integration_mapping.load("non_existing_integration", "severity") is None
44
- # Uber fail
45
- assert integration_mapping.load(None, None) is None
46
-
47
-
48
- def test_no_config():
49
- mock_app = MagicMock()
50
- mock_app.config = {}
51
- integration_mapping = IntegrationOverride(mock_app)
52
- assert integration_mapping.load("tenable_sc", "remediation") is None
53
- assert integration_mapping.load(None, None) is None
54
-
55
-
56
- def test_singleton():
55
+ app.config = basic_config
56
+ app.logger = MagicMock()
57
+ return app
58
+
59
+
60
+ @pytest.fixture
61
+ def mock_app_with_unique_override(config_with_unique_override: Dict[str, Any]) -> Application:
62
+ """Mock application fixture with unique override configuration."""
57
63
  app = Application()
58
- instance1 = IntegrationOverride(app)
59
- instance2 = IntegrationOverride(app)
60
- assert instance1 is instance2
64
+ app.config = config_with_unique_override
65
+ app.logger = MagicMock()
66
+ return app
67
+
68
+
69
+ @pytest.fixture
70
+ def empty_config_app() -> Application:
71
+ """Mock application fixture with empty configuration."""
72
+ app = Application()
73
+ app.config = {}
74
+ app.logger = MagicMock()
75
+ return app
76
+
77
+
78
+ class TestIntegrationOverrideSingleton:
79
+ """Test the singleton pattern implementation."""
80
+
81
+ def test_singleton_pattern(self, mock_app: Application):
82
+ """Test that IntegrationOverride follows singleton pattern."""
83
+ # Reset singleton instance for clean test
84
+ IntegrationOverride._instance = None
85
+
86
+ instance1 = IntegrationOverride(mock_app)
87
+ instance2 = IntegrationOverride(mock_app)
88
+
89
+ assert instance1 is instance2
90
+ assert id(instance1) == id(instance2)
91
+
92
+ def test_singleton_thread_safety(self, mock_app: Application):
93
+ """Test that singleton creation is thread-safe."""
94
+ # Reset singleton instance for clean test
95
+ IntegrationOverride._instance = None
96
+
97
+ import threading
98
+ import time
99
+
100
+ instances = []
101
+
102
+ def create_instance():
103
+ time.sleep(0.01) # Small delay to increase race condition chance
104
+ instances.append(IntegrationOverride(mock_app))
105
+
106
+ threads = [threading.Thread(target=create_instance) for _ in range(5)]
107
+ for thread in threads:
108
+ thread.start()
109
+ for thread in threads:
110
+ thread.join()
111
+
112
+ # All instances should be the same
113
+ assert len(set(instances)) == 1
114
+ assert all(instance is instances[0] for instance in instances)
115
+
116
+
117
+ class TestIntegrationOverrideInitialization:
118
+ """Test the initialization and configuration loading."""
119
+
120
+ def test_initialization_with_config(self, mock_app: Application):
121
+ """Test initialization with valid configuration."""
122
+ integration_override = IntegrationOverride(mock_app)
123
+
124
+ assert integration_override.app == mock_app
125
+ assert "tenable_sc" in integration_override.mapping
126
+ assert "qualys" in integration_override.mapping
127
+ assert integration_override.mapping["tenable_sc"]["severity"] == "risk_level"
128
+
129
+ def test_initialization_with_empty_config(self, empty_config_app: Application):
130
+ """Test initialization with empty configuration."""
131
+ integration_override = IntegrationOverride(empty_config_app)
132
+
133
+ assert integration_override.app == empty_config_app
134
+ assert integration_override.mapping == {}
135
+
136
+ def test_initialization_only_once(self, mock_app: Application):
137
+ """Test that console and logging are only initialized once."""
138
+ # Reset singleton instance for clean test
139
+ IntegrationOverride._instance = None
140
+
141
+ # Patch the rich imports inside the method
142
+ with patch("rich.console.Console") as mock_console, patch("rich.table.Table") as mock_table:
143
+ mock_table_instance = Mock()
144
+ mock_table.return_value = mock_table_instance
145
+ mock_table_instance.row_count = 0
146
+
147
+ IntegrationOverride(mock_app)
148
+ IntegrationOverride(mock_app)
149
+
150
+ # Console should only be created once
151
+ mock_console.assert_called_once()
152
+
153
+
154
+ class TestIntegrationOverrideMappingMethods:
155
+ """Test the mapping-related methods."""
156
+
157
+ def test_get_mapping(self, mock_app: Application):
158
+ """Test _get_mapping method."""
159
+ integration_override = IntegrationOverride(mock_app)
160
+
161
+ mapping = integration_override._get_mapping(mock_app.config)
162
+ assert mapping == mock_app.config["findingFromMapping"]
163
+
164
+ def test_get_mapping_without_finding_from_mapping(self, empty_config_app: Application):
165
+ """Test _get_mapping when findingFromMapping is not present."""
166
+ integration_override = IntegrationOverride(empty_config_app)
167
+
168
+ mapping = integration_override._get_mapping(empty_config_app.config)
169
+ assert mapping == {}
170
+
171
+ def test_mapping_exists_valid_mapping(self, mock_app: Application):
172
+ """Test mapping_exists with valid mapping."""
173
+ integration_override = IntegrationOverride(mock_app)
174
+
175
+ assert integration_override.mapping_exists("tenable_sc", "severity") is True
176
+ assert integration_override.mapping_exists("qualys", "severity") is True
177
+
178
+ def test_mapping_exists_default_value(self, mock_app: Application):
179
+ """Test mapping_exists with default value."""
180
+ integration_override = IntegrationOverride(mock_app)
181
+
182
+ assert integration_override.mapping_exists("tenable_sc", "title") is False
183
+
184
+ def test_mapping_exists_nonexistent_integration(self, mock_app: Application):
185
+ """Test mapping_exists with nonexistent integration."""
186
+ integration_override = IntegrationOverride(mock_app)
187
+
188
+ # The method now returns False when integration doesn't exist
189
+ assert integration_override.mapping_exists("nonexistent", "severity") is False
190
+
191
+ def test_mapping_exists_nonexistent_field(self, mock_app: Application):
192
+ """Test mapping_exists with nonexistent field."""
193
+ integration_override = IntegrationOverride(mock_app)
194
+
195
+ assert integration_override.mapping_exists("tenable_sc", "nonexistent") is False
196
+
197
+ def test_mapping_exists_case_insensitive(self, mock_app: Application):
198
+ """Test mapping_exists is case insensitive."""
199
+ integration_override = IntegrationOverride(mock_app)
200
+
201
+ assert integration_override.mapping_exists("TENABLE_SC", "SEVERITY") is True
202
+ assert integration_override.mapping_exists("Tenable_Sc", "Severity") is True
203
+
204
+ def test_mapping_exists_with_none_values(self, mock_app: Application):
205
+ """Test mapping_exists with None values."""
206
+ integration_override = IntegrationOverride(mock_app)
207
+
208
+ assert integration_override.mapping_exists(None, "severity") is False
209
+ assert integration_override.mapping_exists("tenable_sc", None) is False
210
+ assert integration_override.mapping_exists(None, None) is False
211
+
212
+
213
+ class TestIntegrationOverrideLoadMethod:
214
+ """Test the load method."""
215
+
216
+ def test_load_valid_mapping(self, mock_app: Application):
217
+ """Test load with valid mapping."""
218
+ integration_override = IntegrationOverride(mock_app)
219
+
220
+ assert integration_override.load("tenable_sc", "severity") == "risk_level"
221
+ assert integration_override.load("tenable_sc", "description") == "details"
222
+ assert integration_override.load("qualys", "severity") == "cvss_score"
223
+
224
+ def test_load_default_value(self, mock_app: Application):
225
+ """Test load with default value."""
226
+ integration_override = IntegrationOverride(mock_app)
227
+
228
+ assert integration_override.load("tenable_sc", "title") is None
229
+
230
+ def test_load_nonexistent_integration(self, mock_app: Application):
231
+ """Test load with nonexistent integration."""
232
+ integration_override = IntegrationOverride(mock_app)
233
+
234
+ assert integration_override.load("nonexistent", "severity") is None
235
+
236
+ def test_load_nonexistent_field(self, mock_app: Application):
237
+ """Test load with nonexistent field."""
238
+ integration_override = IntegrationOverride(mock_app)
239
+
240
+ assert integration_override.load("tenable_sc", "nonexistent") is None
241
+
242
+ def test_load_none_parameters(self, mock_app: Application):
243
+ """Test load with None parameters."""
244
+ integration_override = IntegrationOverride(mock_app)
245
+
246
+ assert integration_override.load(None, None) is None
247
+ # The method should handle None field_name gracefully
248
+ assert integration_override.load("tenable_sc", None) is None
249
+ assert integration_override.load(None, "severity") is None
250
+
251
+ def test_load_case_insensitive(self, mock_app: Application):
252
+ """Test load is case insensitive."""
253
+ integration_override = IntegrationOverride(mock_app)
254
+
255
+ assert integration_override.load("TENABLE_SC", "SEVERITY") == "risk_level"
256
+ assert integration_override.load("Tenable_Sc", "Severity") == "risk_level"
257
+
258
+
259
+ class TestIntegrationOverrideFieldMapValidation:
260
+ """Test the field_map_validation method."""
261
+
262
+ def test_field_map_validation_no_unique_override(self, mock_app: Application):
263
+ """Test field_map_validation when no uniqueOverride is configured."""
264
+ integration_override = IntegrationOverride(mock_app)
265
+ obj = {"ip": "192.168.1.1"}
266
+
267
+ result = integration_override.field_map_validation(obj, "asset")
268
+ assert result is None
269
+
270
+ def test_field_map_validation_empty_unique_override(self, mock_app_with_unique_override: Application):
271
+ """Test field_map_validation with empty uniqueOverride for model type."""
272
+ integration_override = IntegrationOverride(mock_app_with_unique_override)
273
+ obj = {"ip": "192.168.1.1"}
274
+
275
+ # Override config to have empty list for asset
276
+ integration_override.app.config["uniqueOverride"]["asset"] = []
277
+
278
+ result = integration_override.field_map_validation(obj, "asset")
279
+ assert result is None
280
+
281
+ def test_field_map_validation_unsupported_field(self, mock_app_with_unique_override: Application):
282
+ """Test field_map_validation with unsupported field."""
283
+ integration_override = IntegrationOverride(mock_app_with_unique_override)
284
+ obj = {"ip": "192.168.1.1"}
285
+
286
+ # Override config to have unsupported field
287
+ integration_override.app.config["uniqueOverride"]["asset"] = ["unsupported_field"]
288
+
289
+ result = integration_override.field_map_validation(obj, "asset")
290
+ assert result is None
291
+
292
+ def test_field_map_validation_unsupported_model_type(self, mock_app_with_unique_override: Application):
293
+ """Test field_map_validation with unsupported model type."""
294
+ integration_override = IntegrationOverride(mock_app_with_unique_override)
295
+ obj = {"ip": "192.168.1.1"}
296
+
297
+ result = integration_override.field_map_validation(obj, "unsupported_type")
298
+ assert result is None
299
+
300
+ def test_field_map_validation_dict_object_tenableasset(self, mock_app_with_unique_override: Application):
301
+ """Test field_map_validation with dict object for tenableasset model."""
302
+ integration_override = IntegrationOverride(mock_app_with_unique_override)
303
+
304
+ # Create a dict-like object that has a class name
305
+ class DictLikeObject(dict):
306
+ """Dict-like object with custom class name for testing."""
307
+
308
+ def __init__(self, *args, **kwargs):
309
+ super().__init__(*args, **kwargs)
310
+ self.__class__.__name__ = "TenableAsset"
311
+
312
+ obj = DictLikeObject({"ip": "192.168.1.1", "dnsName": "test.example.com"})
313
+
314
+ result = integration_override.field_map_validation(obj, "asset")
315
+ assert result == "192.168.1.1"
316
+
317
+ def test_field_map_validation_dict_object_dict(self, mock_app_with_unique_override: Application):
318
+ """Test field_map_validation with dict object for dict model."""
319
+ integration_override = IntegrationOverride(mock_app_with_unique_override)
320
+
321
+ # Create a dict-like object that has a class name
322
+ class DictLikeObject(dict):
323
+ """Dict-like object with custom class name for testing."""
324
+
325
+ def __init__(self, *args, **kwargs):
326
+ super().__init__(*args, **kwargs)
327
+ self.__class__.__name__ = "dict"
328
+
329
+ obj = DictLikeObject({"ipv4": "192.168.1.1", "fqdn": "test.example.com"})
330
+
331
+ result = integration_override.field_map_validation(obj, "asset")
332
+ assert result == "192.168.1.1"
333
+
334
+ def test_field_map_validation_object_with_attributes(self, mock_app_with_unique_override: Application):
335
+ """Test field_map_validation with object that has attributes."""
336
+ integration_override = IntegrationOverride(mock_app_with_unique_override)
337
+
338
+ # Create mock object with attributes
339
+ obj = Mock()
340
+ obj.__class__.__name__ = "TenableAsset"
341
+ obj.ip = "192.168.1.1"
342
+ obj.dnsName = "test.example.com"
343
+
344
+ result = integration_override.field_map_validation(obj, "asset")
345
+ assert result == "192.168.1.1"
346
+
347
+ def test_field_map_validation_missing_mapped_field(self, mock_app_with_unique_override: Application):
348
+ """Test field_map_validation when mapped field is missing."""
349
+ integration_override = IntegrationOverride(mock_app_with_unique_override)
350
+
351
+ # Create a dict-like object missing the mapped field
352
+ class DictLikeObject(dict):
353
+ """Dict-like object with custom class name for testing."""
354
+
355
+ def __init__(self, *args, **kwargs):
356
+ super().__init__(*args, **kwargs)
357
+ self.__class__.__name__ = "TenableAsset"
358
+
359
+ obj = DictLikeObject({"dnsName": "test.example.com"}) # Missing "ip"
360
+
361
+ result = integration_override.field_map_validation(obj, "asset")
362
+ assert result is None
363
+
364
+ def test_field_map_validation_exception_handling(self, mock_app_with_unique_override: Application):
365
+ """Test field_map_validation handles exceptions gracefully."""
366
+ integration_override = IntegrationOverride(mock_app_with_unique_override)
367
+
368
+ # Create an object that will cause an exception when accessing __class__.__name__
369
+ class ProblematicObject:
370
+ """Object that raises AttributeError when accessing __class__.__name__."""
371
+
372
+ @property
373
+ def __class__(self):
374
+ # This will cause an AttributeError when trying to access __name__
375
+ raise AttributeError("Simulated error")
376
+
377
+ obj = ProblematicObject()
378
+
379
+ result = integration_override.field_map_validation(obj, "asset")
380
+ assert result is None
381
+ integration_override.app.logger.warning.assert_called_once()
382
+
383
+ def test_field_map_validation_different_field_mappings(self, mock_app_with_unique_override: Application):
384
+ """Test field_map_validation with different field mappings."""
385
+ integration_override = IntegrationOverride(mock_app_with_unique_override)
386
+
387
+ # Test name field mapping
388
+ integration_override.app.config["uniqueOverride"]["asset"] = ["name"]
389
+
390
+ class DictLikeObject(dict):
391
+ """Dict-like object with custom class name for testing."""
392
+
393
+ def __init__(self, *args, **kwargs):
394
+ super().__init__(*args, **kwargs)
395
+ self.__class__.__name__ = "TenableAsset"
396
+
397
+ obj = DictLikeObject({"dnsName": "test.example.com"})
398
+
399
+ result = integration_override.field_map_validation(obj, "asset")
400
+ assert result == "test.example.com"
401
+
402
+ # Test dns field mapping
403
+ integration_override.app.config["uniqueOverride"]["asset"] = ["dns"]
404
+
405
+ result = integration_override.field_map_validation(obj, "asset")
406
+ assert result == "test.example.com"
407
+
408
+ # Test fqdn field mapping
409
+ integration_override.app.config["uniqueOverride"]["asset"] = ["fqdn"]
410
+
411
+ result = integration_override.field_map_validation(obj, "asset")
412
+ assert result == "test.example.com"
413
+
414
+
415
+ class TestIntegrationOverrideLogging:
416
+ """Test the logging functionality."""
417
+
418
+ def test_log_mappings_with_mappings(self, mock_app: Application):
419
+ """Test _log_mappings when mappings exist."""
420
+ # Reset singleton instance for clean test
421
+ IntegrationOverride._instance = None
422
+
423
+ # Patch the rich imports inside the method
424
+ with patch("rich.table.Table") as mock_table, patch("rich.console.Console") as mock_console:
425
+ mock_table_instance = Mock()
426
+ mock_table.return_value = mock_table_instance
427
+ mock_table_instance.row_count = 2 # Simulate rows in table
428
+
429
+ IntegrationOverride(mock_app)
430
+
431
+ # Verify table was created and printed
432
+ mock_table.assert_called_once()
433
+ mock_console_instance = mock_console.return_value
434
+ mock_console_instance.print.assert_called_once_with(mock_table_instance)
435
+
436
+ def test_log_mappings_no_mappings(self, empty_config_app: Application):
437
+ """Test _log_mappings when no mappings exist."""
438
+ # Reset singleton instance for clean test
439
+ IntegrationOverride._instance = None
440
+
441
+ # Patch the rich imports inside the method
442
+ with patch("rich.table.Table") as mock_table, patch("rich.console.Console") as mock_console:
443
+ mock_table_instance = Mock()
444
+ mock_table.return_value = mock_table_instance
445
+ mock_table_instance.row_count = 0 # No rows in table
446
+
447
+ IntegrationOverride(empty_config_app)
448
+
449
+ # Verify table was created but not printed (no rows)
450
+ mock_table.assert_called_once()
451
+ mock_console_instance = mock_console.return_value
452
+ mock_console_instance.print.assert_not_called()
453
+
454
+
455
+ class TestIntegrationOverrideEdgeCases:
456
+ """Test edge cases and error conditions."""
457
+
458
+ def test_load_with_empty_strings(self, mock_app: Application):
459
+ """Test load with empty strings."""
460
+ integration_override = IntegrationOverride(mock_app)
461
+
462
+ assert integration_override.load("", "") is None
463
+ assert integration_override.load("tenable_sc", "") is None
464
+ assert integration_override.load("", "severity") is None
465
+
466
+ def test_mapping_exists_with_empty_strings(self, mock_app: Application):
467
+ """Test mapping_exists with empty strings."""
468
+ integration_override = IntegrationOverride(mock_app)
469
+
470
+ # Empty strings should be handled gracefully and return False
471
+ assert integration_override.mapping_exists("", "") is False
472
+ assert integration_override.mapping_exists("tenable_sc", "") is False
473
+ assert integration_override.mapping_exists("", "severity") is False
474
+
475
+ def test_field_map_validation_with_none_object(self, mock_app_with_unique_override: Application):
476
+ """Test field_map_validation with None object."""
477
+ integration_override = IntegrationOverride(mock_app_with_unique_override)
478
+
479
+ result = integration_override.field_map_validation(None, "asset")
480
+ assert result is None
481
+
482
+ def test_field_map_validation_with_none_model_type(self, mock_app_with_unique_override: Application):
483
+ """Test field_map_validation with None model type."""
484
+ integration_override = IntegrationOverride(mock_app_with_unique_override)
485
+ obj = {"ip": "192.168.1.1"}
486
+
487
+ result = integration_override.field_map_validation(obj, None)
488
+ assert result is None
489
+
490
+ def test_field_map_validation_with_empty_string_model_type(self, mock_app_with_unique_override: Application):
491
+ """Test field_map_validation with empty string model type."""
492
+ integration_override = IntegrationOverride(mock_app_with_unique_override)
493
+ obj = {"ip": "192.168.1.1"}
494
+
495
+ result = integration_override.field_map_validation(obj, "")
496
+ assert result is None
497
+
498
+
499
+ @pytest.mark.integration
500
+ class TestIntegrationOverrideIntegration:
501
+ """Integration tests for IntegrationOverride."""
502
+
503
+ def test_full_workflow_with_tenable_sc(self, mock_app: Application):
504
+ """Test complete workflow with Tenable SC integration."""
505
+ integration_override = IntegrationOverride(mock_app)
506
+
507
+ # Test mapping exists
508
+ assert integration_override.mapping_exists("tenable_sc", "severity") is True
509
+
510
+ # Test load mapping
511
+ mapped_field = integration_override.load("tenable_sc", "severity")
512
+ assert mapped_field == "risk_level"
513
+
514
+ # Test that default values are not loaded
515
+ assert integration_override.load("tenable_sc", "title") is None
516
+
517
+ def test_full_workflow_with_qualys(self, mock_app: Application):
518
+ """Test complete workflow with Qualys integration."""
519
+ integration_override = IntegrationOverride(mock_app)
520
+
521
+ # Test mapping exists
522
+ assert integration_override.mapping_exists("qualys", "severity") is True
523
+
524
+ # Test load mapping
525
+ mapped_field = integration_override.load("qualys", "severity")
526
+ assert mapped_field == "cvss_score"
527
+
528
+ # Test solution mapping
529
+ solution_field = integration_override.load("qualys", "solution")
530
+ assert solution_field == "fix"
531
+
532
+ def test_case_insensitive_workflow(self, mock_app: Application):
533
+ """Test complete workflow with case insensitive handling."""
534
+ integration_override = IntegrationOverride(mock_app)
535
+
536
+ # Test with mixed case
537
+ assert integration_override.mapping_exists("TENABLE_SC", "SEVERITY") is True
538
+ assert integration_override.load("TENABLE_SC", "SEVERITY") == "risk_level"
539
+
540
+ # Test with title case
541
+ assert integration_override.mapping_exists("Tenable_Sc", "Severity") is True
542
+ assert integration_override.load("Tenable_Sc", "Severity") == "risk_level"
@@ -30,7 +30,7 @@ def test_issue_due_date():
30
30
 
31
31
  # Test with defaults
32
32
  severity = regscale_models.IssueSeverity.Moderate
33
- expected_due_date = "2023-07-30"
33
+ expected_due_date = "2023-05-01"
34
34
  result = issue_due_date(severity, created_date)
35
35
  assert result == expected_due_date
36
36