@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.
- package/package.json +2 -2
- package/src/global.js +65 -39
- package/tools/skills/vds-scripts-skill/.openskills.json +6 -0
- package/tools/skills/vds-scripts-skill/QUALITY.md +44 -0
- package/tools/skills/vds-scripts-skill/SKILL.md +135 -0
- package/tools/skills/vds-scripts-skill/references/audit-commands.md +171 -0
- package/tools/skills/vds-scripts-skill/references/capability-index.md +34 -0
- package/tools/skills/vds-scripts-skill/references/development-commands.md +12 -0
- package/tools/skills/vds-scripts-skill/references/google-sheets.md +73 -0
- package/tools/skills/vds-scripts-skill/references/integration-commands.md +17 -0
- package/tools/skills/vds-scripts-skill/references/platform-bootstrap.md +31 -0
- package/tools/skills/vds-scripts-skill/references/specialist-routing.md +14 -0
- package/tools/skills/vds-scripts-skill/references/validation-commands.md +15 -0
- package/tools/skills/vsaf-build/SKILL.md +32 -2
- package/tools/skills/vsaf-ship/SKILL.md +41 -10
- package/tools/skills/vsaf-test/SKILL.md +8 -0
- package/tools/vds-scripts/.mcp.json +11 -0
- package/tools/vds-scripts/.secrets.baseline +133 -0
- package/tools/vds-scripts/AGENTS.md +152 -0
- package/tools/vds-scripts/CLAUDE.md +101 -0
- package/tools/vds-scripts/CLI_COMMAND_OPTIMIZATION.md +156 -0
- package/tools/vds-scripts/PACKAGE_P125B_IMPLEMENTATION_SUMMARY.md +131 -0
- package/tools/vds-scripts/PROJECT_COMPLETION_SUMMARY.md +45 -0
- package/tools/vds-scripts/README.md +97 -0
- package/tools/vds-scripts/bitbucket_manifest_mapping.toml +34 -0
- package/tools/vds-scripts/bitbucket_orchestrator/ARCHITECTURE_ANALYSIS.md +258 -0
- package/tools/vds-scripts/bitbucket_orchestrator/BITBUCKET_API_PRACTICES.md +393 -0
- package/tools/vds-scripts/bitbucket_orchestrator/EVALUATION_REPORT.md +61 -0
- package/tools/vds-scripts/bitbucket_orchestrator/FEATURES.md +908 -0
- package/tools/vds-scripts/bitbucket_orchestrator/README.md +687 -0
- package/tools/vds-scripts/bitbucket_orchestrator/pyproject.toml +40 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/__init__.py +20 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/async_client.py +657 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/cli.py +2108 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/client.py +2534 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/config.py +171 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/errors.py +67 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/factory.py +185 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/protocols.py +244 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/__init__.py +8 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/conftest.py +65 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_advanced_search.py +151 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_async_client.py +546 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_branch_permissions.py +145 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_cli.py +115 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client.py +157 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_branch_conditions.py +79 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_code_advanced.py +163 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_code_file.py +32 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_deployment_environments.py +194 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_issues.py +164 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_pipelines_advanced.py +179 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_pr_blockers.py +119 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_repository_variables.py +156 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code.py +98 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code_advanced.py +282 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code_insights.py +335 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_conditions.py +147 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_config.py +131 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_deployment_env.py +352 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_factory.py +371 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_fork_operations.py +204 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_issue_cli.py +261 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_pipeline_advanced.py +270 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_pr_blocker.py +204 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_protocols.py +334 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_repo_settings.py +343 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_repo_variables.py +270 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_webhooks.py +189 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_workspace.py +233 -0
- package/tools/vds-scripts/bitbucket_orchestrator/uv.lock +742 -0
- package/tools/vds-scripts/confluence_orchestrator/Dockerfile +19 -0
- package/tools/vds-scripts/confluence_orchestrator/README.md +412 -0
- package/tools/vds-scripts/confluence_orchestrator/SYNC_SCRIPTS.md +127 -0
- package/tools/vds-scripts/confluence_orchestrator/SYNC_STANDARDIZATION.md +108 -0
- package/tools/vds-scripts/confluence_orchestrator/pyproject.toml +48 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/__init__.py +20 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/cli.py +2532 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/config.py +175 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/content.py +290 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/content_v2.py +94 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/crawl_tree.py +1835 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/errors.py +80 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/eventing.py +109 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/http.py +1114 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/orchestration.py +165 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/reporting.py +78 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/tree.py +121 -0
- package/tools/vds-scripts/confluence_orchestrator/sync_pdfs_from_markdown.py +213 -0
- package/tools/vds-scripts/confluence_orchestrator/sync_pdfs_to_confluence.py +305 -0
- package/tools/vds-scripts/confluence_orchestrator/sync_png_attachments.py +305 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/conftest.py +8 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_advanced_content.py +224 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_advanced_search.py +188 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_cache_management.py +247 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_cli.py +499 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_config.py +83 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_content.py +186 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_content_flags.py +27 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_crawl_tree.py +2250 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_draft_management.py +223 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing.py +71 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_chaos.py +37 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_rate_limit.py +44 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_timeout.py +49 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_export.py +230 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_history.py +204 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_http.py +117 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_orchestration.py +91 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_reporting.py +24 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_search_cql.py +34 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_space_management.py +237 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_space_permissions.py +332 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_user_group_management.py +388 -0
- package/tools/vds-scripts/confluence_orchestrator/uv.lock +1023 -0
- package/tools/vds-scripts/git_orchestrator/ENHANCEMENT_SUMMARY.md +119 -0
- package/tools/vds-scripts/git_orchestrator/README.md +280 -0
- package/tools/vds-scripts/git_orchestrator/VERIFICATION_REPORT.md +152 -0
- package/tools/vds-scripts/git_orchestrator/pyproject.toml +35 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/__init__.py +7 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/__main__.py +4 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/cli.py +847 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/logging_config.py +63 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/manifest.py +129 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/orchestrator.py +819 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/reporting.py +53 -0
- package/tools/vds-scripts/git_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_cli_settings.py +21 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_integration.py +74 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_manifest.py +79 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_orchestrator.py +204 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_public_api.py +236 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_resilience.py +345 -0
- package/tools/vds-scripts/git_orchestrator/uv.lock +271 -0
- package/tools/vds-scripts/jira_orchestrator/README.md +770 -0
- package/tools/vds-scripts/jira_orchestrator/pyproject.toml +39 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/__init__.py +1 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/adapter.py +1320 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/cli.py +2271 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/config.py +138 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/errors.py +67 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/reporting.py +65 -0
- package/tools/vds-scripts/jira_orchestrator/tests/__init__.py +1 -0
- package/tools/vds-scripts/jira_orchestrator/tests/conftest.py +86 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_agile_list_payloads.py +54 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_bulk_operations.py +69 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_components.py +57 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_createmeta.py +45 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_dashboard.py +117 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_issue_properties.py +54 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_permissions_compat.py +42 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_reindex.py +42 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_remote_links.py +76 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_transitions.py +91 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_user_management.py +110 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_version_management.py +133 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_watchers.py +41 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_advanced_search.py +164 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_agile.py +256 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_application_properties.py +193 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_backlog.py +91 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_bulk_operations.py +277 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_cli.py +106 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_components.py +106 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_config.py +164 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_dashboard.py +122 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_discover_fields.py +207 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_filter_management.py +333 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_issue_archiving.py +164 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_issue_links.py +257 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_issue_properties.py +171 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_link_types.py +314 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_parse_set.py +37 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_permissions.py +273 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_reindex.py +81 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_remote_links.py +254 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_security_schemes.py +170 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_transitions_changelog.py +114 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_user_management.py +226 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_version_management.py +339 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_watchers.py +101 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_worklog.py +223 -0
- package/tools/vds-scripts/jira_orchestrator/uv.lock +738 -0
- package/tools/vds-scripts/mcp_server/Dockerfile +34 -0
- package/tools/vds-scripts/mcp_server/README.md +140 -0
- package/tools/vds-scripts/mcp_server/pyproject.toml +42 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/__init__.py +4 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/config.py +36 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/server.py +66 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/__init__.py +14 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/bitbucket_tools.py +47 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/confluence_tools.py +59 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/git_tools.py +71 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/jira_tools.py +63 -0
- package/tools/vds-scripts/mcp_server/tests/__init__.py +2 -0
- package/tools/vds-scripts/mcp_server/tests/conftest.py +29 -0
- package/tools/vds-scripts/mcp_server/tests/unit/__init__.py +2 -0
- package/tools/vds-scripts/mcp_server/tests/unit/test_bitbucket_tools.py +25 -0
- package/tools/vds-scripts/mcp_server/tests/unit/test_confluence_tools.py +25 -0
- package/tools/vds-scripts/mcp_server/tests/unit/test_git_tools.py +32 -0
- package/tools/vds-scripts/mcp_server/tests/unit/test_jira_tools.py +32 -0
- package/tools/vds-scripts/mcp_server/tests/verification/__init__.py +2 -0
- package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_confluence_tools.py +40 -0
- package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_jira_tools.py +37 -0
- package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_tool_registration.py +47 -0
- package/tools/vds-scripts/mcp_server/uv.lock +1032 -0
- package/tools/vds-scripts/mypy.ini +5 -0
- package/tools/vds-scripts/pyproject.toml +29 -0
- package/tools/vds-scripts/repo-manifest.yaml +273 -0
- package/tools/vds-scripts/repo-manifest.yaml.example +25 -0
- package/tools/vds-scripts/scripts/BRD-Validation-API.postman_collection.json +706 -0
- package/tools/vds-scripts/scripts/BRD-Validation-README.md +308 -0
- package/tools/vds-scripts/scripts/README.md +162 -0
- package/tools/vds-scripts/scripts/bootstrap_uv.sh +30 -0
- package/tools/vds-scripts/scripts/brd-validation-environment.json +51 -0
- package/tools/vds-scripts/scripts/brd-validation-test-results.json +13023 -0
- package/tools/vds-scripts/scripts/brd_coverage_report.json +276 -0
- package/tools/vds-scripts/scripts/create_memory_session.py +35 -0
- package/tools/vds-scripts/scripts/deployment/load_docker_images_offline.sh +90 -0
- package/tools/vds-scripts/scripts/final_completion_report.md +139 -0
- package/tools/vds-scripts/scripts/folder_structure_report.json +321 -0
- package/tools/vds-scripts/scripts/generate_completion_report.py +125 -0
- package/tools/vds-scripts/scripts/generate_intellij_modules.py +150 -0
- package/tools/vds-scripts/scripts/link_integrity_report.json +807 -0
- package/tools/vds-scripts/scripts/move_audit_artifact_pages.py +255 -0
- package/tools/vds-scripts/scripts/move_audit_artifact_pages_rest.py +165 -0
- package/tools/vds-scripts/scripts/move_wrong_dept_pages.py +216 -0
- package/tools/vds-scripts/scripts/save_intellij_memories.py +120 -0
- package/tools/vds-scripts/scripts/save_memories_to_vds_ai.py +83 -0
- package/tools/vds-scripts/scripts/save_memories_vds_style.py +129 -0
- package/tools/vds-scripts/scripts/search_intellij_memories.py +50 -0
- package/tools/vds-scripts/scripts/setup_intellij_workspace.py +65 -0
- package/tools/vds-scripts/scripts/target-state-automation/README.md +89 -0
- package/tools/vds-scripts/scripts/target-state-automation/confluence_sync_coordinator.sh +27 -0
- package/tools/vds-scripts/scripts/target-state-automation/coordination.sh +114 -0
- package/tools/vds-scripts/scripts/target-state-automation/diagram_coordinator.sh +25 -0
- package/tools/vds-scripts/scripts/target-state-automation/docs_root.sh +22 -0
- package/tools/vds-scripts/scripts/target-state-automation/generate_diagrams.sh +22 -0
- package/tools/vds-scripts/scripts/target-state-automation/markdown_coordinator.sh +25 -0
- package/tools/vds-scripts/scripts/target-state-automation/progress_dashboard.sh +17 -0
- package/tools/vds-scripts/scripts/target-state-automation/schema_coordinator.sh +25 -0
- package/tools/vds-scripts/scripts/target-state-automation/sync_confluence.sh +30 -0
- package/tools/vds-scripts/scripts/target-state-automation/update_dependencies.sh +19 -0
- package/tools/vds-scripts/scripts/target-state-automation/validate_links.sh +86 -0
- package/tools/vds-scripts/scripts/target-state-automation/validate_markdown.sh +52 -0
- package/tools/vds-scripts/scripts/target-state-automation/validate_schemas.sh +26 -0
- package/tools/vds-scripts/scripts/target-state-automation/validate_structure.sh +98 -0
- package/tools/vds-scripts/scripts/update_modules_xml.py +190 -0
- package/tools/vds-scripts/scripts/uv-workspace-alignment-verification-2026-03-25.md +128 -0
- package/tools/vds-scripts/scripts/validate_brd_coverage.py +179 -0
- package/tools/vds-scripts/scripts/validate_folder_structure.py +240 -0
- package/tools/vds-scripts/scripts/validate_link_integrity.py +272 -0
- package/tools/vds-scripts/scripts/vds_sh_helpers.sh +180 -0
- package/tools/vds-scripts/scripts/verification/phase2_portable_paths_ubuntu_docker.sh +26 -0
- package/tools/vds-scripts/scripts/worktree_uv.sh +48 -0
- package/tools/vds-scripts/uv.lock +8 -0
- package/tools/vds-scripts/vds_cli/README.md +126 -0
- package/tools/vds-scripts/vds_cli/VERIFICATION_REPORT.md +41 -0
- package/tools/vds-scripts/vds_cli/pyproject.toml +38 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/__init__.py +3 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/cli.py +173 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/docs_sync.py +1203 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/env.py +41 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/google_sheets_orchestrator/__init__.py +3 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/google_sheets_orchestrator/google_sheets_orchestrator.py +198 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/router.py +93 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/sync_api.py +647 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/sync_service.py +266 -0
- package/tools/vds-scripts/vds_cli/tests/__init__.py +2 -0
- package/tools/vds-scripts/vds_cli/tests/conftest.py +49 -0
- package/tools/vds-scripts/vds_cli/tests/unit/__init__.py +2 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_cli.py +143 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_docs_sync.py +422 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_env.py +51 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_router.py +72 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_sync_api.py +357 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_sync_service.py +160 -0
- package/tools/vds-scripts/vds_cli/tests/verification/__init__.py +2 -0
- package/tools/vds-scripts/vds_cli/tests/verification/test_bitbucket_real.py +33 -0
- package/tools/vds-scripts/vds_cli/tests/verification/test_confluence_real.py +35 -0
- package/tools/vds-scripts/vds_cli/tests/verification/test_jira_real.py +41 -0
- package/tools/vds-scripts/vds_cli/uv.lock +524 -0
- package/tools/vds-scripts/vds_cli_common/README.md +190 -0
- package/tools/vds-scripts/vds_cli_common/pyproject.toml +92 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/__init__.py +34 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/completers.py +139 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/context.py +201 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/env.py +119 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/errors.py +318 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/output.py +284 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/paths.py +78 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/testing.py +213 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/version.py +85 -0
- package/tools/vds-scripts/vds_cli_common/tests/__init__.py +1 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_completers.py +148 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_context.py +192 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_env.py +102 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_errors.py +186 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_output.py +229 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_paths.py +61 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_testing.py +138 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_version.py +64 -0
|
@@ -0,0 +1,117 @@
|
|
|
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, JiraAuthError, JiraNotFound
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
10
|
+
def test_get_dashboard_calls_client(mock_jira: MagicMock) -> None:
|
|
11
|
+
"""Test that get_dashboard calls the SDK client."""
|
|
12
|
+
client = MagicMock()
|
|
13
|
+
client.get_dashboard.return_value = {"id": "10000", "name": "Test Dashboard"}
|
|
14
|
+
mock_jira.return_value = client
|
|
15
|
+
|
|
16
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
17
|
+
result = adapter.get_dashboard("10000")
|
|
18
|
+
|
|
19
|
+
client.get_dashboard.assert_called_once_with("10000")
|
|
20
|
+
assert result == {"id": "10000", "name": "Test Dashboard"}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
24
|
+
def test_get_dashboard_with_int_id(mock_jira: MagicMock) -> None:
|
|
25
|
+
"""Test that get_dashboard works with integer ID."""
|
|
26
|
+
client = MagicMock()
|
|
27
|
+
client.get_dashboard.return_value = {"id": 10000, "name": "Test Dashboard"}
|
|
28
|
+
mock_jira.return_value = client
|
|
29
|
+
|
|
30
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
31
|
+
result = adapter.get_dashboard(10000)
|
|
32
|
+
|
|
33
|
+
client.get_dashboard.assert_called_once_with(10000)
|
|
34
|
+
assert result == {"id": 10000, "name": "Test Dashboard"}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
38
|
+
def test_get_dashboard_not_found(mock_jira: MagicMock) -> None:
|
|
39
|
+
"""Test that get_dashboard raises JiraNotFound when dashboard is not found."""
|
|
40
|
+
client = MagicMock()
|
|
41
|
+
client.get_dashboard.return_value = None
|
|
42
|
+
mock_jira.return_value = client
|
|
43
|
+
|
|
44
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
45
|
+
|
|
46
|
+
with pytest.raises(JiraNotFound, match="Dashboard not found: 99999"):
|
|
47
|
+
adapter.get_dashboard("99999")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
51
|
+
def test_get_dashboard_maps_error(mock_jira: MagicMock) -> None:
|
|
52
|
+
"""Test that get_dashboard maps authentication errors."""
|
|
53
|
+
client = MagicMock()
|
|
54
|
+
client.get_dashboard.side_effect = Exception("401 unauthorized")
|
|
55
|
+
mock_jira.return_value = client
|
|
56
|
+
|
|
57
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
58
|
+
|
|
59
|
+
with pytest.raises(JiraAuthError):
|
|
60
|
+
adapter.get_dashboard("10000")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
64
|
+
def test_get_dashboards_calls_client(mock_jira: MagicMock) -> None:
|
|
65
|
+
"""Test that get_dashboards calls the SDK client."""
|
|
66
|
+
client = MagicMock()
|
|
67
|
+
client.get_dashboards.return_value = {
|
|
68
|
+
"dashboards": [{"id": "10000", "name": "Dashboard 1"}, {"id": "10001", "name": "Dashboard 2"}]
|
|
69
|
+
}
|
|
70
|
+
mock_jira.return_value = client
|
|
71
|
+
|
|
72
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
73
|
+
result = adapter.get_dashboards()
|
|
74
|
+
|
|
75
|
+
client.get_dashboards.assert_called_once_with(filter="", start=0, limit=10)
|
|
76
|
+
assert len(result["dashboards"]) == 2
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
80
|
+
def test_get_dashboards_with_filter(mock_jira: MagicMock) -> None:
|
|
81
|
+
"""Test that get_dashboards works with filter and pagination."""
|
|
82
|
+
client = MagicMock()
|
|
83
|
+
client.get_dashboards.return_value = {"dashboards": []}
|
|
84
|
+
mock_jira.return_value = client
|
|
85
|
+
|
|
86
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
87
|
+
result = adapter.get_dashboards(filter="test", start=10, limit=20)
|
|
88
|
+
|
|
89
|
+
client.get_dashboards.assert_called_once_with(filter="test", start=10, limit=20)
|
|
90
|
+
assert result == {"dashboards": []}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
94
|
+
def test_get_dashboards_returns_empty_dict_when_none(mock_jira: MagicMock) -> None:
|
|
95
|
+
"""Test that get_dashboards returns empty dict when client returns None."""
|
|
96
|
+
client = MagicMock()
|
|
97
|
+
client.get_dashboards.return_value = None
|
|
98
|
+
mock_jira.return_value = client
|
|
99
|
+
|
|
100
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
101
|
+
result = adapter.get_dashboards()
|
|
102
|
+
|
|
103
|
+
assert result == {}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
107
|
+
def test_get_dashboards_maps_error(mock_jira: MagicMock) -> None:
|
|
108
|
+
"""Test that get_dashboards maps authentication errors."""
|
|
109
|
+
client = MagicMock()
|
|
110
|
+
client.get_dashboards.side_effect = Exception("401 unauthorized")
|
|
111
|
+
mock_jira.return_value = client
|
|
112
|
+
|
|
113
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
114
|
+
|
|
115
|
+
with pytest.raises(JiraAuthError):
|
|
116
|
+
adapter.get_dashboards()
|
|
117
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
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_issue_property_keys_calls_sdk(mock_jira: MagicMock) -> None:
|
|
10
|
+
client = MagicMock()
|
|
11
|
+
client.get_issue_property_keys.return_value = {"keys": [{"key": "custom"}]}
|
|
12
|
+
mock_jira.return_value = client
|
|
13
|
+
|
|
14
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
15
|
+
payload = adapter.get_issue_property_keys("NTTC-1")
|
|
16
|
+
|
|
17
|
+
client.get_issue_property_keys.assert_called_once_with("NTTC-1")
|
|
18
|
+
assert payload == {"keys": [{"key": "custom"}]}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
22
|
+
def test_set_issue_property_calls_sdk(mock_jira: MagicMock) -> None:
|
|
23
|
+
client = MagicMock()
|
|
24
|
+
mock_jira.return_value = client
|
|
25
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
26
|
+
|
|
27
|
+
adapter.set_issue_property("NTTC-2", "custom.key", {"foo": "bar"})
|
|
28
|
+
|
|
29
|
+
client.set_issue_property.assert_called_once_with("NTTC-2", "custom.key", {"foo": "bar"})
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
33
|
+
def test_get_issue_property_calls_sdk(mock_jira: MagicMock) -> None:
|
|
34
|
+
client = MagicMock()
|
|
35
|
+
client.get_issue_property.return_value = {"value": {"foo": "bar"}}
|
|
36
|
+
mock_jira.return_value = client
|
|
37
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
38
|
+
|
|
39
|
+
payload = adapter.get_issue_property("NTTC-3", "custom.key")
|
|
40
|
+
|
|
41
|
+
client.get_issue_property.assert_called_once_with("NTTC-3", "custom.key")
|
|
42
|
+
assert payload == {"value": {"foo": "bar"}}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
46
|
+
def test_delete_issue_property_calls_sdk(mock_jira: MagicMock) -> None:
|
|
47
|
+
client = MagicMock()
|
|
48
|
+
mock_jira.return_value = client
|
|
49
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
50
|
+
|
|
51
|
+
adapter.delete_issue_property("NTTC-4", "custom.key")
|
|
52
|
+
|
|
53
|
+
client.delete_issue_property.assert_called_once_with("NTTC-4", "custom.key")
|
|
54
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from unittest.mock import Mock, patch
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from vds_jira_orchestrator.adapter import JiraAdapter
|
|
8
|
+
from vds_jira_orchestrator.errors import JiraAdapterError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
12
|
+
def test_get_permissions_uses_official_get_permissions(mock_jira: Mock) -> None:
|
|
13
|
+
class Client:
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
self.get_permissions = Mock(return_value={"permissions": {}})
|
|
16
|
+
|
|
17
|
+
client = Client()
|
|
18
|
+
mock_jira.return_value = client
|
|
19
|
+
|
|
20
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
21
|
+
adapter.get_permissions(permissions=["BROWSE_PROJECTS", "ADMINISTER_PROJECTS"], project_key="NTTC")
|
|
22
|
+
|
|
23
|
+
client.get_permissions.assert_called_once_with(
|
|
24
|
+
permissions="BROWSE_PROJECTS,ADMINISTER_PROJECTS",
|
|
25
|
+
project_id=None,
|
|
26
|
+
project_key="NTTC",
|
|
27
|
+
issue_id=None,
|
|
28
|
+
issue_key=None,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
33
|
+
def test_get_permissions_raises_when_not_supported(mock_jira: Mock) -> None:
|
|
34
|
+
class Client:
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
client = Client()
|
|
38
|
+
mock_jira.return_value = client
|
|
39
|
+
|
|
40
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
41
|
+
with pytest.raises(JiraAdapterError):
|
|
42
|
+
adapter.get_permissions(permissions=["BROWSE_PROJECTS"])
|
|
@@ -0,0 +1,42 @@
|
|
|
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, JiraAuthError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
10
|
+
def test_reindex_calls_client(mock_jira: MagicMock) -> None:
|
|
11
|
+
client = MagicMock()
|
|
12
|
+
client.reindex.return_value = {"taskId": 123}
|
|
13
|
+
mock_jira.return_value = client
|
|
14
|
+
|
|
15
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
16
|
+
result = adapter.reindex()
|
|
17
|
+
|
|
18
|
+
client.reindex.assert_called_once_with()
|
|
19
|
+
assert result == {"taskId": 123}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
23
|
+
def test_reindex_status_maps_error(mock_jira: MagicMock) -> None:
|
|
24
|
+
client = MagicMock()
|
|
25
|
+
client.reindex_status.side_effect = Exception("401 unauthorized")
|
|
26
|
+
mock_jira.return_value = client
|
|
27
|
+
|
|
28
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
29
|
+
|
|
30
|
+
with pytest.raises(JiraAuthError):
|
|
31
|
+
adapter.reindex_status()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
35
|
+
def test_move_issues_to_backlog(mock_jira: MagicMock) -> None:
|
|
36
|
+
client = MagicMock()
|
|
37
|
+
mock_jira.return_value = client
|
|
38
|
+
|
|
39
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
40
|
+
adapter.move_issues_to_backlog(["NTTC-1", "NTTC-2"])
|
|
41
|
+
|
|
42
|
+
client.move_issues_to_backlog.assert_called_once_with(["NTTC-1", "NTTC-2"])
|
|
@@ -0,0 +1,76 @@
|
|
|
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_issue_remote_links_returns_list(mock_jira: MagicMock) -> None:
|
|
10
|
+
"""Ensure get_issue_remote_links proxies to SDK and normalises response."""
|
|
11
|
+
client = MagicMock()
|
|
12
|
+
client.get_issue_remote_links.return_value = [{"id": 1}, {"id": 2}]
|
|
13
|
+
mock_jira.return_value = client
|
|
14
|
+
|
|
15
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
16
|
+
result = adapter.get_issue_remote_links("NTTC-1")
|
|
17
|
+
|
|
18
|
+
client.get_issue_remote_links.assert_called_once_with("NTTC-1")
|
|
19
|
+
assert result == [{"id": 1}, {"id": 2}]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
23
|
+
def test_create_or_update_issue_remote_link_calls_sdk(mock_jira: MagicMock) -> None:
|
|
24
|
+
"""Ensure create_or_update_issue_remote_link delegates to SDK."""
|
|
25
|
+
client = MagicMock()
|
|
26
|
+
client.create_or_update_issue_remote_links.return_value = {"created": True}
|
|
27
|
+
mock_jira.return_value = client
|
|
28
|
+
payload = {"object": {"url": "https://example.com"}}
|
|
29
|
+
|
|
30
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
31
|
+
result = adapter.create_or_update_issue_remote_link("NTTC-2", payload)
|
|
32
|
+
|
|
33
|
+
client.create_or_update_issue_remote_links.assert_called_once_with("NTTC-2", payload)
|
|
34
|
+
assert result == {"created": True}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
38
|
+
def test_get_issue_remote_link_by_id_calls_sdk(mock_jira: MagicMock) -> None:
|
|
39
|
+
"""Ensure get_issue_remote_link_by_id delegates to SDK."""
|
|
40
|
+
client = MagicMock()
|
|
41
|
+
client.get_issue_remote_link_by_id.return_value = {"id": 5}
|
|
42
|
+
mock_jira.return_value = client
|
|
43
|
+
|
|
44
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
45
|
+
result = adapter.get_issue_remote_link_by_id("NTTC-3", 5)
|
|
46
|
+
|
|
47
|
+
client.get_issue_remote_link_by_id.assert_called_once_with("NTTC-3", 5)
|
|
48
|
+
assert result == {"id": 5}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
52
|
+
def test_update_issue_remote_link_by_id_calls_sdk(mock_jira: MagicMock) -> None:
|
|
53
|
+
"""Ensure update_issue_remote_link_by_id delegates to SDK."""
|
|
54
|
+
client = MagicMock()
|
|
55
|
+
client.update_issue_remote_link_by_id.return_value = {"updated": True}
|
|
56
|
+
mock_jira.return_value = client
|
|
57
|
+
payload = {"object": {"url": "https://example.com/updated"}}
|
|
58
|
+
|
|
59
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
60
|
+
result = adapter.update_issue_remote_link_by_id("NTTC-4", "abc", payload)
|
|
61
|
+
|
|
62
|
+
client.update_issue_remote_link_by_id.assert_called_once_with("NTTC-4", "abc", payload)
|
|
63
|
+
assert result == {"updated": True}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
67
|
+
def test_delete_issue_remote_link_by_id_calls_sdk(mock_jira: MagicMock) -> None:
|
|
68
|
+
"""Ensure delete_issue_remote_link_by_id delegates to SDK."""
|
|
69
|
+
client = MagicMock()
|
|
70
|
+
mock_jira.return_value = client
|
|
71
|
+
|
|
72
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
73
|
+
adapter.delete_issue_remote_link_by_id("NTTC-5", 9)
|
|
74
|
+
|
|
75
|
+
client.delete_issue_remote_link_by_id.assert_called_once_with("NTTC-5", 9)
|
|
76
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from unittest.mock import Mock, patch
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from vds_jira_orchestrator.adapter import JiraAdapter
|
|
8
|
+
from vds_jira_orchestrator.errors import JiraAdapterError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
12
|
+
def test_transition_issue_uses_issue_transition(mock_jira: Mock) -> None:
|
|
13
|
+
class Client:
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
self.get_issue_transitions = Mock(return_value=[{"id": 71, "name": "Cancelled"}])
|
|
16
|
+
self.issue_transition = Mock()
|
|
17
|
+
|
|
18
|
+
client = Client()
|
|
19
|
+
mock_jira.return_value = client
|
|
20
|
+
|
|
21
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
22
|
+
adapter.transition_issue("NTTC-1", "Cancelled")
|
|
23
|
+
|
|
24
|
+
client.get_issue_transitions.assert_called_once_with("NTTC-1")
|
|
25
|
+
client.issue_transition.assert_called_once_with("NTTC-1", "Cancelled")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
29
|
+
def test_transition_issue_falls_back_to_set_issue_status_by_transition_name(mock_jira: Mock) -> None:
|
|
30
|
+
class Client:
|
|
31
|
+
def __init__(self) -> None:
|
|
32
|
+
self.get_issue_transitions = Mock(return_value=[{"id": "11", "name": "Develop"}])
|
|
33
|
+
self.set_issue_status_by_transition_name = Mock()
|
|
34
|
+
|
|
35
|
+
client = Client()
|
|
36
|
+
mock_jira.return_value = client
|
|
37
|
+
|
|
38
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
39
|
+
adapter.transition_issue("NTTC-2", "Develop")
|
|
40
|
+
|
|
41
|
+
client.get_issue_transitions.assert_called_once_with("NTTC-2")
|
|
42
|
+
client.set_issue_status_by_transition_name.assert_called_once_with("NTTC-2", "Develop")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
46
|
+
def test_transition_issue_falls_back_to_set_issue_status_by_transition_id(mock_jira: Mock) -> None:
|
|
47
|
+
class Client:
|
|
48
|
+
def __init__(self) -> None:
|
|
49
|
+
self.get_issue_transitions = Mock(return_value=[{"id": 81, "name": "Pending"}])
|
|
50
|
+
self.set_issue_status_by_transition_id = Mock()
|
|
51
|
+
|
|
52
|
+
client = Client()
|
|
53
|
+
mock_jira.return_value = client
|
|
54
|
+
|
|
55
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
56
|
+
adapter.transition_issue("NTTC-3", "Pending")
|
|
57
|
+
|
|
58
|
+
client.get_issue_transitions.assert_called_once_with("NTTC-3")
|
|
59
|
+
client.set_issue_status_by_transition_id.assert_called_once_with("NTTC-3", 81)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
63
|
+
def test_get_issue_transitions_falls_back_to_full_payload(mock_jira: Mock) -> None:
|
|
64
|
+
class Client:
|
|
65
|
+
def __init__(self) -> None:
|
|
66
|
+
self.get_issue_transitions_full = Mock(
|
|
67
|
+
return_value={"transitions": [{"id": "41", "name": "Assign OS", "to": {"name": "Assign OS"}}]}
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
client = Client()
|
|
71
|
+
mock_jira.return_value = client
|
|
72
|
+
|
|
73
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
74
|
+
transitions = adapter.get_issue_transitions("NTTC-4")
|
|
75
|
+
|
|
76
|
+
assert transitions[0]["name"] == "Assign OS"
|
|
77
|
+
client.get_issue_transitions_full.assert_called_once_with("NTTC-4")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
81
|
+
def test_transition_issue_raises_when_transition_not_found(mock_jira: Mock) -> None:
|
|
82
|
+
class Client:
|
|
83
|
+
def __init__(self) -> None:
|
|
84
|
+
self.get_issue_transitions = Mock(return_value=[{"id": 71, "name": "Cancelled"}])
|
|
85
|
+
|
|
86
|
+
client = Client()
|
|
87
|
+
mock_jira.return_value = client
|
|
88
|
+
|
|
89
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
90
|
+
with pytest.raises(JiraAdapterError):
|
|
91
|
+
adapter.transition_issue("NTTC-5", "Done")
|
|
@@ -0,0 +1,110 @@
|
|
|
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
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
10
|
+
def test_find_users_calls_client(mock_jira: MagicMock) -> None:
|
|
11
|
+
"""Test that find_users calls the SDK client."""
|
|
12
|
+
client = MagicMock()
|
|
13
|
+
client.user_find_by_user_string.return_value = [
|
|
14
|
+
{"accountId": "123", "displayName": "Test User", "emailAddress": "test@example.com"}
|
|
15
|
+
]
|
|
16
|
+
mock_jira.return_value = client
|
|
17
|
+
|
|
18
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
19
|
+
result = adapter.find_users(query="test@example.com")
|
|
20
|
+
|
|
21
|
+
client.user_find_by_user_string.assert_called_once_with(
|
|
22
|
+
query="test@example.com", start=0, limit=50, include_inactive_users=False
|
|
23
|
+
)
|
|
24
|
+
assert len(result) == 1
|
|
25
|
+
assert result[0]["accountId"] == "123"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
29
|
+
def test_find_users_with_pagination(mock_jira: MagicMock) -> None:
|
|
30
|
+
"""Test that find_users passes pagination parameters."""
|
|
31
|
+
client = MagicMock()
|
|
32
|
+
client.user_find_by_user_string.return_value = []
|
|
33
|
+
mock_jira.return_value = client
|
|
34
|
+
|
|
35
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
36
|
+
adapter.find_users(query="test", start=10, limit=20, include_inactive=True)
|
|
37
|
+
|
|
38
|
+
client.user_find_by_user_string.assert_called_once_with(
|
|
39
|
+
query="test", start=10, limit=20, include_inactive_users=True
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
44
|
+
def test_get_user_groups_cloud_mode(mock_jira: MagicMock) -> None:
|
|
45
|
+
"""Test that get_user_groups works in Cloud mode."""
|
|
46
|
+
client = MagicMock()
|
|
47
|
+
client.get_user_groups.return_value = [{"name": "jira-users"}, {"name": "jira-administrators"}]
|
|
48
|
+
mock_jira.return_value = client
|
|
49
|
+
|
|
50
|
+
adapter = JiraAdapter(url="https://test.atlassian.net", username="user", password="pass")
|
|
51
|
+
result = adapter.get_user_groups("account-123")
|
|
52
|
+
|
|
53
|
+
client.get_user_groups.assert_called_once_with("account-123")
|
|
54
|
+
assert len(result) == 2
|
|
55
|
+
assert result[0]["name"] == "jira-users"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
59
|
+
def test_get_user_groups_server_mode_raises_error(mock_jira: MagicMock) -> None:
|
|
60
|
+
"""Test that get_user_groups raises error in Server/DC mode."""
|
|
61
|
+
client = MagicMock()
|
|
62
|
+
mock_jira.return_value = client
|
|
63
|
+
|
|
64
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
65
|
+
|
|
66
|
+
with pytest.raises(JiraAdapterError, match="only available in Jira Cloud mode"):
|
|
67
|
+
adapter.get_user_groups("account-123")
|
|
68
|
+
|
|
69
|
+
client.get_user_groups.assert_not_called()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
73
|
+
def test_get_user_groups_explicit_cloud_mode(mock_jira: MagicMock) -> None:
|
|
74
|
+
"""Test that get_user_groups works with explicit cloud=True."""
|
|
75
|
+
client = MagicMock()
|
|
76
|
+
client.get_user_groups.return_value = [{"name": "jira-users"}]
|
|
77
|
+
mock_jira.return_value = client
|
|
78
|
+
|
|
79
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass", cloud=True)
|
|
80
|
+
result = adapter.get_user_groups("account-123")
|
|
81
|
+
|
|
82
|
+
client.get_user_groups.assert_called_once_with("account-123")
|
|
83
|
+
assert len(result) == 1
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
87
|
+
def test_deactivate_user_calls_client(mock_jira: MagicMock) -> None:
|
|
88
|
+
"""Test that deactivate_user calls the SDK client."""
|
|
89
|
+
client = MagicMock()
|
|
90
|
+
client.user_deactivate.return_value = None
|
|
91
|
+
mock_jira.return_value = client
|
|
92
|
+
|
|
93
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
94
|
+
adapter.deactivate_user("testuser")
|
|
95
|
+
|
|
96
|
+
client.user_deactivate.assert_called_once_with("testuser")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
100
|
+
def test_deactivate_user_handles_error(mock_jira: MagicMock) -> None:
|
|
101
|
+
"""Test that deactivate_user handles errors correctly."""
|
|
102
|
+
client = MagicMock()
|
|
103
|
+
client.user_deactivate.side_effect = Exception("User not found")
|
|
104
|
+
mock_jira.return_value = client
|
|
105
|
+
|
|
106
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
107
|
+
|
|
108
|
+
with pytest.raises(JiraAdapterError):
|
|
109
|
+
adapter.deactivate_user("nonexistent")
|
|
110
|
+
|
|
@@ -0,0 +1,133 @@
|
|
|
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_get_project_versions_paginated_calls_client(mock_adapter: JiraAdapter) -> None:
|
|
21
|
+
"""Test that get_project_versions_paginated calls the SDK client with correct parameters."""
|
|
22
|
+
mock_adapter._client.get_project_versions_paginated.return_value = {
|
|
23
|
+
"values": [{"id": "10000", "name": "1.0.0"}],
|
|
24
|
+
"startAt": 0,
|
|
25
|
+
"maxResults": 25,
|
|
26
|
+
}
|
|
27
|
+
result = mock_adapter.get_project_versions_paginated(project_key="TEST")
|
|
28
|
+
mock_adapter._client.get_project_versions_paginated.assert_called_once_with(
|
|
29
|
+
"TEST", start=None, limit=None, order_by=None, expand=None, query=None, status=None
|
|
30
|
+
)
|
|
31
|
+
assert "values" in result
|
|
32
|
+
assert len(result["values"]) == 1
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_get_project_versions_paginated_with_pagination(mock_adapter: JiraAdapter) -> None:
|
|
36
|
+
"""Test get_project_versions_paginated with pagination parameters."""
|
|
37
|
+
mock_adapter._client.get_project_versions_paginated.return_value = {
|
|
38
|
+
"values": [{"id": "10001", "name": "1.0.1"}],
|
|
39
|
+
"startAt": 10,
|
|
40
|
+
"maxResults": 20,
|
|
41
|
+
}
|
|
42
|
+
result = mock_adapter.get_project_versions_paginated(project_key="TEST", start=10, limit=20)
|
|
43
|
+
mock_adapter._client.get_project_versions_paginated.assert_called_once_with(
|
|
44
|
+
"TEST", start=10, limit=20, order_by=None, expand=None, query=None, status=None
|
|
45
|
+
)
|
|
46
|
+
assert result["startAt"] == 10
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_get_project_versions_paginated_with_filtering(mock_adapter: JiraAdapter) -> None:
|
|
50
|
+
"""Test get_project_versions_paginated with filtering and sorting."""
|
|
51
|
+
mock_adapter._client.get_project_versions_paginated.return_value = {
|
|
52
|
+
"values": [{"id": "10002", "name": "2.0.0"}],
|
|
53
|
+
"startAt": 0,
|
|
54
|
+
"maxResults": 25,
|
|
55
|
+
}
|
|
56
|
+
result = mock_adapter.get_project_versions_paginated(
|
|
57
|
+
project_key="TEST", order_by="name", query="2.0", status="released"
|
|
58
|
+
)
|
|
59
|
+
mock_adapter._client.get_project_versions_paginated.assert_called_once_with(
|
|
60
|
+
"TEST", start=None, limit=None, order_by="name", expand=None, query="2.0", status="released"
|
|
61
|
+
)
|
|
62
|
+
assert len(result["values"]) == 1
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_add_version_calls_client(mock_adapter: JiraAdapter) -> None:
|
|
66
|
+
"""Test that add_version calls the SDK client."""
|
|
67
|
+
mock_adapter._client.add_version.return_value = {"id": "10000", "name": "1.0.0"}
|
|
68
|
+
result = mock_adapter.add_version(key="TEST", project_id="10000", version="1.0.0")
|
|
69
|
+
mock_adapter._client.add_version.assert_called_once_with("TEST", "10000", "1.0.0", is_archived=False, is_released=False)
|
|
70
|
+
assert result["name"] == "1.0.0"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_add_version_with_flags(mock_adapter: JiraAdapter) -> None:
|
|
74
|
+
"""Test add_version with archived and released flags."""
|
|
75
|
+
mock_adapter._client.add_version.return_value = {"id": "10001", "name": "1.0.1", "archived": True, "released": True}
|
|
76
|
+
result = mock_adapter.add_version(key="TEST", project_id="10000", version="1.0.1", is_archived=True, is_released=True)
|
|
77
|
+
mock_adapter._client.add_version.assert_called_once_with("TEST", "10000", "1.0.1", is_archived=True, is_released=True)
|
|
78
|
+
assert result["archived"] is True
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_add_version_handles_error(mock_adapter: JiraAdapter) -> None:
|
|
82
|
+
"""Test that add_version handles errors from the SDK client."""
|
|
83
|
+
mock_adapter._client.add_version.side_effect = Exception("API Error")
|
|
84
|
+
with pytest.raises(JiraAdapterError, match="API Error"):
|
|
85
|
+
mock_adapter.add_version(key="TEST", project_id="10000", version="1.0.0")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_update_version_calls_client(mock_adapter: JiraAdapter) -> None:
|
|
89
|
+
"""Test that update_version calls the SDK client."""
|
|
90
|
+
mock_adapter._client.update_version.return_value = {"id": "10000", "name": "1.0.1"}
|
|
91
|
+
result = mock_adapter.update_version(version="10000", name="1.0.1")
|
|
92
|
+
mock_adapter._client.update_version.assert_called_once_with(
|
|
93
|
+
"10000", name="1.0.1", description=None, is_archived=None, is_released=None, start_date=None, release_date=None
|
|
94
|
+
)
|
|
95
|
+
assert result["name"] == "1.0.1"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_update_version_partial_update(mock_adapter: JiraAdapter) -> None:
|
|
99
|
+
"""Test update_version with partial updates."""
|
|
100
|
+
mock_adapter._client.update_version.return_value = {
|
|
101
|
+
"id": "10000",
|
|
102
|
+
"name": "1.0.0",
|
|
103
|
+
"description": "Updated description",
|
|
104
|
+
"archived": True,
|
|
105
|
+
}
|
|
106
|
+
result = mock_adapter.update_version(version="10000", description="Updated description", is_archived=True)
|
|
107
|
+
mock_adapter._client.update_version.assert_called_once_with(
|
|
108
|
+
"10000", name=None, description="Updated description", is_archived=True, is_released=None, start_date=None, release_date=None
|
|
109
|
+
)
|
|
110
|
+
assert result["description"] == "Updated description"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_update_version_with_dates(mock_adapter: JiraAdapter) -> None:
|
|
114
|
+
"""Test update_version with start and release dates."""
|
|
115
|
+
mock_adapter._client.update_version.return_value = {
|
|
116
|
+
"id": "10000",
|
|
117
|
+
"name": "1.0.0",
|
|
118
|
+
"startDate": "2025-01-01",
|
|
119
|
+
"releaseDate": "2025-01-31",
|
|
120
|
+
}
|
|
121
|
+
result = mock_adapter.update_version(version="10000", start_date="2025-01-01", release_date="2025-01-31")
|
|
122
|
+
mock_adapter._client.update_version.assert_called_once_with(
|
|
123
|
+
"10000", name=None, description=None, is_archived=None, is_released=None, start_date="2025-01-01", release_date="2025-01-31"
|
|
124
|
+
)
|
|
125
|
+
assert result["startDate"] == "2025-01-01"
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_update_version_handles_error(mock_adapter: JiraAdapter) -> None:
|
|
129
|
+
"""Test that update_version handles errors from the SDK client."""
|
|
130
|
+
mock_adapter._client.update_version.side_effect = Exception("API Error")
|
|
131
|
+
with pytest.raises(JiraAdapterError, match="API Error"):
|
|
132
|
+
mock_adapter.update_version(version="10000", name="1.0.1")
|
|
133
|
+
|