@ngocsangairvds/vsaf 3.1.26 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/global.js +70 -10
- 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-push-prd/SKILL.md +43 -40
- package/tools/skills/vsaf-push-srs/SKILL.md +44 -41
- 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,546 @@
|
|
|
1
|
+
"""Unit tests for AsyncBitbucketClient (TSK-225).
|
|
2
|
+
|
|
3
|
+
Tests for the async wrapper that uses asyncio.to_thread() to wrap
|
|
4
|
+
synchronous BitbucketClient methods.
|
|
5
|
+
|
|
6
|
+
References:
|
|
7
|
+
- FR-21.2.3: AsyncBitbucketClient using asyncio.to_thread()
|
|
8
|
+
- TSK-225: Async Bitbucket wrapper
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import asyncio
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from unittest.mock import AsyncMock, MagicMock, patch
|
|
16
|
+
|
|
17
|
+
import pytest
|
|
18
|
+
|
|
19
|
+
from vds_bitbucket_orchestrator.async_client import AsyncBitbucketClient
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def write_env(tmp_path: Path, content: str) -> Path:
|
|
23
|
+
"""Write a temporary .env file for testing."""
|
|
24
|
+
env_path = tmp_path / ".env"
|
|
25
|
+
env_path.write_text(content)
|
|
26
|
+
return env_path
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def clear_runtime_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
30
|
+
"""Clear runtime environment variables for testing."""
|
|
31
|
+
for key in (
|
|
32
|
+
"VDS_USERNAME",
|
|
33
|
+
"VDS_PASSWORD",
|
|
34
|
+
"BITBUCKET_TOKEN",
|
|
35
|
+
"VDS_BITBUCKET_URL",
|
|
36
|
+
"BITBUCKET_BASE_URL",
|
|
37
|
+
"VDS_BITBUCKET_HTTP_BASE",
|
|
38
|
+
"BITBUCKET_CLOUD_WORKSPACE",
|
|
39
|
+
"BITBUCKET_MODE",
|
|
40
|
+
):
|
|
41
|
+
monkeypatch.delenv(key, raising=False)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TestAsyncBitbucketClientInit:
|
|
45
|
+
"""Tests for AsyncBitbucketClient initialization."""
|
|
46
|
+
|
|
47
|
+
def test_async_client_creates_sync_client(
|
|
48
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Test that AsyncBitbucketClient creates an underlying sync client."""
|
|
51
|
+
clear_runtime_env(monkeypatch)
|
|
52
|
+
env_path = write_env(
|
|
53
|
+
tmp_path,
|
|
54
|
+
"""
|
|
55
|
+
VDS_USERNAME=test_user
|
|
56
|
+
VDS_PASSWORD=test_pass
|
|
57
|
+
VDS_BITBUCKET_URL=https://bitbucket.test.com
|
|
58
|
+
""".strip(),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
|
|
62
|
+
mock_instance = MagicMock()
|
|
63
|
+
mock_client.return_value = mock_instance
|
|
64
|
+
|
|
65
|
+
from vds_bitbucket_orchestrator.config import load_settings
|
|
66
|
+
|
|
67
|
+
settings = load_settings(env_path=env_path)
|
|
68
|
+
async_client = AsyncBitbucketClient(settings)
|
|
69
|
+
|
|
70
|
+
# Verify sync client was created
|
|
71
|
+
mock_client.assert_called_once()
|
|
72
|
+
assert async_client.sync_client is mock_instance
|
|
73
|
+
|
|
74
|
+
def test_async_client_passes_timeout(
|
|
75
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
76
|
+
) -> None:
|
|
77
|
+
"""Test that timeout is passed to the underlying sync client."""
|
|
78
|
+
clear_runtime_env(monkeypatch)
|
|
79
|
+
env_path = write_env(
|
|
80
|
+
tmp_path,
|
|
81
|
+
"""
|
|
82
|
+
VDS_USERNAME=test_user
|
|
83
|
+
VDS_PASSWORD=test_pass
|
|
84
|
+
""".strip(),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
|
|
88
|
+
mock_instance = MagicMock()
|
|
89
|
+
mock_client.return_value = mock_instance
|
|
90
|
+
|
|
91
|
+
from vds_bitbucket_orchestrator.config import load_settings
|
|
92
|
+
|
|
93
|
+
settings = load_settings(env_path=env_path)
|
|
94
|
+
AsyncBitbucketClient(settings, timeout=60)
|
|
95
|
+
|
|
96
|
+
call_args = mock_client.call_args
|
|
97
|
+
assert call_args.kwargs.get("timeout") == 60
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class TestAsyncRepositoryOperations:
|
|
101
|
+
"""Tests for async repository operations."""
|
|
102
|
+
|
|
103
|
+
@pytest.mark.asyncio
|
|
104
|
+
async def test_list_projects_async(
|
|
105
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
106
|
+
) -> None:
|
|
107
|
+
"""Test async list_projects method."""
|
|
108
|
+
clear_runtime_env(monkeypatch)
|
|
109
|
+
env_path = write_env(
|
|
110
|
+
tmp_path,
|
|
111
|
+
"""
|
|
112
|
+
VDS_USERNAME=test_user
|
|
113
|
+
VDS_PASSWORD=test_pass
|
|
114
|
+
""".strip(),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
|
|
118
|
+
mock_instance = MagicMock()
|
|
119
|
+
mock_instance.list_projects.return_value = [
|
|
120
|
+
{"key": "INS", "name": "Insurance"},
|
|
121
|
+
{"key": "LEP", "name": "Lending"},
|
|
122
|
+
]
|
|
123
|
+
mock_client.return_value = mock_instance
|
|
124
|
+
|
|
125
|
+
from vds_bitbucket_orchestrator.config import load_settings
|
|
126
|
+
|
|
127
|
+
settings = load_settings(env_path=env_path)
|
|
128
|
+
async_client = AsyncBitbucketClient(settings)
|
|
129
|
+
|
|
130
|
+
result = await async_client.list_projects(limit=10)
|
|
131
|
+
|
|
132
|
+
assert len(result) == 2
|
|
133
|
+
assert result[0]["key"] == "INS"
|
|
134
|
+
mock_instance.list_projects.assert_called_once_with(10)
|
|
135
|
+
|
|
136
|
+
@pytest.mark.asyncio
|
|
137
|
+
async def test_get_project_async(
|
|
138
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
139
|
+
) -> None:
|
|
140
|
+
"""Test async get_project method."""
|
|
141
|
+
clear_runtime_env(monkeypatch)
|
|
142
|
+
env_path = write_env(
|
|
143
|
+
tmp_path,
|
|
144
|
+
"""
|
|
145
|
+
VDS_USERNAME=test_user
|
|
146
|
+
VDS_PASSWORD=test_pass
|
|
147
|
+
""".strip(),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
|
|
151
|
+
mock_instance = MagicMock()
|
|
152
|
+
mock_instance.get_project.return_value = {"key": "INS", "name": "Insurance"}
|
|
153
|
+
mock_client.return_value = mock_instance
|
|
154
|
+
|
|
155
|
+
from vds_bitbucket_orchestrator.config import load_settings
|
|
156
|
+
|
|
157
|
+
settings = load_settings(env_path=env_path)
|
|
158
|
+
async_client = AsyncBitbucketClient(settings)
|
|
159
|
+
|
|
160
|
+
result = await async_client.get_project("INS")
|
|
161
|
+
|
|
162
|
+
assert result["key"] == "INS"
|
|
163
|
+
mock_instance.get_project.assert_called_once_with("INS")
|
|
164
|
+
|
|
165
|
+
@pytest.mark.asyncio
|
|
166
|
+
async def test_list_repositories_async(
|
|
167
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
168
|
+
) -> None:
|
|
169
|
+
"""Test async list_repositories method."""
|
|
170
|
+
clear_runtime_env(monkeypatch)
|
|
171
|
+
env_path = write_env(
|
|
172
|
+
tmp_path,
|
|
173
|
+
"""
|
|
174
|
+
VDS_USERNAME=test_user
|
|
175
|
+
VDS_PASSWORD=test_pass
|
|
176
|
+
""".strip(),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
|
|
180
|
+
mock_instance = MagicMock()
|
|
181
|
+
mock_instance.list_repositories.return_value = [
|
|
182
|
+
{"slug": "insurance-core", "name": "Insurance Core"},
|
|
183
|
+
{"slug": "insurance-api", "name": "Insurance API"},
|
|
184
|
+
]
|
|
185
|
+
mock_client.return_value = mock_instance
|
|
186
|
+
|
|
187
|
+
from vds_bitbucket_orchestrator.config import load_settings
|
|
188
|
+
|
|
189
|
+
settings = load_settings(env_path=env_path)
|
|
190
|
+
async_client = AsyncBitbucketClient(settings)
|
|
191
|
+
|
|
192
|
+
result = await async_client.list_repositories("INS", limit=50)
|
|
193
|
+
|
|
194
|
+
assert len(result) == 2
|
|
195
|
+
assert result[0]["slug"] == "insurance-core"
|
|
196
|
+
mock_instance.list_repositories.assert_called_once_with("INS", 50)
|
|
197
|
+
|
|
198
|
+
@pytest.mark.asyncio
|
|
199
|
+
async def test_get_repository_async(
|
|
200
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
201
|
+
) -> None:
|
|
202
|
+
"""Test async get_repository method."""
|
|
203
|
+
clear_runtime_env(monkeypatch)
|
|
204
|
+
env_path = write_env(
|
|
205
|
+
tmp_path,
|
|
206
|
+
"""
|
|
207
|
+
VDS_USERNAME=test_user
|
|
208
|
+
VDS_PASSWORD=test_pass
|
|
209
|
+
""".strip(),
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
|
|
213
|
+
mock_instance = MagicMock()
|
|
214
|
+
mock_instance.get_repository.return_value = {
|
|
215
|
+
"slug": "insurance-core",
|
|
216
|
+
"name": "Insurance Core",
|
|
217
|
+
}
|
|
218
|
+
mock_client.return_value = mock_instance
|
|
219
|
+
|
|
220
|
+
from vds_bitbucket_orchestrator.config import load_settings
|
|
221
|
+
|
|
222
|
+
settings = load_settings(env_path=env_path)
|
|
223
|
+
async_client = AsyncBitbucketClient(settings)
|
|
224
|
+
|
|
225
|
+
result = await async_client.get_repository("INS", "insurance-core")
|
|
226
|
+
|
|
227
|
+
assert result["slug"] == "insurance-core"
|
|
228
|
+
mock_instance.get_repository.assert_called_once_with("INS", "insurance-core")
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class TestAsyncBranchOperations:
|
|
232
|
+
"""Tests for async branch operations."""
|
|
233
|
+
|
|
234
|
+
@pytest.mark.asyncio
|
|
235
|
+
async def test_get_branches_async(
|
|
236
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
237
|
+
) -> None:
|
|
238
|
+
"""Test async get_branches method."""
|
|
239
|
+
clear_runtime_env(monkeypatch)
|
|
240
|
+
env_path = write_env(
|
|
241
|
+
tmp_path,
|
|
242
|
+
"""
|
|
243
|
+
VDS_USERNAME=test_user
|
|
244
|
+
VDS_PASSWORD=test_pass
|
|
245
|
+
""".strip(),
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
|
|
249
|
+
mock_instance = MagicMock()
|
|
250
|
+
mock_instance.get_branches.return_value = [
|
|
251
|
+
{"displayId": "main", "isDefault": True},
|
|
252
|
+
{"displayId": "develop", "isDefault": False},
|
|
253
|
+
]
|
|
254
|
+
mock_client.return_value = mock_instance
|
|
255
|
+
|
|
256
|
+
from vds_bitbucket_orchestrator.config import load_settings
|
|
257
|
+
|
|
258
|
+
settings = load_settings(env_path=env_path)
|
|
259
|
+
async_client = AsyncBitbucketClient(settings)
|
|
260
|
+
|
|
261
|
+
result = await async_client.get_branches("INS", "insurance-core")
|
|
262
|
+
|
|
263
|
+
assert len(result) == 2
|
|
264
|
+
assert result[0]["displayId"] == "main"
|
|
265
|
+
mock_instance.get_branches.assert_called_once_with(
|
|
266
|
+
"INS", "insurance-core", "", 99999, True
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
@pytest.mark.asyncio
|
|
270
|
+
async def test_list_branch_permissions_async(
|
|
271
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
272
|
+
) -> None:
|
|
273
|
+
"""Test async list_branch_permissions method."""
|
|
274
|
+
clear_runtime_env(monkeypatch)
|
|
275
|
+
env_path = write_env(
|
|
276
|
+
tmp_path,
|
|
277
|
+
"""
|
|
278
|
+
VDS_USERNAME=test_user
|
|
279
|
+
VDS_PASSWORD=test_pass
|
|
280
|
+
""".strip(),
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
|
|
284
|
+
mock_instance = MagicMock()
|
|
285
|
+
mock_instance.list_branch_permissions.return_value = [
|
|
286
|
+
{"type": "no-deletes", "matcher": {"id": "refs/heads/main"}},
|
|
287
|
+
]
|
|
288
|
+
mock_client.return_value = mock_instance
|
|
289
|
+
|
|
290
|
+
from vds_bitbucket_orchestrator.config import load_settings
|
|
291
|
+
|
|
292
|
+
settings = load_settings(env_path=env_path)
|
|
293
|
+
async_client = AsyncBitbucketClient(settings)
|
|
294
|
+
|
|
295
|
+
result = await async_client.list_branch_permissions("INS", "insurance-core")
|
|
296
|
+
|
|
297
|
+
assert len(result) == 1
|
|
298
|
+
mock_instance.list_branch_permissions.assert_called_once_with(
|
|
299
|
+
"INS", "insurance-core", 0, 25
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class TestAsyncCodeOperations:
|
|
304
|
+
"""Tests for async code operations (BitbucketCodeClient protocol)."""
|
|
305
|
+
|
|
306
|
+
@pytest.mark.asyncio
|
|
307
|
+
async def test_get_content_of_file_async(
|
|
308
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
309
|
+
) -> None:
|
|
310
|
+
"""Test async get_content_of_file method."""
|
|
311
|
+
clear_runtime_env(monkeypatch)
|
|
312
|
+
env_path = write_env(
|
|
313
|
+
tmp_path,
|
|
314
|
+
"""
|
|
315
|
+
VDS_USERNAME=test_user
|
|
316
|
+
VDS_PASSWORD=test_pass
|
|
317
|
+
""".strip(),
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
|
|
321
|
+
mock_instance = MagicMock()
|
|
322
|
+
mock_instance.get_content_of_file.return_value = "# README\n\nThis is a test."
|
|
323
|
+
mock_client.return_value = mock_instance
|
|
324
|
+
|
|
325
|
+
from vds_bitbucket_orchestrator.config import load_settings
|
|
326
|
+
|
|
327
|
+
settings = load_settings(env_path=env_path)
|
|
328
|
+
async_client = AsyncBitbucketClient(settings)
|
|
329
|
+
|
|
330
|
+
result = await async_client.get_content_of_file(
|
|
331
|
+
"INS", "insurance-core", "README.md"
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
assert "README" in result
|
|
335
|
+
mock_instance.get_content_of_file.assert_called_once_with(
|
|
336
|
+
"INS", "insurance-core", "README.md", None, None
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
@pytest.mark.asyncio
|
|
340
|
+
async def test_get_commits_async(
|
|
341
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
342
|
+
) -> None:
|
|
343
|
+
"""Test async get_commits method."""
|
|
344
|
+
clear_runtime_env(monkeypatch)
|
|
345
|
+
env_path = write_env(
|
|
346
|
+
tmp_path,
|
|
347
|
+
"""
|
|
348
|
+
VDS_USERNAME=test_user
|
|
349
|
+
VDS_PASSWORD=test_pass
|
|
350
|
+
""".strip(),
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
|
|
354
|
+
mock_instance = MagicMock()
|
|
355
|
+
mock_instance.get_commits.return_value = [
|
|
356
|
+
{"id": "abc123", "message": "Initial commit"},
|
|
357
|
+
{"id": "def456", "message": "Add feature"},
|
|
358
|
+
]
|
|
359
|
+
mock_client.return_value = mock_instance
|
|
360
|
+
|
|
361
|
+
from vds_bitbucket_orchestrator.config import load_settings
|
|
362
|
+
|
|
363
|
+
settings = load_settings(env_path=env_path)
|
|
364
|
+
async_client = AsyncBitbucketClient(settings)
|
|
365
|
+
|
|
366
|
+
result = await async_client.get_commits("INS", "insurance-core", limit=10)
|
|
367
|
+
|
|
368
|
+
assert len(result) == 2
|
|
369
|
+
assert result[0]["id"] == "abc123"
|
|
370
|
+
mock_instance.get_commits.assert_called_once_with(
|
|
371
|
+
"INS", "insurance-core", None, None, 10
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class TestAsyncConcurrentOperations:
|
|
376
|
+
"""Tests for concurrent async operations."""
|
|
377
|
+
|
|
378
|
+
@pytest.mark.asyncio
|
|
379
|
+
async def test_concurrent_api_calls(
|
|
380
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
381
|
+
) -> None:
|
|
382
|
+
"""Test that multiple async calls can run concurrently."""
|
|
383
|
+
clear_runtime_env(monkeypatch)
|
|
384
|
+
env_path = write_env(
|
|
385
|
+
tmp_path,
|
|
386
|
+
"""
|
|
387
|
+
VDS_USERNAME=test_user
|
|
388
|
+
VDS_PASSWORD=test_pass
|
|
389
|
+
""".strip(),
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
|
|
393
|
+
mock_instance = MagicMock()
|
|
394
|
+
mock_instance.list_repositories.return_value = [{"slug": "repo1"}]
|
|
395
|
+
mock_instance.get_branches.return_value = [{"displayId": "main"}]
|
|
396
|
+
mock_instance.get_content_of_file.return_value = "# README"
|
|
397
|
+
mock_client.return_value = mock_instance
|
|
398
|
+
|
|
399
|
+
from vds_bitbucket_orchestrator.config import load_settings
|
|
400
|
+
|
|
401
|
+
settings = load_settings(env_path=env_path)
|
|
402
|
+
async_client = AsyncBitbucketClient(settings)
|
|
403
|
+
|
|
404
|
+
# Run multiple operations concurrently
|
|
405
|
+
repos, branches, readme = await asyncio.gather(
|
|
406
|
+
async_client.list_repositories("INS"),
|
|
407
|
+
async_client.get_branches("INS", "insurance-core"),
|
|
408
|
+
async_client.get_content_of_file("INS", "insurance-core", "README.md"),
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
assert len(repos) == 1
|
|
412
|
+
assert len(branches) == 1
|
|
413
|
+
assert "README" in readme
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
class TestAsyncPullRequestOperations:
|
|
417
|
+
"""Tests for async pull request operations."""
|
|
418
|
+
|
|
419
|
+
@pytest.mark.asyncio
|
|
420
|
+
async def test_list_pull_requests_async(
|
|
421
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
422
|
+
) -> None:
|
|
423
|
+
"""Test async list_pull_requests method."""
|
|
424
|
+
clear_runtime_env(monkeypatch)
|
|
425
|
+
env_path = write_env(
|
|
426
|
+
tmp_path,
|
|
427
|
+
"""
|
|
428
|
+
VDS_USERNAME=test_user
|
|
429
|
+
VDS_PASSWORD=test_pass
|
|
430
|
+
""".strip(),
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient") as mock_client:
|
|
434
|
+
mock_instance = MagicMock()
|
|
435
|
+
mock_instance.list_pull_requests.return_value = [
|
|
436
|
+
{"id": 1, "title": "Feature PR", "state": "OPEN"},
|
|
437
|
+
]
|
|
438
|
+
mock_client.return_value = mock_instance
|
|
439
|
+
|
|
440
|
+
from vds_bitbucket_orchestrator.config import load_settings
|
|
441
|
+
|
|
442
|
+
settings = load_settings(env_path=env_path)
|
|
443
|
+
async_client = AsyncBitbucketClient(settings)
|
|
444
|
+
|
|
445
|
+
result = await async_client.list_pull_requests("INS", "insurance-core")
|
|
446
|
+
|
|
447
|
+
assert len(result) == 1
|
|
448
|
+
assert result[0]["title"] == "Feature PR"
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
class TestCreateAsyncClientFactory:
|
|
452
|
+
"""Tests for create_async_client() factory function."""
|
|
453
|
+
|
|
454
|
+
def test_create_async_client_returns_async_client(
|
|
455
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
456
|
+
) -> None:
|
|
457
|
+
"""Test that create_async_client returns AsyncBitbucketClient."""
|
|
458
|
+
clear_runtime_env(monkeypatch)
|
|
459
|
+
env_path = write_env(
|
|
460
|
+
tmp_path,
|
|
461
|
+
"""
|
|
462
|
+
VDS_USERNAME=test_user
|
|
463
|
+
VDS_PASSWORD=test_pass
|
|
464
|
+
VDS_BITBUCKET_URL=https://bitbucket.test.com
|
|
465
|
+
""".strip(),
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
with patch("vds_bitbucket_orchestrator.factory.AsyncBitbucketClient") as mock_async:
|
|
469
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient"):
|
|
470
|
+
mock_instance = MagicMock(spec=AsyncBitbucketClient)
|
|
471
|
+
mock_async.return_value = mock_instance
|
|
472
|
+
|
|
473
|
+
from vds_bitbucket_orchestrator import create_async_client
|
|
474
|
+
|
|
475
|
+
client = create_async_client(env_path=env_path)
|
|
476
|
+
|
|
477
|
+
assert client is mock_instance
|
|
478
|
+
mock_async.assert_called_once()
|
|
479
|
+
|
|
480
|
+
def test_create_async_client_with_mode_override(
|
|
481
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
482
|
+
) -> None:
|
|
483
|
+
"""Test that mode parameter is passed correctly."""
|
|
484
|
+
clear_runtime_env(monkeypatch)
|
|
485
|
+
env_path = write_env(
|
|
486
|
+
tmp_path,
|
|
487
|
+
"""
|
|
488
|
+
VDS_USERNAME=test_user
|
|
489
|
+
VDS_PASSWORD=test_pass
|
|
490
|
+
BITBUCKET_CLOUD_WORKSPACE=testworkspace
|
|
491
|
+
""".strip(),
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
with patch("vds_bitbucket_orchestrator.factory.AsyncBitbucketClient") as mock_async:
|
|
495
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient"):
|
|
496
|
+
mock_instance = MagicMock(spec=AsyncBitbucketClient)
|
|
497
|
+
mock_async.return_value = mock_instance
|
|
498
|
+
|
|
499
|
+
from vds_bitbucket_orchestrator import create_async_client
|
|
500
|
+
|
|
501
|
+
create_async_client(env_path=env_path, mode="cloud")
|
|
502
|
+
|
|
503
|
+
call_args = mock_async.call_args
|
|
504
|
+
settings = call_args[0][0]
|
|
505
|
+
assert settings.mode == "cloud"
|
|
506
|
+
|
|
507
|
+
def test_create_async_client_with_custom_timeout(
|
|
508
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
509
|
+
) -> None:
|
|
510
|
+
"""Test that timeout is passed to AsyncBitbucketClient."""
|
|
511
|
+
clear_runtime_env(monkeypatch)
|
|
512
|
+
env_path = write_env(
|
|
513
|
+
tmp_path,
|
|
514
|
+
"""
|
|
515
|
+
VDS_USERNAME=test_user
|
|
516
|
+
VDS_PASSWORD=test_pass
|
|
517
|
+
""".strip(),
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
with patch("vds_bitbucket_orchestrator.factory.AsyncBitbucketClient") as mock_async:
|
|
521
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient"):
|
|
522
|
+
mock_instance = MagicMock(spec=AsyncBitbucketClient)
|
|
523
|
+
mock_async.return_value = mock_instance
|
|
524
|
+
|
|
525
|
+
from vds_bitbucket_orchestrator import create_async_client
|
|
526
|
+
|
|
527
|
+
create_async_client(env_path=env_path, timeout=120)
|
|
528
|
+
|
|
529
|
+
call_args = mock_async.call_args
|
|
530
|
+
assert call_args.kwargs.get("timeout") == 120
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
class TestAsyncClientImports:
|
|
534
|
+
"""Test that async client is properly exported from package."""
|
|
535
|
+
|
|
536
|
+
def test_async_client_importable_from_package(self) -> None:
|
|
537
|
+
"""Test that AsyncBitbucketClient can be imported from package root."""
|
|
538
|
+
from vds_bitbucket_orchestrator import AsyncBitbucketClient
|
|
539
|
+
|
|
540
|
+
assert AsyncBitbucketClient is not None
|
|
541
|
+
|
|
542
|
+
def test_create_async_client_importable(self) -> None:
|
|
543
|
+
"""Test that create_async_client can be imported from package root."""
|
|
544
|
+
from vds_bitbucket_orchestrator import create_async_client
|
|
545
|
+
|
|
546
|
+
assert callable(create_async_client)
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from unittest.mock import Mock, patch
|
|
4
|
+
|
|
5
|
+
from typer.testing import CliRunner
|
|
6
|
+
from vds_bitbucket_orchestrator.cli import app
|
|
7
|
+
from vds_bitbucket_orchestrator.errors import BitbucketClientError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _runner() -> CliRunner:
|
|
11
|
+
return CliRunner()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@patch("vds_bitbucket_orchestrator.cli._build_client")
|
|
15
|
+
@patch("vds_bitbucket_orchestrator.cli.load_settings")
|
|
16
|
+
def test_branch_permissions_get(mock_load: Mock, mock_build: Mock) -> None:
|
|
17
|
+
runner = _runner()
|
|
18
|
+
client = Mock()
|
|
19
|
+
client.get_branch_permission.return_value = {"id": 1}
|
|
20
|
+
mock_build.return_value.__enter__.return_value = client
|
|
21
|
+
|
|
22
|
+
result = runner.invoke(app, ["branch-permissions", "get", "PRJ", "--permission-id", "1"])
|
|
23
|
+
assert result.exit_code == 0
|
|
24
|
+
client.get_branch_permission.assert_called_once_with("PRJ", 1, repository_slug=None)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@patch("vds_bitbucket_orchestrator.cli._build_client")
|
|
28
|
+
@patch("vds_bitbucket_orchestrator.cli.load_settings")
|
|
29
|
+
def test_branch_permissions_get_requires_id(mock_load: Mock, mock_build: Mock) -> None:
|
|
30
|
+
runner = _runner()
|
|
31
|
+
|
|
32
|
+
result = runner.invoke(app, ["branch-permissions", "get", "PRJ"])
|
|
33
|
+
assert result.exit_code != 0
|
|
34
|
+
assert "--permission-id required for get" in result.stdout
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@patch("vds_bitbucket_orchestrator.cli._build_client")
|
|
38
|
+
@patch("vds_bitbucket_orchestrator.cli.load_settings")
|
|
39
|
+
def test_branch_permissions_set_requires_yes(mock_load: Mock, mock_build: Mock) -> None:
|
|
40
|
+
runner = _runner()
|
|
41
|
+
result = runner.invoke(app, [
|
|
42
|
+
"branch-permissions", "set", "PRJ",
|
|
43
|
+
"--matcher-type", "BRANCH", "--matcher-value", "refs/heads/master",
|
|
44
|
+
"--permission-type", "READ_ONLY"
|
|
45
|
+
])
|
|
46
|
+
assert result.exit_code != 0
|
|
47
|
+
assert "Refusing to set permissions without --yes" in result.stdout
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@patch("vds_bitbucket_orchestrator.cli._build_client")
|
|
51
|
+
@patch("vds_bitbucket_orchestrator.cli.load_settings")
|
|
52
|
+
def test_branch_permissions_set_success(mock_load: Mock, mock_build: Mock) -> None:
|
|
53
|
+
runner = _runner()
|
|
54
|
+
client = Mock()
|
|
55
|
+
mock_build.return_value.__enter__.return_value = client
|
|
56
|
+
|
|
57
|
+
result = runner.invoke(app, [
|
|
58
|
+
"branch-permissions", "set", "PRJ",
|
|
59
|
+
"--matcher-type", "BRANCH", "--matcher-value", "refs/heads/master",
|
|
60
|
+
"--permission-type", "READ_ONLY", "--yes"
|
|
61
|
+
])
|
|
62
|
+
assert result.exit_code == 0
|
|
63
|
+
client.set_branches_permissions.assert_called_once_with(
|
|
64
|
+
"PRJ",
|
|
65
|
+
matcher_type="BRANCH",
|
|
66
|
+
matcher_value="refs/heads/master",
|
|
67
|
+
permission_type="READ_ONLY",
|
|
68
|
+
repository_slug=None,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@patch("vds_bitbucket_orchestrator.cli._build_client")
|
|
73
|
+
@patch("vds_bitbucket_orchestrator.cli.load_settings")
|
|
74
|
+
def test_branch_permissions_set_passes_repository(mock_load: Mock, mock_build: Mock) -> None:
|
|
75
|
+
runner = _runner()
|
|
76
|
+
client = Mock()
|
|
77
|
+
mock_build.return_value.__enter__.return_value = client
|
|
78
|
+
|
|
79
|
+
result = runner.invoke(app, [
|
|
80
|
+
"branch-permissions",
|
|
81
|
+
"set",
|
|
82
|
+
"PRJ",
|
|
83
|
+
"--repo",
|
|
84
|
+
"app-repo",
|
|
85
|
+
"--matcher-type",
|
|
86
|
+
"BRANCH",
|
|
87
|
+
"--matcher-value",
|
|
88
|
+
"refs/heads/develop",
|
|
89
|
+
"--permission-type",
|
|
90
|
+
"WRITE",
|
|
91
|
+
"--yes",
|
|
92
|
+
])
|
|
93
|
+
assert result.exit_code == 0
|
|
94
|
+
client.set_branches_permissions.assert_called_once_with(
|
|
95
|
+
"PRJ",
|
|
96
|
+
matcher_type="BRANCH",
|
|
97
|
+
matcher_value="refs/heads/develop",
|
|
98
|
+
permission_type="WRITE",
|
|
99
|
+
repository_slug="app-repo",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@patch("vds_bitbucket_orchestrator.cli._build_client")
|
|
104
|
+
@patch("vds_bitbucket_orchestrator.cli.load_settings")
|
|
105
|
+
def test_branch_permissions_set_error(mock_load: Mock, mock_build: Mock) -> None:
|
|
106
|
+
runner = _runner()
|
|
107
|
+
client = Mock()
|
|
108
|
+
client.set_branches_permissions.side_effect = BitbucketClientError("oops")
|
|
109
|
+
mock_build.return_value.__enter__.return_value = client
|
|
110
|
+
|
|
111
|
+
result = runner.invoke(app, [
|
|
112
|
+
"branch-permissions",
|
|
113
|
+
"set",
|
|
114
|
+
"PRJ",
|
|
115
|
+
"--matcher-type",
|
|
116
|
+
"BRANCH",
|
|
117
|
+
"--matcher-value",
|
|
118
|
+
"refs/heads/master",
|
|
119
|
+
"--permission-type",
|
|
120
|
+
"READ_ONLY",
|
|
121
|
+
"--yes",
|
|
122
|
+
])
|
|
123
|
+
assert result.exit_code != 0
|
|
124
|
+
assert "bitbucket_error" in result.stdout
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@patch("vds_bitbucket_orchestrator.cli._build_client")
|
|
128
|
+
@patch("vds_bitbucket_orchestrator.cli.load_settings")
|
|
129
|
+
def test_branch_permissions_delete_requires_yes(mock_load: Mock, mock_build: Mock) -> None:
|
|
130
|
+
runner = _runner()
|
|
131
|
+
result = runner.invoke(app, ["branch-permissions", "delete", "PRJ", "--permission-id", "7"])
|
|
132
|
+
assert result.exit_code != 0
|
|
133
|
+
assert "Refusing to delete without --yes" in result.stdout
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@patch("vds_bitbucket_orchestrator.cli._build_client")
|
|
137
|
+
@patch("vds_bitbucket_orchestrator.cli.load_settings")
|
|
138
|
+
def test_branch_permissions_delete_success(mock_load: Mock, mock_build: Mock) -> None:
|
|
139
|
+
runner = _runner()
|
|
140
|
+
client = Mock()
|
|
141
|
+
mock_build.return_value.__enter__.return_value = client
|
|
142
|
+
|
|
143
|
+
result = runner.invoke(app, ["branch-permissions", "delete", "PRJ", "--permission-id", "7", "--yes"])
|
|
144
|
+
assert result.exit_code == 0
|
|
145
|
+
client.delete_branch_permission.assert_called_once_with("PRJ", 7, repository_slug=None)
|