@ngocsangairvds/vsaf 3.1.26 → 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 (305) 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-push-prd/SKILL.md +43 -40
  16. package/tools/skills/vsaf-push-srs/SKILL.md +44 -41
  17. package/tools/skills/vsaf-ship/SKILL.md +41 -10
  18. package/tools/skills/vsaf-test/SKILL.md +8 -0
  19. package/tools/vds-scripts/.mcp.json +11 -0
  20. package/tools/vds-scripts/.secrets.baseline +133 -0
  21. package/tools/vds-scripts/AGENTS.md +152 -0
  22. package/tools/vds-scripts/CLAUDE.md +101 -0
  23. package/tools/vds-scripts/CLI_COMMAND_OPTIMIZATION.md +156 -0
  24. package/tools/vds-scripts/PACKAGE_P125B_IMPLEMENTATION_SUMMARY.md +131 -0
  25. package/tools/vds-scripts/PROJECT_COMPLETION_SUMMARY.md +45 -0
  26. package/tools/vds-scripts/README.md +97 -0
  27. package/tools/vds-scripts/bitbucket_manifest_mapping.toml +34 -0
  28. package/tools/vds-scripts/bitbucket_orchestrator/ARCHITECTURE_ANALYSIS.md +258 -0
  29. package/tools/vds-scripts/bitbucket_orchestrator/BITBUCKET_API_PRACTICES.md +393 -0
  30. package/tools/vds-scripts/bitbucket_orchestrator/EVALUATION_REPORT.md +61 -0
  31. package/tools/vds-scripts/bitbucket_orchestrator/FEATURES.md +908 -0
  32. package/tools/vds-scripts/bitbucket_orchestrator/README.md +687 -0
  33. package/tools/vds-scripts/bitbucket_orchestrator/pyproject.toml +40 -0
  34. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/__init__.py +20 -0
  35. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/async_client.py +657 -0
  36. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/cli.py +2108 -0
  37. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/client.py +2534 -0
  38. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/config.py +171 -0
  39. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/errors.py +67 -0
  40. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/factory.py +185 -0
  41. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/protocols.py +244 -0
  42. package/tools/vds-scripts/bitbucket_orchestrator/tests/__init__.py +8 -0
  43. package/tools/vds-scripts/bitbucket_orchestrator/tests/conftest.py +65 -0
  44. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_advanced_search.py +151 -0
  45. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_async_client.py +546 -0
  46. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_branch_permissions.py +145 -0
  47. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_cli.py +115 -0
  48. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client.py +157 -0
  49. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_branch_conditions.py +79 -0
  50. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_code_advanced.py +163 -0
  51. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_code_file.py +32 -0
  52. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_deployment_environments.py +194 -0
  53. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_issues.py +164 -0
  54. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_pipelines_advanced.py +179 -0
  55. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_pr_blockers.py +119 -0
  56. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_repository_variables.py +156 -0
  57. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code.py +98 -0
  58. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code_advanced.py +282 -0
  59. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code_insights.py +335 -0
  60. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_conditions.py +147 -0
  61. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_config.py +131 -0
  62. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_deployment_env.py +352 -0
  63. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_factory.py +371 -0
  64. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_fork_operations.py +204 -0
  65. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_issue_cli.py +261 -0
  66. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_pipeline_advanced.py +270 -0
  67. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_pr_blocker.py +204 -0
  68. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_protocols.py +334 -0
  69. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_repo_settings.py +343 -0
  70. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_repo_variables.py +270 -0
  71. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_webhooks.py +189 -0
  72. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_workspace.py +233 -0
  73. package/tools/vds-scripts/bitbucket_orchestrator/uv.lock +742 -0
  74. package/tools/vds-scripts/confluence_orchestrator/Dockerfile +19 -0
  75. package/tools/vds-scripts/confluence_orchestrator/README.md +412 -0
  76. package/tools/vds-scripts/confluence_orchestrator/SYNC_SCRIPTS.md +127 -0
  77. package/tools/vds-scripts/confluence_orchestrator/SYNC_STANDARDIZATION.md +108 -0
  78. package/tools/vds-scripts/confluence_orchestrator/pyproject.toml +48 -0
  79. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/__init__.py +20 -0
  80. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/cli.py +2532 -0
  81. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/config.py +175 -0
  82. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/content.py +290 -0
  83. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/content_v2.py +94 -0
  84. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/crawl_tree.py +1835 -0
  85. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/errors.py +80 -0
  86. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/eventing.py +109 -0
  87. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/http.py +1114 -0
  88. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/orchestration.py +165 -0
  89. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/reporting.py +78 -0
  90. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/tree.py +121 -0
  91. package/tools/vds-scripts/confluence_orchestrator/sync_pdfs_from_markdown.py +213 -0
  92. package/tools/vds-scripts/confluence_orchestrator/sync_pdfs_to_confluence.py +305 -0
  93. package/tools/vds-scripts/confluence_orchestrator/sync_png_attachments.py +305 -0
  94. package/tools/vds-scripts/confluence_orchestrator/tests/__init__.py +0 -0
  95. package/tools/vds-scripts/confluence_orchestrator/tests/conftest.py +8 -0
  96. package/tools/vds-scripts/confluence_orchestrator/tests/test_advanced_content.py +224 -0
  97. package/tools/vds-scripts/confluence_orchestrator/tests/test_advanced_search.py +188 -0
  98. package/tools/vds-scripts/confluence_orchestrator/tests/test_cache_management.py +247 -0
  99. package/tools/vds-scripts/confluence_orchestrator/tests/test_cli.py +499 -0
  100. package/tools/vds-scripts/confluence_orchestrator/tests/test_config.py +83 -0
  101. package/tools/vds-scripts/confluence_orchestrator/tests/test_content.py +186 -0
  102. package/tools/vds-scripts/confluence_orchestrator/tests/test_content_flags.py +27 -0
  103. package/tools/vds-scripts/confluence_orchestrator/tests/test_crawl_tree.py +2250 -0
  104. package/tools/vds-scripts/confluence_orchestrator/tests/test_draft_management.py +223 -0
  105. package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing.py +71 -0
  106. package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_chaos.py +37 -0
  107. package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_rate_limit.py +44 -0
  108. package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_timeout.py +49 -0
  109. package/tools/vds-scripts/confluence_orchestrator/tests/test_export.py +230 -0
  110. package/tools/vds-scripts/confluence_orchestrator/tests/test_history.py +204 -0
  111. package/tools/vds-scripts/confluence_orchestrator/tests/test_http.py +117 -0
  112. package/tools/vds-scripts/confluence_orchestrator/tests/test_orchestration.py +91 -0
  113. package/tools/vds-scripts/confluence_orchestrator/tests/test_reporting.py +24 -0
  114. package/tools/vds-scripts/confluence_orchestrator/tests/test_search_cql.py +34 -0
  115. package/tools/vds-scripts/confluence_orchestrator/tests/test_space_management.py +237 -0
  116. package/tools/vds-scripts/confluence_orchestrator/tests/test_space_permissions.py +332 -0
  117. package/tools/vds-scripts/confluence_orchestrator/tests/test_user_group_management.py +388 -0
  118. package/tools/vds-scripts/confluence_orchestrator/uv.lock +1023 -0
  119. package/tools/vds-scripts/git_orchestrator/ENHANCEMENT_SUMMARY.md +119 -0
  120. package/tools/vds-scripts/git_orchestrator/README.md +280 -0
  121. package/tools/vds-scripts/git_orchestrator/VERIFICATION_REPORT.md +152 -0
  122. package/tools/vds-scripts/git_orchestrator/pyproject.toml +35 -0
  123. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/__init__.py +7 -0
  124. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/__main__.py +4 -0
  125. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/cli.py +847 -0
  126. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/logging_config.py +63 -0
  127. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/manifest.py +129 -0
  128. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/orchestrator.py +819 -0
  129. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/reporting.py +53 -0
  130. package/tools/vds-scripts/git_orchestrator/tests/__init__.py +0 -0
  131. package/tools/vds-scripts/git_orchestrator/tests/test_cli_settings.py +21 -0
  132. package/tools/vds-scripts/git_orchestrator/tests/test_integration.py +74 -0
  133. package/tools/vds-scripts/git_orchestrator/tests/test_manifest.py +79 -0
  134. package/tools/vds-scripts/git_orchestrator/tests/test_orchestrator.py +204 -0
  135. package/tools/vds-scripts/git_orchestrator/tests/test_public_api.py +236 -0
  136. package/tools/vds-scripts/git_orchestrator/tests/test_resilience.py +345 -0
  137. package/tools/vds-scripts/git_orchestrator/uv.lock +271 -0
  138. package/tools/vds-scripts/jira_orchestrator/README.md +770 -0
  139. package/tools/vds-scripts/jira_orchestrator/pyproject.toml +39 -0
  140. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/__init__.py +1 -0
  141. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/adapter.py +1320 -0
  142. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/cli.py +2271 -0
  143. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/config.py +138 -0
  144. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/errors.py +67 -0
  145. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/reporting.py +65 -0
  146. package/tools/vds-scripts/jira_orchestrator/tests/__init__.py +1 -0
  147. package/tools/vds-scripts/jira_orchestrator/tests/conftest.py +86 -0
  148. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_agile_list_payloads.py +54 -0
  149. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_bulk_operations.py +69 -0
  150. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_components.py +57 -0
  151. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_createmeta.py +45 -0
  152. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_dashboard.py +117 -0
  153. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_issue_properties.py +54 -0
  154. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_permissions_compat.py +42 -0
  155. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_reindex.py +42 -0
  156. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_remote_links.py +76 -0
  157. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_transitions.py +91 -0
  158. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_user_management.py +110 -0
  159. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_version_management.py +133 -0
  160. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_watchers.py +41 -0
  161. package/tools/vds-scripts/jira_orchestrator/tests/test_advanced_search.py +164 -0
  162. package/tools/vds-scripts/jira_orchestrator/tests/test_agile.py +256 -0
  163. package/tools/vds-scripts/jira_orchestrator/tests/test_application_properties.py +193 -0
  164. package/tools/vds-scripts/jira_orchestrator/tests/test_backlog.py +91 -0
  165. package/tools/vds-scripts/jira_orchestrator/tests/test_bulk_operations.py +277 -0
  166. package/tools/vds-scripts/jira_orchestrator/tests/test_cli.py +106 -0
  167. package/tools/vds-scripts/jira_orchestrator/tests/test_components.py +106 -0
  168. package/tools/vds-scripts/jira_orchestrator/tests/test_config.py +164 -0
  169. package/tools/vds-scripts/jira_orchestrator/tests/test_dashboard.py +122 -0
  170. package/tools/vds-scripts/jira_orchestrator/tests/test_discover_fields.py +207 -0
  171. package/tools/vds-scripts/jira_orchestrator/tests/test_filter_management.py +333 -0
  172. package/tools/vds-scripts/jira_orchestrator/tests/test_issue_archiving.py +164 -0
  173. package/tools/vds-scripts/jira_orchestrator/tests/test_issue_links.py +257 -0
  174. package/tools/vds-scripts/jira_orchestrator/tests/test_issue_properties.py +171 -0
  175. package/tools/vds-scripts/jira_orchestrator/tests/test_link_types.py +314 -0
  176. package/tools/vds-scripts/jira_orchestrator/tests/test_parse_set.py +37 -0
  177. package/tools/vds-scripts/jira_orchestrator/tests/test_permissions.py +273 -0
  178. package/tools/vds-scripts/jira_orchestrator/tests/test_reindex.py +81 -0
  179. package/tools/vds-scripts/jira_orchestrator/tests/test_remote_links.py +254 -0
  180. package/tools/vds-scripts/jira_orchestrator/tests/test_security_schemes.py +170 -0
  181. package/tools/vds-scripts/jira_orchestrator/tests/test_transitions_changelog.py +114 -0
  182. package/tools/vds-scripts/jira_orchestrator/tests/test_user_management.py +226 -0
  183. package/tools/vds-scripts/jira_orchestrator/tests/test_version_management.py +339 -0
  184. package/tools/vds-scripts/jira_orchestrator/tests/test_watchers.py +101 -0
  185. package/tools/vds-scripts/jira_orchestrator/tests/test_worklog.py +223 -0
  186. package/tools/vds-scripts/jira_orchestrator/uv.lock +738 -0
  187. package/tools/vds-scripts/mcp_server/Dockerfile +34 -0
  188. package/tools/vds-scripts/mcp_server/README.md +140 -0
  189. package/tools/vds-scripts/mcp_server/pyproject.toml +42 -0
  190. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/__init__.py +4 -0
  191. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/config.py +36 -0
  192. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/server.py +66 -0
  193. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/__init__.py +14 -0
  194. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/bitbucket_tools.py +47 -0
  195. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/confluence_tools.py +59 -0
  196. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/git_tools.py +71 -0
  197. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/jira_tools.py +63 -0
  198. package/tools/vds-scripts/mcp_server/tests/__init__.py +2 -0
  199. package/tools/vds-scripts/mcp_server/tests/conftest.py +29 -0
  200. package/tools/vds-scripts/mcp_server/tests/unit/__init__.py +2 -0
  201. package/tools/vds-scripts/mcp_server/tests/unit/test_bitbucket_tools.py +25 -0
  202. package/tools/vds-scripts/mcp_server/tests/unit/test_confluence_tools.py +25 -0
  203. package/tools/vds-scripts/mcp_server/tests/unit/test_git_tools.py +32 -0
  204. package/tools/vds-scripts/mcp_server/tests/unit/test_jira_tools.py +32 -0
  205. package/tools/vds-scripts/mcp_server/tests/verification/__init__.py +2 -0
  206. package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_confluence_tools.py +40 -0
  207. package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_jira_tools.py +37 -0
  208. package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_tool_registration.py +47 -0
  209. package/tools/vds-scripts/mcp_server/uv.lock +1032 -0
  210. package/tools/vds-scripts/mypy.ini +5 -0
  211. package/tools/vds-scripts/pyproject.toml +29 -0
  212. package/tools/vds-scripts/repo-manifest.yaml +273 -0
  213. package/tools/vds-scripts/repo-manifest.yaml.example +25 -0
  214. package/tools/vds-scripts/scripts/BRD-Validation-API.postman_collection.json +706 -0
  215. package/tools/vds-scripts/scripts/BRD-Validation-README.md +308 -0
  216. package/tools/vds-scripts/scripts/README.md +162 -0
  217. package/tools/vds-scripts/scripts/bootstrap_uv.sh +30 -0
  218. package/tools/vds-scripts/scripts/brd-validation-environment.json +51 -0
  219. package/tools/vds-scripts/scripts/brd-validation-test-results.json +13023 -0
  220. package/tools/vds-scripts/scripts/brd_coverage_report.json +276 -0
  221. package/tools/vds-scripts/scripts/create_memory_session.py +35 -0
  222. package/tools/vds-scripts/scripts/deployment/load_docker_images_offline.sh +90 -0
  223. package/tools/vds-scripts/scripts/final_completion_report.md +139 -0
  224. package/tools/vds-scripts/scripts/folder_structure_report.json +321 -0
  225. package/tools/vds-scripts/scripts/generate_completion_report.py +125 -0
  226. package/tools/vds-scripts/scripts/generate_intellij_modules.py +150 -0
  227. package/tools/vds-scripts/scripts/link_integrity_report.json +807 -0
  228. package/tools/vds-scripts/scripts/move_audit_artifact_pages.py +255 -0
  229. package/tools/vds-scripts/scripts/move_audit_artifact_pages_rest.py +165 -0
  230. package/tools/vds-scripts/scripts/move_wrong_dept_pages.py +216 -0
  231. package/tools/vds-scripts/scripts/save_intellij_memories.py +120 -0
  232. package/tools/vds-scripts/scripts/save_memories_to_vds_ai.py +83 -0
  233. package/tools/vds-scripts/scripts/save_memories_vds_style.py +129 -0
  234. package/tools/vds-scripts/scripts/search_intellij_memories.py +50 -0
  235. package/tools/vds-scripts/scripts/setup_intellij_workspace.py +65 -0
  236. package/tools/vds-scripts/scripts/target-state-automation/README.md +89 -0
  237. package/tools/vds-scripts/scripts/target-state-automation/confluence_sync_coordinator.sh +27 -0
  238. package/tools/vds-scripts/scripts/target-state-automation/coordination.sh +114 -0
  239. package/tools/vds-scripts/scripts/target-state-automation/diagram_coordinator.sh +25 -0
  240. package/tools/vds-scripts/scripts/target-state-automation/docs_root.sh +22 -0
  241. package/tools/vds-scripts/scripts/target-state-automation/generate_diagrams.sh +22 -0
  242. package/tools/vds-scripts/scripts/target-state-automation/markdown_coordinator.sh +25 -0
  243. package/tools/vds-scripts/scripts/target-state-automation/progress_dashboard.sh +17 -0
  244. package/tools/vds-scripts/scripts/target-state-automation/schema_coordinator.sh +25 -0
  245. package/tools/vds-scripts/scripts/target-state-automation/sync_confluence.sh +30 -0
  246. package/tools/vds-scripts/scripts/target-state-automation/update_dependencies.sh +19 -0
  247. package/tools/vds-scripts/scripts/target-state-automation/validate_links.sh +86 -0
  248. package/tools/vds-scripts/scripts/target-state-automation/validate_markdown.sh +52 -0
  249. package/tools/vds-scripts/scripts/target-state-automation/validate_schemas.sh +26 -0
  250. package/tools/vds-scripts/scripts/target-state-automation/validate_structure.sh +98 -0
  251. package/tools/vds-scripts/scripts/update_modules_xml.py +190 -0
  252. package/tools/vds-scripts/scripts/uv-workspace-alignment-verification-2026-03-25.md +128 -0
  253. package/tools/vds-scripts/scripts/validate_brd_coverage.py +179 -0
  254. package/tools/vds-scripts/scripts/validate_folder_structure.py +240 -0
  255. package/tools/vds-scripts/scripts/validate_link_integrity.py +272 -0
  256. package/tools/vds-scripts/scripts/vds_sh_helpers.sh +180 -0
  257. package/tools/vds-scripts/scripts/verification/phase2_portable_paths_ubuntu_docker.sh +26 -0
  258. package/tools/vds-scripts/scripts/worktree_uv.sh +48 -0
  259. package/tools/vds-scripts/uv.lock +8 -0
  260. package/tools/vds-scripts/vds_cli/README.md +126 -0
  261. package/tools/vds-scripts/vds_cli/VERIFICATION_REPORT.md +41 -0
  262. package/tools/vds-scripts/vds_cli/pyproject.toml +38 -0
  263. package/tools/vds-scripts/vds_cli/src/vds_cli/__init__.py +3 -0
  264. package/tools/vds-scripts/vds_cli/src/vds_cli/cli.py +173 -0
  265. package/tools/vds-scripts/vds_cli/src/vds_cli/docs_sync.py +1203 -0
  266. package/tools/vds-scripts/vds_cli/src/vds_cli/env.py +41 -0
  267. package/tools/vds-scripts/vds_cli/src/vds_cli/google_sheets_orchestrator/__init__.py +3 -0
  268. package/tools/vds-scripts/vds_cli/src/vds_cli/google_sheets_orchestrator/google_sheets_orchestrator.py +198 -0
  269. package/tools/vds-scripts/vds_cli/src/vds_cli/router.py +93 -0
  270. package/tools/vds-scripts/vds_cli/src/vds_cli/sync_api.py +647 -0
  271. package/tools/vds-scripts/vds_cli/src/vds_cli/sync_service.py +266 -0
  272. package/tools/vds-scripts/vds_cli/tests/__init__.py +2 -0
  273. package/tools/vds-scripts/vds_cli/tests/conftest.py +49 -0
  274. package/tools/vds-scripts/vds_cli/tests/unit/__init__.py +2 -0
  275. package/tools/vds-scripts/vds_cli/tests/unit/test_cli.py +143 -0
  276. package/tools/vds-scripts/vds_cli/tests/unit/test_docs_sync.py +422 -0
  277. package/tools/vds-scripts/vds_cli/tests/unit/test_env.py +51 -0
  278. package/tools/vds-scripts/vds_cli/tests/unit/test_router.py +72 -0
  279. package/tools/vds-scripts/vds_cli/tests/unit/test_sync_api.py +357 -0
  280. package/tools/vds-scripts/vds_cli/tests/unit/test_sync_service.py +160 -0
  281. package/tools/vds-scripts/vds_cli/tests/verification/__init__.py +2 -0
  282. package/tools/vds-scripts/vds_cli/tests/verification/test_bitbucket_real.py +33 -0
  283. package/tools/vds-scripts/vds_cli/tests/verification/test_confluence_real.py +35 -0
  284. package/tools/vds-scripts/vds_cli/tests/verification/test_jira_real.py +41 -0
  285. package/tools/vds-scripts/vds_cli/uv.lock +524 -0
  286. package/tools/vds-scripts/vds_cli_common/README.md +190 -0
  287. package/tools/vds-scripts/vds_cli_common/pyproject.toml +92 -0
  288. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/__init__.py +34 -0
  289. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/completers.py +139 -0
  290. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/context.py +201 -0
  291. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/env.py +119 -0
  292. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/errors.py +318 -0
  293. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/output.py +284 -0
  294. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/paths.py +78 -0
  295. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/testing.py +213 -0
  296. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/version.py +85 -0
  297. package/tools/vds-scripts/vds_cli_common/tests/__init__.py +1 -0
  298. package/tools/vds-scripts/vds_cli_common/tests/test_completers.py +148 -0
  299. package/tools/vds-scripts/vds_cli_common/tests/test_context.py +192 -0
  300. package/tools/vds-scripts/vds_cli_common/tests/test_env.py +102 -0
  301. package/tools/vds-scripts/vds_cli_common/tests/test_errors.py +186 -0
  302. package/tools/vds-scripts/vds_cli_common/tests/test_output.py +229 -0
  303. package/tools/vds-scripts/vds_cli_common/tests/test_paths.py +61 -0
  304. package/tools/vds-scripts/vds_cli_common/tests/test_testing.py +138 -0
  305. package/tools/vds-scripts/vds_cli_common/tests/test_version.py +64 -0
@@ -0,0 +1,41 @@
1
+ from __future__ import annotations
2
+
3
+ from unittest.mock import MagicMock, patch
4
+
5
+ from vds_jira_orchestrator.adapter import JiraAdapter
6
+
7
+
8
+ @patch("vds_jira_orchestrator.adapter.Jira")
9
+ def test_issue_get_watchers_calls_sdk(mock_jira: MagicMock) -> None:
10
+ client = MagicMock()
11
+ client.issue_get_watchers.return_value = {"watchCount": 1, "watchers": [{"accountId": "123"}]}
12
+ mock_jira.return_value = client
13
+
14
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
15
+ payload = adapter.issue_get_watchers("NTTC-1")
16
+
17
+ client.issue_get_watchers.assert_called_once_with("NTTC-1")
18
+ assert payload["watchCount"] == 1
19
+
20
+
21
+ @patch("vds_jira_orchestrator.adapter.Jira")
22
+ def test_issue_add_watcher_calls_sdk(mock_jira: MagicMock) -> None:
23
+ client = MagicMock()
24
+ mock_jira.return_value = client
25
+
26
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
27
+ adapter.issue_add_watcher("NTTC-2", "userAccount")
28
+
29
+ client.issue_add_watcher.assert_called_once_with("NTTC-2", "userAccount")
30
+
31
+
32
+ @patch("vds_jira_orchestrator.adapter.Jira")
33
+ def test_issue_delete_watcher_calls_sdk(mock_jira: MagicMock) -> None:
34
+ client = MagicMock()
35
+ mock_jira.return_value = client
36
+
37
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
38
+ adapter.issue_delete_watcher("NTTC-3", "userAccount")
39
+
40
+ client.issue_delete_watcher.assert_called_once_with("NTTC-3", "userAccount")
41
+
@@ -0,0 +1,164 @@
1
+ """Tests for JIRA advanced search methods."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from unittest.mock import MagicMock, patch
6
+
7
+ import pytest
8
+ from typer.testing import CliRunner
9
+ from vds_jira_orchestrator.adapter import JiraAdapter, JiraAdapterError, JiraAuthError
10
+ from vds_jira_orchestrator.cli import app
11
+
12
+ runner = CliRunner()
13
+
14
+
15
+ # ---------------------------------------------------------------------------
16
+ # Adapter method tests
17
+ # ---------------------------------------------------------------------------
18
+
19
+
20
+ @patch("vds_jira_orchestrator.adapter.Jira")
21
+ def test_search_issues_advanced_calls_client(mock_jira: MagicMock) -> None:
22
+ """Test that search_issues_advanced calls the SDK client with pagination."""
23
+ client = MagicMock()
24
+ client.jql.return_value = {"issues": [{"key": "TEST-1"}, {"key": "TEST-2"}], "total": 2}
25
+ mock_jira.return_value = client
26
+
27
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
28
+ result = adapter.search_issues_advanced("project = TEST", start=10, limit=25, fields=["summary", "status"])
29
+
30
+ client.jql.assert_called_once_with("project = TEST", fields="summary,status", start=10, limit=25)
31
+ assert result["total"] == 2
32
+
33
+
34
+ @patch("vds_jira_orchestrator.adapter.Jira")
35
+ def test_search_issues_advanced_defaults(mock_jira: MagicMock) -> None:
36
+ """Test that search_issues_advanced uses default parameters."""
37
+ client = MagicMock()
38
+ client.jql.return_value = {"issues": [], "total": 0}
39
+ mock_jira.return_value = client
40
+
41
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
42
+ adapter.search_issues_advanced("project = TEST")
43
+
44
+ client.jql.assert_called_once_with("project = TEST", fields="*all", start=0, limit=50)
45
+
46
+
47
+ @patch("vds_jira_orchestrator.adapter.Jira")
48
+ def test_search_issues_by_filter_calls_get_filter(mock_jira: MagicMock) -> None:
49
+ """Test that search_issues_by_filter gets filter and uses its JQL."""
50
+ client = MagicMock()
51
+ client.get_filter.return_value = {"id": 10000, "name": "My Filter", "jql": "project = TEST AND status = Open"}
52
+ client.jql.return_value = {"issues": [{"key": "TEST-1"}], "total": 1}
53
+ mock_jira.return_value = client
54
+
55
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
56
+ result = adapter.search_issues_by_filter(10000, start=0, limit=50)
57
+
58
+ client.get_filter.assert_called_once_with(10000)
59
+ client.jql.assert_called_once_with("project = TEST AND status = Open", fields="*all", start=0, limit=50)
60
+ assert result["total"] == 1
61
+
62
+
63
+ @patch("vds_jira_orchestrator.adapter.Jira")
64
+ def test_search_issues_by_filter_no_jql(mock_jira: MagicMock) -> None:
65
+ """Test that search_issues_by_filter raises error when filter has no JQL."""
66
+ client = MagicMock()
67
+ client.get_filter.return_value = {"id": 10000, "name": "My Filter", "jql": ""}
68
+ mock_jira.return_value = client
69
+
70
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
71
+
72
+ with pytest.raises(JiraAdapterError, match="does not have a JQL query"):
73
+ adapter.search_issues_by_filter(10000)
74
+
75
+
76
+ @patch("vds_jira_orchestrator.adapter.Jira")
77
+ def test_search_issues_advanced_handles_error(mock_jira: MagicMock) -> None:
78
+ """Test that search_issues_advanced handles errors."""
79
+ client = MagicMock()
80
+ client.jql.side_effect = Exception("401 unauthorized")
81
+ mock_jira.return_value = client
82
+
83
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
84
+
85
+ with pytest.raises(JiraAuthError):
86
+ adapter.search_issues_advanced("project = TEST")
87
+
88
+
89
+ # ---------------------------------------------------------------------------
90
+ # CLI command tests
91
+ # ---------------------------------------------------------------------------
92
+
93
+
94
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
95
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
96
+ def test_cli_search_advanced_flag(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
97
+ """Test search command with --advanced flag."""
98
+ mock_adapter = MagicMock()
99
+ mock_adapter.search_issues_advanced.return_value = {"issues": [{"key": "TEST-1"}], "total": 1}
100
+ mock_adapter_class.return_value = mock_adapter
101
+
102
+ result = runner.invoke(app, ["search", "project = TEST", "--advanced", "--start", "10", "--limit", "25"])
103
+
104
+ assert result.exit_code == 0
105
+ mock_adapter.search_issues_advanced.assert_called_once()
106
+
107
+
108
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
109
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
110
+ def test_cli_search_with_start_uses_advanced(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
111
+ """Test search command with --start automatically uses advanced search."""
112
+ mock_adapter = MagicMock()
113
+ mock_adapter.search_issues_advanced.return_value = {"issues": [], "total": 0}
114
+ mock_adapter_class.return_value = mock_adapter
115
+
116
+ result = runner.invoke(app, ["search", "project = TEST", "--start", "5"])
117
+
118
+ assert result.exit_code == 0
119
+ mock_adapter.search_issues_advanced.assert_called_once()
120
+ mock_adapter.jql.assert_not_called()
121
+
122
+
123
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
124
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
125
+ def test_cli_search_by_filter_success(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
126
+ """Test search-by-filter command success."""
127
+ mock_adapter = MagicMock()
128
+ mock_adapter.search_issues_by_filter.return_value = {"issues": [{"key": "TEST-1"}], "total": 1}
129
+ mock_adapter_class.return_value = mock_adapter
130
+
131
+ result = runner.invoke(app, ["search-by-filter", "10000", "--limit", "50"])
132
+
133
+ assert result.exit_code == 0
134
+ assert "TEST-1" in result.stdout
135
+ mock_adapter.search_issues_by_filter.assert_called_once()
136
+
137
+
138
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
139
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
140
+ def test_cli_search_by_filter_with_pagination(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
141
+ """Test search-by-filter command with pagination."""
142
+ mock_adapter = MagicMock()
143
+ mock_adapter.search_issues_by_filter.return_value = {"issues": [], "total": 0}
144
+ mock_adapter_class.return_value = mock_adapter
145
+
146
+ result = runner.invoke(app, ["search-by-filter", "10000", "--start", "20", "--limit", "25"])
147
+
148
+ assert result.exit_code == 0
149
+ mock_adapter.search_issues_by_filter.assert_called_once_with(10000, start=20, limit=25, fields=None)
150
+
151
+
152
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
153
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
154
+ def test_cli_search_by_filter_handles_error(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
155
+ """Test search-by-filter command error handling."""
156
+ mock_adapter = MagicMock()
157
+ mock_adapter.search_issues_by_filter.side_effect = JiraAdapterError("Filter not found")
158
+ mock_adapter_class.return_value = mock_adapter
159
+
160
+ result = runner.invoke(app, ["search-by-filter", "99999"])
161
+
162
+ assert result.exit_code != 0
163
+ assert "Error" in result.stdout or "Error" in result.stderr
164
+
@@ -0,0 +1,256 @@
1
+ """Tests for JIRA Agile/Scrum operations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from unittest.mock import Mock, patch
6
+
7
+ import pytest
8
+ from typer.testing import CliRunner
9
+ from vds_jira_orchestrator.cli import app
10
+ from vds_jira_orchestrator.errors import JiraAdapterError
11
+
12
+
13
+ @pytest.fixture
14
+ def runner() -> CliRunner:
15
+ """Create a CLI runner for testing."""
16
+ return CliRunner()
17
+
18
+
19
+ @pytest.fixture
20
+ def sample_board() -> dict:
21
+ """Sample agile board response."""
22
+ return {
23
+ "id": 1,
24
+ "name": "Test Board",
25
+ "type": "scrum",
26
+ "location": {"projectId": 10000, "displayName": "Test Project"},
27
+ }
28
+
29
+
30
+ @pytest.fixture
31
+ def sample_epic() -> dict:
32
+ """Sample epic response."""
33
+ return {
34
+ "id": 1,
35
+ "key": "TEST-100",
36
+ "name": "Test Epic",
37
+ "summary": "Test Epic Summary",
38
+ "done": False,
39
+ }
40
+
41
+
42
+ class TestAgileBoardOperations:
43
+ """Test suite for Agile board operations."""
44
+
45
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
46
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
47
+ def test_board_list(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner, sample_board: dict) -> None:
48
+ """Test board list command."""
49
+ mock_adapter = Mock()
50
+ mock_adapter.get_all_agile_boards.return_value = [sample_board]
51
+ mock_adapter_class.return_value = mock_adapter
52
+
53
+ result = runner.invoke(app, ["board", "list", "--limit", "10"])
54
+ assert result.exit_code == 0
55
+ mock_adapter.get_all_agile_boards.assert_called_once_with(
56
+ project_key=None, board_type=None, start=0, limit=10
57
+ )
58
+
59
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
60
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
61
+ def test_board_get_by_id(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner, sample_board: dict) -> None:
62
+ """Test board get by ID command."""
63
+ mock_adapter = Mock()
64
+ mock_adapter.get_agile_board.return_value = sample_board
65
+ mock_adapter_class.return_value = mock_adapter
66
+
67
+ result = runner.invoke(app, ["board", "get", "--board-id", "1"])
68
+ assert result.exit_code == 0
69
+ mock_adapter.get_agile_board.assert_called_once_with(1)
70
+
71
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
72
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
73
+ def test_board_get_by_filter(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner, sample_board: dict) -> None:
74
+ """Test board get by filter ID command."""
75
+ mock_adapter = Mock()
76
+ mock_adapter.get_agile_board_by_filter_id.return_value = sample_board
77
+ mock_adapter_class.return_value = mock_adapter
78
+
79
+ result = runner.invoke(app, ["board", "get", "--filter-id", "100"])
80
+ assert result.exit_code == 0
81
+ mock_adapter.get_agile_board_by_filter_id.assert_called_once_with(100)
82
+
83
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
84
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
85
+ def test_board_create(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner, sample_board: dict) -> None:
86
+ """Test board create command."""
87
+ mock_adapter = Mock()
88
+ mock_adapter.create_agile_board.return_value = sample_board
89
+ mock_adapter_class.return_value = mock_adapter
90
+
91
+ result = runner.invoke(
92
+ app,
93
+ [
94
+ "board",
95
+ "create",
96
+ "--name",
97
+ "Test Board",
98
+ "--type",
99
+ "scrum",
100
+ "--filter-id",
101
+ "100",
102
+ "--yes",
103
+ ],
104
+ )
105
+ assert result.exit_code == 0
106
+ mock_adapter.create_agile_board.assert_called_once_with("Test Board", "scrum", 100)
107
+
108
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
109
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
110
+ def test_board_create_requires_yes(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
111
+ """Test board create requires --yes flag."""
112
+ result = runner.invoke(
113
+ app,
114
+ [
115
+ "board",
116
+ "create",
117
+ "--name",
118
+ "Test Board",
119
+ "--type",
120
+ "scrum",
121
+ "--filter-id",
122
+ "100",
123
+ ],
124
+ )
125
+ assert result.exit_code != 0
126
+ assert "Refusing to create without --yes" in result.stderr
127
+
128
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
129
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
130
+ def test_board_delete(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
131
+ """Test board delete command."""
132
+ mock_adapter = Mock()
133
+ mock_adapter_class.return_value = mock_adapter
134
+
135
+ result = runner.invoke(app, ["board", "delete", "--board-id", "1", "--yes"])
136
+ assert result.exit_code == 0
137
+ mock_adapter.delete_agile_board.assert_called_once_with(1)
138
+
139
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
140
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
141
+ def test_board_config(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
142
+ """Test board config command."""
143
+ mock_adapter = Mock()
144
+ mock_adapter.get_agile_board_configuration.return_value = {"config": "test"}
145
+ mock_adapter_class.return_value = mock_adapter
146
+
147
+ result = runner.invoke(app, ["board", "config", "--board-id", "1"])
148
+ assert result.exit_code == 0
149
+ mock_adapter.get_agile_board_configuration.assert_called_once_with(1)
150
+
151
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
152
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
153
+ def test_board_issues(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
154
+ """Test board issues command."""
155
+ mock_adapter = Mock()
156
+ mock_adapter.get_issues_for_board.return_value = []
157
+ mock_adapter_class.return_value = mock_adapter
158
+
159
+ result = runner.invoke(app, ["board", "issues", "--board-id", "1"])
160
+ assert result.exit_code == 0
161
+ mock_adapter.get_issues_for_board.assert_called_once_with(1)
162
+
163
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
164
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
165
+ def test_board_properties(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
166
+ """Test board properties command."""
167
+ mock_adapter = Mock()
168
+ mock_adapter.get_agile_board_properties.return_value = [{"key": "prop1", "value": "val1"}]
169
+ mock_adapter_class.return_value = mock_adapter
170
+
171
+ result = runner.invoke(app, ["board", "properties", "--board-id", "1"])
172
+ assert result.exit_code == 0
173
+ mock_adapter.get_agile_board_properties.assert_called_once_with(1)
174
+
175
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
176
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
177
+ def test_board_velocity(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
178
+ """Test board velocity command."""
179
+ mock_adapter = Mock()
180
+ mock_adapter.get_agile_board_refined_velocity.return_value = {"velocity": 10}
181
+ mock_adapter_class.return_value = mock_adapter
182
+
183
+ result = runner.invoke(app, ["board", "velocity", "--board-id", "1"])
184
+ assert result.exit_code == 0
185
+ mock_adapter.get_agile_board_refined_velocity.assert_called_once_with(1)
186
+
187
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
188
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
189
+ def test_board_error_handling(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
190
+ """Test board command error handling."""
191
+ mock_adapter = Mock()
192
+ mock_adapter.get_agile_board.side_effect = JiraAdapterError("Test error")
193
+ mock_adapter_class.return_value = mock_adapter
194
+
195
+ result = runner.invoke(app, ["board", "get", "--board-id", "1"])
196
+ assert result.exit_code != 0
197
+
198
+
199
+ class TestEpicOperations:
200
+ """Test suite for Epic operations."""
201
+
202
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
203
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
204
+ def test_epic_issues(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
205
+ """Test epic issues command."""
206
+ mock_adapter = Mock()
207
+ mock_adapter.epic_issues.return_value = []
208
+ mock_adapter_class.return_value = mock_adapter
209
+
210
+ result = runner.invoke(app, ["epic", "issues", "--epic-key", "TEST-100"])
211
+ assert result.exit_code == 0
212
+ mock_adapter.epic_issues.assert_called_once_with("TEST-100")
213
+
214
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
215
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
216
+ def test_epic_list(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner, sample_epic: dict) -> None:
217
+ """Test epic list command."""
218
+ mock_adapter = Mock()
219
+ mock_adapter.get_epics.return_value = [sample_epic]
220
+ mock_adapter_class.return_value = mock_adapter
221
+
222
+ result = runner.invoke(app, ["epic", "list", "--board-id", "1", "--limit", "10"])
223
+ assert result.exit_code == 0
224
+ mock_adapter.get_epics.assert_called_once_with(1, done=False, start=0, limit=10)
225
+
226
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
227
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
228
+ def test_epic_issues_for_epic(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
229
+ """Test epic issues-for-epic command."""
230
+ mock_adapter = Mock()
231
+ mock_adapter.get_issues_for_epic.return_value = []
232
+ mock_adapter_class.return_value = mock_adapter
233
+
234
+ result = runner.invoke(app, ["epic", "issues-for-epic", "--board-id", "1", "--epic-id", "100", "--limit", "10"])
235
+ assert result.exit_code == 0
236
+ mock_adapter.get_issues_for_epic.assert_called_once_with(1, 100, jql="", validate_query="", fields="*all", expand="", start=0, limit=10)
237
+
238
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
239
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
240
+ def test_epic_requires_epic_key(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
241
+ """Test epic issues requires epic-key."""
242
+ result = runner.invoke(app, ["epic", "issues"])
243
+ assert result.exit_code != 0
244
+ assert "--epic-key required" in result.stderr
245
+
246
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
247
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
248
+ def test_epic_error_handling(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
249
+ """Test epic command error handling."""
250
+ mock_adapter = Mock()
251
+ mock_adapter.epic_issues.side_effect = JiraAdapterError("Test error")
252
+ mock_adapter_class.return_value = mock_adapter
253
+
254
+ result = runner.invoke(app, ["epic", "issues", "--epic-key", "TEST-100"])
255
+ assert result.exit_code != 0
256
+
@@ -0,0 +1,193 @@
1
+ from __future__ import annotations
2
+
3
+ from unittest.mock import MagicMock, Mock, patch
4
+
5
+ import pytest
6
+ from typer.testing import CliRunner
7
+ from vds_jira_orchestrator.adapter import JiraAdapter, JiraAdapterError
8
+ from vds_jira_orchestrator.cli import app
9
+
10
+
11
+ @pytest.fixture
12
+ def mock_adapter() -> JiraAdapter:
13
+ """Fixture for a JiraAdapter."""
14
+ with patch("vds_jira_orchestrator.adapter.Jira") as mock_jira_class:
15
+ mock_client = MagicMock()
16
+ mock_jira_class.return_value = mock_client
17
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
18
+ adapter._client = mock_client
19
+ return adapter
20
+
21
+
22
+ @pytest.fixture
23
+ def runner() -> CliRunner:
24
+ return CliRunner()
25
+
26
+
27
+ @pytest.fixture
28
+ def mock_settings_class() -> MagicMock:
29
+ """Mock JiraSettings for CLI tests."""
30
+ mock_settings = Mock()
31
+ mock_settings.base_url = "https://jira.example.com"
32
+ mock_settings.username = "testuser"
33
+ mock_settings.password = "testpass"
34
+ mock_settings.token = None
35
+ mock_settings.require_basic_or_token = Mock()
36
+ with patch("vds_jira_orchestrator.cli.JiraSettings") as mock_class:
37
+ mock_class.return_value = mock_settings
38
+ yield mock_class
39
+
40
+
41
+ @pytest.fixture
42
+ def mock_adapter_class() -> MagicMock:
43
+ """Mock JiraAdapter for CLI tests."""
44
+ with patch("vds_jira_orchestrator.cli.JiraAdapter") as mock_class:
45
+ yield mock_class
46
+
47
+
48
+ # ---------------------------------------------------------------------------
49
+ # Adapter method tests
50
+ # ---------------------------------------------------------------------------
51
+
52
+
53
+ def test_get_property_calls_client(mock_adapter: JiraAdapter) -> None:
54
+ """Test that get_property calls the SDK client with correct parameters."""
55
+ mock_adapter._client.get_property.return_value = {"key": "test.key", "value": "test_value"}
56
+ result = mock_adapter.get_property(key="test.key")
57
+ mock_adapter._client.get_property.assert_called_once_with(key="test.key", permission_level=None, key_filter=None)
58
+ assert result["key"] == "test.key"
59
+
60
+
61
+ def test_get_property_with_filters(mock_adapter: JiraAdapter) -> None:
62
+ """Test get_property with permission level and key filter."""
63
+ mock_adapter._client.get_property.return_value = {"key": "test.key", "value": "test_value"}
64
+ result = mock_adapter.get_property(key="test.key", permission_level="SYSTEM", key_filter="test.*")
65
+ mock_adapter._client.get_property.assert_called_once_with(key="test.key", permission_level="SYSTEM", key_filter="test.*")
66
+ assert result["key"] == "test.key"
67
+
68
+
69
+ def test_get_property_handles_error(mock_adapter: JiraAdapter) -> None:
70
+ """Test that get_property handles errors from the SDK client."""
71
+ mock_adapter._client.get_property.side_effect = Exception("API Error")
72
+ with pytest.raises(JiraAdapterError, match="API Error"):
73
+ mock_adapter.get_property(key="test.key")
74
+
75
+
76
+ def test_set_property_calls_client(mock_adapter: JiraAdapter) -> None:
77
+ """Test that set_property calls the SDK client."""
78
+ mock_adapter._client.set_property.return_value = None
79
+ mock_adapter.set_property("prop.id", "new_value")
80
+ mock_adapter._client.set_property.assert_called_once_with("prop.id", "new_value")
81
+
82
+
83
+ def test_set_property_handles_error(mock_adapter: JiraAdapter) -> None:
84
+ """Test that set_property handles errors from the SDK client."""
85
+ mock_adapter._client.set_property.side_effect = Exception("API Error")
86
+ with pytest.raises(JiraAdapterError, match="API Error"):
87
+ mock_adapter.set_property("prop.id", "value")
88
+
89
+
90
+ def test_get_advanced_settings_calls_client(mock_adapter: JiraAdapter) -> None:
91
+ """Test that get_advanced_settings calls the SDK client."""
92
+ mock_adapter._client.get_advanced_settings.return_value = {"setting1": "value1", "setting2": "value2"}
93
+ result = mock_adapter.get_advanced_settings()
94
+ mock_adapter._client.get_advanced_settings.assert_called_once()
95
+ assert "setting1" in result
96
+
97
+
98
+ def test_get_advanced_settings_handles_error(mock_adapter: JiraAdapter) -> None:
99
+ """Test that get_advanced_settings handles errors from the SDK client."""
100
+ mock_adapter._client.get_advanced_settings.side_effect = Exception("API Error")
101
+ with pytest.raises(JiraAdapterError, match="API Error"):
102
+ mock_adapter.get_advanced_settings()
103
+
104
+
105
+ # ---------------------------------------------------------------------------
106
+ # CLI command tests
107
+ # ---------------------------------------------------------------------------
108
+
109
+
110
+ def test_property_get_success(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
111
+ """Test successful property get."""
112
+ mock_adapter = MagicMock()
113
+ mock_adapter_class.return_value = mock_adapter
114
+ mock_adapter.get_property.return_value = {"key": "test.key", "value": "test_value"}
115
+
116
+ result = runner.invoke(app, ["property", "get", "--key", "test.key"])
117
+ assert result.exit_code == 0
118
+ assert "test.key" in result.stdout
119
+ mock_adapter.get_property.assert_called_once_with(key="test.key", permission_level=None, key_filter=None)
120
+
121
+
122
+ def test_property_get_with_filters(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
123
+ """Test property get with filters."""
124
+ mock_adapter = MagicMock()
125
+ mock_adapter_class.return_value = mock_adapter
126
+ mock_adapter.get_property.return_value = {"key": "test.key", "value": "test_value"}
127
+
128
+ result = runner.invoke(app, ["property", "get", "--key", "test.key", "--permission-level", "SYSTEM", "--key-filter", "test.*"])
129
+ assert result.exit_code == 0
130
+ mock_adapter.get_property.assert_called_once_with(key="test.key", permission_level="SYSTEM", key_filter="test.*")
131
+
132
+
133
+ def test_property_set_success(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
134
+ """Test successful property set."""
135
+ mock_adapter = MagicMock()
136
+ mock_adapter_class.return_value = mock_adapter
137
+ mock_adapter.set_property.return_value = None
138
+
139
+ result = runner.invoke(app, ["property", "set", "--property-id", "prop.id", "--value", "new_value", "--yes"])
140
+ assert result.exit_code == 0
141
+ assert "set" in result.stdout
142
+ mock_adapter.set_property.assert_called_once_with("prop.id", "new_value")
143
+
144
+
145
+ def test_property_set_requires_yes(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
146
+ """Test property set requires --yes flag."""
147
+ result = runner.invoke(app, ["property", "set", "--property-id", "prop.id", "--value", "new_value"])
148
+ assert result.exit_code != 0
149
+ assert "Refusing to set property without --yes" in result.stderr
150
+ mock_adapter_class.return_value.set_property.assert_not_called()
151
+
152
+
153
+ def test_property_set_requires_property_id(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
154
+ """Test property set requires --property-id."""
155
+ result = runner.invoke(app, ["property", "set", "--value", "new_value", "--yes"])
156
+ assert result.exit_code != 0
157
+ assert "--property-id required for set" in result.stderr
158
+ mock_adapter_class.return_value.set_property.assert_not_called()
159
+
160
+
161
+ def test_property_set_requires_value(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
162
+ """Test property set requires --value."""
163
+ result = runner.invoke(app, ["property", "set", "--property-id", "prop.id", "--yes"])
164
+ assert result.exit_code != 0
165
+ assert "--value required for set" in result.stderr
166
+ mock_adapter_class.return_value.set_property.assert_not_called()
167
+
168
+
169
+ def test_settings_advanced_success(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
170
+ """Test successful advanced settings get."""
171
+ mock_adapter = MagicMock()
172
+ mock_adapter_class.return_value = mock_adapter
173
+ mock_adapter.get_advanced_settings.return_value = {"setting1": "value1", "setting2": "value2"}
174
+
175
+ result = runner.invoke(app, ["settings", "advanced"])
176
+ assert result.exit_code == 0
177
+ assert "setting1" in result.stdout
178
+ mock_adapter.get_advanced_settings.assert_called_once()
179
+
180
+
181
+ def test_property_invalid_action(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
182
+ """Test property command with invalid action."""
183
+ result = runner.invoke(app, ["property", "invalid"])
184
+ assert result.exit_code != 0
185
+ assert "Unknown action: invalid" in result.stderr
186
+
187
+
188
+ def test_settings_invalid_action(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
189
+ """Test settings command with invalid action."""
190
+ result = runner.invoke(app, ["settings", "invalid"])
191
+ assert result.exit_code != 0
192
+ assert "Unknown action: invalid" in result.stderr
193
+