@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,371 @@
|
|
|
1
|
+
"""Unit tests for factory functions (TSK-223).
|
|
2
|
+
|
|
3
|
+
Tests for create_client() and create_async_client() factory functions
|
|
4
|
+
that auto-load credentials from ~/.vds/.env.
|
|
5
|
+
|
|
6
|
+
References:
|
|
7
|
+
- FR-21.2.2: Factory functions that load credentials from `~/.vds/.env` automatically
|
|
8
|
+
- TSK-223: Factory functions for Bitbucket
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from unittest.mock import MagicMock, patch
|
|
15
|
+
|
|
16
|
+
import pytest
|
|
17
|
+
from vds_cli_common.paths import get_shared_env_path
|
|
18
|
+
|
|
19
|
+
from vds_bitbucket_orchestrator.factory import (
|
|
20
|
+
DEFAULT_VDS_ENV_PATH,
|
|
21
|
+
create_async_client,
|
|
22
|
+
create_client,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def write_env(tmp_path: Path, content: str) -> Path:
|
|
27
|
+
"""Write a temporary .env file for testing."""
|
|
28
|
+
env_path = tmp_path / ".env"
|
|
29
|
+
env_path.write_text(content)
|
|
30
|
+
return env_path
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def clear_runtime_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
34
|
+
"""Clear runtime environment variables for testing."""
|
|
35
|
+
for key in (
|
|
36
|
+
"VDS_USERNAME",
|
|
37
|
+
"VDS_PASSWORD",
|
|
38
|
+
"BITBUCKET_TOKEN",
|
|
39
|
+
"VDS_BITBUCKET_URL",
|
|
40
|
+
"BITBUCKET_BASE_URL",
|
|
41
|
+
"VDS_BITBUCKET_HTTP_BASE",
|
|
42
|
+
"BITBUCKET_CLOUD_WORKSPACE",
|
|
43
|
+
"BITBUCKET_MODE",
|
|
44
|
+
):
|
|
45
|
+
monkeypatch.delenv(key, raising=False)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TestDefaultEnvPath:
|
|
49
|
+
"""Test default environment path configuration."""
|
|
50
|
+
|
|
51
|
+
def test_default_env_path_is_shared_env(self) -> None:
|
|
52
|
+
"""Test that default env path uses the shared VDS env helper."""
|
|
53
|
+
expected = get_shared_env_path()
|
|
54
|
+
assert DEFAULT_VDS_ENV_PATH == expected
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class TestCreateClient:
|
|
58
|
+
"""Tests for create_client() factory function."""
|
|
59
|
+
|
|
60
|
+
def test_create_client_with_valid_credentials(
|
|
61
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
62
|
+
) -> None:
|
|
63
|
+
"""Test creating client with valid credentials from env file."""
|
|
64
|
+
clear_runtime_env(monkeypatch)
|
|
65
|
+
env_path = write_env(
|
|
66
|
+
tmp_path,
|
|
67
|
+
"""
|
|
68
|
+
VDS_USERNAME=test_user
|
|
69
|
+
VDS_PASSWORD=test_pass
|
|
70
|
+
VDS_BITBUCKET_URL=https://bitbucket.test.com
|
|
71
|
+
BITBUCKET_MODE=server
|
|
72
|
+
""".strip(),
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Mock the BitbucketClient to avoid actual API calls
|
|
76
|
+
with patch("vds_bitbucket_orchestrator.factory.BitbucketClient") as mock_client:
|
|
77
|
+
mock_instance = MagicMock()
|
|
78
|
+
mock_client.return_value = mock_instance
|
|
79
|
+
|
|
80
|
+
client = create_client(env_path=env_path)
|
|
81
|
+
|
|
82
|
+
# Verify client was created
|
|
83
|
+
assert client is mock_instance
|
|
84
|
+
mock_client.assert_called_once()
|
|
85
|
+
|
|
86
|
+
# Verify settings were passed correctly
|
|
87
|
+
call_args = mock_client.call_args
|
|
88
|
+
settings = call_args[0][0] # First positional arg
|
|
89
|
+
assert settings.username == "test_user"
|
|
90
|
+
assert settings.password == "test_pass"
|
|
91
|
+
assert str(settings.base_url) == "https://bitbucket.test.com"
|
|
92
|
+
assert settings.mode == "server"
|
|
93
|
+
|
|
94
|
+
def test_create_client_with_token_auth(
|
|
95
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
96
|
+
) -> None:
|
|
97
|
+
"""Test creating client with token authentication."""
|
|
98
|
+
clear_runtime_env(monkeypatch)
|
|
99
|
+
env_path = write_env(
|
|
100
|
+
tmp_path,
|
|
101
|
+
"""
|
|
102
|
+
BITBUCKET_TOKEN=test_token_12345
|
|
103
|
+
VDS_BITBUCKET_URL=https://bitbucket.test.com
|
|
104
|
+
""".strip(),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
with patch("vds_bitbucket_orchestrator.factory.BitbucketClient") as mock_client:
|
|
108
|
+
mock_instance = MagicMock()
|
|
109
|
+
mock_client.return_value = mock_instance
|
|
110
|
+
|
|
111
|
+
client = create_client(env_path=env_path)
|
|
112
|
+
|
|
113
|
+
assert client is mock_instance
|
|
114
|
+
call_args = mock_client.call_args
|
|
115
|
+
settings = call_args[0][0]
|
|
116
|
+
assert settings.token == "test_token_12345"
|
|
117
|
+
|
|
118
|
+
def test_create_client_mode_override(
|
|
119
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
120
|
+
) -> None:
|
|
121
|
+
"""Test that mode parameter overrides env file setting."""
|
|
122
|
+
clear_runtime_env(monkeypatch)
|
|
123
|
+
env_path = write_env(
|
|
124
|
+
tmp_path,
|
|
125
|
+
"""
|
|
126
|
+
VDS_USERNAME=test_user
|
|
127
|
+
VDS_PASSWORD=test_pass
|
|
128
|
+
BITBUCKET_MODE=server
|
|
129
|
+
BITBUCKET_CLOUD_WORKSPACE=testworkspace
|
|
130
|
+
""".strip(),
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
with patch("vds_bitbucket_orchestrator.factory.BitbucketClient") as mock_client:
|
|
134
|
+
mock_instance = MagicMock()
|
|
135
|
+
mock_client.return_value = mock_instance
|
|
136
|
+
|
|
137
|
+
# Override mode to cloud
|
|
138
|
+
client = create_client(env_path=env_path, mode="cloud")
|
|
139
|
+
|
|
140
|
+
assert client is mock_instance
|
|
141
|
+
call_args = mock_client.call_args
|
|
142
|
+
settings = call_args[0][0]
|
|
143
|
+
assert settings.mode == "cloud"
|
|
144
|
+
|
|
145
|
+
def test_create_client_server_mode_default(
|
|
146
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
147
|
+
) -> None:
|
|
148
|
+
"""Test that server mode is the default when not specified."""
|
|
149
|
+
clear_runtime_env(monkeypatch)
|
|
150
|
+
env_path = write_env(
|
|
151
|
+
tmp_path,
|
|
152
|
+
"""
|
|
153
|
+
VDS_USERNAME=test_user
|
|
154
|
+
VDS_PASSWORD=test_pass
|
|
155
|
+
VDS_BITBUCKET_URL=https://bitbucket.test.com
|
|
156
|
+
""".strip(),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
with patch("vds_bitbucket_orchestrator.factory.BitbucketClient") as mock_client:
|
|
160
|
+
mock_instance = MagicMock()
|
|
161
|
+
mock_client.return_value = mock_instance
|
|
162
|
+
|
|
163
|
+
client = create_client(env_path=env_path)
|
|
164
|
+
|
|
165
|
+
call_args = mock_client.call_args
|
|
166
|
+
settings = call_args[0][0]
|
|
167
|
+
assert settings.mode == "server"
|
|
168
|
+
|
|
169
|
+
def test_create_client_custom_timeout(
|
|
170
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
171
|
+
) -> None:
|
|
172
|
+
"""Test creating client with custom timeout."""
|
|
173
|
+
clear_runtime_env(monkeypatch)
|
|
174
|
+
env_path = write_env(
|
|
175
|
+
tmp_path,
|
|
176
|
+
"""
|
|
177
|
+
VDS_USERNAME=test_user
|
|
178
|
+
VDS_PASSWORD=test_pass
|
|
179
|
+
""".strip(),
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
with patch("vds_bitbucket_orchestrator.factory.BitbucketClient") as mock_client:
|
|
183
|
+
mock_instance = MagicMock()
|
|
184
|
+
mock_client.return_value = mock_instance
|
|
185
|
+
|
|
186
|
+
create_client(env_path=env_path, timeout=60)
|
|
187
|
+
|
|
188
|
+
call_args = mock_client.call_args
|
|
189
|
+
assert call_args.kwargs.get("timeout") == 60
|
|
190
|
+
|
|
191
|
+
def test_create_client_strict_mode_fails_without_credentials(
|
|
192
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
193
|
+
) -> None:
|
|
194
|
+
"""Test that strict mode raises error when credentials are missing."""
|
|
195
|
+
clear_runtime_env(monkeypatch)
|
|
196
|
+
env_path = write_env(tmp_path, "")
|
|
197
|
+
|
|
198
|
+
with pytest.raises(ValueError, match="Missing credentials"):
|
|
199
|
+
create_client(env_path=env_path, strict=True)
|
|
200
|
+
|
|
201
|
+
def test_create_client_non_strict_mode_allows_missing_credentials(
|
|
202
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
203
|
+
) -> None:
|
|
204
|
+
"""Test that non-strict mode allows missing credentials."""
|
|
205
|
+
clear_runtime_env(monkeypatch)
|
|
206
|
+
env_path = write_env(
|
|
207
|
+
tmp_path,
|
|
208
|
+
"""
|
|
209
|
+
VDS_BITBUCKET_URL=https://bitbucket.test.com
|
|
210
|
+
""".strip(),
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Non-strict mode should not raise during settings load
|
|
214
|
+
# But BitbucketClient will still validate credentials
|
|
215
|
+
with patch("vds_bitbucket_orchestrator.factory.BitbucketClient") as mock_client:
|
|
216
|
+
mock_instance = MagicMock()
|
|
217
|
+
mock_client.return_value = mock_instance
|
|
218
|
+
|
|
219
|
+
# This should not raise during load_settings
|
|
220
|
+
client = create_client(env_path=env_path, strict=False)
|
|
221
|
+
assert client is mock_instance
|
|
222
|
+
|
|
223
|
+
def test_create_client_cloud_mode(
|
|
224
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
225
|
+
) -> None:
|
|
226
|
+
"""Test creating client in cloud mode."""
|
|
227
|
+
clear_runtime_env(monkeypatch)
|
|
228
|
+
env_path = write_env(
|
|
229
|
+
tmp_path,
|
|
230
|
+
"""
|
|
231
|
+
BITBUCKET_TOKEN=cloud_token
|
|
232
|
+
BITBUCKET_CLOUD_WORKSPACE=myworkspace
|
|
233
|
+
""".strip(),
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
with patch("vds_bitbucket_orchestrator.factory.BitbucketClient") as mock_client:
|
|
237
|
+
mock_instance = MagicMock()
|
|
238
|
+
mock_client.return_value = mock_instance
|
|
239
|
+
|
|
240
|
+
client = create_client(env_path=env_path, mode="cloud")
|
|
241
|
+
|
|
242
|
+
call_args = mock_client.call_args
|
|
243
|
+
settings = call_args[0][0]
|
|
244
|
+
assert settings.mode == "cloud"
|
|
245
|
+
assert settings.cloud_workspace == "myworkspace"
|
|
246
|
+
assert settings.is_cloud_mode
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class TestCreateAsyncClient:
|
|
250
|
+
"""Tests for create_async_client() factory function."""
|
|
251
|
+
|
|
252
|
+
def test_create_async_client_returns_async_client(
|
|
253
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
254
|
+
) -> None:
|
|
255
|
+
"""Test that create_async_client returns AsyncBitbucketClient (TSK-225 implemented)."""
|
|
256
|
+
clear_runtime_env(monkeypatch)
|
|
257
|
+
env_path = write_env(
|
|
258
|
+
tmp_path,
|
|
259
|
+
"""
|
|
260
|
+
VDS_USERNAME=test_user
|
|
261
|
+
VDS_PASSWORD=test_pass
|
|
262
|
+
VDS_BITBUCKET_URL=https://bitbucket.test.com
|
|
263
|
+
""".strip(),
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
with patch("vds_bitbucket_orchestrator.factory.AsyncBitbucketClient") as mock_async:
|
|
267
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient"):
|
|
268
|
+
mock_instance = MagicMock()
|
|
269
|
+
mock_async.return_value = mock_instance
|
|
270
|
+
|
|
271
|
+
client = create_async_client(env_path=env_path)
|
|
272
|
+
|
|
273
|
+
# Verify AsyncBitbucketClient was created (not NotImplementedError)
|
|
274
|
+
assert client is mock_instance
|
|
275
|
+
mock_async.assert_called_once()
|
|
276
|
+
|
|
277
|
+
def test_create_async_client_passes_settings_and_timeout(
|
|
278
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
279
|
+
) -> None:
|
|
280
|
+
"""Test that create_async_client passes settings and timeout correctly."""
|
|
281
|
+
clear_runtime_env(monkeypatch)
|
|
282
|
+
env_path = write_env(
|
|
283
|
+
tmp_path,
|
|
284
|
+
"""
|
|
285
|
+
VDS_USERNAME=test_user
|
|
286
|
+
VDS_PASSWORD=test_pass
|
|
287
|
+
VDS_BITBUCKET_URL=https://bitbucket.test.com
|
|
288
|
+
""".strip(),
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
with patch("vds_bitbucket_orchestrator.factory.AsyncBitbucketClient") as mock_async:
|
|
292
|
+
with patch("vds_bitbucket_orchestrator.async_client.BitbucketClient"):
|
|
293
|
+
mock_instance = MagicMock()
|
|
294
|
+
mock_async.return_value = mock_instance
|
|
295
|
+
|
|
296
|
+
create_async_client(env_path=env_path, timeout=60)
|
|
297
|
+
|
|
298
|
+
call_args = mock_async.call_args
|
|
299
|
+
settings = call_args[0][0]
|
|
300
|
+
assert settings.username == "test_user"
|
|
301
|
+
assert call_args.kwargs.get("timeout") == 60
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
class TestFactoryImports:
|
|
305
|
+
"""Test that factory functions are properly exported from package."""
|
|
306
|
+
|
|
307
|
+
def test_factory_functions_importable_from_package(self) -> None:
|
|
308
|
+
"""Test that factory functions can be imported from package root."""
|
|
309
|
+
from vds_bitbucket_orchestrator import create_async_client, create_client
|
|
310
|
+
|
|
311
|
+
assert callable(create_client)
|
|
312
|
+
assert callable(create_async_client)
|
|
313
|
+
|
|
314
|
+
def test_default_env_path_importable(self) -> None:
|
|
315
|
+
"""Test that DEFAULT_VDS_ENV_PATH is accessible."""
|
|
316
|
+
from vds_bitbucket_orchestrator.factory import DEFAULT_VDS_ENV_PATH
|
|
317
|
+
|
|
318
|
+
assert isinstance(DEFAULT_VDS_ENV_PATH, Path)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
class TestFactoryWithEnvVarPriority:
|
|
322
|
+
"""Test factory functions respect env var priority (FR-21.1.1)."""
|
|
323
|
+
|
|
324
|
+
def test_factory_uses_primary_env_var(
|
|
325
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
326
|
+
) -> None:
|
|
327
|
+
"""Test that factory uses VDS_BITBUCKET_URL as primary."""
|
|
328
|
+
clear_runtime_env(monkeypatch)
|
|
329
|
+
env_path = write_env(
|
|
330
|
+
tmp_path,
|
|
331
|
+
"""
|
|
332
|
+
VDS_USERNAME=test_user
|
|
333
|
+
VDS_PASSWORD=test_pass
|
|
334
|
+
VDS_BITBUCKET_URL=https://primary.bitbucket.com
|
|
335
|
+
BITBUCKET_BASE_URL=https://legacy.bitbucket.com
|
|
336
|
+
""".strip(),
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
with patch("vds_bitbucket_orchestrator.factory.BitbucketClient") as mock_client:
|
|
340
|
+
mock_instance = MagicMock()
|
|
341
|
+
mock_client.return_value = mock_instance
|
|
342
|
+
|
|
343
|
+
create_client(env_path=env_path)
|
|
344
|
+
|
|
345
|
+
call_args = mock_client.call_args
|
|
346
|
+
settings = call_args[0][0]
|
|
347
|
+
assert str(settings.base_url) == "https://primary.bitbucket.com"
|
|
348
|
+
|
|
349
|
+
def test_factory_falls_back_to_legacy_env_var(
|
|
350
|
+
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
351
|
+
) -> None:
|
|
352
|
+
"""Test that factory falls back to BITBUCKET_BASE_URL."""
|
|
353
|
+
clear_runtime_env(monkeypatch)
|
|
354
|
+
env_path = write_env(
|
|
355
|
+
tmp_path,
|
|
356
|
+
"""
|
|
357
|
+
VDS_USERNAME=test_user
|
|
358
|
+
VDS_PASSWORD=test_pass
|
|
359
|
+
BITBUCKET_BASE_URL=https://legacy.bitbucket.com
|
|
360
|
+
""".strip(),
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
with patch("vds_bitbucket_orchestrator.factory.BitbucketClient") as mock_client:
|
|
364
|
+
mock_instance = MagicMock()
|
|
365
|
+
mock_client.return_value = mock_instance
|
|
366
|
+
|
|
367
|
+
create_client(env_path=env_path)
|
|
368
|
+
|
|
369
|
+
call_args = mock_client.call_args
|
|
370
|
+
settings = call_args[0][0]
|
|
371
|
+
assert str(settings.base_url) == "https://legacy.bitbucket.com"
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""Tests for Bitbucket fork operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from unittest.mock import MagicMock, Mock, patch
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
from typer.testing import CliRunner
|
|
9
|
+
from vds_bitbucket_orchestrator.cli import app
|
|
10
|
+
from vds_bitbucket_orchestrator.client import BitbucketClient
|
|
11
|
+
from vds_bitbucket_orchestrator.config import BitbucketSettings
|
|
12
|
+
from vds_bitbucket_orchestrator.errors import BitbucketClientError
|
|
13
|
+
|
|
14
|
+
runner = CliRunner()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def mock_settings() -> BitbucketSettings:
|
|
19
|
+
"""Create server-mode settings for testing."""
|
|
20
|
+
return BitbucketSettings(
|
|
21
|
+
VDS_USERNAME="test_user",
|
|
22
|
+
VDS_PASSWORD="test_pass",
|
|
23
|
+
BITBUCKET_TOKEN="test_token",
|
|
24
|
+
BITBUCKET_BASE_URL="https://bitbucket.test.com",
|
|
25
|
+
BITBUCKET_MODE="server",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@pytest.fixture
|
|
30
|
+
def mock_sdk_client() -> MagicMock:
|
|
31
|
+
"""Mock SDK client."""
|
|
32
|
+
client = MagicMock()
|
|
33
|
+
client.fork_repository.return_value = {"slug": "repo-fork", "name": "Repository Fork"}
|
|
34
|
+
client.fork_repository_new_project.return_value = {"slug": "new-repo", "name": "New Repository"}
|
|
35
|
+
client.repo_list.return_value = [
|
|
36
|
+
{"slug": "repo-fork", "name": "Repository Fork"},
|
|
37
|
+
{"slug": "repo-fork-2", "name": "Repository Fork 2"},
|
|
38
|
+
]
|
|
39
|
+
return client
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pytest.fixture
|
|
43
|
+
def mock_client(mock_settings: BitbucketSettings, mock_sdk_client: MagicMock) -> BitbucketClient:
|
|
44
|
+
"""Create a BitbucketClient with mocked SDK."""
|
|
45
|
+
with patch("vds_bitbucket_orchestrator.client.AtlassianBitbucket", return_value=mock_sdk_client):
|
|
46
|
+
client = BitbucketClient(mock_settings)
|
|
47
|
+
client._client = mock_sdk_client
|
|
48
|
+
return client
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class TestForkRepository:
|
|
52
|
+
"""Tests for fork_repository method."""
|
|
53
|
+
|
|
54
|
+
def test_fork_repository_same_project(self, mock_client: BitbucketClient, mock_sdk_client: MagicMock) -> None:
|
|
55
|
+
"""Test forking repository within same project."""
|
|
56
|
+
result = mock_client.fork_repository("PROJ", "repo", new_repository_slug="repo-fork")
|
|
57
|
+
|
|
58
|
+
assert result == {"slug": "repo-fork", "name": "Repository Fork"}
|
|
59
|
+
mock_sdk_client.fork_repository.assert_called_once_with("PROJ", "repo", "repo-fork")
|
|
60
|
+
|
|
61
|
+
def test_fork_repository_same_project_default_name(
|
|
62
|
+
self, mock_client: BitbucketClient, mock_sdk_client: MagicMock
|
|
63
|
+
) -> None:
|
|
64
|
+
"""Test forking repository within same project with default name."""
|
|
65
|
+
result = mock_client.fork_repository("PROJ", "repo")
|
|
66
|
+
|
|
67
|
+
assert result == {"slug": "repo-fork", "name": "Repository Fork"}
|
|
68
|
+
mock_sdk_client.fork_repository.assert_called_once_with("PROJ", "repo", "repo-fork")
|
|
69
|
+
|
|
70
|
+
def test_fork_repository_new_project(self, mock_client: BitbucketClient, mock_sdk_client: MagicMock) -> None:
|
|
71
|
+
"""Test forking repository to new project."""
|
|
72
|
+
result = mock_client.fork_repository("PROJ", "repo", new_project_key="NEWPROJ", new_repository_slug="new-repo")
|
|
73
|
+
|
|
74
|
+
assert result == {"slug": "new-repo", "name": "New Repository"}
|
|
75
|
+
mock_sdk_client.fork_repository_new_project.assert_called_once_with("PROJ", "repo", "NEWPROJ", "new-repo")
|
|
76
|
+
|
|
77
|
+
def test_fork_repository_new_project_default_name(
|
|
78
|
+
self, mock_client: BitbucketClient, mock_sdk_client: MagicMock
|
|
79
|
+
) -> None:
|
|
80
|
+
"""Test forking repository to new project with default name."""
|
|
81
|
+
result = mock_client.fork_repository("PROJ", "repo", new_project_key="NEWPROJ")
|
|
82
|
+
|
|
83
|
+
assert result == {"slug": "new-repo", "name": "New Repository"}
|
|
84
|
+
mock_sdk_client.fork_repository_new_project.assert_called_once_with("PROJ", "repo", "NEWPROJ", "repo")
|
|
85
|
+
|
|
86
|
+
def test_fork_repository_handles_error(self, mock_client: BitbucketClient, mock_sdk_client: MagicMock) -> None:
|
|
87
|
+
"""Test fork_repository error handling."""
|
|
88
|
+
mock_sdk_client.fork_repository.side_effect = Exception("Repository already exists")
|
|
89
|
+
|
|
90
|
+
with pytest.raises(BitbucketClientError, match="fork_repository"):
|
|
91
|
+
mock_client.fork_repository("PROJ", "repo", new_repository_slug="repo-fork")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class TestGetForkedRepositories:
|
|
95
|
+
"""Tests for get_forked_repositories method."""
|
|
96
|
+
|
|
97
|
+
def test_get_forked_repositories_success(self, mock_client: BitbucketClient, mock_sdk_client: MagicMock) -> None:
|
|
98
|
+
"""Test getting forked repositories."""
|
|
99
|
+
result = mock_client.get_forked_repositories("PROJ", "repo")
|
|
100
|
+
|
|
101
|
+
assert len(result) == 2
|
|
102
|
+
assert result[0]["slug"] == "repo-fork"
|
|
103
|
+
mock_sdk_client.repo_list.assert_called_once()
|
|
104
|
+
|
|
105
|
+
def test_get_forked_repositories_empty(self, mock_client: BitbucketClient, mock_sdk_client: MagicMock) -> None:
|
|
106
|
+
"""Test getting forked repositories when none exist."""
|
|
107
|
+
mock_sdk_client.repo_list.return_value = []
|
|
108
|
+
|
|
109
|
+
result = mock_client.get_forked_repositories("PROJ", "repo")
|
|
110
|
+
|
|
111
|
+
assert result == []
|
|
112
|
+
|
|
113
|
+
def test_get_forked_repositories_handles_error(
|
|
114
|
+
self, mock_client: BitbucketClient, mock_sdk_client: MagicMock
|
|
115
|
+
) -> None:
|
|
116
|
+
"""Test get_forked_repositories error handling."""
|
|
117
|
+
mock_sdk_client.repo_list.side_effect = Exception("Project not found")
|
|
118
|
+
|
|
119
|
+
with pytest.raises(BitbucketClientError, match="get_forked_repositories"):
|
|
120
|
+
mock_client.get_forked_repositories("PROJ", "repo")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class TestForkCLI:
|
|
124
|
+
"""Tests for fork CLI commands."""
|
|
125
|
+
|
|
126
|
+
@patch("vds_bitbucket_orchestrator.cli.load_settings")
|
|
127
|
+
@patch("vds_bitbucket_orchestrator.cli._build_client")
|
|
128
|
+
def test_fork_create_success(self, mock_build_client: Mock, mock_load_settings: Mock) -> None:
|
|
129
|
+
"""Test fork create command success."""
|
|
130
|
+
mock_client = MagicMock()
|
|
131
|
+
mock_client.fork_repository.return_value = {"slug": "repo-fork", "name": "Repository Fork"}
|
|
132
|
+
mock_build_client.return_value.__enter__.return_value = mock_client
|
|
133
|
+
|
|
134
|
+
result = runner.invoke(app, ["fork", "create", "PROJ", "repo", "--new-repo", "repo-fork", "--yes"])
|
|
135
|
+
|
|
136
|
+
assert result.exit_code == 0
|
|
137
|
+
assert "success" in result.stdout
|
|
138
|
+
mock_client.fork_repository.assert_called_once_with("PROJ", "repo", None, "repo-fork")
|
|
139
|
+
|
|
140
|
+
@patch("vds_bitbucket_orchestrator.cli.load_settings")
|
|
141
|
+
@patch("vds_bitbucket_orchestrator.cli._build_client")
|
|
142
|
+
def test_fork_create_new_project(self, mock_build_client: Mock, mock_load_settings: Mock) -> None:
|
|
143
|
+
"""Test fork create command with new project."""
|
|
144
|
+
mock_client = MagicMock()
|
|
145
|
+
mock_client.fork_repository.return_value = {"slug": "new-repo", "name": "New Repository"}
|
|
146
|
+
mock_build_client.return_value.__enter__.return_value = mock_client
|
|
147
|
+
|
|
148
|
+
result = runner.invoke(
|
|
149
|
+
app, ["fork", "create", "PROJ", "repo", "--new-project", "NEWPROJ", "--new-repo", "new-repo", "--yes"]
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
assert result.exit_code == 0
|
|
153
|
+
assert "success" in result.stdout
|
|
154
|
+
mock_client.fork_repository.assert_called_once_with("PROJ", "repo", "NEWPROJ", "new-repo")
|
|
155
|
+
|
|
156
|
+
@patch("vds_bitbucket_orchestrator.cli.load_settings")
|
|
157
|
+
@patch("vds_bitbucket_orchestrator.cli._build_client")
|
|
158
|
+
def test_fork_create_requires_yes(self, mock_build_client: Mock, mock_load_settings: Mock) -> None:
|
|
159
|
+
"""Test fork create requires --yes flag."""
|
|
160
|
+
result = runner.invoke(app, ["fork", "create", "PROJ", "repo", "--new-repo", "repo-fork"])
|
|
161
|
+
|
|
162
|
+
assert result.exit_code != 0
|
|
163
|
+
assert "--yes" in result.stdout or "without --yes" in result.stdout or "Refusing" in result.stdout
|
|
164
|
+
|
|
165
|
+
@patch("vds_bitbucket_orchestrator.cli.load_settings")
|
|
166
|
+
@patch("vds_bitbucket_orchestrator.cli._build_client")
|
|
167
|
+
def test_fork_list_success(self, mock_build_client: Mock, mock_load_settings: Mock) -> None:
|
|
168
|
+
"""Test fork list command success."""
|
|
169
|
+
mock_client = MagicMock()
|
|
170
|
+
mock_client.get_forked_repositories.return_value = [
|
|
171
|
+
{"slug": "repo-fork", "name": "Repository Fork"},
|
|
172
|
+
{"slug": "repo-fork-2", "name": "Repository Fork 2"},
|
|
173
|
+
]
|
|
174
|
+
mock_build_client.return_value.__enter__.return_value = mock_client
|
|
175
|
+
|
|
176
|
+
result = runner.invoke(app, ["fork", "list", "PROJ", "repo"])
|
|
177
|
+
|
|
178
|
+
assert result.exit_code == 0
|
|
179
|
+
assert "forks" in result.stdout
|
|
180
|
+
assert "count" in result.stdout
|
|
181
|
+
mock_client.get_forked_repositories.assert_called_once_with("PROJ", "repo")
|
|
182
|
+
|
|
183
|
+
@patch("vds_bitbucket_orchestrator.cli.load_settings")
|
|
184
|
+
@patch("vds_bitbucket_orchestrator.cli._build_client")
|
|
185
|
+
def test_fork_invalid_action(self, mock_build_client: Mock, mock_load_settings: Mock) -> None:
|
|
186
|
+
"""Test fork command with invalid action."""
|
|
187
|
+
result = runner.invoke(app, ["fork", "invalid", "PROJ", "repo"])
|
|
188
|
+
|
|
189
|
+
assert result.exit_code != 0
|
|
190
|
+
assert "Unknown action" in result.stdout or "Unknown action" in result.stderr
|
|
191
|
+
|
|
192
|
+
@patch("vds_bitbucket_orchestrator.cli.load_settings")
|
|
193
|
+
@patch("vds_bitbucket_orchestrator.cli._build_client")
|
|
194
|
+
def test_fork_create_handles_error(self, mock_build_client: Mock, mock_load_settings: Mock) -> None:
|
|
195
|
+
"""Test fork create command error handling."""
|
|
196
|
+
mock_client = MagicMock()
|
|
197
|
+
mock_client.fork_repository.side_effect = BitbucketClientError("Repository already exists")
|
|
198
|
+
mock_build_client.return_value.__enter__.return_value = mock_client
|
|
199
|
+
|
|
200
|
+
result = runner.invoke(app, ["fork", "create", "PROJ", "repo", "--new-repo", "repo-fork", "--yes"])
|
|
201
|
+
|
|
202
|
+
assert result.exit_code != 0
|
|
203
|
+
assert "Error" in result.stdout or "Error" in result.stderr
|
|
204
|
+
|