@ngocsangairvds/vsaf 3.1.27 → 3.2.2

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 +65 -39
  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,546 @@
1
+ """Unit tests for AsyncBitbucketClient (TSK-225).
2
+
3
+ Tests for the async wrapper that uses asyncio.to_thread() to wrap
4
+ synchronous BitbucketClient methods.
5
+
6
+ References:
7
+ - FR-21.2.3: AsyncBitbucketClient using asyncio.to_thread()
8
+ - TSK-225: Async Bitbucket wrapper
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import asyncio
14
+ from pathlib import Path
15
+ from unittest.mock import AsyncMock, MagicMock, patch
16
+
17
+ import pytest
18
+
19
+ from vds_bitbucket_orchestrator.async_client import AsyncBitbucketClient
20
+
21
+
22
+ def write_env(tmp_path: Path, content: str) -> Path:
23
+ """Write a temporary .env file for testing."""
24
+ env_path = tmp_path / ".env"
25
+ env_path.write_text(content)
26
+ return env_path
27
+
28
+
29
+ def clear_runtime_env(monkeypatch: pytest.MonkeyPatch) -> None:
30
+ """Clear runtime environment variables for testing."""
31
+ for key in (
32
+ "VDS_USERNAME",
33
+ "VDS_PASSWORD",
34
+ "BITBUCKET_TOKEN",
35
+ "VDS_BITBUCKET_URL",
36
+ "BITBUCKET_BASE_URL",
37
+ "VDS_BITBUCKET_HTTP_BASE",
38
+ "BITBUCKET_CLOUD_WORKSPACE",
39
+ "BITBUCKET_MODE",
40
+ ):
41
+ monkeypatch.delenv(key, raising=False)
42
+
43
+
44
+ class TestAsyncBitbucketClientInit:
45
+ """Tests for AsyncBitbucketClient initialization."""
46
+
47
+ def test_async_client_creates_sync_client(
48
+ self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
49
+ ) -> None:
50
+ """Test that AsyncBitbucketClient creates an underlying sync client."""
51
+ clear_runtime_env(monkeypatch)
52
+ env_path = write_env(
53
+ tmp_path,
54
+ """
55
+ VDS_USERNAME=test_user
56
+ VDS_PASSWORD=test_pass
57
+ VDS_BITBUCKET_URL=https://bitbucket.test.com
58
+ """.strip(),
59
+ )
60
+
61
+ with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
62
+ mock_instance = MagicMock()
63
+ mock_client.return_value = mock_instance
64
+
65
+ from vds_bitbucket_orchestrator.config import load_settings
66
+
67
+ settings = load_settings(env_path=env_path)
68
+ async_client = AsyncBitbucketClient(settings)
69
+
70
+ # Verify sync client was created
71
+ mock_client.assert_called_once()
72
+ assert async_client.sync_client is mock_instance
73
+
74
+ def test_async_client_passes_timeout(
75
+ self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
76
+ ) -> None:
77
+ """Test that timeout is passed to the underlying sync client."""
78
+ clear_runtime_env(monkeypatch)
79
+ env_path = write_env(
80
+ tmp_path,
81
+ """
82
+ VDS_USERNAME=test_user
83
+ VDS_PASSWORD=test_pass
84
+ """.strip(),
85
+ )
86
+
87
+ with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
88
+ mock_instance = MagicMock()
89
+ mock_client.return_value = mock_instance
90
+
91
+ from vds_bitbucket_orchestrator.config import load_settings
92
+
93
+ settings = load_settings(env_path=env_path)
94
+ AsyncBitbucketClient(settings, timeout=60)
95
+
96
+ call_args = mock_client.call_args
97
+ assert call_args.kwargs.get("timeout") == 60
98
+
99
+
100
+ class TestAsyncRepositoryOperations:
101
+ """Tests for async repository operations."""
102
+
103
+ @pytest.mark.asyncio
104
+ async def test_list_projects_async(
105
+ self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
106
+ ) -> None:
107
+ """Test async list_projects method."""
108
+ clear_runtime_env(monkeypatch)
109
+ env_path = write_env(
110
+ tmp_path,
111
+ """
112
+ VDS_USERNAME=test_user
113
+ VDS_PASSWORD=test_pass
114
+ """.strip(),
115
+ )
116
+
117
+ with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
118
+ mock_instance = MagicMock()
119
+ mock_instance.list_projects.return_value = [
120
+ {"key": "INS", "name": "Insurance"},
121
+ {"key": "LEP", "name": "Lending"},
122
+ ]
123
+ mock_client.return_value = mock_instance
124
+
125
+ from vds_bitbucket_orchestrator.config import load_settings
126
+
127
+ settings = load_settings(env_path=env_path)
128
+ async_client = AsyncBitbucketClient(settings)
129
+
130
+ result = await async_client.list_projects(limit=10)
131
+
132
+ assert len(result) == 2
133
+ assert result[0]["key"] == "INS"
134
+ mock_instance.list_projects.assert_called_once_with(10)
135
+
136
+ @pytest.mark.asyncio
137
+ async def test_get_project_async(
138
+ self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
139
+ ) -> None:
140
+ """Test async get_project method."""
141
+ clear_runtime_env(monkeypatch)
142
+ env_path = write_env(
143
+ tmp_path,
144
+ """
145
+ VDS_USERNAME=test_user
146
+ VDS_PASSWORD=test_pass
147
+ """.strip(),
148
+ )
149
+
150
+ with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
151
+ mock_instance = MagicMock()
152
+ mock_instance.get_project.return_value = {"key": "INS", "name": "Insurance"}
153
+ mock_client.return_value = mock_instance
154
+
155
+ from vds_bitbucket_orchestrator.config import load_settings
156
+
157
+ settings = load_settings(env_path=env_path)
158
+ async_client = AsyncBitbucketClient(settings)
159
+
160
+ result = await async_client.get_project("INS")
161
+
162
+ assert result["key"] == "INS"
163
+ mock_instance.get_project.assert_called_once_with("INS")
164
+
165
+ @pytest.mark.asyncio
166
+ async def test_list_repositories_async(
167
+ self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
168
+ ) -> None:
169
+ """Test async list_repositories method."""
170
+ clear_runtime_env(monkeypatch)
171
+ env_path = write_env(
172
+ tmp_path,
173
+ """
174
+ VDS_USERNAME=test_user
175
+ VDS_PASSWORD=test_pass
176
+ """.strip(),
177
+ )
178
+
179
+ with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
180
+ mock_instance = MagicMock()
181
+ mock_instance.list_repositories.return_value = [
182
+ {"slug": "insurance-core", "name": "Insurance Core"},
183
+ {"slug": "insurance-api", "name": "Insurance API"},
184
+ ]
185
+ mock_client.return_value = mock_instance
186
+
187
+ from vds_bitbucket_orchestrator.config import load_settings
188
+
189
+ settings = load_settings(env_path=env_path)
190
+ async_client = AsyncBitbucketClient(settings)
191
+
192
+ result = await async_client.list_repositories("INS", limit=50)
193
+
194
+ assert len(result) == 2
195
+ assert result[0]["slug"] == "insurance-core"
196
+ mock_instance.list_repositories.assert_called_once_with("INS", 50)
197
+
198
+ @pytest.mark.asyncio
199
+ async def test_get_repository_async(
200
+ self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
201
+ ) -> None:
202
+ """Test async get_repository method."""
203
+ clear_runtime_env(monkeypatch)
204
+ env_path = write_env(
205
+ tmp_path,
206
+ """
207
+ VDS_USERNAME=test_user
208
+ VDS_PASSWORD=test_pass
209
+ """.strip(),
210
+ )
211
+
212
+ with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
213
+ mock_instance = MagicMock()
214
+ mock_instance.get_repository.return_value = {
215
+ "slug": "insurance-core",
216
+ "name": "Insurance Core",
217
+ }
218
+ mock_client.return_value = mock_instance
219
+
220
+ from vds_bitbucket_orchestrator.config import load_settings
221
+
222
+ settings = load_settings(env_path=env_path)
223
+ async_client = AsyncBitbucketClient(settings)
224
+
225
+ result = await async_client.get_repository("INS", "insurance-core")
226
+
227
+ assert result["slug"] == "insurance-core"
228
+ mock_instance.get_repository.assert_called_once_with("INS", "insurance-core")
229
+
230
+
231
+ class TestAsyncBranchOperations:
232
+ """Tests for async branch operations."""
233
+
234
+ @pytest.mark.asyncio
235
+ async def test_get_branches_async(
236
+ self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
237
+ ) -> None:
238
+ """Test async get_branches method."""
239
+ clear_runtime_env(monkeypatch)
240
+ env_path = write_env(
241
+ tmp_path,
242
+ """
243
+ VDS_USERNAME=test_user
244
+ VDS_PASSWORD=test_pass
245
+ """.strip(),
246
+ )
247
+
248
+ with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
249
+ mock_instance = MagicMock()
250
+ mock_instance.get_branches.return_value = [
251
+ {"displayId": "main", "isDefault": True},
252
+ {"displayId": "develop", "isDefault": False},
253
+ ]
254
+ mock_client.return_value = mock_instance
255
+
256
+ from vds_bitbucket_orchestrator.config import load_settings
257
+
258
+ settings = load_settings(env_path=env_path)
259
+ async_client = AsyncBitbucketClient(settings)
260
+
261
+ result = await async_client.get_branches("INS", "insurance-core")
262
+
263
+ assert len(result) == 2
264
+ assert result[0]["displayId"] == "main"
265
+ mock_instance.get_branches.assert_called_once_with(
266
+ "INS", "insurance-core", "", 99999, True
267
+ )
268
+
269
+ @pytest.mark.asyncio
270
+ async def test_list_branch_permissions_async(
271
+ self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
272
+ ) -> None:
273
+ """Test async list_branch_permissions method."""
274
+ clear_runtime_env(monkeypatch)
275
+ env_path = write_env(
276
+ tmp_path,
277
+ """
278
+ VDS_USERNAME=test_user
279
+ VDS_PASSWORD=test_pass
280
+ """.strip(),
281
+ )
282
+
283
+ with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
284
+ mock_instance = MagicMock()
285
+ mock_instance.list_branch_permissions.return_value = [
286
+ {"type": "no-deletes", "matcher": {"id": "refs/heads/main"}},
287
+ ]
288
+ mock_client.return_value = mock_instance
289
+
290
+ from vds_bitbucket_orchestrator.config import load_settings
291
+
292
+ settings = load_settings(env_path=env_path)
293
+ async_client = AsyncBitbucketClient(settings)
294
+
295
+ result = await async_client.list_branch_permissions("INS", "insurance-core")
296
+
297
+ assert len(result) == 1
298
+ mock_instance.list_branch_permissions.assert_called_once_with(
299
+ "INS", "insurance-core", 0, 25
300
+ )
301
+
302
+
303
+ class TestAsyncCodeOperations:
304
+ """Tests for async code operations (BitbucketCodeClient protocol)."""
305
+
306
+ @pytest.mark.asyncio
307
+ async def test_get_content_of_file_async(
308
+ self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
309
+ ) -> None:
310
+ """Test async get_content_of_file method."""
311
+ clear_runtime_env(monkeypatch)
312
+ env_path = write_env(
313
+ tmp_path,
314
+ """
315
+ VDS_USERNAME=test_user
316
+ VDS_PASSWORD=test_pass
317
+ """.strip(),
318
+ )
319
+
320
+ with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
321
+ mock_instance = MagicMock()
322
+ mock_instance.get_content_of_file.return_value = "# README\n\nThis is a test."
323
+ mock_client.return_value = mock_instance
324
+
325
+ from vds_bitbucket_orchestrator.config import load_settings
326
+
327
+ settings = load_settings(env_path=env_path)
328
+ async_client = AsyncBitbucketClient(settings)
329
+
330
+ result = await async_client.get_content_of_file(
331
+ "INS", "insurance-core", "README.md"
332
+ )
333
+
334
+ assert "README" in result
335
+ mock_instance.get_content_of_file.assert_called_once_with(
336
+ "INS", "insurance-core", "README.md", None, None
337
+ )
338
+
339
+ @pytest.mark.asyncio
340
+ async def test_get_commits_async(
341
+ self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
342
+ ) -> None:
343
+ """Test async get_commits method."""
344
+ clear_runtime_env(monkeypatch)
345
+ env_path = write_env(
346
+ tmp_path,
347
+ """
348
+ VDS_USERNAME=test_user
349
+ VDS_PASSWORD=test_pass
350
+ """.strip(),
351
+ )
352
+
353
+ with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
354
+ mock_instance = MagicMock()
355
+ mock_instance.get_commits.return_value = [
356
+ {"id": "abc123", "message": "Initial commit"},
357
+ {"id": "def456", "message": "Add feature"},
358
+ ]
359
+ mock_client.return_value = mock_instance
360
+
361
+ from vds_bitbucket_orchestrator.config import load_settings
362
+
363
+ settings = load_settings(env_path=env_path)
364
+ async_client = AsyncBitbucketClient(settings)
365
+
366
+ result = await async_client.get_commits("INS", "insurance-core", limit=10)
367
+
368
+ assert len(result) == 2
369
+ assert result[0]["id"] == "abc123"
370
+ mock_instance.get_commits.assert_called_once_with(
371
+ "INS", "insurance-core", None, None, 10
372
+ )
373
+
374
+
375
+ class TestAsyncConcurrentOperations:
376
+ """Tests for concurrent async operations."""
377
+
378
+ @pytest.mark.asyncio
379
+ async def test_concurrent_api_calls(
380
+ self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
381
+ ) -> None:
382
+ """Test that multiple async calls can run concurrently."""
383
+ clear_runtime_env(monkeypatch)
384
+ env_path = write_env(
385
+ tmp_path,
386
+ """
387
+ VDS_USERNAME=test_user
388
+ VDS_PASSWORD=test_pass
389
+ """.strip(),
390
+ )
391
+
392
+ with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
393
+ mock_instance = MagicMock()
394
+ mock_instance.list_repositories.return_value = [{"slug": "repo1"}]
395
+ mock_instance.get_branches.return_value = [{"displayId": "main"}]
396
+ mock_instance.get_content_of_file.return_value = "# README"
397
+ mock_client.return_value = mock_instance
398
+
399
+ from vds_bitbucket_orchestrator.config import load_settings
400
+
401
+ settings = load_settings(env_path=env_path)
402
+ async_client = AsyncBitbucketClient(settings)
403
+
404
+ # Run multiple operations concurrently
405
+ repos, branches, readme = await asyncio.gather(
406
+ async_client.list_repositories("INS"),
407
+ async_client.get_branches("INS", "insurance-core"),
408
+ async_client.get_content_of_file("INS", "insurance-core", "README.md"),
409
+ )
410
+
411
+ assert len(repos) == 1
412
+ assert len(branches) == 1
413
+ assert "README" in readme
414
+
415
+
416
+ class TestAsyncPullRequestOperations:
417
+ """Tests for async pull request operations."""
418
+
419
+ @pytest.mark.asyncio
420
+ async def test_list_pull_requests_async(
421
+ self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
422
+ ) -> None:
423
+ """Test async list_pull_requests method."""
424
+ clear_runtime_env(monkeypatch)
425
+ env_path = write_env(
426
+ tmp_path,
427
+ """
428
+ VDS_USERNAME=test_user
429
+ VDS_PASSWORD=test_pass
430
+ """.strip(),
431
+ )
432
+
433
+ with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
434
+ mock_instance = MagicMock()
435
+ mock_instance.list_pull_requests.return_value = [
436
+ {"id": 1, "title": "Feature PR", "state": "OPEN"},
437
+ ]
438
+ mock_client.return_value = mock_instance
439
+
440
+ from vds_bitbucket_orchestrator.config import load_settings
441
+
442
+ settings = load_settings(env_path=env_path)
443
+ async_client = AsyncBitbucketClient(settings)
444
+
445
+ result = await async_client.list_pull_requests("INS", "insurance-core")
446
+
447
+ assert len(result) == 1
448
+ assert result[0]["title"] == "Feature PR"
449
+
450
+
451
+ class TestCreateAsyncClientFactory:
452
+ """Tests for create_async_client() factory function."""
453
+
454
+ def test_create_async_client_returns_async_client(
455
+ self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
456
+ ) -> None:
457
+ """Test that create_async_client returns AsyncBitbucketClient."""
458
+ clear_runtime_env(monkeypatch)
459
+ env_path = write_env(
460
+ tmp_path,
461
+ """
462
+ VDS_USERNAME=test_user
463
+ VDS_PASSWORD=test_pass
464
+ VDS_BITBUCKET_URL=https://bitbucket.test.com
465
+ """.strip(),
466
+ )
467
+
468
+ with patch("vds_bitbucket_orchestrator.factory.AsyncBitbucketClient") as mock_async:
469
+ with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient"):
470
+ mock_instance = MagicMock(spec=AsyncBitbucketClient)
471
+ mock_async.return_value = mock_instance
472
+
473
+ from vds_bitbucket_orchestrator import create_async_client
474
+
475
+ client = create_async_client(env_path=env_path)
476
+
477
+ assert client is mock_instance
478
+ mock_async.assert_called_once()
479
+
480
+ def test_create_async_client_with_mode_override(
481
+ self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
482
+ ) -> None:
483
+ """Test that mode parameter is passed correctly."""
484
+ clear_runtime_env(monkeypatch)
485
+ env_path = write_env(
486
+ tmp_path,
487
+ """
488
+ VDS_USERNAME=test_user
489
+ VDS_PASSWORD=test_pass
490
+ BITBUCKET_CLOUD_WORKSPACE=testworkspace
491
+ """.strip(),
492
+ )
493
+
494
+ with patch("vds_bitbucket_orchestrator.factory.AsyncBitbucketClient") as mock_async:
495
+ with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient"):
496
+ mock_instance = MagicMock(spec=AsyncBitbucketClient)
497
+ mock_async.return_value = mock_instance
498
+
499
+ from vds_bitbucket_orchestrator import create_async_client
500
+
501
+ create_async_client(env_path=env_path, mode="cloud")
502
+
503
+ call_args = mock_async.call_args
504
+ settings = call_args[0][0]
505
+ assert settings.mode == "cloud"
506
+
507
+ def test_create_async_client_with_custom_timeout(
508
+ self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
509
+ ) -> None:
510
+ """Test that timeout is passed to AsyncBitbucketClient."""
511
+ clear_runtime_env(monkeypatch)
512
+ env_path = write_env(
513
+ tmp_path,
514
+ """
515
+ VDS_USERNAME=test_user
516
+ VDS_PASSWORD=test_pass
517
+ """.strip(),
518
+ )
519
+
520
+ with patch("vds_bitbucket_orchestrator.factory.AsyncBitbucketClient") as mock_async:
521
+ with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient"):
522
+ mock_instance = MagicMock(spec=AsyncBitbucketClient)
523
+ mock_async.return_value = mock_instance
524
+
525
+ from vds_bitbucket_orchestrator import create_async_client
526
+
527
+ create_async_client(env_path=env_path, timeout=120)
528
+
529
+ call_args = mock_async.call_args
530
+ assert call_args.kwargs.get("timeout") == 120
531
+
532
+
533
+ class TestAsyncClientImports:
534
+ """Test that async client is properly exported from package."""
535
+
536
+ def test_async_client_importable_from_package(self) -> None:
537
+ """Test that AsyncBitbucketClient can be imported from package root."""
538
+ from vds_bitbucket_orchestrator import AsyncBitbucketClient
539
+
540
+ assert AsyncBitbucketClient is not None
541
+
542
+ def test_create_async_client_importable(self) -> None:
543
+ """Test that create_async_client can be imported from package root."""
544
+ from vds_bitbucket_orchestrator import create_async_client
545
+
546
+ assert callable(create_async_client)
@@ -0,0 +1,145 @@
1
+ from __future__ import annotations
2
+
3
+ from unittest.mock import Mock, patch
4
+
5
+ from typer.testing import CliRunner
6
+ from vds_bitbucket_orchestrator.cli import app
7
+ from vds_bitbucket_orchestrator.errors import BitbucketClientError
8
+
9
+
10
+ def _runner() -> CliRunner:
11
+ return CliRunner()
12
+
13
+
14
+ @patch("vds_bitbucket_orchestrator.cli._build_client")
15
+ @patch("vds_bitbucket_orchestrator.cli.load_settings")
16
+ def test_branch_permissions_get(mock_load: Mock, mock_build: Mock) -> None:
17
+ runner = _runner()
18
+ client = Mock()
19
+ client.get_branch_permission.return_value = {"id": 1}
20
+ mock_build.return_value.__enter__.return_value = client
21
+
22
+ result = runner.invoke(app, ["branch-permissions", "get", "PRJ", "--permission-id", "1"])
23
+ assert result.exit_code == 0
24
+ client.get_branch_permission.assert_called_once_with("PRJ", 1, repository_slug=None)
25
+
26
+
27
+ @patch("vds_bitbucket_orchestrator.cli._build_client")
28
+ @patch("vds_bitbucket_orchestrator.cli.load_settings")
29
+ def test_branch_permissions_get_requires_id(mock_load: Mock, mock_build: Mock) -> None:
30
+ runner = _runner()
31
+
32
+ result = runner.invoke(app, ["branch-permissions", "get", "PRJ"])
33
+ assert result.exit_code != 0
34
+ assert "--permission-id required for get" in result.stdout
35
+
36
+
37
+ @patch("vds_bitbucket_orchestrator.cli._build_client")
38
+ @patch("vds_bitbucket_orchestrator.cli.load_settings")
39
+ def test_branch_permissions_set_requires_yes(mock_load: Mock, mock_build: Mock) -> None:
40
+ runner = _runner()
41
+ result = runner.invoke(app, [
42
+ "branch-permissions", "set", "PRJ",
43
+ "--matcher-type", "BRANCH", "--matcher-value", "refs/heads/master",
44
+ "--permission-type", "READ_ONLY"
45
+ ])
46
+ assert result.exit_code != 0
47
+ assert "Refusing to set permissions without --yes" in result.stdout
48
+
49
+
50
+ @patch("vds_bitbucket_orchestrator.cli._build_client")
51
+ @patch("vds_bitbucket_orchestrator.cli.load_settings")
52
+ def test_branch_permissions_set_success(mock_load: Mock, mock_build: Mock) -> None:
53
+ runner = _runner()
54
+ client = Mock()
55
+ mock_build.return_value.__enter__.return_value = client
56
+
57
+ result = runner.invoke(app, [
58
+ "branch-permissions", "set", "PRJ",
59
+ "--matcher-type", "BRANCH", "--matcher-value", "refs/heads/master",
60
+ "--permission-type", "READ_ONLY", "--yes"
61
+ ])
62
+ assert result.exit_code == 0
63
+ client.set_branches_permissions.assert_called_once_with(
64
+ "PRJ",
65
+ matcher_type="BRANCH",
66
+ matcher_value="refs/heads/master",
67
+ permission_type="READ_ONLY",
68
+ repository_slug=None,
69
+ )
70
+
71
+
72
+ @patch("vds_bitbucket_orchestrator.cli._build_client")
73
+ @patch("vds_bitbucket_orchestrator.cli.load_settings")
74
+ def test_branch_permissions_set_passes_repository(mock_load: Mock, mock_build: Mock) -> None:
75
+ runner = _runner()
76
+ client = Mock()
77
+ mock_build.return_value.__enter__.return_value = client
78
+
79
+ result = runner.invoke(app, [
80
+ "branch-permissions",
81
+ "set",
82
+ "PRJ",
83
+ "--repo",
84
+ "app-repo",
85
+ "--matcher-type",
86
+ "BRANCH",
87
+ "--matcher-value",
88
+ "refs/heads/develop",
89
+ "--permission-type",
90
+ "WRITE",
91
+ "--yes",
92
+ ])
93
+ assert result.exit_code == 0
94
+ client.set_branches_permissions.assert_called_once_with(
95
+ "PRJ",
96
+ matcher_type="BRANCH",
97
+ matcher_value="refs/heads/develop",
98
+ permission_type="WRITE",
99
+ repository_slug="app-repo",
100
+ )
101
+
102
+
103
+ @patch("vds_bitbucket_orchestrator.cli._build_client")
104
+ @patch("vds_bitbucket_orchestrator.cli.load_settings")
105
+ def test_branch_permissions_set_error(mock_load: Mock, mock_build: Mock) -> None:
106
+ runner = _runner()
107
+ client = Mock()
108
+ client.set_branches_permissions.side_effect = BitbucketClientError("oops")
109
+ mock_build.return_value.__enter__.return_value = client
110
+
111
+ result = runner.invoke(app, [
112
+ "branch-permissions",
113
+ "set",
114
+ "PRJ",
115
+ "--matcher-type",
116
+ "BRANCH",
117
+ "--matcher-value",
118
+ "refs/heads/master",
119
+ "--permission-type",
120
+ "READ_ONLY",
121
+ "--yes",
122
+ ])
123
+ assert result.exit_code != 0
124
+ assert "bitbucket_error" in result.stdout
125
+
126
+
127
+ @patch("vds_bitbucket_orchestrator.cli._build_client")
128
+ @patch("vds_bitbucket_orchestrator.cli.load_settings")
129
+ def test_branch_permissions_delete_requires_yes(mock_load: Mock, mock_build: Mock) -> None:
130
+ runner = _runner()
131
+ result = runner.invoke(app, ["branch-permissions", "delete", "PRJ", "--permission-id", "7"])
132
+ assert result.exit_code != 0
133
+ assert "Refusing to delete without --yes" in result.stdout
134
+
135
+
136
+ @patch("vds_bitbucket_orchestrator.cli._build_client")
137
+ @patch("vds_bitbucket_orchestrator.cli.load_settings")
138
+ def test_branch_permissions_delete_success(mock_load: Mock, mock_build: Mock) -> None:
139
+ runner = _runner()
140
+ client = Mock()
141
+ mock_build.return_value.__enter__.return_value = client
142
+
143
+ result = runner.invoke(app, ["branch-permissions", "delete", "PRJ", "--permission-id", "7", "--yes"])
144
+ assert result.exit_code == 0
145
+ client.delete_branch_permission.assert_called_once_with("PRJ", 7, repository_slug=None)