regscale-cli 6.21.2.0__py3-none-any.whl → 6.28.2.1__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 (314) hide show
  1. regscale/_version.py +1 -1
  2. regscale/airflow/hierarchy.py +2 -2
  3. regscale/core/app/api.py +5 -2
  4. regscale/core/app/application.py +36 -6
  5. regscale/core/app/internal/control_editor.py +73 -21
  6. regscale/core/app/internal/evidence.py +727 -204
  7. regscale/core/app/internal/login.py +4 -2
  8. regscale/core/app/internal/model_editor.py +219 -64
  9. regscale/core/app/utils/app_utils.py +86 -12
  10. regscale/core/app/utils/catalog_utils/common.py +1 -1
  11. regscale/core/login.py +21 -4
  12. regscale/core/utils/async_graphql_client.py +363 -0
  13. regscale/core/utils/date.py +77 -1
  14. regscale/dev/cli.py +26 -0
  15. regscale/dev/code_gen.py +109 -24
  16. regscale/dev/version.py +72 -0
  17. regscale/integrations/commercial/__init__.py +30 -2
  18. regscale/integrations/commercial/aws/audit_manager_compliance.py +3908 -0
  19. regscale/integrations/commercial/aws/cli.py +3107 -54
  20. regscale/integrations/commercial/aws/cloudtrail_control_mappings.py +333 -0
  21. regscale/integrations/commercial/aws/cloudtrail_evidence.py +501 -0
  22. regscale/integrations/commercial/aws/cloudwatch_control_mappings.py +357 -0
  23. regscale/integrations/commercial/aws/cloudwatch_evidence.py +490 -0
  24. regscale/integrations/commercial/{amazon → aws}/common.py +71 -19
  25. regscale/integrations/commercial/aws/config_compliance.py +914 -0
  26. regscale/integrations/commercial/aws/conformance_pack_mappings.py +198 -0
  27. regscale/integrations/commercial/aws/control_compliance_analyzer.py +439 -0
  28. regscale/integrations/commercial/aws/evidence_generator.py +283 -0
  29. regscale/integrations/commercial/aws/guardduty_control_mappings.py +340 -0
  30. regscale/integrations/commercial/aws/guardduty_evidence.py +1053 -0
  31. regscale/integrations/commercial/aws/iam_control_mappings.py +368 -0
  32. regscale/integrations/commercial/aws/iam_evidence.py +574 -0
  33. regscale/integrations/commercial/aws/inventory/__init__.py +338 -22
  34. regscale/integrations/commercial/aws/inventory/base.py +107 -5
  35. regscale/integrations/commercial/aws/inventory/resources/analytics.py +390 -0
  36. regscale/integrations/commercial/aws/inventory/resources/applications.py +234 -0
  37. regscale/integrations/commercial/aws/inventory/resources/audit_manager.py +513 -0
  38. regscale/integrations/commercial/aws/inventory/resources/cloudtrail.py +315 -0
  39. regscale/integrations/commercial/aws/inventory/resources/cloudtrail_logs_metadata.py +476 -0
  40. regscale/integrations/commercial/aws/inventory/resources/cloudwatch.py +191 -0
  41. regscale/integrations/commercial/aws/inventory/resources/compute.py +328 -9
  42. regscale/integrations/commercial/aws/inventory/resources/config.py +464 -0
  43. regscale/integrations/commercial/aws/inventory/resources/containers.py +74 -9
  44. regscale/integrations/commercial/aws/inventory/resources/database.py +481 -31
  45. regscale/integrations/commercial/aws/inventory/resources/developer_tools.py +253 -0
  46. regscale/integrations/commercial/aws/inventory/resources/guardduty.py +286 -0
  47. regscale/integrations/commercial/aws/inventory/resources/iam.py +470 -0
  48. regscale/integrations/commercial/aws/inventory/resources/inspector.py +476 -0
  49. regscale/integrations/commercial/aws/inventory/resources/integration.py +175 -61
  50. regscale/integrations/commercial/aws/inventory/resources/kms.py +447 -0
  51. regscale/integrations/commercial/aws/inventory/resources/machine_learning.py +358 -0
  52. regscale/integrations/commercial/aws/inventory/resources/networking.py +390 -67
  53. regscale/integrations/commercial/aws/inventory/resources/s3.py +394 -0
  54. regscale/integrations/commercial/aws/inventory/resources/security.py +268 -72
  55. regscale/integrations/commercial/aws/inventory/resources/securityhub.py +473 -0
  56. regscale/integrations/commercial/aws/inventory/resources/storage.py +288 -29
  57. regscale/integrations/commercial/aws/inventory/resources/systems_manager.py +657 -0
  58. regscale/integrations/commercial/aws/inventory/resources/vpc.py +655 -0
  59. regscale/integrations/commercial/aws/kms_control_mappings.py +288 -0
  60. regscale/integrations/commercial/aws/kms_evidence.py +879 -0
  61. regscale/integrations/commercial/aws/ocsf/__init__.py +7 -0
  62. regscale/integrations/commercial/aws/ocsf/constants.py +115 -0
  63. regscale/integrations/commercial/aws/ocsf/mapper.py +435 -0
  64. regscale/integrations/commercial/aws/org_control_mappings.py +286 -0
  65. regscale/integrations/commercial/aws/org_evidence.py +666 -0
  66. regscale/integrations/commercial/aws/s3_control_mappings.py +356 -0
  67. regscale/integrations/commercial/aws/s3_evidence.py +632 -0
  68. regscale/integrations/commercial/aws/scanner.py +1072 -205
  69. regscale/integrations/commercial/aws/security_hub.py +319 -0
  70. regscale/integrations/commercial/aws/session_manager.py +282 -0
  71. regscale/integrations/commercial/aws/ssm_control_mappings.py +291 -0
  72. regscale/integrations/commercial/aws/ssm_evidence.py +492 -0
  73. regscale/integrations/commercial/jira.py +489 -153
  74. regscale/integrations/commercial/microsoft_defender/defender.py +326 -5
  75. regscale/integrations/commercial/microsoft_defender/defender_api.py +348 -14
  76. regscale/integrations/commercial/microsoft_defender/defender_constants.py +157 -0
  77. regscale/integrations/commercial/qualys/__init__.py +167 -68
  78. regscale/integrations/commercial/qualys/scanner.py +305 -39
  79. regscale/integrations/commercial/sarif/sairf_importer.py +432 -0
  80. regscale/integrations/commercial/sarif/sarif_converter.py +67 -0
  81. regscale/integrations/commercial/sicura/api.py +79 -42
  82. regscale/integrations/commercial/sicura/commands.py +8 -2
  83. regscale/integrations/commercial/sicura/scanner.py +83 -44
  84. regscale/integrations/commercial/stigv2/ckl_parser.py +5 -5
  85. regscale/integrations/commercial/synqly/assets.py +133 -16
  86. regscale/integrations/commercial/synqly/edr.py +2 -8
  87. regscale/integrations/commercial/synqly/query_builder.py +536 -0
  88. regscale/integrations/commercial/synqly/ticketing.py +27 -0
  89. regscale/integrations/commercial/synqly/vulnerabilities.py +165 -28
  90. regscale/integrations/commercial/tenablev2/cis_parsers.py +453 -0
  91. regscale/integrations/commercial/tenablev2/cis_scanner.py +447 -0
  92. regscale/integrations/commercial/tenablev2/commands.py +146 -5
  93. regscale/integrations/commercial/tenablev2/scanner.py +1 -3
  94. regscale/integrations/commercial/tenablev2/stig_parsers.py +113 -57
  95. regscale/integrations/commercial/wizv2/WizDataMixin.py +1 -1
  96. regscale/integrations/commercial/wizv2/click.py +191 -76
  97. regscale/integrations/commercial/wizv2/compliance/__init__.py +15 -0
  98. regscale/integrations/commercial/wizv2/{policy_compliance_helpers.py → compliance/helpers.py} +78 -60
  99. regscale/integrations/commercial/wizv2/compliance_report.py +1592 -0
  100. regscale/integrations/commercial/wizv2/core/__init__.py +133 -0
  101. regscale/integrations/commercial/wizv2/{async_client.py → core/client.py} +7 -3
  102. regscale/integrations/commercial/wizv2/{constants.py → core/constants.py} +92 -89
  103. regscale/integrations/commercial/wizv2/core/file_operations.py +237 -0
  104. regscale/integrations/commercial/wizv2/fetchers/__init__.py +11 -0
  105. regscale/integrations/commercial/wizv2/{data_fetcher.py → fetchers/policy_assessment.py} +66 -9
  106. regscale/integrations/commercial/wizv2/file_cleanup.py +104 -0
  107. regscale/integrations/commercial/wizv2/issue.py +776 -28
  108. regscale/integrations/commercial/wizv2/models/__init__.py +0 -0
  109. regscale/integrations/commercial/wizv2/parsers/__init__.py +34 -0
  110. regscale/integrations/commercial/wizv2/{parsers.py → parsers/main.py} +1 -1
  111. regscale/integrations/commercial/wizv2/processors/__init__.py +11 -0
  112. regscale/integrations/commercial/wizv2/{finding_processor.py → processors/finding.py} +1 -1
  113. regscale/integrations/commercial/wizv2/reports.py +243 -0
  114. regscale/integrations/commercial/wizv2/sbom.py +1 -1
  115. regscale/integrations/commercial/wizv2/scanner.py +1031 -441
  116. regscale/integrations/commercial/wizv2/utils/__init__.py +48 -0
  117. regscale/integrations/commercial/wizv2/{utils.py → utils/main.py} +116 -61
  118. regscale/integrations/commercial/wizv2/variables.py +89 -3
  119. regscale/integrations/compliance_integration.py +1036 -151
  120. regscale/integrations/control_matcher.py +432 -0
  121. regscale/integrations/due_date_handler.py +333 -0
  122. regscale/integrations/milestone_manager.py +291 -0
  123. regscale/integrations/public/__init__.py +14 -0
  124. regscale/integrations/public/cci_importer.py +834 -0
  125. regscale/integrations/public/csam/__init__.py +0 -0
  126. regscale/integrations/public/csam/csam.py +938 -0
  127. regscale/integrations/public/csam/csam_agency_defined.py +179 -0
  128. regscale/integrations/public/csam/csam_common.py +154 -0
  129. regscale/integrations/public/csam/csam_controls.py +432 -0
  130. regscale/integrations/public/csam/csam_poam.py +124 -0
  131. regscale/integrations/public/fedramp/click.py +77 -6
  132. regscale/integrations/public/fedramp/docx_parser.py +10 -1
  133. regscale/integrations/public/fedramp/fedramp_cis_crm.py +675 -289
  134. regscale/integrations/public/fedramp/fedramp_five.py +1 -1
  135. regscale/integrations/public/fedramp/poam/scanner.py +75 -7
  136. regscale/integrations/public/fedramp/poam_export_v5.py +888 -0
  137. regscale/integrations/scanner_integration.py +1961 -430
  138. regscale/models/integration_models/CCI_List.xml +1 -0
  139. regscale/models/integration_models/aqua.py +2 -2
  140. regscale/models/integration_models/cisa_kev_data.json +805 -11
  141. regscale/models/integration_models/flat_file_importer/__init__.py +5 -8
  142. regscale/models/integration_models/nexpose.py +36 -10
  143. regscale/models/integration_models/qualys.py +3 -4
  144. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  145. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +87 -18
  146. regscale/models/integration_models/synqly_models/filter_parser.py +332 -0
  147. regscale/models/integration_models/synqly_models/ocsf_mapper.py +124 -25
  148. regscale/models/integration_models/synqly_models/synqly_model.py +89 -16
  149. regscale/models/locking.py +12 -8
  150. regscale/models/platform.py +4 -2
  151. regscale/models/regscale_models/__init__.py +7 -0
  152. regscale/models/regscale_models/assessment.py +2 -1
  153. regscale/models/regscale_models/catalog.py +1 -1
  154. regscale/models/regscale_models/compliance_settings.py +251 -1
  155. regscale/models/regscale_models/component.py +1 -0
  156. regscale/models/regscale_models/control_implementation.py +236 -41
  157. regscale/models/regscale_models/control_objective.py +74 -5
  158. regscale/models/regscale_models/file.py +2 -0
  159. regscale/models/regscale_models/form_field_value.py +5 -3
  160. regscale/models/regscale_models/inheritance.py +44 -0
  161. regscale/models/regscale_models/issue.py +301 -102
  162. regscale/models/regscale_models/milestone.py +33 -14
  163. regscale/models/regscale_models/organization.py +3 -0
  164. regscale/models/regscale_models/regscale_model.py +310 -73
  165. regscale/models/regscale_models/security_plan.py +4 -2
  166. regscale/models/regscale_models/vulnerability.py +3 -3
  167. regscale/regscale.py +25 -4
  168. regscale/templates/__init__.py +0 -0
  169. regscale/utils/threading/threadhandler.py +20 -15
  170. regscale/validation/record.py +23 -1
  171. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/METADATA +17 -33
  172. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/RECORD +310 -111
  173. tests/core/__init__.py +0 -0
  174. tests/core/utils/__init__.py +0 -0
  175. tests/core/utils/test_async_graphql_client.py +472 -0
  176. tests/fixtures/test_fixture.py +13 -8
  177. tests/regscale/core/test_login.py +171 -4
  178. tests/regscale/integrations/commercial/__init__.py +0 -0
  179. tests/regscale/integrations/commercial/aws/__init__.py +0 -0
  180. tests/regscale/integrations/commercial/aws/test_audit_manager_compliance.py +1304 -0
  181. tests/regscale/integrations/commercial/aws/test_audit_manager_evidence_aggregation.py +341 -0
  182. tests/regscale/integrations/commercial/aws/test_aws_analytics_collector.py +260 -0
  183. tests/regscale/integrations/commercial/aws/test_aws_applications_collector.py +242 -0
  184. tests/regscale/integrations/commercial/aws/test_aws_audit_manager_collector.py +1155 -0
  185. tests/regscale/integrations/commercial/aws/test_aws_cloudtrail_collector.py +534 -0
  186. tests/regscale/integrations/commercial/aws/test_aws_config_collector.py +400 -0
  187. tests/regscale/integrations/commercial/aws/test_aws_developer_tools_collector.py +203 -0
  188. tests/regscale/integrations/commercial/aws/test_aws_guardduty_collector.py +315 -0
  189. tests/regscale/integrations/commercial/aws/test_aws_iam_collector.py +458 -0
  190. tests/regscale/integrations/commercial/aws/test_aws_inspector_collector.py +353 -0
  191. tests/regscale/integrations/commercial/aws/test_aws_inventory_integration.py +530 -0
  192. tests/regscale/integrations/commercial/aws/test_aws_kms_collector.py +919 -0
  193. tests/regscale/integrations/commercial/aws/test_aws_machine_learning_collector.py +237 -0
  194. tests/regscale/integrations/commercial/aws/test_aws_s3_collector.py +722 -0
  195. tests/regscale/integrations/commercial/aws/test_aws_scanner_integration.py +722 -0
  196. tests/regscale/integrations/commercial/aws/test_aws_securityhub_collector.py +792 -0
  197. tests/regscale/integrations/commercial/aws/test_aws_systems_manager_collector.py +918 -0
  198. tests/regscale/integrations/commercial/aws/test_aws_vpc_collector.py +996 -0
  199. tests/regscale/integrations/commercial/aws/test_cli_evidence.py +431 -0
  200. tests/regscale/integrations/commercial/aws/test_cloudtrail_control_mappings.py +452 -0
  201. tests/regscale/integrations/commercial/aws/test_cloudtrail_evidence.py +788 -0
  202. tests/regscale/integrations/commercial/aws/test_config_compliance.py +298 -0
  203. tests/regscale/integrations/commercial/aws/test_conformance_pack_mappings.py +200 -0
  204. tests/regscale/integrations/commercial/aws/test_control_compliance_analyzer.py +375 -0
  205. tests/regscale/integrations/commercial/aws/test_datetime_parsing.py +223 -0
  206. tests/regscale/integrations/commercial/aws/test_evidence_generator.py +386 -0
  207. tests/regscale/integrations/commercial/aws/test_guardduty_control_mappings.py +564 -0
  208. tests/regscale/integrations/commercial/aws/test_guardduty_evidence.py +1041 -0
  209. tests/regscale/integrations/commercial/aws/test_iam_control_mappings.py +718 -0
  210. tests/regscale/integrations/commercial/aws/test_iam_evidence.py +1375 -0
  211. tests/regscale/integrations/commercial/aws/test_kms_control_mappings.py +656 -0
  212. tests/regscale/integrations/commercial/aws/test_kms_evidence.py +1163 -0
  213. tests/regscale/integrations/commercial/aws/test_ocsf_mapper.py +370 -0
  214. tests/regscale/integrations/commercial/aws/test_org_control_mappings.py +546 -0
  215. tests/regscale/integrations/commercial/aws/test_org_evidence.py +1240 -0
  216. tests/regscale/integrations/commercial/aws/test_s3_control_mappings.py +672 -0
  217. tests/regscale/integrations/commercial/aws/test_s3_evidence.py +987 -0
  218. tests/regscale/integrations/commercial/aws/test_scanner_evidence.py +373 -0
  219. tests/regscale/integrations/commercial/aws/test_security_hub_config_filtering.py +539 -0
  220. tests/regscale/integrations/commercial/aws/test_session_manager.py +516 -0
  221. tests/regscale/integrations/commercial/aws/test_ssm_control_mappings.py +588 -0
  222. tests/regscale/integrations/commercial/aws/test_ssm_evidence.py +735 -0
  223. tests/regscale/integrations/commercial/conftest.py +28 -0
  224. tests/regscale/integrations/commercial/microsoft_defender/__init__.py +1 -0
  225. tests/regscale/integrations/commercial/microsoft_defender/test_defender.py +1517 -0
  226. tests/regscale/integrations/commercial/microsoft_defender/test_defender_api.py +1748 -0
  227. tests/regscale/integrations/commercial/microsoft_defender/test_defender_constants.py +327 -0
  228. tests/regscale/integrations/commercial/microsoft_defender/test_defender_scanner.py +487 -0
  229. tests/regscale/integrations/commercial/test_aws.py +3742 -0
  230. tests/regscale/integrations/commercial/test_burp.py +48 -0
  231. tests/regscale/integrations/commercial/test_crowdstrike.py +49 -0
  232. tests/regscale/integrations/commercial/test_dependabot.py +341 -0
  233. tests/regscale/integrations/commercial/test_gcp.py +1543 -0
  234. tests/regscale/integrations/commercial/test_gitlab.py +549 -0
  235. tests/regscale/integrations/commercial/test_ip_mac_address_length.py +84 -0
  236. tests/regscale/integrations/commercial/test_jira.py +2204 -0
  237. tests/regscale/integrations/commercial/test_npm_audit.py +42 -0
  238. tests/regscale/integrations/commercial/test_okta.py +1228 -0
  239. tests/regscale/integrations/commercial/test_sarif_converter.py +251 -0
  240. tests/regscale/integrations/commercial/test_sicura.py +349 -0
  241. tests/regscale/integrations/commercial/test_snow.py +423 -0
  242. tests/regscale/integrations/commercial/test_sonarcloud.py +394 -0
  243. tests/regscale/integrations/commercial/test_sqlserver.py +186 -0
  244. tests/regscale/integrations/commercial/test_stig.py +33 -0
  245. tests/regscale/integrations/commercial/test_stig_mapper.py +153 -0
  246. tests/regscale/integrations/commercial/test_stigv2.py +406 -0
  247. tests/regscale/integrations/commercial/test_wiz.py +1365 -0
  248. tests/regscale/integrations/commercial/test_wiz_inventory.py +256 -0
  249. tests/regscale/integrations/commercial/wizv2/__init__.py +339 -0
  250. tests/regscale/integrations/commercial/wizv2/compliance/__init__.py +1 -0
  251. tests/regscale/integrations/commercial/wizv2/compliance/test_helpers.py +903 -0
  252. tests/regscale/integrations/commercial/wizv2/core/__init__.py +1 -0
  253. tests/regscale/integrations/commercial/wizv2/core/test_auth.py +701 -0
  254. tests/regscale/integrations/commercial/wizv2/core/test_client.py +1037 -0
  255. tests/regscale/integrations/commercial/wizv2/core/test_file_operations.py +989 -0
  256. tests/regscale/integrations/commercial/wizv2/fetchers/__init__.py +1 -0
  257. tests/regscale/integrations/commercial/wizv2/fetchers/test_policy_assessment.py +805 -0
  258. tests/regscale/integrations/commercial/wizv2/parsers/__init__.py +1 -0
  259. tests/regscale/integrations/commercial/wizv2/parsers/test_main.py +1153 -0
  260. tests/regscale/integrations/commercial/wizv2/processors/__init__.py +1 -0
  261. tests/regscale/integrations/commercial/wizv2/processors/test_finding.py +671 -0
  262. tests/regscale/integrations/commercial/wizv2/test_WizDataMixin.py +537 -0
  263. tests/regscale/integrations/commercial/wizv2/test_click_comprehensive.py +851 -0
  264. tests/regscale/integrations/commercial/wizv2/test_compliance_report_comprehensive.py +910 -0
  265. tests/regscale/integrations/commercial/wizv2/test_compliance_report_normalization.py +138 -0
  266. tests/regscale/integrations/commercial/wizv2/test_file_cleanup.py +283 -0
  267. tests/regscale/integrations/commercial/wizv2/test_file_operations.py +260 -0
  268. tests/regscale/integrations/commercial/wizv2/test_issue.py +343 -0
  269. tests/regscale/integrations/commercial/wizv2/test_issue_comprehensive.py +1203 -0
  270. tests/regscale/integrations/commercial/wizv2/test_reports.py +497 -0
  271. tests/regscale/integrations/commercial/wizv2/test_sbom.py +643 -0
  272. tests/regscale/integrations/commercial/wizv2/test_scanner_comprehensive.py +805 -0
  273. tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +165 -0
  274. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +1394 -0
  275. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_unit.py +341 -0
  276. tests/regscale/integrations/commercial/wizv2/test_wiz_control_normalization.py +138 -0
  277. tests/regscale/integrations/commercial/wizv2/test_wiz_findings_comprehensive.py +364 -0
  278. tests/regscale/integrations/commercial/wizv2/test_wiz_inventory_comprehensive.py +644 -0
  279. tests/regscale/integrations/commercial/wizv2/test_wiz_status_mapping.py +149 -0
  280. tests/regscale/integrations/commercial/wizv2/test_wizv2.py +1218 -0
  281. tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +519 -0
  282. tests/regscale/integrations/commercial/wizv2/utils/__init__.py +1 -0
  283. tests/regscale/integrations/commercial/wizv2/utils/test_main.py +1523 -0
  284. tests/regscale/integrations/public/__init__.py +0 -0
  285. tests/regscale/integrations/public/fedramp/__init__.py +1 -0
  286. tests/regscale/integrations/public/fedramp/test_gen_asset_list.py +150 -0
  287. tests/regscale/integrations/public/fedramp/test_poam_export_v5.py +1293 -0
  288. tests/regscale/integrations/public/test_alienvault.py +220 -0
  289. tests/regscale/integrations/public/test_cci.py +1053 -0
  290. tests/regscale/integrations/public/test_cisa.py +1021 -0
  291. tests/regscale/integrations/public/test_emass.py +518 -0
  292. tests/regscale/integrations/public/test_fedramp.py +1152 -0
  293. tests/regscale/integrations/public/test_fedramp_cis_crm.py +3661 -0
  294. tests/regscale/integrations/public/test_file_uploads.py +506 -0
  295. tests/regscale/integrations/public/test_oscal.py +453 -0
  296. tests/regscale/integrations/test_compliance_status_mapping.py +406 -0
  297. tests/regscale/integrations/test_control_matcher.py +1421 -0
  298. tests/regscale/integrations/test_control_matching.py +155 -0
  299. tests/regscale/integrations/test_milestone_manager.py +408 -0
  300. tests/regscale/models/test_control_implementation.py +118 -3
  301. tests/regscale/models/test_form_field_value_integration.py +304 -0
  302. tests/regscale/models/test_issue.py +378 -1
  303. tests/regscale/models/test_module_integration.py +582 -0
  304. tests/regscale/models/test_tenable_integrations.py +811 -105
  305. regscale/integrations/commercial/wizv2/policy_compliance.py +0 -3057
  306. regscale/integrations/public/fedramp/mappings/fedramp_r4_parts.json +0 -7388
  307. regscale/integrations/public/fedramp/mappings/fedramp_r5_parts.json +0 -9605
  308. regscale/integrations/public/fedramp/parts_mapper.py +0 -107
  309. /regscale/integrations/commercial/{amazon → sarif}/__init__.py +0 -0
  310. /regscale/integrations/commercial/wizv2/{wiz_auth.py → core/auth.py} +0 -0
  311. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/LICENSE +0 -0
  312. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/WHEEL +0 -0
  313. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/entry_points.txt +0 -0
  314. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,341 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Unit tests for AWS Audit Manager Evidence-Based Compliance Aggregation."""
4
+
5
+ import unittest
6
+
7
+ from regscale.integrations.commercial.aws.audit_manager_compliance import AWSAuditManagerComplianceItem
8
+
9
+
10
+ class TestEvidenceAggregation(unittest.TestCase):
11
+ """Test cases for evidence-based compliance aggregation logic."""
12
+
13
+ def setUp(self):
14
+ """Set up test fixtures."""
15
+ self.assessment_data = {
16
+ "name": "Test Assessment",
17
+ "arn": "arn:aws:auditmanager:us-east-1:123456789012:assessment/test",
18
+ "framework": {"type": "Standard", "metadata": {"name": "NIST SP 800-53 Revision 5"}},
19
+ "complianceType": "NIST800-53",
20
+ "awsAccount": {"id": "123456789012", "name": "Test Account"},
21
+ }
22
+
23
+ self.control_data = {
24
+ "id": "test-control-id",
25
+ "name": "CP-10(2) - Transaction Recovery",
26
+ "description": "Control description",
27
+ "status": "REVIEWED", # Workflow status - not used for compliance
28
+ "evidenceCount": 8,
29
+ }
30
+
31
+ def test_all_compliant_evidence_passes(self):
32
+ """Test that all COMPLIANT evidence results in PASS."""
33
+ evidence = [
34
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Security Hub"},
35
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"},
36
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Security Hub"},
37
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"},
38
+ ]
39
+
40
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
41
+ self.assertEqual(item.compliance_result, "PASS")
42
+
43
+ def test_any_failed_evidence_fails_control(self):
44
+ """Test that ANY FAILED evidence causes control to FAIL."""
45
+ evidence = [
46
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"},
47
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"},
48
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"},
49
+ {"complianceCheck": "FAILED", "dataSource": "AWS Security Hub"}, # ONE failure
50
+ ]
51
+
52
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
53
+ self.assertEqual(item.compliance_result, "FAIL")
54
+
55
+ def test_multiple_failed_evidence_fails_control(self):
56
+ """Test that multiple FAILED evidence items causes control to FAIL."""
57
+ evidence = [
58
+ {"complianceCheck": "FAILED", "dataSource": "AWS Security Hub"},
59
+ {"complianceCheck": "FAILED", "dataSource": "AWS Security Hub"},
60
+ {"complianceCheck": "FAILED", "dataSource": "AWS Security Hub"},
61
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"},
62
+ ]
63
+
64
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
65
+ self.assertEqual(item.compliance_result, "FAIL")
66
+
67
+ def test_all_failed_evidence_fails_control(self):
68
+ """Test that all FAILED evidence results in FAIL."""
69
+ evidence = [
70
+ {"complianceCheck": "FAILED", "dataSource": "AWS Security Hub"},
71
+ {"complianceCheck": "FAILED", "dataSource": "AWS Security Hub"},
72
+ {"complianceCheck": "FAILED", "dataSource": "AWS Security Hub"},
73
+ ]
74
+
75
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
76
+ self.assertEqual(item.compliance_result, "FAIL")
77
+
78
+ def test_no_evidence_returns_none(self):
79
+ """Test that no evidence results in None (control should not be updated)."""
80
+ evidence = []
81
+
82
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
83
+ self.assertIsNone(item.compliance_result)
84
+
85
+ def test_none_evidence_returns_none(self):
86
+ """Test that None evidence results in None (control should not be updated)."""
87
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, None)
88
+ self.assertIsNone(item.compliance_result)
89
+
90
+ def test_inconclusive_evidence_only_returns_none(self):
91
+ """Test that only inconclusive evidence (null checks) results in None (control should not be updated)."""
92
+ evidence = [
93
+ {"dataSource": "AWS CloudTrail"}, # No complianceCheck field
94
+ {"complianceCheck": None, "dataSource": "Manual"},
95
+ {"dataSource": "API Call"}, # No complianceCheck field
96
+ ]
97
+
98
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
99
+ self.assertIsNone(item.compliance_result)
100
+
101
+ def test_compliant_with_inconclusive_passes(self):
102
+ """Test that COMPLIANT evidence with some inconclusive results in PASS."""
103
+ evidence = [
104
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Security Hub"},
105
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"},
106
+ {"dataSource": "AWS CloudTrail"}, # No complianceCheck
107
+ {"complianceCheck": None, "dataSource": "Manual"}, # Null check
108
+ ]
109
+
110
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
111
+ self.assertEqual(item.compliance_result, "PASS")
112
+
113
+ def test_failed_with_inconclusive_fails(self):
114
+ """Test that FAILED evidence with inconclusive results in FAIL."""
115
+ evidence = [
116
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"},
117
+ {"complianceCheck": "FAILED", "dataSource": "AWS Security Hub"},
118
+ {"dataSource": "AWS CloudTrail"}, # No complianceCheck
119
+ {"complianceCheck": None, "dataSource": "Manual"}, # Null check
120
+ ]
121
+
122
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
123
+ self.assertEqual(item.compliance_result, "FAIL")
124
+
125
+ def test_real_world_mixed_evidence_cp10(self):
126
+ """Test real-world scenario: CP-10(2) with 4 COMPLIANT and 4 FAILED."""
127
+ # Based on actual audit_manager_evidence_cp_10(2)_2025-10-17.jsonl data
128
+ evidence = [
129
+ {"complianceCheck": "FAILED", "dataSource": "AWS Security Hub"},
130
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"},
131
+ {"complianceCheck": "FAILED", "dataSource": "AWS Security Hub"},
132
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"},
133
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"},
134
+ {"complianceCheck": "FAILED", "dataSource": "AWS Security Hub"},
135
+ {"complianceCheck": "FAILED", "dataSource": "AWS Security Hub"},
136
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"},
137
+ ]
138
+
139
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
140
+ # Should FAIL because there are FAILED evidence items
141
+ self.assertEqual(item.compliance_result, "FAIL")
142
+
143
+ def test_workflow_status_not_used_for_compliance(self):
144
+ """Test that control workflow status (REVIEWED) does NOT determine compliance."""
145
+ # Control marked as REVIEWED (workflow status) but has failing evidence
146
+ self.control_data["status"] = "REVIEWED"
147
+ evidence = [
148
+ {"complianceCheck": "FAILED", "dataSource": "AWS Security Hub"},
149
+ ]
150
+
151
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
152
+ # Should FAIL based on evidence, not REVIEWED status
153
+ self.assertEqual(item.compliance_result, "FAIL")
154
+ # Verify workflow status is still REVIEWED
155
+ self.assertEqual(item.control_status, "REVIEWED")
156
+
157
+ def test_under_review_with_passing_evidence(self):
158
+ """Test that UNDER_REVIEW workflow status with passing evidence results in PASS."""
159
+ self.control_data["status"] = "UNDER_REVIEW"
160
+ evidence = [
161
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"},
162
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Security Hub"},
163
+ ]
164
+
165
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
166
+ # Should PASS based on evidence, regardless of UNDER_REVIEW status
167
+ self.assertEqual(item.compliance_result, "PASS")
168
+ # Verify workflow status is still UNDER_REVIEW
169
+ self.assertEqual(item.control_status, "UNDER_REVIEW")
170
+
171
+ def test_compliance_result_caching(self):
172
+ """Test that compliance result is cached after first calculation."""
173
+ evidence = [
174
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"},
175
+ ]
176
+
177
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
178
+
179
+ # First call
180
+ result1 = item.compliance_result
181
+ self.assertEqual(result1, "PASS")
182
+
183
+ # Verify cached value is used
184
+ self.assertEqual(item._aggregated_compliance_result, "PASS")
185
+
186
+ # Second call should return same cached result
187
+ result2 = item.compliance_result
188
+ self.assertEqual(result2, "PASS")
189
+
190
+ def test_severity_when_passed(self):
191
+ """Test that severity is None when control passes."""
192
+ evidence = [
193
+ {"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"},
194
+ ]
195
+
196
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
197
+ self.assertIsNone(item.severity)
198
+
199
+ def test_severity_when_failed(self):
200
+ """Test that severity is set when control fails."""
201
+ evidence = [
202
+ {"complianceCheck": "FAILED", "dataSource": "AWS Security Hub"},
203
+ ]
204
+
205
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
206
+ self.assertEqual(item.severity, "MEDIUM")
207
+
208
+
209
+ class TestEvidenceAggregationEdgeCases(unittest.TestCase):
210
+ """Test edge cases for evidence aggregation."""
211
+
212
+ def setUp(self):
213
+ """Set up test fixtures."""
214
+ self.assessment_data = {
215
+ "name": "Edge Case Assessment",
216
+ "arn": "arn:aws:auditmanager:us-east-1:123456789012:assessment/test",
217
+ "framework": {"type": "Standard", "metadata": {"name": "NIST SP 800-53 Revision 5"}},
218
+ "complianceType": "NIST800-53",
219
+ "awsAccount": {"id": "123456789012"},
220
+ }
221
+
222
+ self.control_data = {
223
+ "id": "test-control-id",
224
+ "name": "AC-2 - Account Management",
225
+ "description": "Control description",
226
+ "status": "REVIEWED",
227
+ }
228
+
229
+ def test_case_sensitivity_compliant(self):
230
+ """Test that complianceCheck is case-sensitive (COMPLIANT vs compliant)."""
231
+ evidence = [
232
+ {"complianceCheck": "compliant", "dataSource": "AWS Config"}, # lowercase
233
+ ]
234
+
235
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
236
+ # Lowercase "compliant" should NOT match "COMPLIANT", treated as inconclusive
237
+ self.assertIsNone(item.compliance_result)
238
+
239
+ def test_case_sensitivity_failed(self):
240
+ """Test that complianceCheck is case-sensitive (FAILED vs failed)."""
241
+ evidence = [
242
+ {"complianceCheck": "failed", "dataSource": "AWS Security Hub"}, # lowercase
243
+ ]
244
+
245
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
246
+ # Lowercase "failed" should NOT match "FAILED", treated as inconclusive
247
+ self.assertIsNone(item.compliance_result)
248
+
249
+ def test_empty_string_compliance_check(self):
250
+ """Test that empty string complianceCheck is treated as inconclusive."""
251
+ evidence = [
252
+ {"complianceCheck": "", "dataSource": "AWS Config"},
253
+ ]
254
+
255
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
256
+ self.assertIsNone(item.compliance_result)
257
+
258
+ def test_whitespace_compliance_check(self):
259
+ """Test that whitespace-only complianceCheck is treated as inconclusive."""
260
+ evidence = [
261
+ {"complianceCheck": " ", "dataSource": "AWS Config"},
262
+ ]
263
+
264
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
265
+ self.assertIsNone(item.compliance_result)
266
+
267
+ def test_unexpected_compliance_check_value(self):
268
+ """Test that unexpected values are treated as inconclusive."""
269
+ evidence = [
270
+ {"complianceCheck": "UNKNOWN", "dataSource": "AWS Config"},
271
+ {"complianceCheck": "PENDING", "dataSource": "AWS Config"},
272
+ {"complianceCheck": 123, "dataSource": "AWS Config"}, # Number instead of string
273
+ ]
274
+
275
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
276
+ self.assertIsNone(item.compliance_result)
277
+
278
+ def test_large_number_of_evidence_items(self):
279
+ """Test aggregation with large number of evidence items."""
280
+ # Create 1000 evidence items
281
+ evidence = [{"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"} for _ in range(1000)]
282
+
283
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
284
+ self.assertEqual(item.compliance_result, "PASS")
285
+
286
+ def test_large_number_with_one_failure(self):
287
+ """Test that one failure in many evidence items still causes FAIL."""
288
+ evidence = [{"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"} for _ in range(999)]
289
+ evidence.append({"complianceCheck": "FAILED", "dataSource": "AWS Security Hub"}) # One failure
290
+
291
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
292
+ self.assertEqual(item.compliance_result, "FAIL")
293
+
294
+
295
+ class TestComplianceItemCreation(unittest.TestCase):
296
+ """Test creating compliance items with evidence."""
297
+
298
+ def setUp(self):
299
+ """Set up test fixtures."""
300
+ self.assessment_data = {
301
+ "name": "Test Assessment",
302
+ "arn": "arn:aws:auditmanager:us-east-1:123456789012:assessment/test",
303
+ "framework": {"type": "Standard", "metadata": {"name": "NIST 800-53"}},
304
+ "awsAccount": {"id": "123456789012"},
305
+ }
306
+
307
+ self.control_data = {
308
+ "id": "test-id",
309
+ "name": "AC-2 - Account Management",
310
+ "status": "REVIEWED",
311
+ }
312
+
313
+ def test_create_with_evidence(self):
314
+ """Test creating compliance item with evidence."""
315
+ evidence = [{"complianceCheck": "COMPLIANT", "dataSource": "AWS Config"}]
316
+
317
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, evidence)
318
+
319
+ self.assertEqual(item.control_id, "AC-2")
320
+ self.assertEqual(len(item.evidence_items), 1)
321
+ self.assertEqual(item.compliance_result, "PASS")
322
+
323
+ def test_create_without_evidence(self):
324
+ """Test creating compliance item without evidence (backward compatibility)."""
325
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data)
326
+
327
+ self.assertEqual(item.control_id, "AC-2")
328
+ self.assertEqual(len(item.evidence_items), 0)
329
+ self.assertIsNone(item.compliance_result) # No evidence = None (skip update)
330
+
331
+ def test_create_with_explicit_none_evidence(self):
332
+ """Test creating compliance item with explicit None evidence."""
333
+ item = AWSAuditManagerComplianceItem(self.assessment_data, self.control_data, None)
334
+
335
+ self.assertEqual(item.control_id, "AC-2")
336
+ self.assertEqual(len(item.evidence_items), 0)
337
+ self.assertIsNone(item.compliance_result) # No evidence = None (skip update)
338
+
339
+
340
+ if __name__ == "__main__":
341
+ unittest.main()
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Unit tests for AWS Analytics Collector."""
4
+
5
+ import logging
6
+ from datetime import datetime
7
+ from unittest.mock import MagicMock, patch
8
+
9
+ import pytest
10
+ from botocore.exceptions import ClientError
11
+
12
+ from regscale.integrations.commercial.aws.inventory.resources.analytics import AnalyticsCollector
13
+
14
+ logger = logging.getLogger("regscale")
15
+
16
+
17
+ class TestAnalyticsCollector:
18
+ """Test suite for AnalyticsCollector class."""
19
+
20
+ @pytest.fixture
21
+ def mock_session(self):
22
+ """Create a mock boto3 session."""
23
+ return MagicMock()
24
+
25
+ @pytest.fixture
26
+ def collector(self, mock_session):
27
+ """Create an AnalyticsCollector instance."""
28
+ return AnalyticsCollector(session=mock_session, region="us-east-1", account_id=None)
29
+
30
+ @pytest.fixture
31
+ def collector_with_filters(self, mock_session):
32
+ """Create an AnalyticsCollector instance with filters."""
33
+ return AnalyticsCollector(
34
+ session=mock_session,
35
+ region="us-east-1",
36
+ account_id="123456789012",
37
+ tags={"Environment": "Production"},
38
+ )
39
+
40
+ # Test initialization
41
+ def test_collector_initialization(self, mock_session):
42
+ """Test collector initialization."""
43
+ collector = AnalyticsCollector(session=mock_session, region="us-west-2")
44
+ assert collector.session == mock_session
45
+ assert collector.region == "us-west-2"
46
+ assert collector.account_id is None
47
+ assert collector.tags == {}
48
+
49
+ def test_collector_initialization_with_filters(self, mock_session):
50
+ """Test collector initialization with filters."""
51
+ collector = AnalyticsCollector(
52
+ session=mock_session,
53
+ region="us-east-1",
54
+ account_id="123456789012",
55
+ tags={"Environment": "Production"},
56
+ )
57
+ assert collector.account_id == "123456789012"
58
+ assert collector.tags == {"Environment": "Production"}
59
+
60
+ # Test EMR clusters
61
+ def test_get_emr_clusters_success(self, collector):
62
+ """Test successful EMR cluster collection."""
63
+ mock_client = MagicMock()
64
+ collector._get_client = MagicMock(return_value=mock_client)
65
+
66
+ mock_paginator = MagicMock()
67
+ mock_client.get_paginator.return_value = mock_paginator
68
+ mock_paginator.paginate.return_value = [{"Clusters": [{"Id": "j-123456", "Name": "test-cluster"}]}]
69
+
70
+ mock_client.describe_cluster.return_value = {
71
+ "Cluster": {
72
+ "Id": "j-123456",
73
+ "Name": "test-cluster",
74
+ "ClusterArn": "arn:aws:elasticmapreduce:us-east-1:123456789012:cluster/j-123456",
75
+ "Status": {"State": "RUNNING"},
76
+ "Tags": [{"Key": "Environment", "Value": "Test"}],
77
+ }
78
+ }
79
+
80
+ clusters = collector.get_emr_clusters()
81
+ assert len(clusters) == 1
82
+ assert clusters[0]["ClusterId"] == "j-123456"
83
+ assert clusters[0]["Name"] == "test-cluster"
84
+
85
+ def test_get_emr_clusters_with_account_filter(self, collector_with_filters):
86
+ """Test EMR cluster collection with account filtering."""
87
+ mock_client = MagicMock()
88
+ collector_with_filters._get_client = MagicMock(return_value=mock_client)
89
+
90
+ mock_paginator = MagicMock()
91
+ mock_client.get_paginator.return_value = mock_paginator
92
+ mock_paginator.paginate.return_value = [{"Clusters": [{"Id": "j-123456"}]}]
93
+
94
+ mock_client.describe_cluster.return_value = {
95
+ "Cluster": {
96
+ "ClusterArn": "arn:aws:elasticmapreduce:us-east-1:999999999999:cluster/j-123456",
97
+ "Tags": [],
98
+ }
99
+ }
100
+
101
+ clusters = collector_with_filters.get_emr_clusters()
102
+ assert len(clusters) == 0
103
+
104
+ # Test Kinesis Data Streams
105
+ def test_get_kinesis_streams_success(self, collector):
106
+ """Test successful Kinesis stream collection."""
107
+ mock_client = MagicMock()
108
+ collector._get_client = MagicMock(return_value=mock_client)
109
+
110
+ mock_paginator = MagicMock()
111
+ mock_client.get_paginator.return_value = mock_paginator
112
+ mock_paginator.paginate.return_value = [{"StreamNames": ["test-stream"]}]
113
+
114
+ mock_client.describe_stream.return_value = {
115
+ "StreamDescription": {
116
+ "StreamName": "test-stream",
117
+ "StreamARN": "arn:aws:kinesis:us-east-1:123456789012:stream/test-stream",
118
+ "StreamStatus": "ACTIVE",
119
+ "RetentionPeriodHours": 24,
120
+ }
121
+ }
122
+
123
+ mock_client.list_tags_for_stream.return_value = {"Tags": [{"Key": "Environment", "Value": "Test"}]}
124
+
125
+ streams = collector.get_kinesis_streams()
126
+ assert len(streams) == 1
127
+ assert streams[0]["StreamName"] == "test-stream"
128
+ assert streams[0]["StreamStatus"] == "ACTIVE"
129
+
130
+ # Test Kinesis Firehose
131
+ def test_get_kinesis_firehose_streams_success(self, collector):
132
+ """Test successful Kinesis Firehose stream collection."""
133
+ mock_client = MagicMock()
134
+ collector._get_client = MagicMock(return_value=mock_client)
135
+
136
+ mock_paginator = MagicMock()
137
+ mock_client.get_paginator.return_value = mock_paginator
138
+ mock_paginator.paginate.return_value = [{"DeliveryStreamNames": ["test-firehose"]}]
139
+
140
+ mock_client.describe_delivery_stream.return_value = {
141
+ "DeliveryStreamDescription": {
142
+ "DeliveryStreamName": "test-firehose",
143
+ "DeliveryStreamARN": "arn:aws:firehose:us-east-1:123456789012:deliverystream/test-firehose",
144
+ "DeliveryStreamStatus": "ACTIVE",
145
+ }
146
+ }
147
+
148
+ mock_client.list_tags_for_delivery_stream.return_value = {"Tags": [{"Key": "Team", "Value": "DataEng"}]}
149
+
150
+ streams = collector.get_kinesis_firehose_streams()
151
+ assert len(streams) == 1
152
+ assert streams[0]["DeliveryStreamName"] == "test-firehose"
153
+
154
+ # Test Glue Databases
155
+ def test_get_glue_databases_success(self, collector):
156
+ """Test successful Glue database collection."""
157
+ mock_client = MagicMock()
158
+ collector._get_client = MagicMock(return_value=mock_client)
159
+
160
+ mock_paginator = MagicMock()
161
+ mock_client.get_paginator.return_value = mock_paginator
162
+ mock_paginator.paginate.return_value = [{"DatabaseList": [{"Name": "test-db", "Description": "Test database"}]}]
163
+
164
+ mock_client.get_tags.return_value = {"Tags": {"Environment": "Dev"}}
165
+
166
+ databases = collector.get_glue_databases()
167
+ assert len(databases) == 1
168
+ assert databases[0]["DatabaseName"] == "test-db"
169
+
170
+ # Test Athena Workgroups
171
+ def test_get_athena_workgroups_success(self, collector):
172
+ """Test successful Athena workgroup collection."""
173
+ mock_client = MagicMock()
174
+ collector._get_client = MagicMock(return_value=mock_client)
175
+
176
+ mock_paginator = MagicMock()
177
+ mock_client.get_paginator.return_value = mock_paginator
178
+ mock_paginator.paginate.return_value = [{"WorkGroups": [{"Name": "primary"}]}]
179
+
180
+ mock_client.get_work_group.return_value = {
181
+ "WorkGroup": {
182
+ "Name": "primary",
183
+ "State": "ENABLED",
184
+ "Description": "Primary workgroup",
185
+ }
186
+ }
187
+
188
+ mock_client.list_tags_for_resource.return_value = {"Tags": [{"Key": "Type", "Value": "Analytics"}]}
189
+
190
+ workgroups = collector.get_athena_workgroups()
191
+ assert len(workgroups) == 1
192
+ assert workgroups[0]["WorkGroupName"] == "primary"
193
+
194
+ # Test MSK Clusters
195
+ def test_get_msk_clusters_success(self, collector):
196
+ """Test successful MSK cluster collection."""
197
+ mock_client = MagicMock()
198
+ collector._get_client = MagicMock(return_value=mock_client)
199
+
200
+ mock_paginator = MagicMock()
201
+ mock_client.get_paginator.return_value = mock_paginator
202
+ mock_paginator.paginate.return_value = [
203
+ {
204
+ "ClusterInfoList": [
205
+ {
206
+ "ClusterName": "test-msk",
207
+ "ClusterArn": "arn:aws:kafka:us-east-1:123456789012:cluster/test-msk",
208
+ "State": "ACTIVE",
209
+ "Tags": {"Environment": "Production"},
210
+ }
211
+ ]
212
+ }
213
+ ]
214
+
215
+ clusters = collector.get_msk_clusters()
216
+ assert len(clusters) == 1
217
+ assert clusters[0]["ClusterName"] == "test-msk"
218
+
219
+ # Test error handling
220
+ def test_get_emr_clusters_error_handling(self, collector):
221
+ """Test EMR cluster collection error handling."""
222
+ mock_client = MagicMock()
223
+ collector._get_client = MagicMock(return_value=mock_client)
224
+
225
+ mock_paginator = MagicMock()
226
+ mock_client.get_paginator.return_value = mock_paginator
227
+ mock_paginator.paginate.side_effect = ClientError(
228
+ {"Error": {"Code": "AccessDenied", "Message": "Access denied"}}, "ListClusters"
229
+ )
230
+
231
+ clusters = collector.get_emr_clusters()
232
+ assert clusters == []
233
+
234
+ # Test collect method
235
+ def test_collect_all_services(self, collector):
236
+ """Test collecting all analytics services."""
237
+ with patch.object(collector, "get_emr_clusters", return_value=[{"Id": "j-123"}]):
238
+ with patch.object(collector, "get_kinesis_streams", return_value=[{"Name": "stream"}]):
239
+ with patch.object(collector, "get_kinesis_firehose_streams", return_value=[]):
240
+ with patch.object(collector, "get_glue_databases", return_value=[]):
241
+ with patch.object(collector, "get_athena_workgroups", return_value=[]):
242
+ with patch.object(collector, "get_msk_clusters", return_value=[]):
243
+ result = collector.collect()
244
+
245
+ assert "EMRClusters" in result
246
+ assert "KinesisStreams" in result
247
+ assert len(result["EMRClusters"]) == 1
248
+ assert len(result["KinesisStreams"]) == 1
249
+
250
+ def test_collect_with_disabled_services(self, mock_session):
251
+ """Test collecting with some services disabled."""
252
+ collector = AnalyticsCollector(
253
+ session=mock_session, region="us-east-1", enabled_services={"emr": False, "kinesis_streams": True}
254
+ )
255
+
256
+ with patch.object(collector, "get_kinesis_streams", return_value=[{"Name": "stream"}]):
257
+ result = collector.collect()
258
+
259
+ assert "EMRClusters" not in result
260
+ assert "KinesisStreams" in result