@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,333 @@
1
+ """Tests for JIRA filter management."""
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, JiraNotFound
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_get_filters_calls_client(mock_jira: MagicMock) -> None:
22
+ """Test that get_filters calls the SDK client."""
23
+ client = MagicMock()
24
+ client.resource_url.return_value = "https://jira.example.com/rest/api/2/filter"
25
+ client.get.return_value = {"values": [{"id": 10000, "name": "Filter 1"}, {"id": 10001, "name": "Filter 2"}]}
26
+ mock_jira.return_value = client
27
+
28
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
29
+ result = adapter.get_filters()
30
+
31
+ assert len(result) == 2
32
+ assert result[0]["id"] == 10000
33
+
34
+
35
+ @patch("vds_jira_orchestrator.adapter.Jira")
36
+ def test_get_filters_with_owner(mock_jira: MagicMock) -> None:
37
+ """Test that get_filters filters by owner."""
38
+ client = MagicMock()
39
+ client.resource_url.return_value = "https://jira.example.com/rest/api/2/filter"
40
+ client.get.return_value = {"values": [{"id": 10000, "name": "My Filter", "owner": {"name": "user"}}]}
41
+ mock_jira.return_value = client
42
+
43
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
44
+ adapter.get_filters(owner="user")
45
+
46
+ client.get.assert_called_once()
47
+ assert "username" in str(client.get.call_args)
48
+
49
+
50
+ @patch("vds_jira_orchestrator.adapter.Jira")
51
+ def test_get_filter_calls_client(mock_jira: MagicMock) -> None:
52
+ """Test that get_filter calls the SDK client."""
53
+ client = MagicMock()
54
+ client.get_filter.return_value = {"id": 10000, "name": "Test Filter", "jql": "project = TEST"}
55
+ mock_jira.return_value = client
56
+
57
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
58
+ result = adapter.get_filter(10000)
59
+
60
+ client.get_filter.assert_called_once_with(10000)
61
+ assert result["id"] == 10000
62
+
63
+
64
+ @patch("vds_jira_orchestrator.adapter.Jira")
65
+ def test_get_filter_not_found(mock_jira: MagicMock) -> None:
66
+ """Test that get_filter raises JiraNotFound when filter is not found."""
67
+ client = MagicMock()
68
+ client.get_filter.return_value = None
69
+ mock_jira.return_value = client
70
+
71
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
72
+
73
+ with pytest.raises(JiraNotFound, match="Filter not found: 99999"):
74
+ adapter.get_filter(99999)
75
+
76
+
77
+ @patch("vds_jira_orchestrator.adapter.Jira")
78
+ def test_create_filter_calls_client(mock_jira: MagicMock) -> None:
79
+ """Test that create_filter calls the SDK client."""
80
+ client = MagicMock()
81
+ client.create_filter.return_value = {"id": 10000, "name": "New Filter", "jql": "project = TEST"}
82
+ mock_jira.return_value = client
83
+
84
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
85
+ result = adapter.create_filter(name="New Filter", jql="project = TEST", description="Test filter")
86
+
87
+ client.create_filter.assert_called_once_with(name="New Filter", jql="project = TEST", description="Test filter", favourite=False)
88
+ assert result["id"] == 10000
89
+
90
+
91
+ @patch("vds_jira_orchestrator.adapter.Jira")
92
+ def test_create_filter_with_favourite(mock_jira: MagicMock) -> None:
93
+ """Test that create_filter can mark filter as favourite."""
94
+ client = MagicMock()
95
+ client.create_filter.return_value = {"id": 10000, "name": "Favourite Filter", "favourite": True}
96
+ mock_jira.return_value = client
97
+
98
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
99
+ result = adapter.create_filter(name="Favourite Filter", jql="project = TEST", favourite=True)
100
+
101
+ client.create_filter.assert_called_once_with(name="Favourite Filter", jql="project = TEST", description=None, favourite=True)
102
+ assert result["favourite"] is True
103
+
104
+
105
+ @patch("vds_jira_orchestrator.adapter.Jira")
106
+ def test_update_filter_calls_client(mock_jira: MagicMock) -> None:
107
+ """Test that update_filter calls the SDK client."""
108
+ client = MagicMock()
109
+ client.get_filter.return_value = {"id": 10000, "name": "Old Name", "jql": "project = TEST"}
110
+ client.update_filter.return_value = {"id": 10000, "name": "New Name", "jql": "project = TEST"}
111
+ mock_jira.return_value = client
112
+
113
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
114
+ result = adapter.update_filter(filter_id=10000, name="New Name")
115
+
116
+ client.update_filter.assert_called_once()
117
+ assert result["name"] == "New Name"
118
+
119
+
120
+ @patch("vds_jira_orchestrator.adapter.Jira")
121
+ def test_update_filter_with_jql(mock_jira: MagicMock) -> None:
122
+ """Test that update_filter can update JQL."""
123
+ client = MagicMock()
124
+ client.update_filter.return_value = {"id": 10000, "name": "Filter", "jql": "project = NEW"}
125
+ mock_jira.return_value = client
126
+
127
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
128
+ result = adapter.update_filter(filter_id=10000, jql="project = NEW")
129
+
130
+ client.update_filter.assert_called_once()
131
+ assert result["jql"] == "project = NEW"
132
+
133
+
134
+ @patch("vds_jira_orchestrator.adapter.Jira")
135
+ def test_delete_filter_calls_client(mock_jira: MagicMock) -> None:
136
+ """Test that delete_filter calls the SDK client."""
137
+ client = MagicMock()
138
+ client.delete_filter.return_value = None
139
+ mock_jira.return_value = client
140
+
141
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
142
+ adapter.delete_filter(10000)
143
+
144
+ client.delete_filter.assert_called_once_with(10000)
145
+
146
+
147
+ @patch("vds_jira_orchestrator.adapter.Jira")
148
+ def test_get_filters_handles_list_response(mock_jira: MagicMock) -> None:
149
+ """Test that get_filters handles list response format."""
150
+ client = MagicMock()
151
+ client.resource_url.return_value = "https://jira.example.com/rest/api/2/filter"
152
+ client.get.return_value = [{"id": 10000, "name": "Filter 1"}]
153
+ mock_jira.return_value = client
154
+
155
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
156
+ result = adapter.get_filters()
157
+
158
+ assert len(result) == 1
159
+ assert result[0]["id"] == 10000
160
+
161
+
162
+ # ---------------------------------------------------------------------------
163
+ # CLI command tests
164
+ # ---------------------------------------------------------------------------
165
+
166
+
167
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
168
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
169
+ def test_cli_filter_list_success(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
170
+ """Test filter list CLI command success."""
171
+ mock_adapter = MagicMock()
172
+ mock_adapter.get_filters.return_value = [{"id": 10000, "name": "Filter 1"}]
173
+ mock_adapter_class.return_value = mock_adapter
174
+
175
+ result = runner.invoke(app, ["filter", "list"])
176
+
177
+ assert result.exit_code == 0
178
+ assert "filters" in result.stdout
179
+ mock_adapter.get_filters.assert_called_once_with(owner=None)
180
+
181
+
182
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
183
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
184
+ def test_cli_filter_list_with_owner(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
185
+ """Test filter list CLI command with owner filter."""
186
+ mock_adapter = MagicMock()
187
+ mock_adapter.get_filters.return_value = [{"id": 10000, "name": "My Filter"}]
188
+ mock_adapter_class.return_value = mock_adapter
189
+
190
+ result = runner.invoke(app, ["filter", "list", "--owner", "user"])
191
+
192
+ assert result.exit_code == 0
193
+ mock_adapter.get_filters.assert_called_once_with(owner="user")
194
+
195
+
196
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
197
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
198
+ def test_cli_filter_get_success(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
199
+ """Test filter get CLI command success."""
200
+ mock_adapter = MagicMock()
201
+ mock_adapter.get_filter.return_value = {"id": 10000, "name": "Test Filter", "jql": "project = TEST"}
202
+ mock_adapter_class.return_value = mock_adapter
203
+
204
+ result = runner.invoke(app, ["filter", "get", "--filter-id", "10000"])
205
+
206
+ assert result.exit_code == 0
207
+ assert "10000" in result.stdout
208
+ mock_adapter.get_filter.assert_called_once_with(10000)
209
+
210
+
211
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
212
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
213
+ def test_cli_filter_get_requires_id(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
214
+ """Test filter get CLI command requires filter ID."""
215
+ result = runner.invoke(app, ["filter", "get"])
216
+
217
+ assert result.exit_code != 0
218
+ assert "--filter-id required" in result.stdout or "--filter-id required" in result.stderr
219
+
220
+
221
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
222
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
223
+ def test_cli_filter_create_success(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
224
+ """Test filter create CLI command success."""
225
+ mock_adapter = MagicMock()
226
+ mock_adapter.create_filter.return_value = {"id": 10000, "name": "New Filter", "jql": "project = TEST"}
227
+ mock_adapter_class.return_value = mock_adapter
228
+
229
+ result = runner.invoke(app, ["filter", "create", "--name", "New Filter", "--jql", "project = TEST", "--yes"])
230
+
231
+ assert result.exit_code == 0
232
+ mock_adapter.create_filter.assert_called_once()
233
+
234
+
235
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
236
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
237
+ def test_cli_filter_create_requires_yes(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
238
+ """Test filter create CLI command requires --yes flag."""
239
+ result = runner.invoke(app, ["filter", "create", "--name", "New Filter", "--jql", "project = TEST"])
240
+
241
+ assert result.exit_code != 0
242
+ assert "--yes" in result.stdout or "--yes" in result.stderr or "without --yes" in result.stdout or "without --yes" in result.stderr or "Refusing" in result.stdout or "Refusing" in result.stderr
243
+
244
+
245
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
246
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
247
+ def test_cli_filter_create_requires_name(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
248
+ """Test filter create CLI command requires name."""
249
+ result = runner.invoke(app, ["filter", "create", "--jql", "project = TEST", "--yes"])
250
+
251
+ assert result.exit_code != 0
252
+ assert "--name required" in result.stdout or "--name required" in result.stderr
253
+
254
+
255
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
256
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
257
+ def test_cli_filter_create_requires_jql(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
258
+ """Test filter create CLI command requires JQL."""
259
+ result = runner.invoke(app, ["filter", "create", "--name", "New Filter", "--yes"])
260
+
261
+ assert result.exit_code != 0
262
+ assert "--jql required" in result.stdout or "--jql required" in result.stderr
263
+
264
+
265
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
266
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
267
+ def test_cli_filter_update_success(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
268
+ """Test filter update CLI command success."""
269
+ mock_adapter = MagicMock()
270
+ mock_adapter.get_filter.return_value = {"id": 10000, "name": "Old Name", "jql": "project = TEST"}
271
+ mock_adapter.update_filter.return_value = {"id": 10000, "name": "New Name", "jql": "project = TEST"}
272
+ mock_adapter_class.return_value = mock_adapter
273
+
274
+ result = runner.invoke(app, ["filter", "update", "--filter-id", "10000", "--name", "New Name", "--yes"])
275
+
276
+ assert result.exit_code == 0
277
+ mock_adapter.update_filter.assert_called_once()
278
+
279
+
280
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
281
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
282
+ def test_cli_filter_update_requires_yes(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
283
+ """Test filter update CLI command requires --yes flag."""
284
+ result = runner.invoke(app, ["filter", "update", "--filter-id", "10000", "--name", "New Name"])
285
+
286
+ assert result.exit_code != 0
287
+ assert "--yes" in result.stdout or "--yes" in result.stderr or "without --yes" in result.stdout or "without --yes" in result.stderr or "Refusing" in result.stdout or "Refusing" in result.stderr
288
+
289
+
290
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
291
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
292
+ def test_cli_filter_update_requires_field(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
293
+ """Test filter update CLI command requires at least one field."""
294
+ result = runner.invoke(app, ["filter", "update", "--filter-id", "10000", "--yes"])
295
+
296
+ assert result.exit_code != 0
297
+ assert "At least one" in result.stdout or "At least one" in result.stderr
298
+
299
+
300
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
301
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
302
+ def test_cli_filter_delete_success(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
303
+ """Test filter delete CLI command success."""
304
+ mock_adapter = MagicMock()
305
+ mock_adapter.delete_filter.return_value = None
306
+ mock_adapter_class.return_value = mock_adapter
307
+
308
+ result = runner.invoke(app, ["filter", "delete", "--filter-id", "10000", "--yes"])
309
+
310
+ assert result.exit_code == 0
311
+ assert "success" in result.stdout
312
+ mock_adapter.delete_filter.assert_called_once_with(10000)
313
+
314
+
315
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
316
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
317
+ def test_cli_filter_delete_requires_yes(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
318
+ """Test filter delete CLI command requires --yes flag."""
319
+ result = runner.invoke(app, ["filter", "delete", "--filter-id", "10000"])
320
+
321
+ assert result.exit_code != 0
322
+ assert "--yes" in result.stdout or "--yes" in result.stderr or "without --yes" in result.stdout or "without --yes" in result.stderr or "Refusing" in result.stdout or "Refusing" in result.stderr
323
+
324
+
325
+ @patch("vds_jira_orchestrator.cli.JiraAdapter")
326
+ @patch("vds_jira_orchestrator.cli.JiraSettings")
327
+ def test_cli_filter_invalid_action(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
328
+ """Test filter CLI command with invalid action."""
329
+ result = runner.invoke(app, ["filter", "invalid"])
330
+
331
+ assert result.exit_code != 0
332
+ assert "Unknown action" in result.stdout or "Unknown action" in result.stderr
333
+
@@ -0,0 +1,164 @@
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_issue_archive_calls_client(mock_adapter: JiraAdapter) -> None:
54
+ """Test that issue_archive calls the SDK client."""
55
+ mock_adapter._client.issue_archive.return_value = None
56
+ mock_adapter.issue_archive("PROJ-1")
57
+ mock_adapter._client.issue_archive.assert_called_once_with("PROJ-1")
58
+
59
+
60
+ def test_issue_archive_handles_error(mock_adapter: JiraAdapter) -> None:
61
+ """Test that issue_archive handles errors from the SDK client."""
62
+ mock_adapter._client.issue_archive.side_effect = Exception("API Error")
63
+ with pytest.raises(JiraAdapterError, match="API Error"):
64
+ mock_adapter.issue_archive("PROJ-1")
65
+
66
+
67
+ def test_issue_restore_calls_client(mock_adapter: JiraAdapter) -> None:
68
+ """Test that issue_restore calls the SDK client."""
69
+ mock_adapter._client.issue_restore.return_value = None
70
+ mock_adapter.issue_restore("PROJ-1")
71
+ mock_adapter._client.issue_restore.assert_called_once_with("PROJ-1")
72
+
73
+
74
+ def test_issue_restore_handles_error(mock_adapter: JiraAdapter) -> None:
75
+ """Test that issue_restore handles errors from the SDK client."""
76
+ mock_adapter._client.issue_restore.side_effect = Exception("API Error")
77
+ with pytest.raises(JiraAdapterError, match="API Error"):
78
+ mock_adapter.issue_restore("PROJ-1")
79
+
80
+
81
+ # ---------------------------------------------------------------------------
82
+ # CLI command tests
83
+ # ---------------------------------------------------------------------------
84
+
85
+
86
+ def test_issue_archive_success(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
87
+ """Test successful issue archive."""
88
+ mock_adapter = MagicMock()
89
+ mock_adapter_class.return_value = mock_adapter
90
+ mock_adapter.issue_archive.return_value = None
91
+
92
+ result = runner.invoke(app, ["issue-archive", "--issue-key", "PROJ-1", "--yes"])
93
+ assert result.exit_code == 0
94
+ assert "archived" in result.stdout
95
+ assert "PROJ-1" in result.stdout
96
+ mock_adapter.issue_archive.assert_called_once_with("PROJ-1")
97
+
98
+
99
+ def test_issue_archive_requires_yes(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
100
+ """Test issue archive requires --yes flag."""
101
+ result = runner.invoke(app, ["issue-archive", "--issue-key", "PROJ-1"])
102
+ assert result.exit_code != 0
103
+ assert "Refusing to archive issue without --yes" in result.stderr
104
+ mock_adapter_class.return_value.issue_archive.assert_not_called()
105
+
106
+
107
+ def test_issue_archive_requires_issue_key(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
108
+ """Test issue archive requires --issue-key."""
109
+ result = runner.invoke(app, ["issue-archive", "--yes"])
110
+ assert result.exit_code != 0
111
+ # Typer will show error about missing required option
112
+ mock_adapter_class.return_value.issue_archive.assert_not_called()
113
+
114
+
115
+ def test_issue_restore_success(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
116
+ """Test successful issue restore."""
117
+ mock_adapter = MagicMock()
118
+ mock_adapter_class.return_value = mock_adapter
119
+ mock_adapter.issue_restore.return_value = None
120
+
121
+ result = runner.invoke(app, ["issue-restore", "--issue-key", "PROJ-1", "--yes"])
122
+ assert result.exit_code == 0
123
+ assert "restored" in result.stdout
124
+ assert "PROJ-1" in result.stdout
125
+ mock_adapter.issue_restore.assert_called_once_with("PROJ-1")
126
+
127
+
128
+ def test_issue_restore_requires_yes(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
129
+ """Test issue restore requires --yes flag."""
130
+ result = runner.invoke(app, ["issue-restore", "--issue-key", "PROJ-1"])
131
+ assert result.exit_code != 0
132
+ assert "Refusing to restore issue without --yes" in result.stderr
133
+ mock_adapter_class.return_value.issue_restore.assert_not_called()
134
+
135
+
136
+ def test_issue_restore_requires_issue_key(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
137
+ """Test issue restore requires --issue-key."""
138
+ result = runner.invoke(app, ["issue-restore", "--yes"])
139
+ assert result.exit_code != 0
140
+ # Typer will show error about missing required option
141
+ mock_adapter_class.return_value.issue_restore.assert_not_called()
142
+
143
+
144
+ def test_issue_archive_cli_handles_error(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
145
+ """Test issue archive CLI handles adapter errors."""
146
+ mock_adapter = MagicMock()
147
+ mock_adapter_class.return_value = mock_adapter
148
+ mock_adapter.issue_archive.side_effect = JiraAdapterError("Issue not found")
149
+
150
+ result = runner.invoke(app, ["issue-archive", "--issue-key", "PROJ-999", "--yes"])
151
+ assert result.exit_code != 0
152
+ assert "Issue not found" in result.stderr
153
+
154
+
155
+ def test_issue_restore_cli_handles_error(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
156
+ """Test issue restore CLI handles adapter errors."""
157
+ mock_adapter = MagicMock()
158
+ mock_adapter_class.return_value = mock_adapter
159
+ mock_adapter.issue_restore.side_effect = JiraAdapterError("Issue not found")
160
+
161
+ result = runner.invoke(app, ["issue-restore", "--issue-key", "PROJ-999", "--yes"])
162
+ assert result.exit_code != 0
163
+ assert "Issue not found" in result.stderr
164
+