@ngocsangairvds/vsaf 3.1.27 → 3.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (303) hide show
  1. package/package.json +2 -2
  2. package/src/global.js +65 -39
  3. package/tools/skills/vds-scripts-skill/.openskills.json +6 -0
  4. package/tools/skills/vds-scripts-skill/QUALITY.md +44 -0
  5. package/tools/skills/vds-scripts-skill/SKILL.md +135 -0
  6. package/tools/skills/vds-scripts-skill/references/audit-commands.md +171 -0
  7. package/tools/skills/vds-scripts-skill/references/capability-index.md +34 -0
  8. package/tools/skills/vds-scripts-skill/references/development-commands.md +12 -0
  9. package/tools/skills/vds-scripts-skill/references/google-sheets.md +73 -0
  10. package/tools/skills/vds-scripts-skill/references/integration-commands.md +17 -0
  11. package/tools/skills/vds-scripts-skill/references/platform-bootstrap.md +31 -0
  12. package/tools/skills/vds-scripts-skill/references/specialist-routing.md +14 -0
  13. package/tools/skills/vds-scripts-skill/references/validation-commands.md +15 -0
  14. package/tools/skills/vsaf-build/SKILL.md +32 -2
  15. package/tools/skills/vsaf-ship/SKILL.md +41 -10
  16. package/tools/skills/vsaf-test/SKILL.md +8 -0
  17. package/tools/vds-scripts/.mcp.json +11 -0
  18. package/tools/vds-scripts/.secrets.baseline +133 -0
  19. package/tools/vds-scripts/AGENTS.md +152 -0
  20. package/tools/vds-scripts/CLAUDE.md +101 -0
  21. package/tools/vds-scripts/CLI_COMMAND_OPTIMIZATION.md +156 -0
  22. package/tools/vds-scripts/PACKAGE_P125B_IMPLEMENTATION_SUMMARY.md +131 -0
  23. package/tools/vds-scripts/PROJECT_COMPLETION_SUMMARY.md +45 -0
  24. package/tools/vds-scripts/README.md +97 -0
  25. package/tools/vds-scripts/bitbucket_manifest_mapping.toml +34 -0
  26. package/tools/vds-scripts/bitbucket_orchestrator/ARCHITECTURE_ANALYSIS.md +258 -0
  27. package/tools/vds-scripts/bitbucket_orchestrator/BITBUCKET_API_PRACTICES.md +393 -0
  28. package/tools/vds-scripts/bitbucket_orchestrator/EVALUATION_REPORT.md +61 -0
  29. package/tools/vds-scripts/bitbucket_orchestrator/FEATURES.md +908 -0
  30. package/tools/vds-scripts/bitbucket_orchestrator/README.md +687 -0
  31. package/tools/vds-scripts/bitbucket_orchestrator/pyproject.toml +40 -0
  32. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/__init__.py +20 -0
  33. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/async_client.py +657 -0
  34. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/cli.py +2108 -0
  35. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/client.py +2534 -0
  36. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/config.py +171 -0
  37. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/errors.py +67 -0
  38. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/factory.py +185 -0
  39. package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/protocols.py +244 -0
  40. package/tools/vds-scripts/bitbucket_orchestrator/tests/__init__.py +8 -0
  41. package/tools/vds-scripts/bitbucket_orchestrator/tests/conftest.py +65 -0
  42. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_advanced_search.py +151 -0
  43. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_async_client.py +546 -0
  44. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_branch_permissions.py +145 -0
  45. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_cli.py +115 -0
  46. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client.py +157 -0
  47. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_branch_conditions.py +79 -0
  48. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_code_advanced.py +163 -0
  49. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_code_file.py +32 -0
  50. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_deployment_environments.py +194 -0
  51. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_issues.py +164 -0
  52. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_pipelines_advanced.py +179 -0
  53. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_pr_blockers.py +119 -0
  54. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_repository_variables.py +156 -0
  55. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code.py +98 -0
  56. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code_advanced.py +282 -0
  57. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code_insights.py +335 -0
  58. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_conditions.py +147 -0
  59. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_config.py +131 -0
  60. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_deployment_env.py +352 -0
  61. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_factory.py +371 -0
  62. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_fork_operations.py +204 -0
  63. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_issue_cli.py +261 -0
  64. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_pipeline_advanced.py +270 -0
  65. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_pr_blocker.py +204 -0
  66. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_protocols.py +334 -0
  67. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_repo_settings.py +343 -0
  68. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_repo_variables.py +270 -0
  69. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_webhooks.py +189 -0
  70. package/tools/vds-scripts/bitbucket_orchestrator/tests/test_workspace.py +233 -0
  71. package/tools/vds-scripts/bitbucket_orchestrator/uv.lock +742 -0
  72. package/tools/vds-scripts/confluence_orchestrator/Dockerfile +19 -0
  73. package/tools/vds-scripts/confluence_orchestrator/README.md +412 -0
  74. package/tools/vds-scripts/confluence_orchestrator/SYNC_SCRIPTS.md +127 -0
  75. package/tools/vds-scripts/confluence_orchestrator/SYNC_STANDARDIZATION.md +108 -0
  76. package/tools/vds-scripts/confluence_orchestrator/pyproject.toml +48 -0
  77. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/__init__.py +20 -0
  78. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/cli.py +2532 -0
  79. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/config.py +175 -0
  80. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/content.py +290 -0
  81. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/content_v2.py +94 -0
  82. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/crawl_tree.py +1835 -0
  83. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/errors.py +80 -0
  84. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/eventing.py +109 -0
  85. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/http.py +1114 -0
  86. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/orchestration.py +165 -0
  87. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/reporting.py +78 -0
  88. package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/tree.py +121 -0
  89. package/tools/vds-scripts/confluence_orchestrator/sync_pdfs_from_markdown.py +213 -0
  90. package/tools/vds-scripts/confluence_orchestrator/sync_pdfs_to_confluence.py +305 -0
  91. package/tools/vds-scripts/confluence_orchestrator/sync_png_attachments.py +305 -0
  92. package/tools/vds-scripts/confluence_orchestrator/tests/__init__.py +0 -0
  93. package/tools/vds-scripts/confluence_orchestrator/tests/conftest.py +8 -0
  94. package/tools/vds-scripts/confluence_orchestrator/tests/test_advanced_content.py +224 -0
  95. package/tools/vds-scripts/confluence_orchestrator/tests/test_advanced_search.py +188 -0
  96. package/tools/vds-scripts/confluence_orchestrator/tests/test_cache_management.py +247 -0
  97. package/tools/vds-scripts/confluence_orchestrator/tests/test_cli.py +499 -0
  98. package/tools/vds-scripts/confluence_orchestrator/tests/test_config.py +83 -0
  99. package/tools/vds-scripts/confluence_orchestrator/tests/test_content.py +186 -0
  100. package/tools/vds-scripts/confluence_orchestrator/tests/test_content_flags.py +27 -0
  101. package/tools/vds-scripts/confluence_orchestrator/tests/test_crawl_tree.py +2250 -0
  102. package/tools/vds-scripts/confluence_orchestrator/tests/test_draft_management.py +223 -0
  103. package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing.py +71 -0
  104. package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_chaos.py +37 -0
  105. package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_rate_limit.py +44 -0
  106. package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_timeout.py +49 -0
  107. package/tools/vds-scripts/confluence_orchestrator/tests/test_export.py +230 -0
  108. package/tools/vds-scripts/confluence_orchestrator/tests/test_history.py +204 -0
  109. package/tools/vds-scripts/confluence_orchestrator/tests/test_http.py +117 -0
  110. package/tools/vds-scripts/confluence_orchestrator/tests/test_orchestration.py +91 -0
  111. package/tools/vds-scripts/confluence_orchestrator/tests/test_reporting.py +24 -0
  112. package/tools/vds-scripts/confluence_orchestrator/tests/test_search_cql.py +34 -0
  113. package/tools/vds-scripts/confluence_orchestrator/tests/test_space_management.py +237 -0
  114. package/tools/vds-scripts/confluence_orchestrator/tests/test_space_permissions.py +332 -0
  115. package/tools/vds-scripts/confluence_orchestrator/tests/test_user_group_management.py +388 -0
  116. package/tools/vds-scripts/confluence_orchestrator/uv.lock +1023 -0
  117. package/tools/vds-scripts/git_orchestrator/ENHANCEMENT_SUMMARY.md +119 -0
  118. package/tools/vds-scripts/git_orchestrator/README.md +280 -0
  119. package/tools/vds-scripts/git_orchestrator/VERIFICATION_REPORT.md +152 -0
  120. package/tools/vds-scripts/git_orchestrator/pyproject.toml +35 -0
  121. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/__init__.py +7 -0
  122. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/__main__.py +4 -0
  123. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/cli.py +847 -0
  124. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/logging_config.py +63 -0
  125. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/manifest.py +129 -0
  126. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/orchestrator.py +819 -0
  127. package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/reporting.py +53 -0
  128. package/tools/vds-scripts/git_orchestrator/tests/__init__.py +0 -0
  129. package/tools/vds-scripts/git_orchestrator/tests/test_cli_settings.py +21 -0
  130. package/tools/vds-scripts/git_orchestrator/tests/test_integration.py +74 -0
  131. package/tools/vds-scripts/git_orchestrator/tests/test_manifest.py +79 -0
  132. package/tools/vds-scripts/git_orchestrator/tests/test_orchestrator.py +204 -0
  133. package/tools/vds-scripts/git_orchestrator/tests/test_public_api.py +236 -0
  134. package/tools/vds-scripts/git_orchestrator/tests/test_resilience.py +345 -0
  135. package/tools/vds-scripts/git_orchestrator/uv.lock +271 -0
  136. package/tools/vds-scripts/jira_orchestrator/README.md +770 -0
  137. package/tools/vds-scripts/jira_orchestrator/pyproject.toml +39 -0
  138. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/__init__.py +1 -0
  139. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/adapter.py +1320 -0
  140. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/cli.py +2271 -0
  141. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/config.py +138 -0
  142. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/errors.py +67 -0
  143. package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/reporting.py +65 -0
  144. package/tools/vds-scripts/jira_orchestrator/tests/__init__.py +1 -0
  145. package/tools/vds-scripts/jira_orchestrator/tests/conftest.py +86 -0
  146. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_agile_list_payloads.py +54 -0
  147. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_bulk_operations.py +69 -0
  148. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_components.py +57 -0
  149. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_createmeta.py +45 -0
  150. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_dashboard.py +117 -0
  151. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_issue_properties.py +54 -0
  152. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_permissions_compat.py +42 -0
  153. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_reindex.py +42 -0
  154. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_remote_links.py +76 -0
  155. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_transitions.py +91 -0
  156. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_user_management.py +110 -0
  157. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_version_management.py +133 -0
  158. package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_watchers.py +41 -0
  159. package/tools/vds-scripts/jira_orchestrator/tests/test_advanced_search.py +164 -0
  160. package/tools/vds-scripts/jira_orchestrator/tests/test_agile.py +256 -0
  161. package/tools/vds-scripts/jira_orchestrator/tests/test_application_properties.py +193 -0
  162. package/tools/vds-scripts/jira_orchestrator/tests/test_backlog.py +91 -0
  163. package/tools/vds-scripts/jira_orchestrator/tests/test_bulk_operations.py +277 -0
  164. package/tools/vds-scripts/jira_orchestrator/tests/test_cli.py +106 -0
  165. package/tools/vds-scripts/jira_orchestrator/tests/test_components.py +106 -0
  166. package/tools/vds-scripts/jira_orchestrator/tests/test_config.py +164 -0
  167. package/tools/vds-scripts/jira_orchestrator/tests/test_dashboard.py +122 -0
  168. package/tools/vds-scripts/jira_orchestrator/tests/test_discover_fields.py +207 -0
  169. package/tools/vds-scripts/jira_orchestrator/tests/test_filter_management.py +333 -0
  170. package/tools/vds-scripts/jira_orchestrator/tests/test_issue_archiving.py +164 -0
  171. package/tools/vds-scripts/jira_orchestrator/tests/test_issue_links.py +257 -0
  172. package/tools/vds-scripts/jira_orchestrator/tests/test_issue_properties.py +171 -0
  173. package/tools/vds-scripts/jira_orchestrator/tests/test_link_types.py +314 -0
  174. package/tools/vds-scripts/jira_orchestrator/tests/test_parse_set.py +37 -0
  175. package/tools/vds-scripts/jira_orchestrator/tests/test_permissions.py +273 -0
  176. package/tools/vds-scripts/jira_orchestrator/tests/test_reindex.py +81 -0
  177. package/tools/vds-scripts/jira_orchestrator/tests/test_remote_links.py +254 -0
  178. package/tools/vds-scripts/jira_orchestrator/tests/test_security_schemes.py +170 -0
  179. package/tools/vds-scripts/jira_orchestrator/tests/test_transitions_changelog.py +114 -0
  180. package/tools/vds-scripts/jira_orchestrator/tests/test_user_management.py +226 -0
  181. package/tools/vds-scripts/jira_orchestrator/tests/test_version_management.py +339 -0
  182. package/tools/vds-scripts/jira_orchestrator/tests/test_watchers.py +101 -0
  183. package/tools/vds-scripts/jira_orchestrator/tests/test_worklog.py +223 -0
  184. package/tools/vds-scripts/jira_orchestrator/uv.lock +738 -0
  185. package/tools/vds-scripts/mcp_server/Dockerfile +34 -0
  186. package/tools/vds-scripts/mcp_server/README.md +140 -0
  187. package/tools/vds-scripts/mcp_server/pyproject.toml +42 -0
  188. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/__init__.py +4 -0
  189. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/config.py +36 -0
  190. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/server.py +66 -0
  191. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/__init__.py +14 -0
  192. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/bitbucket_tools.py +47 -0
  193. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/confluence_tools.py +59 -0
  194. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/git_tools.py +71 -0
  195. package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/jira_tools.py +63 -0
  196. package/tools/vds-scripts/mcp_server/tests/__init__.py +2 -0
  197. package/tools/vds-scripts/mcp_server/tests/conftest.py +29 -0
  198. package/tools/vds-scripts/mcp_server/tests/unit/__init__.py +2 -0
  199. package/tools/vds-scripts/mcp_server/tests/unit/test_bitbucket_tools.py +25 -0
  200. package/tools/vds-scripts/mcp_server/tests/unit/test_confluence_tools.py +25 -0
  201. package/tools/vds-scripts/mcp_server/tests/unit/test_git_tools.py +32 -0
  202. package/tools/vds-scripts/mcp_server/tests/unit/test_jira_tools.py +32 -0
  203. package/tools/vds-scripts/mcp_server/tests/verification/__init__.py +2 -0
  204. package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_confluence_tools.py +40 -0
  205. package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_jira_tools.py +37 -0
  206. package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_tool_registration.py +47 -0
  207. package/tools/vds-scripts/mcp_server/uv.lock +1032 -0
  208. package/tools/vds-scripts/mypy.ini +5 -0
  209. package/tools/vds-scripts/pyproject.toml +29 -0
  210. package/tools/vds-scripts/repo-manifest.yaml +273 -0
  211. package/tools/vds-scripts/repo-manifest.yaml.example +25 -0
  212. package/tools/vds-scripts/scripts/BRD-Validation-API.postman_collection.json +706 -0
  213. package/tools/vds-scripts/scripts/BRD-Validation-README.md +308 -0
  214. package/tools/vds-scripts/scripts/README.md +162 -0
  215. package/tools/vds-scripts/scripts/bootstrap_uv.sh +30 -0
  216. package/tools/vds-scripts/scripts/brd-validation-environment.json +51 -0
  217. package/tools/vds-scripts/scripts/brd-validation-test-results.json +13023 -0
  218. package/tools/vds-scripts/scripts/brd_coverage_report.json +276 -0
  219. package/tools/vds-scripts/scripts/create_memory_session.py +35 -0
  220. package/tools/vds-scripts/scripts/deployment/load_docker_images_offline.sh +90 -0
  221. package/tools/vds-scripts/scripts/final_completion_report.md +139 -0
  222. package/tools/vds-scripts/scripts/folder_structure_report.json +321 -0
  223. package/tools/vds-scripts/scripts/generate_completion_report.py +125 -0
  224. package/tools/vds-scripts/scripts/generate_intellij_modules.py +150 -0
  225. package/tools/vds-scripts/scripts/link_integrity_report.json +807 -0
  226. package/tools/vds-scripts/scripts/move_audit_artifact_pages.py +255 -0
  227. package/tools/vds-scripts/scripts/move_audit_artifact_pages_rest.py +165 -0
  228. package/tools/vds-scripts/scripts/move_wrong_dept_pages.py +216 -0
  229. package/tools/vds-scripts/scripts/save_intellij_memories.py +120 -0
  230. package/tools/vds-scripts/scripts/save_memories_to_vds_ai.py +83 -0
  231. package/tools/vds-scripts/scripts/save_memories_vds_style.py +129 -0
  232. package/tools/vds-scripts/scripts/search_intellij_memories.py +50 -0
  233. package/tools/vds-scripts/scripts/setup_intellij_workspace.py +65 -0
  234. package/tools/vds-scripts/scripts/target-state-automation/README.md +89 -0
  235. package/tools/vds-scripts/scripts/target-state-automation/confluence_sync_coordinator.sh +27 -0
  236. package/tools/vds-scripts/scripts/target-state-automation/coordination.sh +114 -0
  237. package/tools/vds-scripts/scripts/target-state-automation/diagram_coordinator.sh +25 -0
  238. package/tools/vds-scripts/scripts/target-state-automation/docs_root.sh +22 -0
  239. package/tools/vds-scripts/scripts/target-state-automation/generate_diagrams.sh +22 -0
  240. package/tools/vds-scripts/scripts/target-state-automation/markdown_coordinator.sh +25 -0
  241. package/tools/vds-scripts/scripts/target-state-automation/progress_dashboard.sh +17 -0
  242. package/tools/vds-scripts/scripts/target-state-automation/schema_coordinator.sh +25 -0
  243. package/tools/vds-scripts/scripts/target-state-automation/sync_confluence.sh +30 -0
  244. package/tools/vds-scripts/scripts/target-state-automation/update_dependencies.sh +19 -0
  245. package/tools/vds-scripts/scripts/target-state-automation/validate_links.sh +86 -0
  246. package/tools/vds-scripts/scripts/target-state-automation/validate_markdown.sh +52 -0
  247. package/tools/vds-scripts/scripts/target-state-automation/validate_schemas.sh +26 -0
  248. package/tools/vds-scripts/scripts/target-state-automation/validate_structure.sh +98 -0
  249. package/tools/vds-scripts/scripts/update_modules_xml.py +190 -0
  250. package/tools/vds-scripts/scripts/uv-workspace-alignment-verification-2026-03-25.md +128 -0
  251. package/tools/vds-scripts/scripts/validate_brd_coverage.py +179 -0
  252. package/tools/vds-scripts/scripts/validate_folder_structure.py +240 -0
  253. package/tools/vds-scripts/scripts/validate_link_integrity.py +272 -0
  254. package/tools/vds-scripts/scripts/vds_sh_helpers.sh +180 -0
  255. package/tools/vds-scripts/scripts/verification/phase2_portable_paths_ubuntu_docker.sh +26 -0
  256. package/tools/vds-scripts/scripts/worktree_uv.sh +48 -0
  257. package/tools/vds-scripts/uv.lock +8 -0
  258. package/tools/vds-scripts/vds_cli/README.md +126 -0
  259. package/tools/vds-scripts/vds_cli/VERIFICATION_REPORT.md +41 -0
  260. package/tools/vds-scripts/vds_cli/pyproject.toml +38 -0
  261. package/tools/vds-scripts/vds_cli/src/vds_cli/__init__.py +3 -0
  262. package/tools/vds-scripts/vds_cli/src/vds_cli/cli.py +173 -0
  263. package/tools/vds-scripts/vds_cli/src/vds_cli/docs_sync.py +1203 -0
  264. package/tools/vds-scripts/vds_cli/src/vds_cli/env.py +41 -0
  265. package/tools/vds-scripts/vds_cli/src/vds_cli/google_sheets_orchestrator/__init__.py +3 -0
  266. package/tools/vds-scripts/vds_cli/src/vds_cli/google_sheets_orchestrator/google_sheets_orchestrator.py +198 -0
  267. package/tools/vds-scripts/vds_cli/src/vds_cli/router.py +93 -0
  268. package/tools/vds-scripts/vds_cli/src/vds_cli/sync_api.py +647 -0
  269. package/tools/vds-scripts/vds_cli/src/vds_cli/sync_service.py +266 -0
  270. package/tools/vds-scripts/vds_cli/tests/__init__.py +2 -0
  271. package/tools/vds-scripts/vds_cli/tests/conftest.py +49 -0
  272. package/tools/vds-scripts/vds_cli/tests/unit/__init__.py +2 -0
  273. package/tools/vds-scripts/vds_cli/tests/unit/test_cli.py +143 -0
  274. package/tools/vds-scripts/vds_cli/tests/unit/test_docs_sync.py +422 -0
  275. package/tools/vds-scripts/vds_cli/tests/unit/test_env.py +51 -0
  276. package/tools/vds-scripts/vds_cli/tests/unit/test_router.py +72 -0
  277. package/tools/vds-scripts/vds_cli/tests/unit/test_sync_api.py +357 -0
  278. package/tools/vds-scripts/vds_cli/tests/unit/test_sync_service.py +160 -0
  279. package/tools/vds-scripts/vds_cli/tests/verification/__init__.py +2 -0
  280. package/tools/vds-scripts/vds_cli/tests/verification/test_bitbucket_real.py +33 -0
  281. package/tools/vds-scripts/vds_cli/tests/verification/test_confluence_real.py +35 -0
  282. package/tools/vds-scripts/vds_cli/tests/verification/test_jira_real.py +41 -0
  283. package/tools/vds-scripts/vds_cli/uv.lock +524 -0
  284. package/tools/vds-scripts/vds_cli_common/README.md +190 -0
  285. package/tools/vds-scripts/vds_cli_common/pyproject.toml +92 -0
  286. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/__init__.py +34 -0
  287. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/completers.py +139 -0
  288. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/context.py +201 -0
  289. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/env.py +119 -0
  290. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/errors.py +318 -0
  291. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/output.py +284 -0
  292. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/paths.py +78 -0
  293. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/testing.py +213 -0
  294. package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/version.py +85 -0
  295. package/tools/vds-scripts/vds_cli_common/tests/__init__.py +1 -0
  296. package/tools/vds-scripts/vds_cli_common/tests/test_completers.py +148 -0
  297. package/tools/vds-scripts/vds_cli_common/tests/test_context.py +192 -0
  298. package/tools/vds-scripts/vds_cli_common/tests/test_env.py +102 -0
  299. package/tools/vds-scripts/vds_cli_common/tests/test_errors.py +186 -0
  300. package/tools/vds-scripts/vds_cli_common/tests/test_output.py +229 -0
  301. package/tools/vds-scripts/vds_cli_common/tests/test_paths.py +61 -0
  302. package/tools/vds-scripts/vds_cli_common/tests/test_testing.py +138 -0
  303. package/tools/vds-scripts/vds_cli_common/tests/test_version.py +64 -0
@@ -0,0 +1,138 @@
1
+ """Strongly-typed Jira settings using pydantic-settings (SDK-only)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from collections.abc import Iterable, Mapping
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+
10
+ from pydantic import AnyHttpUrl, Field, field_validator
11
+ from pydantic_settings import BaseSettings, SettingsConfigDict
12
+
13
+ _PROJECT_ROOT = Path(__file__).resolve().parents[3]
14
+ _SCRIPTS_DIR = _PROJECT_ROOT.parent
15
+
16
+ DEFAULT_ENV_PATHS = (
17
+ Path.home() / ".vds" / ".env",
18
+ _SCRIPTS_DIR / ".env",
19
+ )
20
+
21
+ _REQUIRED_KEYS: tuple[str, ...] = (
22
+ "VDS_USERNAME",
23
+ "VDS_PASSWORD",
24
+ "JIRA_TOKEN",
25
+ )
26
+
27
+ _OPTIONAL_KEYS: tuple[str, ...] = ("JIRA_BASE_URL",)
28
+
29
+
30
+ class SettingsError(RuntimeError):
31
+ """Raised when the environment configuration cannot be loaded."""
32
+
33
+
34
+ class JiraSettings(BaseSettings):
35
+ model_config = SettingsConfigDict(env_file=None, env_prefix="", case_sensitive=True, populate_by_name=True)
36
+
37
+ username: str | None = Field(default=None, alias="VDS_USERNAME")
38
+ password: str | None = Field(default=None, alias="VDS_PASSWORD")
39
+
40
+ base_url: str = Field(default="http://jira.digital.vn", alias="JIRA_BASE_URL")
41
+
42
+ # Optional: PAT/token if available for Jira Server/DC
43
+ token: str | None = Field(default=None, alias="JIRA_TOKEN")
44
+
45
+ def require_basic_or_token(self) -> None:
46
+ if not ((self.username and self.password) or self.token):
47
+ raise ValueError("Missing credentials: set VDS_USERNAME/VDS_PASSWORD or JIRA_TOKEN")
48
+
49
+ @field_validator("base_url")
50
+ @classmethod
51
+ def _validate_base_url(cls, value: str) -> str:
52
+ AnyHttpUrl(value)
53
+ return value.rstrip("/")
54
+
55
+
56
+ @dataclass(frozen=True)
57
+ class EnvSnapshot:
58
+ """A snapshot of relevant environment variables."""
59
+
60
+ values: Mapping[str, str]
61
+ source: Path | str | None
62
+
63
+
64
+ def _parse_env_file(env_path: Path) -> dict[str, str]:
65
+ if not env_path.exists():
66
+ return {}
67
+
68
+ result: dict[str, str] = {}
69
+ for raw_line in env_path.read_text().splitlines():
70
+ line = raw_line.strip()
71
+ if not line or line.startswith("#"):
72
+ continue
73
+ if "=" not in line:
74
+ continue
75
+ key, raw_value = line.split("=", 1)
76
+ key = key.strip()
77
+ value = raw_value.strip().strip('"').strip("'")
78
+ if key:
79
+ result[key] = value
80
+ return result
81
+
82
+
83
+ def _merge_env_sources(*snapshots: EnvSnapshot) -> dict[str, str]:
84
+ merged: dict[str, str] = {}
85
+ for snapshot in snapshots:
86
+ merged.update(snapshot.values)
87
+ return merged
88
+
89
+
90
+ def _filter_keys(source: Mapping[str, str]) -> dict[str, str]:
91
+ relevant_keys = set(_REQUIRED_KEYS + _OPTIONAL_KEYS)
92
+ return {key: value for key, value in source.items() if key in relevant_keys}
93
+
94
+
95
+ def _resolve_env_paths(env_path: Path | None) -> Iterable[Path]:
96
+ if env_path is not None:
97
+ return (env_path,)
98
+ return DEFAULT_ENV_PATHS
99
+
100
+
101
+ def load_settings(
102
+ *,
103
+ env_path: Path | None = None,
104
+ overrides: Mapping[str, str] | None = None,
105
+ strict: bool = False,
106
+ ) -> JiraSettings:
107
+ """Load orchestrator settings from the environment and optional .env file."""
108
+
109
+ snapshots = []
110
+ for candidate in _resolve_env_paths(env_path):
111
+ snapshots.append(
112
+ EnvSnapshot(
113
+ values=_filter_keys(_parse_env_file(candidate)), source=candidate if candidate.exists() else None
114
+ )
115
+ )
116
+
117
+ snapshots.append(EnvSnapshot(values=_filter_keys(os.environ), source="os.environ"))
118
+
119
+ if overrides:
120
+ snapshots.append(EnvSnapshot(values=_filter_keys(overrides), source="overrides"))
121
+
122
+ merged = _merge_env_sources(*snapshots)
123
+
124
+ try:
125
+ settings = JiraSettings.model_validate(merged)
126
+ except Exception as exc:
127
+ raise SettingsError(str(exc)) from exc
128
+
129
+ if strict:
130
+ try:
131
+ settings.require_basic_or_token()
132
+ except ValueError as exc:
133
+ raise SettingsError(str(exc)) from exc
134
+
135
+ return settings
136
+
137
+
138
+ __all__ = ["JiraSettings", "SettingsError", "load_settings", "DEFAULT_ENV_PATHS"]
@@ -0,0 +1,67 @@
1
+ """Domain errors for Jira adapter (consistent with VDS orchestrators)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+
8
+ class JiraAdapterError(RuntimeError):
9
+ """Base class for Jira adapter errors."""
10
+
11
+ def __init__(self, message: str, *, context: dict[str, Any] | None = None) -> None:
12
+ super().__init__(message)
13
+ self.context = context or {}
14
+
15
+
16
+ class JiraAuthError(JiraAdapterError):
17
+ """Raised when authentication fails (401/403)."""
18
+
19
+ pass
20
+
21
+
22
+ class JiraNotFound(JiraAdapterError):
23
+ """Raised when a resource is not found (404)."""
24
+
25
+ pass
26
+
27
+
28
+ class JiraRateLimited(JiraAdapterError):
29
+ """Raised when rate limited by JIRA (429)."""
30
+
31
+ def __init__(
32
+ self, message: str, *, retry_after: float | None = None, context: dict[str, Any] | None = None
33
+ ) -> None:
34
+ super().__init__(message, context=context)
35
+ self.retry_after = retry_after
36
+
37
+
38
+ class JiraConflictError(JiraAdapterError):
39
+ """Raised when there's a conflict (409)."""
40
+
41
+ pass
42
+
43
+
44
+ class JiraResponseError(JiraAdapterError):
45
+ """Raised for other HTTP errors (4xx/5xx)."""
46
+
47
+ def __init__(
48
+ self, message: str, *, status_code: int | None = None, context: dict[str, Any] | None = None
49
+ ) -> None:
50
+ super().__init__(message, context=context)
51
+ self.status_code = status_code
52
+
53
+
54
+ class JiraTransportError(JiraAdapterError):
55
+ """Raised for network/transport issues."""
56
+
57
+ pass
58
+
59
+
60
+ class JiraRetryError(JiraAdapterError):
61
+ """Raised when retry attempts are exhausted."""
62
+
63
+ def __init__(
64
+ self, message: str, *, attempts: int | None = None, context: dict[str, Any] | None = None
65
+ ) -> None:
66
+ super().__init__(message, context=context)
67
+ self.attempts = attempts
@@ -0,0 +1,65 @@
1
+ """Run reporting utilities for the Jira orchestrator."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ from dataclasses import dataclass
6
+ from datetime import UTC, datetime
7
+ from pathlib import Path
8
+
9
+ TIMESTAMP_FMT = "%Y-%m-%dT%H-%M-%SZ"
10
+
11
+
12
+ @dataclass(slots=True)
13
+ class RunSummary:
14
+ command: str
15
+ args: list[str]
16
+ exit_code: int
17
+ duration_ms: int
18
+ started_at: datetime
19
+ finished_at: datetime
20
+
21
+ @property
22
+ def success(self) -> bool:
23
+ return self.exit_code == 0
24
+
25
+
26
+ def _ensure_directory(base: Path, timestamp: datetime) -> Path:
27
+ day_dir = base / str(timestamp.year) / f"{timestamp.month:02d}" / f"{timestamp.day:02d}"
28
+ day_dir.mkdir(parents=True, exist_ok=True)
29
+ return day_dir
30
+
31
+
32
+ def write_reports(summary: RunSummary, *, base_dir: Path, include_markdown: bool = True) -> Path:
33
+ """Persist JSON (and optional Markdown) reports and return JSON path."""
34
+ timestamp = summary.finished_at.astimezone(UTC)
35
+ directory = _ensure_directory(base_dir, timestamp)
36
+ slug = f"{timestamp.strftime(TIMESTAMP_FMT)}_{summary.command}"
37
+
38
+ json_path = directory / f"{slug}.json"
39
+ json_payload = {
40
+ "command": summary.command,
41
+ "args": summary.args,
42
+ "exit_code": summary.exit_code,
43
+ "duration_ms": summary.duration_ms,
44
+ "started_at": summary.started_at.astimezone(UTC).isoformat().replace("+00:00", "Z"),
45
+ "finished_at": summary.finished_at.astimezone(UTC).isoformat().replace("+00:00", "Z"),
46
+ "success": summary.success,
47
+ }
48
+ json_path.write_text(json.dumps(json_payload, indent=2, sort_keys=True))
49
+
50
+ if include_markdown:
51
+ md_path = directory / f"{slug}.md"
52
+ status = "✅ Success" if summary.success else "❌ Failure"
53
+ md_lines = [
54
+ f"# Jira Run Report — {summary.command}",
55
+ "",
56
+ f"- **Status**: {status} (exit code {summary.exit_code})",
57
+ f"- **Duration**: {summary.duration_ms} ms",
58
+ f"- **Started**: {json_payload['started_at']}",
59
+ f"- **Finished**: {json_payload['finished_at']}",
60
+ ]
61
+ if summary.args:
62
+ md_lines.append(f"- **Arguments**: {' '.join(summary.args)}")
63
+ md_path.write_text("\n".join(md_lines) + "\n")
64
+
65
+ return json_path
@@ -0,0 +1 @@
1
+ """Test configuration and fixtures for Jira orchestrator tests."""
@@ -0,0 +1,86 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ import pytest
6
+ from vds_jira_orchestrator.config import JiraSettings
7
+
8
+
9
+ def clear_runtime_env(monkeypatch: pytest.MonkeyPatch) -> None:
10
+ """Clear runtime environment variables for testing."""
11
+ for key in (
12
+ "VDS_USERNAME",
13
+ "VDS_PASSWORD",
14
+ "JIRA_TOKEN",
15
+ "JIRA_BASE_URL",
16
+ ):
17
+ monkeypatch.delenv(key, raising=False)
18
+
19
+
20
+ def write_env(tmp_path: Path, content: str) -> Path:
21
+ """Write a temporary .env file for testing."""
22
+ env_path = tmp_path / ".env"
23
+ env_path.write_text(content)
24
+ return env_path
25
+
26
+
27
+ @pytest.fixture
28
+ def mock_jira_settings() -> JiraSettings:
29
+ """Create mock Jira settings for testing."""
30
+ return JiraSettings(
31
+ VDS_USERNAME="test_user",
32
+ VDS_PASSWORD="test_pass",
33
+ JIRA_TOKEN="test_token",
34
+ JIRA_BASE_URL="https://jira.test.com",
35
+ )
36
+
37
+
38
+ @pytest.fixture
39
+ def sample_issue_response() -> dict[str, object]:
40
+ """Sample Jira issue response."""
41
+ return {
42
+ "id": "10001",
43
+ "key": "TEST-123",
44
+ "fields": {
45
+ "summary": "Test Issue",
46
+ "description": "This is a test issue",
47
+ "status": {"name": "Open"},
48
+ "priority": {"name": "Medium"},
49
+ "issuetype": {"name": "Task"},
50
+ "project": {"key": "TEST", "name": "Test Project"},
51
+ },
52
+ }
53
+
54
+
55
+ @pytest.fixture
56
+ def sample_project_response() -> dict[str, object]:
57
+ """Sample Jira project response."""
58
+ return {
59
+ "id": "10000",
60
+ "key": "TEST",
61
+ "name": "Test Project",
62
+ "description": "A test project",
63
+ "projectTypeKey": "software",
64
+ "lead": {"displayName": "Test User"},
65
+ }
66
+
67
+
68
+ @pytest.fixture
69
+ def sample_search_results() -> dict[str, object]:
70
+ """Sample Jira search results."""
71
+ return {
72
+ "startAt": 0,
73
+ "maxResults": 50,
74
+ "total": 1,
75
+ "issues": [
76
+ {
77
+ "id": "10001",
78
+ "key": "TEST-123",
79
+ "fields": {
80
+ "summary": "Test Issue",
81
+ "status": {"name": "Open"},
82
+ "priority": {"name": "Medium"},
83
+ },
84
+ }
85
+ ],
86
+ }
@@ -0,0 +1,54 @@
1
+ from unittest.mock import Mock, patch
2
+
3
+ from vds_jira_orchestrator.adapter import JiraAdapter
4
+
5
+
6
+ @patch("vds_jira_orchestrator.adapter.Jira")
7
+ def test_get_all_agile_boards_unwraps_values_list(mock_jira: Mock) -> None:
8
+ client = Mock()
9
+ client.get_all_agile_boards.return_value = {
10
+ "maxResults": 50,
11
+ "startAt": 0,
12
+ "isLast": True,
13
+ "values": [{"id": 1, "name": "NTTC Scrum Board"}],
14
+ }
15
+ mock_jira.return_value = client
16
+
17
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
18
+ result = adapter.get_all_agile_boards(project_key="NTTC", limit=50)
19
+
20
+ assert result == [{"id": 1, "name": "NTTC Scrum Board"}]
21
+
22
+
23
+ @patch("vds_jira_orchestrator.adapter.Jira")
24
+ def test_get_issues_for_board_unwraps_issues_list(mock_jira: Mock) -> None:
25
+ client = Mock()
26
+ client.get_issues_for_board.return_value = {
27
+ "startAt": 0,
28
+ "maxResults": 50,
29
+ "total": 1,
30
+ "issues": [{"key": "NTTC-5705"}],
31
+ }
32
+ mock_jira.return_value = client
33
+
34
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
35
+ result = adapter.get_issues_for_board(1)
36
+
37
+ assert result == [{"key": "NTTC-5705"}]
38
+
39
+
40
+ @patch("vds_jira_orchestrator.adapter.Jira")
41
+ def test_get_all_sprints_from_board_unwraps_values_list(mock_jira: Mock) -> None:
42
+ client = Mock()
43
+ client.get_all_sprints_from_board.return_value = {
44
+ "maxResults": 50,
45
+ "startAt": 0,
46
+ "isLast": True,
47
+ "values": [{"id": 101, "name": "Sprint 1"}],
48
+ }
49
+ mock_jira.return_value = client
50
+
51
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
52
+ result = adapter.get_all_sprints_from_board(1)
53
+
54
+ assert result == [{"id": 101, "name": "Sprint 1"}]
@@ -0,0 +1,69 @@
1
+ from __future__ import annotations
2
+
3
+ from unittest.mock import MagicMock, patch
4
+
5
+ import pytest
6
+ from vds_jira_orchestrator.adapter import JiraAdapter, JiraAdapterError
7
+
8
+
9
+ @pytest.fixture
10
+ def mock_adapter() -> JiraAdapter:
11
+ """Fixture for a JiraAdapter."""
12
+ with patch("vds_jira_orchestrator.adapter.Jira") as mock_jira_class:
13
+ mock_client = MagicMock()
14
+ mock_jira_class.return_value = mock_client
15
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
16
+ adapter._client = mock_client
17
+ return adapter
18
+
19
+
20
+ def test_bulk_update_issue_field_calls_client(mock_adapter: JiraAdapter) -> None:
21
+ """Test that bulk_update_issue_field calls the SDK client with correct parameters."""
22
+ mock_adapter._client.bulk_update_issue_field.return_value = {"updated": 2, "errors": []}
23
+ result = mock_adapter.bulk_update_issue_field(key_list=["TEST-1", "TEST-2"], fields="*all")
24
+ mock_adapter._client.bulk_update_issue_field.assert_called_once_with(["TEST-1", "TEST-2"], fields="*all")
25
+ assert result["updated"] == 2
26
+
27
+
28
+ def test_bulk_update_issue_field_with_fields(mock_adapter: JiraAdapter) -> None:
29
+ """Test bulk_update_issue_field with specific fields."""
30
+ mock_adapter._client.bulk_update_issue_field.return_value = {"updated": 1, "errors": []}
31
+ fields_json = '{"summary": "Updated summary"}'
32
+ result = mock_adapter.bulk_update_issue_field(key_list=["TEST-1"], fields=fields_json)
33
+ mock_adapter._client.bulk_update_issue_field.assert_called_once_with(["TEST-1"], fields=fields_json)
34
+ assert result["updated"] == 1
35
+
36
+
37
+ def test_bulk_update_issue_field_handles_error(mock_adapter: JiraAdapter) -> None:
38
+ """Test that bulk_update_issue_field handles errors from the SDK client."""
39
+ mock_adapter._client.bulk_update_issue_field.side_effect = Exception("API Error")
40
+ with pytest.raises(JiraAdapterError, match="API Error"):
41
+ mock_adapter.bulk_update_issue_field(key_list=["TEST-1"], fields="*all")
42
+
43
+
44
+ def test_issue_field_value_append_calls_client(mock_adapter: JiraAdapter) -> None:
45
+ """Test that issue_field_value_append calls the SDK client with correct parameters."""
46
+ mock_adapter._client.issue_field_value_append.return_value = {"id": "10000", "value": "appended"}
47
+ value_dict = {"name": "test-value"}
48
+ result = mock_adapter.issue_field_value_append(issue_id_or_key="TEST-1", field="customfield_10000", value=value_dict)
49
+ mock_adapter._client.issue_field_value_append.assert_called_once_with("TEST-1", "customfield_10000", value_dict, notify_users=True)
50
+ assert result["id"] == "10000"
51
+
52
+
53
+ def test_issue_field_value_append_with_notify_false(mock_adapter: JiraAdapter) -> None:
54
+ """Test issue_field_value_append with notify_users=False."""
55
+ mock_adapter._client.issue_field_value_append.return_value = {"id": "10001", "value": "appended"}
56
+ value_dict = {"name": "another-value"}
57
+ result = mock_adapter.issue_field_value_append(
58
+ issue_id_or_key="TEST-2", field="customfield_10000", value=value_dict, notify_users=False
59
+ )
60
+ mock_adapter._client.issue_field_value_append.assert_called_once_with("TEST-2", "customfield_10000", value_dict, notify_users=False)
61
+ assert result["id"] == "10001"
62
+
63
+
64
+ def test_issue_field_value_append_handles_error(mock_adapter: JiraAdapter) -> None:
65
+ """Test that issue_field_value_append handles errors from the SDK client."""
66
+ mock_adapter._client.issue_field_value_append.side_effect = Exception("API Error")
67
+ with pytest.raises(JiraAdapterError, match="API Error"):
68
+ mock_adapter.issue_field_value_append(issue_id_or_key="TEST-1", field="customfield_10000", value={"name": "value"})
69
+
@@ -0,0 +1,57 @@
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_get_component_calls_sdk(mock_jira: MagicMock) -> None:
10
+ client = MagicMock()
11
+ client.component.return_value = {"id": 10, "name": "Comp"}
12
+ mock_jira.return_value = client
13
+
14
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
15
+ payload = adapter.get_component(10)
16
+
17
+ client.component.assert_called_once_with(10)
18
+ assert payload["id"] == 10
19
+
20
+
21
+ @patch("vds_jira_orchestrator.adapter.Jira")
22
+ def test_create_component_calls_sdk(mock_jira: MagicMock) -> None:
23
+ client = MagicMock()
24
+ client.create_component.return_value = {"id": 11}
25
+ mock_jira.return_value = client
26
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
27
+
28
+ payload = {"name": "NewComp", "projectKey": "NTTC"}
29
+ result = adapter.create_component(payload)
30
+
31
+ client.create_component.assert_called_once_with(payload)
32
+ assert result["id"] == 11
33
+
34
+
35
+ @patch("vds_jira_orchestrator.adapter.Jira")
36
+ def test_update_component_calls_sdk(mock_jira: MagicMock) -> None:
37
+ client = MagicMock()
38
+ client.update_component.return_value = {"id": 12, "name": "Updated"}
39
+ mock_jira.return_value = client
40
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
41
+
42
+ result = adapter.update_component(12, {"name": "Updated"})
43
+
44
+ client.update_component.assert_called_once_with(12, {"name": "Updated"})
45
+ assert result["name"] == "Updated"
46
+
47
+
48
+ @patch("vds_jira_orchestrator.adapter.Jira")
49
+ def test_delete_component_calls_sdk(mock_jira: MagicMock) -> None:
50
+ client = MagicMock()
51
+ mock_jira.return_value = client
52
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
53
+
54
+ adapter.delete_component("13")
55
+
56
+ client.delete_component.assert_called_once_with("13")
57
+
@@ -0,0 +1,45 @@
1
+ from __future__ import annotations
2
+
3
+ from unittest.mock import MagicMock, patch
4
+
5
+ import requests
6
+ from vds_jira_orchestrator.adapter import JiraAdapter
7
+
8
+
9
+ @patch("vds_jira_orchestrator.adapter.Jira")
10
+ def test_get_createmeta_falls_back_to_rest_endpoint(mock_jira: MagicMock) -> None:
11
+ client = MagicMock()
12
+ client.issue_createmeta.side_effect = Exception("boom")
13
+ client.resource_url.return_value = "https://jira.example.com/rest/api/2/issue"
14
+ client.get.return_value = {"projects": []}
15
+ mock_jira.return_value = client
16
+
17
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
18
+ result = adapter.get_createmeta("TEST", issuetype="Task", expand="projects.issuetypes.fields")
19
+
20
+ client.get.assert_called_once_with(
21
+ "https://jira.example.com/rest/api/2/issue/createmeta",
22
+ params={
23
+ "projectKeys": "TEST",
24
+ "issuetypeNames": "Task",
25
+ "expand": "projects.issuetypes.fields",
26
+ },
27
+ )
28
+ assert result == {"projects": []}
29
+
30
+
31
+ @patch("vds_jira_orchestrator.adapter.Jira")
32
+ def test_get_createmeta_falls_back_when_v2_unavailable(mock_jira: MagicMock) -> None:
33
+ client = MagicMock()
34
+ response = MagicMock()
35
+ response.status_code = 404
36
+ client.issue_createmeta_issuetypes.side_effect = requests.exceptions.HTTPError(response=response)
37
+ client.issue_createmeta_fieldtypes.return_value = {}
38
+ client.issue_createmeta.return_value = {"projects": []}
39
+ mock_jira.return_value = client
40
+
41
+ adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
42
+ result = adapter.get_createmeta("TEST", issuetype="Task", expand="projects.issuetypes.fields")
43
+
44
+ client.issue_createmeta.assert_called_once()
45
+ assert result == {"projects": []}