@ngocsangairvds/vsaf 3.1.27 → 3.2.1

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 (303) hide show
  1. package/package.json +2 -2
  2. package/src/global.js +70 -10
  3. package/tools/skills/vds-scripts-skill/.openskills.json +6 -0
  4. package/tools/skills/vds-scripts-skill/QUALITY.md +44 -0
  5. package/tools/skills/vds-scripts-skill/SKILL.md +135 -0
  6. package/tools/skills/vds-scripts-skill/references/audit-commands.md +171 -0
  7. package/tools/skills/vds-scripts-skill/references/capability-index.md +34 -0
  8. package/tools/skills/vds-scripts-skill/references/development-commands.md +12 -0
  9. package/tools/skills/vds-scripts-skill/references/google-sheets.md +73 -0
  10. package/tools/skills/vds-scripts-skill/references/integration-commands.md +17 -0
  11. package/tools/skills/vds-scripts-skill/references/platform-bootstrap.md +31 -0
  12. package/tools/skills/vds-scripts-skill/references/specialist-routing.md +14 -0
  13. package/tools/skills/vds-scripts-skill/references/validation-commands.md +15 -0
  14. package/tools/skills/vsaf-build/SKILL.md +32 -2
  15. package/tools/skills/vsaf-ship/SKILL.md +41 -10
  16. package/tools/skills/vsaf-test/SKILL.md +8 -0
  17. package/tools/vds-scripts/.mcp.json +11 -0
  18. package/tools/vds-scripts/.secrets.baseline +133 -0
  19. package/tools/vds-scripts/AGENTS.md +152 -0
  20. package/tools/vds-scripts/CLAUDE.md +101 -0
  21. package/tools/vds-scripts/CLI_COMMAND_OPTIMIZATION.md +156 -0
  22. package/tools/vds-scripts/PACKAGE_P125B_IMPLEMENTATION_SUMMARY.md +131 -0
  23. package/tools/vds-scripts/PROJECT_COMPLETION_SUMMARY.md +45 -0
  24. package/tools/vds-scripts/README.md +97 -0
  25. package/tools/vds-scripts/bitbucket_manifest_mapping.toml +34 -0
  26. package/tools/vds-scripts/bitbucket_orchestrator/ARCHITECTURE_ANALYSIS.md +258 -0
  27. package/tools/vds-scripts/bitbucket_orchestrator/BITBUCKET_API_PRACTICES.md +393 -0
  28. package/tools/vds-scripts/bitbucket_orchestrator/EVALUATION_REPORT.md +61 -0
  29. package/tools/vds-scripts/bitbucket_orchestrator/FEATURES.md +908 -0
  30. package/tools/vds-scripts/bitbucket_orchestrator/README.md +687 -0
  31. package/tools/vds-scripts/bitbucket_orchestrator/pyproject.toml +40 -0
  32. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/__init__.py +20 -0
  33. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/async_client.py +657 -0
  34. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/cli.py +2108 -0
  35. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/client.py +2534 -0
  36. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/config.py +171 -0
  37. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/errors.py +67 -0
  38. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/factory.py +185 -0
  39. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/protocols.py +244 -0
  40. package/tools/vds-scripts/bitbucket_orchestrator/tests/__init__.py +8 -0
  41. package/tools/vds-scripts/bitbucket_orchestrator/tests/conftest.py +65 -0
  42. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_advanced_search.py +151 -0
  43. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_async_client.py +546 -0
  44. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_branch_permissions.py +145 -0
  45. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_cli.py +115 -0
  46. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client.py +157 -0
  47. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_branch_conditions.py +79 -0
  48. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_code_advanced.py +163 -0
  49. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_code_file.py +32 -0
  50. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_deployment_environments.py +194 -0
  51. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_issues.py +164 -0
  52. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_pipelines_advanced.py +179 -0
  53. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_pr_blockers.py +119 -0
  54. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_repository_variables.py +156 -0
  55. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code.py +98 -0
  56. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code_advanced.py +282 -0
  57. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code_insights.py +335 -0
  58. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_conditions.py +147 -0
  59. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_config.py +131 -0
  60. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_deployment_env.py +352 -0
  61. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_factory.py +371 -0
  62. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_fork_operations.py +204 -0
  63. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_issue_cli.py +261 -0
  64. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_pipeline_advanced.py +270 -0
  65. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_pr_blocker.py +204 -0
  66. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_protocols.py +334 -0
  67. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_repo_settings.py +343 -0
  68. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_repo_variables.py +270 -0
  69. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_webhooks.py +189 -0
  70. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_workspace.py +233 -0
  71. package/tools/vds-scripts/bitbucket_orchestrator/uv.lock +742 -0
  72. package/tools/vds-scripts/confluence_orchestrator/Dockerfile +19 -0
  73. package/tools/vds-scripts/confluence_orchestrator/README.md +412 -0
  74. package/tools/vds-scripts/confluence_orchestrator/SYNC_SCRIPTS.md +127 -0
  75. package/tools/vds-scripts/confluence_orchestrator/SYNC_STANDARDIZATION.md +108 -0
  76. package/tools/vds-scripts/confluence_orchestrator/pyproject.toml +48 -0
  77. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/__init__.py +20 -0
  78. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/cli.py +2532 -0
  79. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/config.py +175 -0
  80. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/content.py +290 -0
  81. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/content_v2.py +94 -0
  82. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/crawl_tree.py +1835 -0
  83. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/errors.py +80 -0
  84. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/eventing.py +109 -0
  85. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/http.py +1114 -0
  86. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/orchestration.py +165 -0
  87. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/reporting.py +78 -0
  88. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/tree.py +121 -0
  89. package/tools/vds-scripts/confluence_orchestrator/sync_pdfs_from_markdown.py +213 -0
  90. package/tools/vds-scripts/confluence_orchestrator/sync_pdfs_to_confluence.py +305 -0
  91. package/tools/vds-scripts/confluence_orchestrator/sync_png_attachments.py +305 -0
  92. package/tools/vds-scripts/confluence_orchestrator/tests/__init__.py +0 -0
  93. package/tools/vds-scripts/confluence_orchestrator/tests/conftest.py +8 -0
  94. package/tools/vds-scripts/confluence_orchestrator/tests/test_advanced_content.py +224 -0
  95. package/tools/vds-scripts/confluence_orchestrator/tests/test_advanced_search.py +188 -0
  96. package/tools/vds-scripts/confluence_orchestrator/tests/test_cache_management.py +247 -0
  97. package/tools/vds-scripts/confluence_orchestrator/tests/test_cli.py +499 -0
  98. package/tools/vds-scripts/confluence_orchestrator/tests/test_config.py +83 -0
  99. package/tools/vds-scripts/confluence_orchestrator/tests/test_content.py +186 -0
  100. package/tools/vds-scripts/confluence_orchestrator/tests/test_content_flags.py +27 -0
  101. package/tools/vds-scripts/confluence_orchestrator/tests/test_crawl_tree.py +2250 -0
  102. package/tools/vds-scripts/confluence_orchestrator/tests/test_draft_management.py +223 -0
  103. package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing.py +71 -0
  104. package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_chaos.py +37 -0
  105. package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_rate_limit.py +44 -0
  106. package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_timeout.py +49 -0
  107. package/tools/vds-scripts/confluence_orchestrator/tests/test_export.py +230 -0
  108. package/tools/vds-scripts/confluence_orchestrator/tests/test_history.py +204 -0
  109. package/tools/vds-scripts/confluence_orchestrator/tests/test_http.py +117 -0
  110. package/tools/vds-scripts/confluence_orchestrator/tests/test_orchestration.py +91 -0
  111. package/tools/vds-scripts/confluence_orchestrator/tests/test_reporting.py +24 -0
  112. package/tools/vds-scripts/confluence_orchestrator/tests/test_search_cql.py +34 -0
  113. package/tools/vds-scripts/confluence_orchestrator/tests/test_space_management.py +237 -0
  114. package/tools/vds-scripts/confluence_orchestrator/tests/test_space_permissions.py +332 -0
  115. package/tools/vds-scripts/confluence_orchestrator/tests/test_user_group_management.py +388 -0
  116. package/tools/vds-scripts/confluence_orchestrator/uv.lock +1023 -0
  117. package/tools/vds-scripts/git_orchestrator/ENHANCEMENT_SUMMARY.md +119 -0
  118. package/tools/vds-scripts/git_orchestrator/README.md +280 -0
  119. package/tools/vds-scripts/git_orchestrator/VERIFICATION_REPORT.md +152 -0
  120. package/tools/vds-scripts/git_orchestrator/pyproject.toml +35 -0
  121. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/__init__.py +7 -0
  122. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/__main__.py +4 -0
  123. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/cli.py +847 -0
  124. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/logging_config.py +63 -0
  125. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/manifest.py +129 -0
  126. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/orchestrator.py +819 -0
  127. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/reporting.py +53 -0
  128. package/tools/vds-scripts/git_orchestrator/tests/__init__.py +0 -0
  129. package/tools/vds-scripts/git_orchestrator/tests/test_cli_settings.py +21 -0
  130. package/tools/vds-scripts/git_orchestrator/tests/test_integration.py +74 -0
  131. package/tools/vds-scripts/git_orchestrator/tests/test_manifest.py +79 -0
  132. package/tools/vds-scripts/git_orchestrator/tests/test_orchestrator.py +204 -0
  133. package/tools/vds-scripts/git_orchestrator/tests/test_public_api.py +236 -0
  134. package/tools/vds-scripts/git_orchestrator/tests/test_resilience.py +345 -0
  135. package/tools/vds-scripts/git_orchestrator/uv.lock +271 -0
  136. package/tools/vds-scripts/jira_orchestrator/README.md +770 -0
  137. package/tools/vds-scripts/jira_orchestrator/pyproject.toml +39 -0
  138. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/__init__.py +1 -0
  139. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/adapter.py +1320 -0
  140. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/cli.py +2271 -0
  141. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/config.py +138 -0
  142. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/errors.py +67 -0
  143. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/reporting.py +65 -0
  144. package/tools/vds-scripts/jira_orchestrator/tests/__init__.py +1 -0
  145. package/tools/vds-scripts/jira_orchestrator/tests/conftest.py +86 -0
  146. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_agile_list_payloads.py +54 -0
  147. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_bulk_operations.py +69 -0
  148. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_components.py +57 -0
  149. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_createmeta.py +45 -0
  150. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_dashboard.py +117 -0
  151. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_issue_properties.py +54 -0
  152. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_permissions_compat.py +42 -0
  153. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_reindex.py +42 -0
  154. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_remote_links.py +76 -0
  155. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_transitions.py +91 -0
  156. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_user_management.py +110 -0
  157. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_version_management.py +133 -0
  158. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_watchers.py +41 -0
  159. package/tools/vds-scripts/jira_orchestrator/tests/test_advanced_search.py +164 -0
  160. package/tools/vds-scripts/jira_orchestrator/tests/test_agile.py +256 -0
  161. package/tools/vds-scripts/jira_orchestrator/tests/test_application_properties.py +193 -0
  162. package/tools/vds-scripts/jira_orchestrator/tests/test_backlog.py +91 -0
  163. package/tools/vds-scripts/jira_orchestrator/tests/test_bulk_operations.py +277 -0
  164. package/tools/vds-scripts/jira_orchestrator/tests/test_cli.py +106 -0
  165. package/tools/vds-scripts/jira_orchestrator/tests/test_components.py +106 -0
  166. package/tools/vds-scripts/jira_orchestrator/tests/test_config.py +164 -0
  167. package/tools/vds-scripts/jira_orchestrator/tests/test_dashboard.py +122 -0
  168. package/tools/vds-scripts/jira_orchestrator/tests/test_discover_fields.py +207 -0
  169. package/tools/vds-scripts/jira_orchestrator/tests/test_filter_management.py +333 -0
  170. package/tools/vds-scripts/jira_orchestrator/tests/test_issue_archiving.py +164 -0
  171. package/tools/vds-scripts/jira_orchestrator/tests/test_issue_links.py +257 -0
  172. package/tools/vds-scripts/jira_orchestrator/tests/test_issue_properties.py +171 -0
  173. package/tools/vds-scripts/jira_orchestrator/tests/test_link_types.py +314 -0
  174. package/tools/vds-scripts/jira_orchestrator/tests/test_parse_set.py +37 -0
  175. package/tools/vds-scripts/jira_orchestrator/tests/test_permissions.py +273 -0
  176. package/tools/vds-scripts/jira_orchestrator/tests/test_reindex.py +81 -0
  177. package/tools/vds-scripts/jira_orchestrator/tests/test_remote_links.py +254 -0
  178. package/tools/vds-scripts/jira_orchestrator/tests/test_security_schemes.py +170 -0
  179. package/tools/vds-scripts/jira_orchestrator/tests/test_transitions_changelog.py +114 -0
  180. package/tools/vds-scripts/jira_orchestrator/tests/test_user_management.py +226 -0
  181. package/tools/vds-scripts/jira_orchestrator/tests/test_version_management.py +339 -0
  182. package/tools/vds-scripts/jira_orchestrator/tests/test_watchers.py +101 -0
  183. package/tools/vds-scripts/jira_orchestrator/tests/test_worklog.py +223 -0
  184. package/tools/vds-scripts/jira_orchestrator/uv.lock +738 -0
  185. package/tools/vds-scripts/mcp_server/Dockerfile +34 -0
  186. package/tools/vds-scripts/mcp_server/README.md +140 -0
  187. package/tools/vds-scripts/mcp_server/pyproject.toml +42 -0
  188. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/__init__.py +4 -0
  189. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/config.py +36 -0
  190. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/server.py +66 -0
  191. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/__init__.py +14 -0
  192. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/bitbucket_tools.py +47 -0
  193. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/confluence_tools.py +59 -0
  194. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/git_tools.py +71 -0
  195. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/jira_tools.py +63 -0
  196. package/tools/vds-scripts/mcp_server/tests/__init__.py +2 -0
  197. package/tools/vds-scripts/mcp_server/tests/conftest.py +29 -0
  198. package/tools/vds-scripts/mcp_server/tests/unit/__init__.py +2 -0
  199. package/tools/vds-scripts/mcp_server/tests/unit/test_bitbucket_tools.py +25 -0
  200. package/tools/vds-scripts/mcp_server/tests/unit/test_confluence_tools.py +25 -0
  201. package/tools/vds-scripts/mcp_server/tests/unit/test_git_tools.py +32 -0
  202. package/tools/vds-scripts/mcp_server/tests/unit/test_jira_tools.py +32 -0
  203. package/tools/vds-scripts/mcp_server/tests/verification/__init__.py +2 -0
  204. package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_confluence_tools.py +40 -0
  205. package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_jira_tools.py +37 -0
  206. package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_tool_registration.py +47 -0
  207. package/tools/vds-scripts/mcp_server/uv.lock +1032 -0
  208. package/tools/vds-scripts/mypy.ini +5 -0
  209. package/tools/vds-scripts/pyproject.toml +29 -0
  210. package/tools/vds-scripts/repo-manifest.yaml +273 -0
  211. package/tools/vds-scripts/repo-manifest.yaml.example +25 -0
  212. package/tools/vds-scripts/scripts/BRD-Validation-API.postman_collection.json +706 -0
  213. package/tools/vds-scripts/scripts/BRD-Validation-README.md +308 -0
  214. package/tools/vds-scripts/scripts/README.md +162 -0
  215. package/tools/vds-scripts/scripts/bootstrap_uv.sh +30 -0
  216. package/tools/vds-scripts/scripts/brd-validation-environment.json +51 -0
  217. package/tools/vds-scripts/scripts/brd-validation-test-results.json +13023 -0
  218. package/tools/vds-scripts/scripts/brd_coverage_report.json +276 -0
  219. package/tools/vds-scripts/scripts/create_memory_session.py +35 -0
  220. package/tools/vds-scripts/scripts/deployment/load_docker_images_offline.sh +90 -0
  221. package/tools/vds-scripts/scripts/final_completion_report.md +139 -0
  222. package/tools/vds-scripts/scripts/folder_structure_report.json +321 -0
  223. package/tools/vds-scripts/scripts/generate_completion_report.py +125 -0
  224. package/tools/vds-scripts/scripts/generate_intellij_modules.py +150 -0
  225. package/tools/vds-scripts/scripts/link_integrity_report.json +807 -0
  226. package/tools/vds-scripts/scripts/move_audit_artifact_pages.py +255 -0
  227. package/tools/vds-scripts/scripts/move_audit_artifact_pages_rest.py +165 -0
  228. package/tools/vds-scripts/scripts/move_wrong_dept_pages.py +216 -0
  229. package/tools/vds-scripts/scripts/save_intellij_memories.py +120 -0
  230. package/tools/vds-scripts/scripts/save_memories_to_vds_ai.py +83 -0
  231. package/tools/vds-scripts/scripts/save_memories_vds_style.py +129 -0
  232. package/tools/vds-scripts/scripts/search_intellij_memories.py +50 -0
  233. package/tools/vds-scripts/scripts/setup_intellij_workspace.py +65 -0
  234. package/tools/vds-scripts/scripts/target-state-automation/README.md +89 -0
  235. package/tools/vds-scripts/scripts/target-state-automation/confluence_sync_coordinator.sh +27 -0
  236. package/tools/vds-scripts/scripts/target-state-automation/coordination.sh +114 -0
  237. package/tools/vds-scripts/scripts/target-state-automation/diagram_coordinator.sh +25 -0
  238. package/tools/vds-scripts/scripts/target-state-automation/docs_root.sh +22 -0
  239. package/tools/vds-scripts/scripts/target-state-automation/generate_diagrams.sh +22 -0
  240. package/tools/vds-scripts/scripts/target-state-automation/markdown_coordinator.sh +25 -0
  241. package/tools/vds-scripts/scripts/target-state-automation/progress_dashboard.sh +17 -0
  242. package/tools/vds-scripts/scripts/target-state-automation/schema_coordinator.sh +25 -0
  243. package/tools/vds-scripts/scripts/target-state-automation/sync_confluence.sh +30 -0
  244. package/tools/vds-scripts/scripts/target-state-automation/update_dependencies.sh +19 -0
  245. package/tools/vds-scripts/scripts/target-state-automation/validate_links.sh +86 -0
  246. package/tools/vds-scripts/scripts/target-state-automation/validate_markdown.sh +52 -0
  247. package/tools/vds-scripts/scripts/target-state-automation/validate_schemas.sh +26 -0
  248. package/tools/vds-scripts/scripts/target-state-automation/validate_structure.sh +98 -0
  249. package/tools/vds-scripts/scripts/update_modules_xml.py +190 -0
  250. package/tools/vds-scripts/scripts/uv-workspace-alignment-verification-2026-03-25.md +128 -0
  251. package/tools/vds-scripts/scripts/validate_brd_coverage.py +179 -0
  252. package/tools/vds-scripts/scripts/validate_folder_structure.py +240 -0
  253. package/tools/vds-scripts/scripts/validate_link_integrity.py +272 -0
  254. package/tools/vds-scripts/scripts/vds_sh_helpers.sh +180 -0
  255. package/tools/vds-scripts/scripts/verification/phase2_portable_paths_ubuntu_docker.sh +26 -0
  256. package/tools/vds-scripts/scripts/worktree_uv.sh +48 -0
  257. package/tools/vds-scripts/uv.lock +8 -0
  258. package/tools/vds-scripts/vds_cli/README.md +126 -0
  259. package/tools/vds-scripts/vds_cli/VERIFICATION_REPORT.md +41 -0
  260. package/tools/vds-scripts/vds_cli/pyproject.toml +38 -0
  261. package/tools/vds-scripts/vds_cli/src/vds_cli/__init__.py +3 -0
  262. package/tools/vds-scripts/vds_cli/src/vds_cli/cli.py +173 -0
  263. package/tools/vds-scripts/vds_cli/src/vds_cli/docs_sync.py +1203 -0
  264. package/tools/vds-scripts/vds_cli/src/vds_cli/env.py +41 -0
  265. package/tools/vds-scripts/vds_cli/src/vds_cli/google_sheets_orchestrator/__init__.py +3 -0
  266. package/tools/vds-scripts/vds_cli/src/vds_cli/google_sheets_orchestrator/google_sheets_orchestrator.py +198 -0
  267. package/tools/vds-scripts/vds_cli/src/vds_cli/router.py +93 -0
  268. package/tools/vds-scripts/vds_cli/src/vds_cli/sync_api.py +647 -0
  269. package/tools/vds-scripts/vds_cli/src/vds_cli/sync_service.py +266 -0
  270. package/tools/vds-scripts/vds_cli/tests/__init__.py +2 -0
  271. package/tools/vds-scripts/vds_cli/tests/conftest.py +49 -0
  272. package/tools/vds-scripts/vds_cli/tests/unit/__init__.py +2 -0
  273. package/tools/vds-scripts/vds_cli/tests/unit/test_cli.py +143 -0
  274. package/tools/vds-scripts/vds_cli/tests/unit/test_docs_sync.py +422 -0
  275. package/tools/vds-scripts/vds_cli/tests/unit/test_env.py +51 -0
  276. package/tools/vds-scripts/vds_cli/tests/unit/test_router.py +72 -0
  277. package/tools/vds-scripts/vds_cli/tests/unit/test_sync_api.py +357 -0
  278. package/tools/vds-scripts/vds_cli/tests/unit/test_sync_service.py +160 -0
  279. package/tools/vds-scripts/vds_cli/tests/verification/__init__.py +2 -0
  280. package/tools/vds-scripts/vds_cli/tests/verification/test_bitbucket_real.py +33 -0
  281. package/tools/vds-scripts/vds_cli/tests/verification/test_confluence_real.py +35 -0
  282. package/tools/vds-scripts/vds_cli/tests/verification/test_jira_real.py +41 -0
  283. package/tools/vds-scripts/vds_cli/uv.lock +524 -0
  284. package/tools/vds-scripts/vds_cli_common/README.md +190 -0
  285. package/tools/vds-scripts/vds_cli_common/pyproject.toml +92 -0
  286. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/__init__.py +34 -0
  287. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/completers.py +139 -0
  288. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/context.py +201 -0
  289. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/env.py +119 -0
  290. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/errors.py +318 -0
  291. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/output.py +284 -0
  292. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/paths.py +78 -0
  293. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/testing.py +213 -0
  294. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/version.py +85 -0
  295. package/tools/vds-scripts/vds_cli_common/tests/__init__.py +1 -0
  296. package/tools/vds-scripts/vds_cli_common/tests/test_completers.py +148 -0
  297. package/tools/vds-scripts/vds_cli_common/tests/test_context.py +192 -0
  298. package/tools/vds-scripts/vds_cli_common/tests/test_env.py +102 -0
  299. package/tools/vds-scripts/vds_cli_common/tests/test_errors.py +186 -0
  300. package/tools/vds-scripts/vds_cli_common/tests/test_output.py +229 -0
  301. package/tools/vds-scripts/vds_cli_common/tests/test_paths.py +61 -0
  302. package/tools/vds-scripts/vds_cli_common/tests/test_testing.py +138 -0
  303. package/tools/vds-scripts/vds_cli_common/tests/test_version.py +64 -0
@@ -0,0 +1,148 @@
1
+ """Tests for common completers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from vds_cli_common.completers import (
6
+ complete_index,
7
+ complete_log_level,
8
+ complete_output_format,
9
+ complete_project,
10
+ complete_server,
11
+ complete_time_field,
12
+ )
13
+
14
+
15
+ class TestCompleteIndex:
16
+ """Tests for complete_index function."""
17
+
18
+ def test_returns_matching_patterns(self) -> None:
19
+ """Test returns patterns that match incomplete input."""
20
+ results = complete_index("logs")
21
+ assert "logs-*" in results
22
+
23
+ def test_returns_all_patterns_for_empty_input(self) -> None:
24
+ """Test returns all patterns for empty input."""
25
+ results = complete_index("")
26
+ assert len(results) > 0
27
+ assert "logs-*" in results
28
+ assert "*" in results
29
+
30
+ def test_returns_empty_for_no_match(self) -> None:
31
+ """Test returns empty list for no match."""
32
+ results = complete_index("xyz")
33
+ assert results == []
34
+
35
+ def test_includes_otel_patterns(self) -> None:
36
+ """Test includes OpenTelemetry patterns."""
37
+ results = complete_index("otel")
38
+ assert "otel-logs-*" in results
39
+
40
+
41
+ class TestCompleteTimeField:
42
+ """Tests for complete_time_field function."""
43
+
44
+ def test_returns_matching_fields(self) -> None:
45
+ """Test returns fields that match incomplete input."""
46
+ results = complete_time_field("@")
47
+ assert "@timestamp" in results
48
+
49
+ def test_returns_timestamp_fields(self) -> None:
50
+ """Test returns timestamp-related fields."""
51
+ results = complete_time_field("time")
52
+ assert "timestamp" in results
53
+
54
+ def test_returns_empty_for_no_match(self) -> None:
55
+ """Test returns empty list for no match."""
56
+ results = complete_time_field("xyz")
57
+ assert results == []
58
+
59
+
60
+ class TestCompleteProject:
61
+ """Tests for complete_project function."""
62
+
63
+ def test_returns_matching_projects(self) -> None:
64
+ """Test returns projects that match incomplete input."""
65
+ results = complete_project("ins")
66
+ assert "insurance" in results
67
+
68
+ def test_case_insensitive(self) -> None:
69
+ """Test matching is case-insensitive."""
70
+ results = complete_project("LEP")
71
+ assert "lep" in results
72
+
73
+ def test_returns_all_projects_for_empty(self) -> None:
74
+ """Test returns all projects for empty input."""
75
+ results = complete_project("")
76
+ assert "lep" in results
77
+ assert "insurance" in results
78
+ assert "saving" in results
79
+ assert "ekyc" in results
80
+
81
+ def test_returns_empty_for_no_match(self) -> None:
82
+ """Test returns empty list for no match."""
83
+ results = complete_project("xyz")
84
+ assert results == []
85
+
86
+
87
+ class TestCompleteServer:
88
+ """Tests for complete_server function."""
89
+
90
+ def test_returns_matching_servers(self) -> None:
91
+ """Test returns servers that match incomplete input."""
92
+ results = complete_server("int")
93
+ assert "internal" in results
94
+
95
+ def test_case_insensitive(self) -> None:
96
+ """Test matching is case-insensitive."""
97
+ results = complete_server("EXT")
98
+ assert "external" in results
99
+
100
+ def test_returns_all_servers_for_empty(self) -> None:
101
+ """Test returns all servers for empty input."""
102
+ results = complete_server("")
103
+ assert "internal" in results
104
+ assert "external" in results
105
+
106
+
107
+ class TestCompleteOutputFormat:
108
+ """Tests for complete_output_format function."""
109
+
110
+ def test_returns_matching_formats(self) -> None:
111
+ """Test returns formats that match incomplete input."""
112
+ results = complete_output_format("js")
113
+ assert "json" in results
114
+
115
+ def test_case_insensitive(self) -> None:
116
+ """Test matching is case-insensitive."""
117
+ results = complete_output_format("JSON")
118
+ assert "json" in results
119
+
120
+ def test_returns_all_formats_for_empty(self) -> None:
121
+ """Test returns all formats for empty input."""
122
+ results = complete_output_format("")
123
+ assert "json" in results
124
+ assert "table" in results
125
+ assert "csv" in results
126
+
127
+
128
+ class TestCompleteLogLevel:
129
+ """Tests for complete_log_level function."""
130
+
131
+ def test_returns_matching_levels(self) -> None:
132
+ """Test returns levels that match incomplete input."""
133
+ results = complete_log_level("ERR")
134
+ assert "ERROR" in results
135
+
136
+ def test_case_insensitive(self) -> None:
137
+ """Test matching is case-insensitive."""
138
+ results = complete_log_level("debug")
139
+ assert "DEBUG" in results
140
+
141
+ def test_returns_all_levels_for_empty(self) -> None:
142
+ """Test returns all levels for empty input."""
143
+ results = complete_log_level("")
144
+ assert "DEBUG" in results
145
+ assert "INFO" in results
146
+ assert "WARNING" in results
147
+ assert "ERROR" in results
148
+ assert "CRITICAL" in results
@@ -0,0 +1,192 @@
1
+ """Tests for CLIContext."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from unittest.mock import patch
7
+
8
+ from vds_cli_common import CLIContext
9
+
10
+
11
+ class TestCLIContext:
12
+ """Tests for CLIContext class."""
13
+
14
+ def test_default_values(self) -> None:
15
+ """Test default values are set correctly."""
16
+ ctx = CLIContext()
17
+ assert ctx.json_only is False
18
+ assert ctx.quiet is False
19
+ assert ctx.structured_logs is False
20
+ assert ctx.no_input is False
21
+ assert ctx.no_color is False
22
+ assert ctx.force_color is False
23
+
24
+ def test_from_options_factory(self) -> None:
25
+ """Test from_options factory method."""
26
+ ctx = CLIContext.from_options(
27
+ json_only=True,
28
+ quiet=True,
29
+ no_color=True,
30
+ )
31
+ assert ctx.json_only is True
32
+ assert ctx.quiet is True
33
+ assert ctx.no_color is True
34
+
35
+ def test_is_tty_detected_in_tty(self) -> None:
36
+ """Test TTY is detected when stdout is a terminal."""
37
+ with patch("sys.stdout.isatty", return_value=True):
38
+ ctx = CLIContext()
39
+ assert ctx.is_tty is True
40
+
41
+ def test_is_tty_detected_in_pipe(self) -> None:
42
+ """Test TTY is not detected when stdout is piped."""
43
+ with patch("sys.stdout.isatty", return_value=False):
44
+ ctx = CLIContext()
45
+ assert ctx.is_tty is False
46
+
47
+ def test_use_color_disabled_by_json_only(self) -> None:
48
+ """Test json_only mode disables colors."""
49
+ with patch("sys.stdout.isatty", return_value=True):
50
+ ctx = CLIContext.from_options(json_only=True)
51
+ assert ctx.use_color is False
52
+
53
+ def test_use_color_disabled_by_no_color_flag(self) -> None:
54
+ """Test --no-color flag disables colors."""
55
+ with patch("sys.stdout.isatty", return_value=True):
56
+ ctx = CLIContext.from_options(no_color=True)
57
+ assert ctx.use_color is False
58
+
59
+ def test_use_color_enabled_by_force_color_flag(self) -> None:
60
+ """Test --color flag enables colors."""
61
+ with patch("sys.stdout.isatty", return_value=False):
62
+ ctx = CLIContext.from_options(force_color=True)
63
+ assert ctx.use_color is True
64
+
65
+ def test_no_color_flag_overrides_force_color(self) -> None:
66
+ """Test --no-color flag takes precedence over --color."""
67
+ ctx = CLIContext.from_options(no_color=True, force_color=True)
68
+ assert ctx.use_color is False
69
+
70
+ def test_no_color_env_var_respected(self) -> None:
71
+ """Test NO_COLOR environment variable disables colors."""
72
+ with patch("sys.stdout.isatty", return_value=True), patch.dict(os.environ, {"NO_COLOR": "1"}):
73
+ ctx = CLIContext()
74
+ assert ctx.use_color is False
75
+
76
+ def test_force_color_env_var_respected(self) -> None:
77
+ """Test FORCE_COLOR environment variable enables colors."""
78
+ with patch("sys.stdout.isatty", return_value=False):
79
+ with patch.dict(os.environ, {"FORCE_COLOR": "1"}, clear=False):
80
+ # Clear NO_COLOR if present
81
+ env = os.environ.copy()
82
+ env.pop("NO_COLOR", None)
83
+ env["FORCE_COLOR"] = "1"
84
+ with patch.dict(os.environ, env, clear=True):
85
+ ctx = CLIContext()
86
+ assert ctx.use_color is True
87
+
88
+ def test_use_rich_output_in_tty(self) -> None:
89
+ """Test Rich output is enabled in TTY."""
90
+ with patch("sys.stdout.isatty", return_value=True):
91
+ ctx = CLIContext()
92
+ assert ctx.use_rich_output is True
93
+
94
+ def test_use_rich_output_disabled_in_pipe(self) -> None:
95
+ """Test Rich output is disabled when piped."""
96
+ with patch("sys.stdout.isatty", return_value=False):
97
+ ctx = CLIContext()
98
+ assert ctx.use_rich_output is False
99
+
100
+ def test_use_rich_output_disabled_by_json_only(self) -> None:
101
+ """Test Rich output is disabled in json_only mode."""
102
+ with patch("sys.stdout.isatty", return_value=True):
103
+ ctx = CLIContext.from_options(json_only=True)
104
+ assert ctx.use_rich_output is False
105
+
106
+ def test_use_spinner_in_tty(self) -> None:
107
+ """Test spinner is enabled in TTY."""
108
+ with patch("sys.stdout.isatty", return_value=True):
109
+ ctx = CLIContext()
110
+ assert ctx.use_spinner is True
111
+
112
+ def test_use_spinner_disabled_by_quiet(self) -> None:
113
+ """Test spinner is disabled in quiet mode."""
114
+ with patch("sys.stdout.isatty", return_value=True):
115
+ ctx = CLIContext.from_options(quiet=True)
116
+ assert ctx.use_spinner is False
117
+
118
+ def test_use_progress_in_tty(self) -> None:
119
+ """Test progress is enabled in TTY."""
120
+ with patch("sys.stdout.isatty", return_value=True):
121
+ ctx = CLIContext()
122
+ assert ctx.use_progress is True
123
+
124
+ def test_use_progress_disabled_by_quiet(self) -> None:
125
+ """Test progress is disabled in quiet mode."""
126
+ with patch("sys.stdout.isatty", return_value=True):
127
+ ctx = CLIContext.from_options(quiet=True)
128
+ assert ctx.use_progress is False
129
+
130
+ def test_show_hints_enabled_by_default(self) -> None:
131
+ """Test hints are shown by default."""
132
+ ctx = CLIContext()
133
+ assert ctx.show_hints is True
134
+
135
+ def test_show_hints_disabled_by_json_only(self) -> None:
136
+ """Test hints are hidden in json_only mode."""
137
+ ctx = CLIContext.from_options(json_only=True)
138
+ assert ctx.show_hints is False
139
+
140
+ def test_show_hints_disabled_by_quiet(self) -> None:
141
+ """Test hints are hidden in quiet mode."""
142
+ ctx = CLIContext.from_options(quiet=True)
143
+ assert ctx.show_hints is False
144
+
145
+ def test_console_property_returns_console(self) -> None:
146
+ """Test console property returns Rich Console."""
147
+ ctx = CLIContext()
148
+ console = ctx.console
149
+ assert console is not None
150
+ # Should return same instance on second call
151
+ assert ctx.console is console
152
+
153
+ def test_err_console_property_returns_stderr_console(self) -> None:
154
+ """Test err_console property returns Rich Console for stderr."""
155
+ ctx = CLIContext()
156
+ err_console = ctx.err_console
157
+ assert err_console is not None
158
+ # Should return same instance on second call
159
+ assert ctx.err_console is err_console
160
+
161
+ def test_from_typer_context_with_cli_context(self) -> None:
162
+ """Test from_typer_context extracts CLIContext."""
163
+ from unittest.mock import MagicMock
164
+
165
+ original_ctx = CLIContext.from_options(json_only=True)
166
+ typer_ctx = MagicMock()
167
+ typer_ctx.obj = original_ctx
168
+
169
+ result = CLIContext.from_typer_context(typer_ctx)
170
+ assert result is original_ctx
171
+
172
+ def test_from_typer_context_with_dict(self) -> None:
173
+ """Test from_typer_context extracts CLIContext from dict."""
174
+ from unittest.mock import MagicMock
175
+
176
+ original_ctx = CLIContext.from_options(quiet=True)
177
+ typer_ctx = MagicMock()
178
+ typer_ctx.obj = {"cli_ctx": original_ctx}
179
+
180
+ result = CLIContext.from_typer_context(typer_ctx)
181
+ assert result is original_ctx
182
+
183
+ def test_from_typer_context_returns_default(self) -> None:
184
+ """Test from_typer_context returns default when not found."""
185
+ from unittest.mock import MagicMock
186
+
187
+ typer_ctx = MagicMock()
188
+ typer_ctx.obj = None
189
+
190
+ result = CLIContext.from_typer_context(typer_ctx)
191
+ assert isinstance(result, CLIContext)
192
+ assert result.json_only is False
@@ -0,0 +1,102 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from pathlib import Path
5
+
6
+ from vds_cli_common.env import DEFAULT_AUDIT_ENV_DEFAULTS, ensure_shared_env, load_shared_env
7
+ from vds_cli_common.paths import get_shared_env_path
8
+
9
+
10
+ def _write_env(path: Path, content: str) -> None:
11
+ path.write_text(content, encoding="utf-8")
12
+
13
+
14
+ def test_load_shared_env_first_file_wins_when_keys_overlap(tmp_path: Path, monkeypatch) -> None:
15
+ primary = tmp_path / "primary.env"
16
+ fallback = tmp_path / "fallback.env"
17
+ _write_env(primary, "TOKEN=from-primary\nPRIMARY_ONLY=1\n")
18
+ _write_env(fallback, "TOKEN=from-fallback\nFALLBACK_ONLY=1\n")
19
+
20
+ monkeypatch.delenv("TOKEN", raising=False)
21
+ monkeypatch.delenv("PRIMARY_ONLY", raising=False)
22
+ monkeypatch.delenv("FALLBACK_ONLY", raising=False)
23
+
24
+ merged = load_shared_env(path=primary, extra_paths=(fallback,))
25
+
26
+ assert merged["TOKEN"] == "from-primary"
27
+ assert merged["PRIMARY_ONLY"] == "1"
28
+ assert merged["FALLBACK_ONLY"] == "1"
29
+ assert "TOKEN" in merged
30
+ assert "PRIMARY_ONLY" in merged
31
+ assert "FALLBACK_ONLY" in merged
32
+
33
+
34
+ def test_load_shared_env_does_not_override_existing_environ_by_default(tmp_path: Path, monkeypatch) -> None:
35
+ env_file = tmp_path / "shared.env"
36
+ _write_env(env_file, "TOKEN=from-file\n")
37
+
38
+ monkeypatch.setenv("TOKEN", "from-shell")
39
+ load_shared_env(path=env_file)
40
+
41
+ assert os.environ["TOKEN"] == "from-shell"
42
+
43
+
44
+ def test_load_shared_env_override_true_updates_existing_environ(tmp_path: Path, monkeypatch) -> None:
45
+ env_file = tmp_path / "shared.env"
46
+ _write_env(env_file, "TOKEN=from-file\n")
47
+
48
+ monkeypatch.setenv("TOKEN", "from-shell")
49
+ load_shared_env(path=env_file, override=True)
50
+
51
+ assert os.environ["TOKEN"] == "from-file"
52
+
53
+
54
+ def test_ensure_shared_env_creates_file_with_required_defaults(tmp_path: Path) -> None:
55
+ env_file = tmp_path / ".vds" / ".env"
56
+ created = ensure_shared_env(path=env_file)
57
+
58
+ assert created == env_file
59
+ content = env_file.read_text(encoding="utf-8")
60
+ for key, value in DEFAULT_AUDIT_ENV_DEFAULTS:
61
+ assert f"{key}={value}" in content
62
+
63
+
64
+ def test_ensure_shared_env_appends_missing_defaults_without_overwriting(tmp_path: Path) -> None:
65
+ env_file = tmp_path / ".vds" / ".env"
66
+ env_file.parent.mkdir(parents=True, exist_ok=True)
67
+ _write_env(env_file, "VDS_AUDIT_STATE_DSN=postgresql://custom/db\nTOKEN=kept\n")
68
+
69
+ ensure_shared_env(path=env_file)
70
+
71
+ values = {}
72
+ for line in env_file.read_text(encoding="utf-8").splitlines():
73
+ if "=" in line and not line.lstrip().startswith("#"):
74
+ key, value = line.split("=", 1)
75
+ values[key] = value
76
+ assert values["VDS_AUDIT_STATE_DSN"] == "postgresql://custom/db"
77
+ assert values["TOKEN"] == "kept"
78
+ for key, _ in DEFAULT_AUDIT_ENV_DEFAULTS:
79
+ assert key in values
80
+
81
+
82
+ def test_get_shared_env_path_uses_override(monkeypatch, tmp_path: Path) -> None:
83
+ override = tmp_path / "envs" / "shared.env"
84
+ monkeypatch.setenv("VDS_ENV_FILE", str(override))
85
+
86
+ assert get_shared_env_path() == override.resolve()
87
+
88
+
89
+ def test_load_shared_env_uses_runtime_env_file_override_without_stale_defaults(
90
+ monkeypatch, tmp_path: Path
91
+ ) -> None:
92
+ override = tmp_path / "portable" / ".env"
93
+ override.parent.mkdir(parents=True, exist_ok=True)
94
+ _write_env(override, "TOKEN=from-runtime-override\n")
95
+
96
+ monkeypatch.setenv("VDS_ENV_FILE", str(override))
97
+ monkeypatch.delenv("TOKEN", raising=False)
98
+
99
+ merged = load_shared_env()
100
+
101
+ assert merged["TOKEN"] == "from-runtime-override"
102
+ assert os.environ["TOKEN"] == "from-runtime-override"
@@ -0,0 +1,186 @@
1
+ """Tests for ErrorHandler and ExitCodes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from unittest.mock import MagicMock
6
+
7
+ import pytest
8
+ import typer
9
+
10
+ from vds_cli_common import CLIContext, ErrorHandler, ExitCodes, OutputManager
11
+
12
+
13
+ class TestExitCodes:
14
+ """Tests for ExitCodes enum."""
15
+
16
+ def test_success_is_zero(self) -> None:
17
+ """Test SUCCESS is 0."""
18
+ assert ExitCodes.SUCCESS == 0
19
+
20
+ def test_general_error_is_one(self) -> None:
21
+ """Test GENERAL_ERROR is 1."""
22
+ assert ExitCodes.GENERAL_ERROR == 1
23
+
24
+ def test_all_codes_are_integers(self) -> None:
25
+ """Test all exit codes are integers."""
26
+ for code in ExitCodes:
27
+ assert isinstance(code.value, int)
28
+
29
+ def test_codes_are_unique(self) -> None:
30
+ """Test all exit codes are unique."""
31
+ values = [code.value for code in ExitCodes]
32
+ assert len(values) == len(set(values))
33
+
34
+
35
+ class TestErrorHandler:
36
+ """Tests for ErrorHandler class."""
37
+
38
+ @pytest.fixture
39
+ def handler(self) -> ErrorHandler:
40
+ """Create an ErrorHandler for testing."""
41
+ ctx = CLIContext.from_options(json_only=True)
42
+ output = OutputManager(ctx)
43
+ return ErrorHandler(output, valid_commands=["search", "count", "ping", "export"])
44
+
45
+ def test_suggest_command_finds_match(self, handler: ErrorHandler) -> None:
46
+ """Test suggest_command finds close matches."""
47
+ assert handler.suggest_command("serch") == "search"
48
+ assert handler.suggest_command("cout") == "count"
49
+ assert handler.suggest_command("pimg") == "ping"
50
+
51
+ def test_suggest_command_no_match(self, handler: ErrorHandler) -> None:
52
+ """Test suggest_command returns None for no match."""
53
+ assert handler.suggest_command("xyz") is None
54
+ assert handler.suggest_command("foo") is None
55
+
56
+ def test_suggest_command_exact_match(self, handler: ErrorHandler) -> None:
57
+ """Test suggest_command works with exact match."""
58
+ assert handler.suggest_command("search") == "search"
59
+
60
+ def test_suggest_command_custom_cutoff(self, handler: ErrorHandler) -> None:
61
+ """Test suggest_command respects cutoff parameter."""
62
+ # With high cutoff, weak matches should fail
63
+ assert handler.suggest_command("s", cutoff=0.9) is None
64
+ # With low cutoff, weak matches might succeed
65
+ result = handler.suggest_command("sear", cutoff=0.5)
66
+ assert result is not None
67
+
68
+ def test_handle_unknown_command_exits(self, handler: ErrorHandler) -> None:
69
+ """Test handle_unknown_command exits with USAGE_ERROR."""
70
+ with pytest.raises(typer.Exit) as exc_info:
71
+ handler.handle_unknown_command("foo")
72
+ assert exc_info.value.exit_code == ExitCodes.USAGE_ERROR
73
+
74
+ def test_handle_unknown_command_with_suggestion(self, handler: ErrorHandler) -> None:
75
+ """Test handle_unknown_command provides suggestion."""
76
+ handler.output = MagicMock()
77
+ with pytest.raises(typer.Exit):
78
+ handler.handle_unknown_command("serch")
79
+ # Should have called output_error
80
+ handler.output.output_error.assert_called_once()
81
+
82
+ def test_handle_config_error_exits(self, handler: ErrorHandler) -> None:
83
+ """Test handle_config_error exits with CONFIG_ERROR."""
84
+ with pytest.raises(typer.Exit) as exc_info:
85
+ handler.handle_config_error("Missing config")
86
+ assert exc_info.value.exit_code == ExitCodes.CONFIG_ERROR
87
+
88
+ def test_handle_config_error_with_missing_vars(self, handler: ErrorHandler) -> None:
89
+ """Test handle_config_error includes missing variables."""
90
+ handler.output = MagicMock()
91
+ with pytest.raises(typer.Exit):
92
+ handler.handle_config_error(
93
+ "Config error",
94
+ missing_vars=["VAR1", "VAR2"],
95
+ )
96
+ handler.output.output_error.assert_called_once()
97
+ call_kwargs = handler.output.output_error.call_args[1]
98
+ assert "missing_variables" in call_kwargs["details"]
99
+
100
+ def test_handle_auth_error_exits(self, handler: ErrorHandler) -> None:
101
+ """Test handle_auth_error exits with AUTH_ERROR."""
102
+ with pytest.raises(typer.Exit) as exc_info:
103
+ handler.handle_auth_error("Invalid credentials")
104
+ assert exc_info.value.exit_code == ExitCodes.AUTH_ERROR
105
+
106
+ def test_handle_permission_error_exits(self, handler: ErrorHandler) -> None:
107
+ """Test handle_permission_error exits with PERMISSION_ERROR."""
108
+ with pytest.raises(typer.Exit) as exc_info:
109
+ handler.handle_permission_error("Access denied")
110
+ assert exc_info.value.exit_code == ExitCodes.PERMISSION_ERROR
111
+
112
+ def test_handle_permission_error_with_roles(self, handler: ErrorHandler) -> None:
113
+ """Test handle_permission_error includes role information."""
114
+ handler.output = MagicMock()
115
+ with pytest.raises(typer.Exit):
116
+ handler.handle_permission_error(
117
+ "Access denied",
118
+ required_role="admin",
119
+ current_role="viewer",
120
+ )
121
+ call_kwargs = handler.output.output_error.call_args[1]
122
+ assert call_kwargs["details"]["required_role"] == "admin"
123
+ assert call_kwargs["details"]["current_role"] == "viewer"
124
+
125
+ def test_handle_network_error_exits(self, handler: ErrorHandler) -> None:
126
+ """Test handle_network_error exits with NETWORK_ERROR."""
127
+ with pytest.raises(typer.Exit) as exc_info:
128
+ handler.handle_network_error("Connection failed")
129
+ assert exc_info.value.exit_code == ExitCodes.NETWORK_ERROR
130
+
131
+ def test_handle_network_error_with_url(self, handler: ErrorHandler) -> None:
132
+ """Test handle_network_error includes URL."""
133
+ handler.output = MagicMock()
134
+ with pytest.raises(typer.Exit):
135
+ handler.handle_network_error(
136
+ "Connection failed",
137
+ url="http://example.com",
138
+ )
139
+ call_kwargs = handler.output.output_error.call_args[1]
140
+ assert call_kwargs["details"]["url"] == "http://example.com"
141
+
142
+ def test_handle_not_found_exits(self, handler: ErrorHandler) -> None:
143
+ """Test handle_not_found exits with NOT_FOUND_ERROR."""
144
+ with pytest.raises(typer.Exit) as exc_info:
145
+ handler.handle_not_found("Index", "logs-*")
146
+ assert exc_info.value.exit_code == ExitCodes.NOT_FOUND_ERROR
147
+
148
+ def test_handle_validation_error_exits(self, handler: ErrorHandler) -> None:
149
+ """Test handle_validation_error exits with VALIDATION_ERROR."""
150
+ with pytest.raises(typer.Exit) as exc_info:
151
+ handler.handle_validation_error("Invalid input")
152
+ assert exc_info.value.exit_code == ExitCodes.VALIDATION_ERROR
153
+
154
+ def test_handle_validation_error_with_field(self, handler: ErrorHandler) -> None:
155
+ """Test handle_validation_error includes field information."""
156
+ handler.output = MagicMock()
157
+ with pytest.raises(typer.Exit):
158
+ handler.handle_validation_error(
159
+ "Invalid input",
160
+ field="index",
161
+ expected="string",
162
+ )
163
+ call_kwargs = handler.output.output_error.call_args[1]
164
+ assert call_kwargs["details"]["field"] == "index"
165
+ assert call_kwargs["details"]["expected"] == "string"
166
+
167
+ def test_handle_general_error_exits(self, handler: ErrorHandler) -> None:
168
+ """Test handle_general_error exits with GENERAL_ERROR."""
169
+ with pytest.raises(typer.Exit) as exc_info:
170
+ handler.handle_general_error("Something went wrong")
171
+ assert exc_info.value.exit_code == ExitCodes.GENERAL_ERROR
172
+
173
+ def test_handle_general_error_with_hint(self, handler: ErrorHandler) -> None:
174
+ """Test handle_general_error includes hint."""
175
+ handler.output = MagicMock()
176
+ with pytest.raises(typer.Exit):
177
+ handler.handle_general_error(
178
+ "Error",
179
+ hint="Try again",
180
+ details={"key": "value"},
181
+ )
182
+ handler.output.output_error.assert_called_once_with(
183
+ "Error",
184
+ hint="Try again",
185
+ details={"key": "value"},
186
+ )