@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,499 @@
1
+ from __future__ import annotations
2
+
3
+ import builtins
4
+ import json
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ import pytest
9
+ from confluence_orchestrator.cli import _build_content_client, app
10
+ from confluence_orchestrator.config import ConfluenceSettings
11
+ from confluence_orchestrator.errors import ConfluenceClientError
12
+ from typer.testing import CliRunner
13
+
14
+ from confluence_orchestrator import config as config_module
15
+
16
+ runner = CliRunner()
17
+
18
+
19
+ @pytest.fixture(autouse=True)
20
+ def restore_settings(monkeypatch: pytest.MonkeyPatch) -> None:
21
+ def fake_load_settings(strict: bool = False) -> ConfluenceSettings:
22
+ return ConfluenceSettings.model_validate(
23
+ {
24
+ "VDS_USERNAME": "user",
25
+ "VDS_PASSWORD": "pass",
26
+ "INTERNAL_CONFLUENCE_TOKEN": "token-int",
27
+ "EXTERNAL_CONFLUENCE_TOKEN": "token-ext",
28
+ }
29
+ )
30
+
31
+ monkeypatch.setattr(config_module, "load_settings", fake_load_settings)
32
+ monkeypatch.setattr("confluence_orchestrator.cli.load_settings", fake_load_settings)
33
+
34
+
35
+ def test_templates_command_lists_blueprints(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
36
+ class DummyClient:
37
+ def __init__(self, *_: Any, **__: Any) -> None:
38
+ pass
39
+
40
+ def list_templates(self, space_key: str | None = None) -> dict[str, Any]:
41
+ assert space_key is None
42
+ return {"blueprints": [{"name": "Doc", "templateId": "123"}]}
43
+
44
+ monkeypatch.setattr(
45
+ "confluence_orchestrator.cli._build_content_client",
46
+ lambda settings, server, api_version: DummyClient(),
47
+ )
48
+ monkeypatch.setattr("confluence_orchestrator.cli._build_http_client", lambda settings, server: object())
49
+
50
+ result = runner.invoke(app, ["--report-dir", str(tmp_path), "--api-version", "v2", "templates", "--server", "external"])
51
+
52
+ assert result.exit_code == 0
53
+ assert "Doc" in result.stdout
54
+ assert "ID: 123" in result.stdout
55
+
56
+
57
+ def test_missing_token_triggers_error(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
58
+ def fake_load(strict: bool = False) -> ConfluenceSettings:
59
+ return ConfluenceSettings.model_validate(
60
+ {
61
+ "VDS_USERNAME": "user",
62
+ "VDS_PASSWORD": "pass",
63
+ "INTERNAL_CONFLUENCE_TOKEN": None,
64
+ "EXTERNAL_CONFLUENCE_TOKEN": "token-ext",
65
+ }
66
+ )
67
+
68
+ monkeypatch.setattr("confluence_orchestrator.cli.load_settings", fake_load)
69
+
70
+ def fake_builder(*args, **kwargs): # type: ignore[no-untyped-def]
71
+ raise ConfluenceClientError("Missing token")
72
+
73
+ monkeypatch.setattr("confluence_orchestrator.cli._build_content_client", lambda *args, **kwargs: fake_builder())
74
+
75
+ result = runner.invoke(app, ["--report-dir", str(tmp_path), "templates", "--server", "internal"])
76
+ assert result.exit_code == 1
77
+ assert "Missing token" in result.stderr or "Missing token" in result.output
78
+
79
+
80
+ def test_get_command_fetches_page(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
81
+ class DummyClient:
82
+ def __init__(self, *_: Any, **__: Any) -> None:
83
+ pass
84
+
85
+ def get_content(self, page_id: str, expand: list[str] | None = None, format: str | None = None) -> dict[str, Any]:
86
+ assert page_id == "99647801"
87
+ assert expand == ["body.storage"]
88
+ assert format == "storage"
89
+ return {"id": page_id, "title": "Sample"}
90
+
91
+ monkeypatch.setattr("confluence_orchestrator.cli.ContentClientV2", DummyClient)
92
+ monkeypatch.setattr("confluence_orchestrator.cli._build_http_client", lambda settings, server: object())
93
+
94
+ result = runner.invoke(
95
+ app,
96
+ ["--report-dir", str(tmp_path), "get", "99647801", "--server", "external", "--expand", "body.storage", "--format", "storage"],
97
+ )
98
+
99
+ assert result.exit_code == 0
100
+ assert "Sample" in result.stdout
101
+
102
+
103
+ def test_content_page(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
104
+ class DummyClient:
105
+ def __init__(self, *_: Any, **__: Any) -> None:
106
+ pass
107
+
108
+ def get_page(self, page_id: str, expand: list[str] | None = None) -> dict[str, Any]: # type: ignore[override]
109
+ assert page_id == "996"
110
+ assert expand == ["body.storage", "version"]
111
+ return {"id": page_id}
112
+
113
+ monkeypatch.setattr("confluence_orchestrator.cli._build_content_client", lambda settings, server, api_version: DummyClient())
114
+ monkeypatch.setattr("confluence_orchestrator.cli._build_http_client", lambda settings, server: object())
115
+
116
+ result = runner.invoke(
117
+ app,
118
+ ["--report-dir", str(tmp_path), "content", "page", "996", "--server", "internal", "--expand", "body.storage,version"],
119
+ )
120
+
121
+ assert result.exit_code == 0
122
+ assert json.loads(result.stdout)["id"] == "996"
123
+ assert list(tmp_path.rglob("*.json"))
124
+
125
+
126
+ def test_content_search(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
127
+ class DummyClient:
128
+ def __init__(self, *_: Any, **__: Any) -> None:
129
+ pass
130
+
131
+ def search_cql(self, cql: str, limit: int, start: int, expand: list[str] | None = None) -> dict[str, Any]: # type: ignore[override]
132
+ assert cql == "title ~ 'foo'"
133
+ assert limit == 10
134
+ assert start == 5
135
+ assert expand is None
136
+ return {"results": ["ok"]}
137
+
138
+ monkeypatch.setattr("confluence_orchestrator.cli._build_content_client", lambda settings, server, api_version: DummyClient())
139
+ monkeypatch.setattr("confluence_orchestrator.cli._build_http_client", lambda settings, server: object())
140
+
141
+ result = runner.invoke(
142
+ app,
143
+ ["--report-dir", str(tmp_path), "content", "search", "title ~ 'foo'", "--limit", "10", "--start", "5"],
144
+ )
145
+
146
+ assert result.exit_code == 0
147
+ assert json.loads(result.stdout)["results"] == ["ok"]
148
+
149
+
150
+ def test_content_templates(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
151
+ class DummyClient:
152
+ def __init__(self, *_: Any, **__: Any) -> None:
153
+ pass
154
+
155
+ def list_templates(self, space_key: str | None = None) -> dict[str, Any]: # type: ignore[override]
156
+ assert space_key == "TTTC"
157
+ return {"templates": [1, 2]}
158
+
159
+ monkeypatch.setattr("confluence_orchestrator.cli._build_content_client", lambda settings, server, api_version: DummyClient())
160
+ monkeypatch.setattr("confluence_orchestrator.cli._build_http_client", lambda settings, server: object())
161
+
162
+ result = runner.invoke(
163
+ app,
164
+ ["--report-dir", str(tmp_path), "content", "templates", "--space", "TTTC"],
165
+ )
166
+
167
+ assert result.exit_code == 0
168
+ assert json.loads(result.stdout)["templates"] == [1, 2]
169
+
170
+
171
+ def test_build_content_client_blocks_v2_when_not_supported(monkeypatch: pytest.MonkeyPatch) -> None:
172
+ class DummyHttp:
173
+ def supports_api_version(self, version: str) -> bool:
174
+ return version == "v1"
175
+
176
+ monkeypatch.setattr("confluence_orchestrator.cli._build_http_client", lambda settings, server: DummyHttp())
177
+
178
+ settings = ConfluenceSettings.model_validate(
179
+ {
180
+ "VDS_USERNAME": "user",
181
+ "VDS_PASSWORD": "pass",
182
+ "INTERNAL_CONFLUENCE_TOKEN": "token-int",
183
+ "EXTERNAL_CONFLUENCE_TOKEN": "token-ext",
184
+ }
185
+ )
186
+
187
+ with pytest.raises(ConfluenceClientError) as excinfo:
188
+ _build_content_client(settings, "internal", "v2")
189
+ assert "not supported" in str(excinfo.value)
190
+
191
+
192
+ def test_build_content_client_allows_v2_when_supported(monkeypatch: pytest.MonkeyPatch) -> None:
193
+ class DummyHttp:
194
+ def supports_api_version(self, version: str) -> bool:
195
+ return True
196
+
197
+ class DummyClient:
198
+ pass
199
+
200
+ monkeypatch.setattr("confluence_orchestrator.cli._build_http_client", lambda settings, server: DummyHttp())
201
+ monkeypatch.setattr("confluence_orchestrator.cli.ContentClientV2", lambda http_client: DummyClient())
202
+
203
+ settings = ConfluenceSettings.model_validate(
204
+ {
205
+ "VDS_USERNAME": "user",
206
+ "VDS_PASSWORD": "pass",
207
+ "INTERNAL_CONFLUENCE_TOKEN": "token-int",
208
+ "EXTERNAL_CONFLUENCE_TOKEN": "token-ext",
209
+ }
210
+ )
211
+
212
+ client = _build_content_client(settings, "external", "v2")
213
+ assert isinstance(client, DummyClient)
214
+
215
+
216
+ def test_content_create_page(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
217
+ body_file = tmp_path / "body.html"
218
+ body_file.write_text("<p>body</p>", encoding="utf-8")
219
+
220
+ captured: dict[str, Any] = {}
221
+
222
+ class DummyClient:
223
+ def __init__(self, *_: Any, **__: Any) -> None:
224
+ pass
225
+
226
+ def create_page(self, **kwargs: Any) -> dict[str, Any]:
227
+ captured["args"] = kwargs
228
+ return {"id": "456"}
229
+
230
+ monkeypatch.setattr("confluence_orchestrator.cli.ContentClient", lambda client: DummyClient())
231
+ monkeypatch.setattr("confluence_orchestrator.cli._build_http_client", lambda settings, server: object())
232
+
233
+ result = runner.invoke(
234
+ app,
235
+ [
236
+ "--report-dir",
237
+ str(tmp_path),
238
+ "content",
239
+ "create-page",
240
+ "--space",
241
+ "TTTC",
242
+ "--title",
243
+ "My Page",
244
+ "--body-file",
245
+ str(body_file),
246
+ ],
247
+ )
248
+
249
+ assert result.exit_code == 0
250
+ assert captured["args"]["space_key"] == "TTTC"
251
+ assert captured["args"]["title"] == "My Page"
252
+ assert captured["args"]["body"] == "<p>body</p>"
253
+
254
+
255
+ def test_content_update_page_requires_change(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
256
+ monkeypatch.setattr("confluence_orchestrator.cli._build_http_client", lambda settings, server: object())
257
+ monkeypatch.setattr("confluence_orchestrator.cli.ContentClient", lambda client: object())
258
+
259
+ result = runner.invoke(
260
+ app,
261
+ ["--report-dir", str(tmp_path), "content", "update-page", "123"],
262
+ )
263
+
264
+ assert result.exit_code != 0
265
+ assert "Provide at least one" in result.stderr or "Provide at least one" in result.output
266
+
267
+
268
+ def test_content_update_page_calls_client(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
269
+ body_file = tmp_path / "body.html"
270
+ body_file.write_text("<p>body</p>", encoding="utf-8")
271
+
272
+ captured: dict[str, Any] = {}
273
+
274
+ class DummyClient:
275
+ def __init__(self, *_: Any, **__: Any) -> None:
276
+ pass
277
+
278
+ def update_page(self, page_id: str, **kwargs: Any) -> dict[str, Any]:
279
+ captured["page_id"] = page_id
280
+ captured["kwargs"] = kwargs
281
+ return {"id": page_id, **kwargs}
282
+
283
+ monkeypatch.setattr("confluence_orchestrator.cli.ContentClient", lambda client: DummyClient())
284
+ monkeypatch.setattr("confluence_orchestrator.cli._build_http_client", lambda settings, server: object())
285
+
286
+ result = runner.invoke(
287
+ app,
288
+ [
289
+ "--report-dir",
290
+ str(tmp_path),
291
+ "content",
292
+ "update-page",
293
+ "123",
294
+ "--body-file",
295
+ str(body_file),
296
+ "--minor-edit",
297
+ ],
298
+ )
299
+
300
+ assert result.exit_code == 0
301
+ assert captured["page_id"] == "123"
302
+ assert captured["kwargs"]["body"] == "<p>body</p>"
303
+ assert captured["kwargs"]["minor_edit"] is True
304
+
305
+
306
+ def test_content_add_attachment(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
307
+ attachment = tmp_path / "file.txt"
308
+ attachment.write_text("content", encoding="utf-8")
309
+
310
+ captured: dict[str, Any] = {}
311
+
312
+ class DummyClient:
313
+ def __init__(self, *_: Any, **__: Any) -> None:
314
+ pass
315
+
316
+ def upload_attachment(self, page_id: str, file: str, **kwargs: Any) -> dict[str, Any]:
317
+ captured["page_id"] = page_id
318
+ captured["file"] = file
319
+ captured["kwargs"] = kwargs
320
+ return {"id": "att-1"}
321
+
322
+ monkeypatch.setattr("confluence_orchestrator.cli.ContentClient", lambda client: DummyClient())
323
+ monkeypatch.setattr("confluence_orchestrator.cli._build_http_client", lambda settings, server: object())
324
+
325
+ result = runner.invoke(
326
+ app,
327
+ [
328
+ "--report-dir",
329
+ str(tmp_path),
330
+ "content",
331
+ "add-attachment",
332
+ "123",
333
+ "--file",
334
+ str(attachment),
335
+ "--comment",
336
+ "upload",
337
+ "--content-type",
338
+ "text/plain",
339
+ ],
340
+ )
341
+
342
+ assert result.exit_code == 0
343
+ assert captured["page_id"] == "123"
344
+ assert captured["file"].endswith("file.txt")
345
+ assert captured["kwargs"]["comment"] == "upload"
346
+
347
+
348
+ def test_content_update_template(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
349
+ body_file = tmp_path / "template.html"
350
+ body_file.write_text("<p>template</p>", encoding="utf-8")
351
+
352
+ captured: dict[str, Any] = {}
353
+
354
+ class DummyClient:
355
+ def __init__(self, *_: Any, **__: Any) -> None:
356
+ pass
357
+
358
+ def update_template(self, template_id: str, **kwargs: Any) -> dict[str, Any]:
359
+ captured["template_id"] = template_id
360
+ captured["kwargs"] = kwargs
361
+ return {"id": template_id}
362
+
363
+ monkeypatch.setattr("confluence_orchestrator.cli.ContentClient", lambda client: DummyClient())
364
+ monkeypatch.setattr("confluence_orchestrator.cli._build_http_client", lambda settings, server: object())
365
+
366
+ result = runner.invoke(
367
+ app,
368
+ [
369
+ "--report-dir",
370
+ str(tmp_path),
371
+ "content",
372
+ "update-template",
373
+ "tmpl-1",
374
+ "--body-file",
375
+ str(body_file),
376
+ "--type",
377
+ "page",
378
+ ],
379
+ )
380
+
381
+ assert result.exit_code == 0
382
+ assert captured["template_id"] == "tmpl-1"
383
+ assert captured["kwargs"]["body"] == "<p>template</p>"
384
+ assert captured["kwargs"]["template_type"] == "page"
385
+
386
+
387
+ def test_batch_command_is_deprecated(tmp_path: Path) -> None:
388
+ result = runner.invoke(
389
+ app,
390
+ ["--report-dir", str(tmp_path), "batch", "--server", "internal", "--", "--dry-run"],
391
+ )
392
+
393
+ assert result.exit_code == 1
394
+ assert "deprecated" in result.stderr or "deprecated" in result.stdout
395
+
396
+
397
+ def test_batch_scan_uses_runner(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
398
+ from confluence_orchestrator.orchestration import BatchMetrics
399
+
400
+ class DummyRunner:
401
+ def __init__(self, *_: Any, **__: Any) -> None:
402
+ metrics = BatchMetrics()
403
+ metrics.mark_request()
404
+ metrics.mark_processed(1)
405
+ metrics.mark_finished()
406
+ metrics.scanned = 1
407
+ self._metrics = metrics
408
+
409
+ def scan(self, cql: str, limit: int, max_results: int | None, start: int): # type: ignore[override]
410
+ assert cql == "space = TTTC"
411
+ assert limit == 1
412
+ assert max_results == 2
413
+ assert start == 0
414
+ yield {"id": "123"}
415
+
416
+ @property
417
+ def last_metrics(self) -> BatchMetrics:
418
+ return self._metrics
419
+
420
+ monkeypatch.setattr("confluence_orchestrator.cli.BatchRunner", DummyRunner)
421
+
422
+ result = runner.invoke(
423
+ app,
424
+ ["--report-dir", str(tmp_path), "batch-native", "scan", "--cql", "space = TTTC", "--limit", "1", "--max-results", "2", "--json"],
425
+ )
426
+
427
+ assert result.exit_code == 0
428
+ payload = json.loads(result.stdout)
429
+ assert payload["count"] == 1
430
+ assert payload["metrics"]["requests"] == 1
431
+
432
+
433
+ def test_webhook_create_invokes_client(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
434
+ calls: dict[str, Any] = {}
435
+
436
+ class DummyClient:
437
+ def __init__(self, *_: Any, **__: Any) -> None:
438
+ pass
439
+
440
+ def create(self, **kwargs: Any) -> dict[str, Any]: # type: ignore[override]
441
+ calls["kwargs"] = kwargs
442
+ return {"id": "1", **kwargs}
443
+
444
+ monkeypatch.setattr("confluence_orchestrator.cli.WebhookClient", lambda http_client: DummyClient())
445
+
446
+ result = runner.invoke(
447
+ app,
448
+ [
449
+ "--report-dir",
450
+ str(tmp_path),
451
+ "webhook-native",
452
+ "create",
453
+ "--name",
454
+ "Notify",
455
+ "--url",
456
+ "https://example.com/hook",
457
+ "--event",
458
+ "page_created",
459
+ "--json",
460
+ ],
461
+ )
462
+
463
+ assert result.exit_code == 0
464
+ assert calls["kwargs"]["name"] == "Notify"
465
+ assert json.loads(result.stdout)["id"] == "1"
466
+
467
+
468
+ def test_webhook_list_outputs_count(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
469
+ class DummyClient:
470
+ def __init__(self, *_: Any, **__: Any) -> None:
471
+ pass
472
+
473
+ def list(self) -> builtins.list[dict[str, Any]]: # type: ignore[override]
474
+ return [{"id": "1"}]
475
+
476
+ monkeypatch.setattr("confluence_orchestrator.cli.WebhookClient", lambda http_client: DummyClient())
477
+
478
+ result = runner.invoke(app, ["--report-dir", str(tmp_path), "webhook-native", "list"])
479
+
480
+ assert result.exit_code == 0
481
+ assert "1 webhooks" in result.stdout
482
+
483
+
484
+ def test_webhook_delete_invokes_client(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
485
+ calls: dict[str, Any] = {}
486
+
487
+ class DummyClient:
488
+ def __init__(self, *_: Any, **__: Any) -> None:
489
+ pass
490
+
491
+ def delete(self, webhook_id: str) -> None: # type: ignore[override]
492
+ calls["id"] = webhook_id
493
+
494
+ monkeypatch.setattr("confluence_orchestrator.cli.WebhookClient", lambda http_client: DummyClient())
495
+
496
+ result = runner.invoke(app, ["--report-dir", str(tmp_path), "webhook-native", "delete", "123"])
497
+
498
+ assert result.exit_code == 0
499
+ assert calls["id"] == "123"
@@ -0,0 +1,83 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ import pytest
6
+ from confluence_orchestrator.config import SettingsError, load_settings
7
+
8
+
9
+ def write_env(tmp_path: Path, content: str) -> Path:
10
+ env_path = tmp_path / ".env"
11
+ env_path.write_text(content)
12
+ return env_path
13
+
14
+
15
+ def clear_runtime_env(monkeypatch: pytest.MonkeyPatch) -> None:
16
+ for key in (
17
+ "VDS_USERNAME",
18
+ "VDS_PASSWORD",
19
+ "INTERNAL_CONFLUENCE_TOKEN",
20
+ "EXTERNAL_CONFLUENCE_TOKEN",
21
+ "CONFLUENCE_DEFAULT_SERVER",
22
+ "CONFLUENCE_INTERNAL_URL",
23
+ "CONFLUENCE_EXTERNAL_URL",
24
+ ):
25
+ monkeypatch.delenv(key, raising=False)
26
+
27
+
28
+ def test_load_settings_prefers_env_over_file(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
29
+ clear_runtime_env(monkeypatch)
30
+ env_path = write_env(
31
+ tmp_path,
32
+ """
33
+ VDS_USERNAME=file_user
34
+ VDS_PASSWORD=file_pass
35
+ INTERNAL_CONFLUENCE_TOKEN=file_internal
36
+ EXTERNAL_CONFLUENCE_TOKEN=file_external
37
+ CONFLUENCE_DEFAULT_SERVER=external
38
+ """.strip(),
39
+ )
40
+
41
+ monkeypatch.setenv("VDS_USERNAME", "env_user")
42
+ settings = load_settings(env_path=env_path)
43
+
44
+ assert settings.username == "env_user"
45
+ assert settings.password == "file_pass"
46
+ assert settings.internal_token == "file_internal"
47
+ assert settings.default_server == "external"
48
+
49
+
50
+ def test_load_settings_strict_requires_tokens(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
51
+ clear_runtime_env(monkeypatch)
52
+ env_path = write_env(
53
+ tmp_path,
54
+ """
55
+ VDS_USERNAME=file_user
56
+ VDS_PASSWORD=file_pass
57
+ """.strip(),
58
+ )
59
+
60
+ with pytest.raises(SettingsError):
61
+ load_settings(env_path=env_path, strict=True)
62
+
63
+
64
+ def test_url_and_token_resolution(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
65
+ clear_runtime_env(monkeypatch)
66
+ env_path = write_env(
67
+ tmp_path,
68
+ """
69
+ VDS_USERNAME=user
70
+ VDS_PASSWORD=pass
71
+ INTERNAL_CONFLUENCE_TOKEN=internal
72
+ EXTERNAL_CONFLUENCE_TOKEN=external
73
+ CONFLUENCE_INTERNAL_URL=http://internal.example.local
74
+ CONFLUENCE_EXTERNAL_URL=http://external.example.local
75
+ """.strip(),
76
+ )
77
+
78
+ settings = load_settings(env_path=env_path, strict=True)
79
+
80
+ assert settings.token_for("internal") == "internal"
81
+ assert settings.token_for("external") == "external"
82
+ assert str(settings.url_for("internal")).rstrip("/") == "http://internal.example.local"
83
+ assert str(settings.url_for("external")).rstrip("/") == "http://external.example.local"