@ngocsangairvds/vsaf 3.1.27 → 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-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,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from unittest.mock import MagicMock, patch
|
|
4
|
+
|
|
5
|
+
from vds_jira_orchestrator.adapter import JiraAdapter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
9
|
+
def test_issue_get_watchers_calls_sdk(mock_jira: MagicMock) -> None:
|
|
10
|
+
client = MagicMock()
|
|
11
|
+
client.issue_get_watchers.return_value = {"watchCount": 1, "watchers": [{"accountId": "123"}]}
|
|
12
|
+
mock_jira.return_value = client
|
|
13
|
+
|
|
14
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
15
|
+
payload = adapter.issue_get_watchers("NTTC-1")
|
|
16
|
+
|
|
17
|
+
client.issue_get_watchers.assert_called_once_with("NTTC-1")
|
|
18
|
+
assert payload["watchCount"] == 1
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
22
|
+
def test_issue_add_watcher_calls_sdk(mock_jira: MagicMock) -> None:
|
|
23
|
+
client = MagicMock()
|
|
24
|
+
mock_jira.return_value = client
|
|
25
|
+
|
|
26
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
27
|
+
adapter.issue_add_watcher("NTTC-2", "userAccount")
|
|
28
|
+
|
|
29
|
+
client.issue_add_watcher.assert_called_once_with("NTTC-2", "userAccount")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
33
|
+
def test_issue_delete_watcher_calls_sdk(mock_jira: MagicMock) -> None:
|
|
34
|
+
client = MagicMock()
|
|
35
|
+
mock_jira.return_value = client
|
|
36
|
+
|
|
37
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
38
|
+
adapter.issue_delete_watcher("NTTC-3", "userAccount")
|
|
39
|
+
|
|
40
|
+
client.issue_delete_watcher.assert_called_once_with("NTTC-3", "userAccount")
|
|
41
|
+
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""Tests for JIRA advanced search methods."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from unittest.mock import MagicMock, patch
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
from typer.testing import CliRunner
|
|
9
|
+
from vds_jira_orchestrator.adapter import JiraAdapter, JiraAdapterError, JiraAuthError
|
|
10
|
+
from vds_jira_orchestrator.cli import app
|
|
11
|
+
|
|
12
|
+
runner = CliRunner()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# ---------------------------------------------------------------------------
|
|
16
|
+
# Adapter method tests
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
21
|
+
def test_search_issues_advanced_calls_client(mock_jira: MagicMock) -> None:
|
|
22
|
+
"""Test that search_issues_advanced calls the SDK client with pagination."""
|
|
23
|
+
client = MagicMock()
|
|
24
|
+
client.jql.return_value = {"issues": [{"key": "TEST-1"}, {"key": "TEST-2"}], "total": 2}
|
|
25
|
+
mock_jira.return_value = client
|
|
26
|
+
|
|
27
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
28
|
+
result = adapter.search_issues_advanced("project = TEST", start=10, limit=25, fields=["summary", "status"])
|
|
29
|
+
|
|
30
|
+
client.jql.assert_called_once_with("project = TEST", fields="summary,status", start=10, limit=25)
|
|
31
|
+
assert result["total"] == 2
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
35
|
+
def test_search_issues_advanced_defaults(mock_jira: MagicMock) -> None:
|
|
36
|
+
"""Test that search_issues_advanced uses default parameters."""
|
|
37
|
+
client = MagicMock()
|
|
38
|
+
client.jql.return_value = {"issues": [], "total": 0}
|
|
39
|
+
mock_jira.return_value = client
|
|
40
|
+
|
|
41
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
42
|
+
adapter.search_issues_advanced("project = TEST")
|
|
43
|
+
|
|
44
|
+
client.jql.assert_called_once_with("project = TEST", fields="*all", start=0, limit=50)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
48
|
+
def test_search_issues_by_filter_calls_get_filter(mock_jira: MagicMock) -> None:
|
|
49
|
+
"""Test that search_issues_by_filter gets filter and uses its JQL."""
|
|
50
|
+
client = MagicMock()
|
|
51
|
+
client.get_filter.return_value = {"id": 10000, "name": "My Filter", "jql": "project = TEST AND status = Open"}
|
|
52
|
+
client.jql.return_value = {"issues": [{"key": "TEST-1"}], "total": 1}
|
|
53
|
+
mock_jira.return_value = client
|
|
54
|
+
|
|
55
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
56
|
+
result = adapter.search_issues_by_filter(10000, start=0, limit=50)
|
|
57
|
+
|
|
58
|
+
client.get_filter.assert_called_once_with(10000)
|
|
59
|
+
client.jql.assert_called_once_with("project = TEST AND status = Open", fields="*all", start=0, limit=50)
|
|
60
|
+
assert result["total"] == 1
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
64
|
+
def test_search_issues_by_filter_no_jql(mock_jira: MagicMock) -> None:
|
|
65
|
+
"""Test that search_issues_by_filter raises error when filter has no JQL."""
|
|
66
|
+
client = MagicMock()
|
|
67
|
+
client.get_filter.return_value = {"id": 10000, "name": "My Filter", "jql": ""}
|
|
68
|
+
mock_jira.return_value = client
|
|
69
|
+
|
|
70
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
71
|
+
|
|
72
|
+
with pytest.raises(JiraAdapterError, match="does not have a JQL query"):
|
|
73
|
+
adapter.search_issues_by_filter(10000)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
77
|
+
def test_search_issues_advanced_handles_error(mock_jira: MagicMock) -> None:
|
|
78
|
+
"""Test that search_issues_advanced handles errors."""
|
|
79
|
+
client = MagicMock()
|
|
80
|
+
client.jql.side_effect = Exception("401 unauthorized")
|
|
81
|
+
mock_jira.return_value = client
|
|
82
|
+
|
|
83
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
84
|
+
|
|
85
|
+
with pytest.raises(JiraAuthError):
|
|
86
|
+
adapter.search_issues_advanced("project = TEST")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# ---------------------------------------------------------------------------
|
|
90
|
+
# CLI command tests
|
|
91
|
+
# ---------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
95
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
96
|
+
def test_cli_search_advanced_flag(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
97
|
+
"""Test search command with --advanced flag."""
|
|
98
|
+
mock_adapter = MagicMock()
|
|
99
|
+
mock_adapter.search_issues_advanced.return_value = {"issues": [{"key": "TEST-1"}], "total": 1}
|
|
100
|
+
mock_adapter_class.return_value = mock_adapter
|
|
101
|
+
|
|
102
|
+
result = runner.invoke(app, ["search", "project = TEST", "--advanced", "--start", "10", "--limit", "25"])
|
|
103
|
+
|
|
104
|
+
assert result.exit_code == 0
|
|
105
|
+
mock_adapter.search_issues_advanced.assert_called_once()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
109
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
110
|
+
def test_cli_search_with_start_uses_advanced(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
111
|
+
"""Test search command with --start automatically uses advanced search."""
|
|
112
|
+
mock_adapter = MagicMock()
|
|
113
|
+
mock_adapter.search_issues_advanced.return_value = {"issues": [], "total": 0}
|
|
114
|
+
mock_adapter_class.return_value = mock_adapter
|
|
115
|
+
|
|
116
|
+
result = runner.invoke(app, ["search", "project = TEST", "--start", "5"])
|
|
117
|
+
|
|
118
|
+
assert result.exit_code == 0
|
|
119
|
+
mock_adapter.search_issues_advanced.assert_called_once()
|
|
120
|
+
mock_adapter.jql.assert_not_called()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
124
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
125
|
+
def test_cli_search_by_filter_success(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
126
|
+
"""Test search-by-filter command success."""
|
|
127
|
+
mock_adapter = MagicMock()
|
|
128
|
+
mock_adapter.search_issues_by_filter.return_value = {"issues": [{"key": "TEST-1"}], "total": 1}
|
|
129
|
+
mock_adapter_class.return_value = mock_adapter
|
|
130
|
+
|
|
131
|
+
result = runner.invoke(app, ["search-by-filter", "10000", "--limit", "50"])
|
|
132
|
+
|
|
133
|
+
assert result.exit_code == 0
|
|
134
|
+
assert "TEST-1" in result.stdout
|
|
135
|
+
mock_adapter.search_issues_by_filter.assert_called_once()
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
139
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
140
|
+
def test_cli_search_by_filter_with_pagination(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
141
|
+
"""Test search-by-filter command with pagination."""
|
|
142
|
+
mock_adapter = MagicMock()
|
|
143
|
+
mock_adapter.search_issues_by_filter.return_value = {"issues": [], "total": 0}
|
|
144
|
+
mock_adapter_class.return_value = mock_adapter
|
|
145
|
+
|
|
146
|
+
result = runner.invoke(app, ["search-by-filter", "10000", "--start", "20", "--limit", "25"])
|
|
147
|
+
|
|
148
|
+
assert result.exit_code == 0
|
|
149
|
+
mock_adapter.search_issues_by_filter.assert_called_once_with(10000, start=20, limit=25, fields=None)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
153
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
154
|
+
def test_cli_search_by_filter_handles_error(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
155
|
+
"""Test search-by-filter command error handling."""
|
|
156
|
+
mock_adapter = MagicMock()
|
|
157
|
+
mock_adapter.search_issues_by_filter.side_effect = JiraAdapterError("Filter not found")
|
|
158
|
+
mock_adapter_class.return_value = mock_adapter
|
|
159
|
+
|
|
160
|
+
result = runner.invoke(app, ["search-by-filter", "99999"])
|
|
161
|
+
|
|
162
|
+
assert result.exit_code != 0
|
|
163
|
+
assert "Error" in result.stdout or "Error" in result.stderr
|
|
164
|
+
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""Tests for JIRA Agile/Scrum operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from unittest.mock import Mock, patch
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
from typer.testing import CliRunner
|
|
9
|
+
from vds_jira_orchestrator.cli import app
|
|
10
|
+
from vds_jira_orchestrator.errors import JiraAdapterError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.fixture
|
|
14
|
+
def runner() -> CliRunner:
|
|
15
|
+
"""Create a CLI runner for testing."""
|
|
16
|
+
return CliRunner()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def sample_board() -> dict:
|
|
21
|
+
"""Sample agile board response."""
|
|
22
|
+
return {
|
|
23
|
+
"id": 1,
|
|
24
|
+
"name": "Test Board",
|
|
25
|
+
"type": "scrum",
|
|
26
|
+
"location": {"projectId": 10000, "displayName": "Test Project"},
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@pytest.fixture
|
|
31
|
+
def sample_epic() -> dict:
|
|
32
|
+
"""Sample epic response."""
|
|
33
|
+
return {
|
|
34
|
+
"id": 1,
|
|
35
|
+
"key": "TEST-100",
|
|
36
|
+
"name": "Test Epic",
|
|
37
|
+
"summary": "Test Epic Summary",
|
|
38
|
+
"done": False,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TestAgileBoardOperations:
|
|
43
|
+
"""Test suite for Agile board operations."""
|
|
44
|
+
|
|
45
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
46
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
47
|
+
def test_board_list(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner, sample_board: dict) -> None:
|
|
48
|
+
"""Test board list command."""
|
|
49
|
+
mock_adapter = Mock()
|
|
50
|
+
mock_adapter.get_all_agile_boards.return_value = [sample_board]
|
|
51
|
+
mock_adapter_class.return_value = mock_adapter
|
|
52
|
+
|
|
53
|
+
result = runner.invoke(app, ["board", "list", "--limit", "10"])
|
|
54
|
+
assert result.exit_code == 0
|
|
55
|
+
mock_adapter.get_all_agile_boards.assert_called_once_with(
|
|
56
|
+
project_key=None, board_type=None, start=0, limit=10
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
60
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
61
|
+
def test_board_get_by_id(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner, sample_board: dict) -> None:
|
|
62
|
+
"""Test board get by ID command."""
|
|
63
|
+
mock_adapter = Mock()
|
|
64
|
+
mock_adapter.get_agile_board.return_value = sample_board
|
|
65
|
+
mock_adapter_class.return_value = mock_adapter
|
|
66
|
+
|
|
67
|
+
result = runner.invoke(app, ["board", "get", "--board-id", "1"])
|
|
68
|
+
assert result.exit_code == 0
|
|
69
|
+
mock_adapter.get_agile_board.assert_called_once_with(1)
|
|
70
|
+
|
|
71
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
72
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
73
|
+
def test_board_get_by_filter(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner, sample_board: dict) -> None:
|
|
74
|
+
"""Test board get by filter ID command."""
|
|
75
|
+
mock_adapter = Mock()
|
|
76
|
+
mock_adapter.get_agile_board_by_filter_id.return_value = sample_board
|
|
77
|
+
mock_adapter_class.return_value = mock_adapter
|
|
78
|
+
|
|
79
|
+
result = runner.invoke(app, ["board", "get", "--filter-id", "100"])
|
|
80
|
+
assert result.exit_code == 0
|
|
81
|
+
mock_adapter.get_agile_board_by_filter_id.assert_called_once_with(100)
|
|
82
|
+
|
|
83
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
84
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
85
|
+
def test_board_create(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner, sample_board: dict) -> None:
|
|
86
|
+
"""Test board create command."""
|
|
87
|
+
mock_adapter = Mock()
|
|
88
|
+
mock_adapter.create_agile_board.return_value = sample_board
|
|
89
|
+
mock_adapter_class.return_value = mock_adapter
|
|
90
|
+
|
|
91
|
+
result = runner.invoke(
|
|
92
|
+
app,
|
|
93
|
+
[
|
|
94
|
+
"board",
|
|
95
|
+
"create",
|
|
96
|
+
"--name",
|
|
97
|
+
"Test Board",
|
|
98
|
+
"--type",
|
|
99
|
+
"scrum",
|
|
100
|
+
"--filter-id",
|
|
101
|
+
"100",
|
|
102
|
+
"--yes",
|
|
103
|
+
],
|
|
104
|
+
)
|
|
105
|
+
assert result.exit_code == 0
|
|
106
|
+
mock_adapter.create_agile_board.assert_called_once_with("Test Board", "scrum", 100)
|
|
107
|
+
|
|
108
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
109
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
110
|
+
def test_board_create_requires_yes(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
|
|
111
|
+
"""Test board create requires --yes flag."""
|
|
112
|
+
result = runner.invoke(
|
|
113
|
+
app,
|
|
114
|
+
[
|
|
115
|
+
"board",
|
|
116
|
+
"create",
|
|
117
|
+
"--name",
|
|
118
|
+
"Test Board",
|
|
119
|
+
"--type",
|
|
120
|
+
"scrum",
|
|
121
|
+
"--filter-id",
|
|
122
|
+
"100",
|
|
123
|
+
],
|
|
124
|
+
)
|
|
125
|
+
assert result.exit_code != 0
|
|
126
|
+
assert "Refusing to create without --yes" in result.stderr
|
|
127
|
+
|
|
128
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
129
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
130
|
+
def test_board_delete(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
|
|
131
|
+
"""Test board delete command."""
|
|
132
|
+
mock_adapter = Mock()
|
|
133
|
+
mock_adapter_class.return_value = mock_adapter
|
|
134
|
+
|
|
135
|
+
result = runner.invoke(app, ["board", "delete", "--board-id", "1", "--yes"])
|
|
136
|
+
assert result.exit_code == 0
|
|
137
|
+
mock_adapter.delete_agile_board.assert_called_once_with(1)
|
|
138
|
+
|
|
139
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
140
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
141
|
+
def test_board_config(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
|
|
142
|
+
"""Test board config command."""
|
|
143
|
+
mock_adapter = Mock()
|
|
144
|
+
mock_adapter.get_agile_board_configuration.return_value = {"config": "test"}
|
|
145
|
+
mock_adapter_class.return_value = mock_adapter
|
|
146
|
+
|
|
147
|
+
result = runner.invoke(app, ["board", "config", "--board-id", "1"])
|
|
148
|
+
assert result.exit_code == 0
|
|
149
|
+
mock_adapter.get_agile_board_configuration.assert_called_once_with(1)
|
|
150
|
+
|
|
151
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
152
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
153
|
+
def test_board_issues(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
|
|
154
|
+
"""Test board issues command."""
|
|
155
|
+
mock_adapter = Mock()
|
|
156
|
+
mock_adapter.get_issues_for_board.return_value = []
|
|
157
|
+
mock_adapter_class.return_value = mock_adapter
|
|
158
|
+
|
|
159
|
+
result = runner.invoke(app, ["board", "issues", "--board-id", "1"])
|
|
160
|
+
assert result.exit_code == 0
|
|
161
|
+
mock_adapter.get_issues_for_board.assert_called_once_with(1)
|
|
162
|
+
|
|
163
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
164
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
165
|
+
def test_board_properties(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
|
|
166
|
+
"""Test board properties command."""
|
|
167
|
+
mock_adapter = Mock()
|
|
168
|
+
mock_adapter.get_agile_board_properties.return_value = [{"key": "prop1", "value": "val1"}]
|
|
169
|
+
mock_adapter_class.return_value = mock_adapter
|
|
170
|
+
|
|
171
|
+
result = runner.invoke(app, ["board", "properties", "--board-id", "1"])
|
|
172
|
+
assert result.exit_code == 0
|
|
173
|
+
mock_adapter.get_agile_board_properties.assert_called_once_with(1)
|
|
174
|
+
|
|
175
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
176
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
177
|
+
def test_board_velocity(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
|
|
178
|
+
"""Test board velocity command."""
|
|
179
|
+
mock_adapter = Mock()
|
|
180
|
+
mock_adapter.get_agile_board_refined_velocity.return_value = {"velocity": 10}
|
|
181
|
+
mock_adapter_class.return_value = mock_adapter
|
|
182
|
+
|
|
183
|
+
result = runner.invoke(app, ["board", "velocity", "--board-id", "1"])
|
|
184
|
+
assert result.exit_code == 0
|
|
185
|
+
mock_adapter.get_agile_board_refined_velocity.assert_called_once_with(1)
|
|
186
|
+
|
|
187
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
188
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
189
|
+
def test_board_error_handling(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
|
|
190
|
+
"""Test board command error handling."""
|
|
191
|
+
mock_adapter = Mock()
|
|
192
|
+
mock_adapter.get_agile_board.side_effect = JiraAdapterError("Test error")
|
|
193
|
+
mock_adapter_class.return_value = mock_adapter
|
|
194
|
+
|
|
195
|
+
result = runner.invoke(app, ["board", "get", "--board-id", "1"])
|
|
196
|
+
assert result.exit_code != 0
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class TestEpicOperations:
|
|
200
|
+
"""Test suite for Epic operations."""
|
|
201
|
+
|
|
202
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
203
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
204
|
+
def test_epic_issues(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
|
|
205
|
+
"""Test epic issues command."""
|
|
206
|
+
mock_adapter = Mock()
|
|
207
|
+
mock_adapter.epic_issues.return_value = []
|
|
208
|
+
mock_adapter_class.return_value = mock_adapter
|
|
209
|
+
|
|
210
|
+
result = runner.invoke(app, ["epic", "issues", "--epic-key", "TEST-100"])
|
|
211
|
+
assert result.exit_code == 0
|
|
212
|
+
mock_adapter.epic_issues.assert_called_once_with("TEST-100")
|
|
213
|
+
|
|
214
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
215
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
216
|
+
def test_epic_list(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner, sample_epic: dict) -> None:
|
|
217
|
+
"""Test epic list command."""
|
|
218
|
+
mock_adapter = Mock()
|
|
219
|
+
mock_adapter.get_epics.return_value = [sample_epic]
|
|
220
|
+
mock_adapter_class.return_value = mock_adapter
|
|
221
|
+
|
|
222
|
+
result = runner.invoke(app, ["epic", "list", "--board-id", "1", "--limit", "10"])
|
|
223
|
+
assert result.exit_code == 0
|
|
224
|
+
mock_adapter.get_epics.assert_called_once_with(1, done=False, start=0, limit=10)
|
|
225
|
+
|
|
226
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
227
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
228
|
+
def test_epic_issues_for_epic(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
|
|
229
|
+
"""Test epic issues-for-epic command."""
|
|
230
|
+
mock_adapter = Mock()
|
|
231
|
+
mock_adapter.get_issues_for_epic.return_value = []
|
|
232
|
+
mock_adapter_class.return_value = mock_adapter
|
|
233
|
+
|
|
234
|
+
result = runner.invoke(app, ["epic", "issues-for-epic", "--board-id", "1", "--epic-id", "100", "--limit", "10"])
|
|
235
|
+
assert result.exit_code == 0
|
|
236
|
+
mock_adapter.get_issues_for_epic.assert_called_once_with(1, 100, jql="", validate_query="", fields="*all", expand="", start=0, limit=10)
|
|
237
|
+
|
|
238
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
239
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
240
|
+
def test_epic_requires_epic_key(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
|
|
241
|
+
"""Test epic issues requires epic-key."""
|
|
242
|
+
result = runner.invoke(app, ["epic", "issues"])
|
|
243
|
+
assert result.exit_code != 0
|
|
244
|
+
assert "--epic-key required" in result.stderr
|
|
245
|
+
|
|
246
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
247
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
248
|
+
def test_epic_error_handling(self, mock_settings: Mock, mock_adapter_class: Mock, runner: CliRunner) -> None:
|
|
249
|
+
"""Test epic command error handling."""
|
|
250
|
+
mock_adapter = Mock()
|
|
251
|
+
mock_adapter.epic_issues.side_effect = JiraAdapterError("Test error")
|
|
252
|
+
mock_adapter_class.return_value = mock_adapter
|
|
253
|
+
|
|
254
|
+
result = runner.invoke(app, ["epic", "issues", "--epic-key", "TEST-100"])
|
|
255
|
+
assert result.exit_code != 0
|
|
256
|
+
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from unittest.mock import MagicMock, Mock, patch
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from typer.testing import CliRunner
|
|
7
|
+
from vds_jira_orchestrator.adapter import JiraAdapter, JiraAdapterError
|
|
8
|
+
from vds_jira_orchestrator.cli import app
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.fixture
|
|
12
|
+
def mock_adapter() -> JiraAdapter:
|
|
13
|
+
"""Fixture for a JiraAdapter."""
|
|
14
|
+
with patch("vds_jira_orchestrator.adapter.Jira") as mock_jira_class:
|
|
15
|
+
mock_client = MagicMock()
|
|
16
|
+
mock_jira_class.return_value = mock_client
|
|
17
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
18
|
+
adapter._client = mock_client
|
|
19
|
+
return adapter
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.fixture
|
|
23
|
+
def runner() -> CliRunner:
|
|
24
|
+
return CliRunner()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture
|
|
28
|
+
def mock_settings_class() -> MagicMock:
|
|
29
|
+
"""Mock JiraSettings for CLI tests."""
|
|
30
|
+
mock_settings = Mock()
|
|
31
|
+
mock_settings.base_url = "https://jira.example.com"
|
|
32
|
+
mock_settings.username = "testuser"
|
|
33
|
+
mock_settings.password = "testpass"
|
|
34
|
+
mock_settings.token = None
|
|
35
|
+
mock_settings.require_basic_or_token = Mock()
|
|
36
|
+
with patch("vds_jira_orchestrator.cli.JiraSettings") as mock_class:
|
|
37
|
+
mock_class.return_value = mock_settings
|
|
38
|
+
yield mock_class
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@pytest.fixture
|
|
42
|
+
def mock_adapter_class() -> MagicMock:
|
|
43
|
+
"""Mock JiraAdapter for CLI tests."""
|
|
44
|
+
with patch("vds_jira_orchestrator.cli.JiraAdapter") as mock_class:
|
|
45
|
+
yield mock_class
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
# Adapter method tests
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_get_property_calls_client(mock_adapter: JiraAdapter) -> None:
|
|
54
|
+
"""Test that get_property calls the SDK client with correct parameters."""
|
|
55
|
+
mock_adapter._client.get_property.return_value = {"key": "test.key", "value": "test_value"}
|
|
56
|
+
result = mock_adapter.get_property(key="test.key")
|
|
57
|
+
mock_adapter._client.get_property.assert_called_once_with(key="test.key", permission_level=None, key_filter=None)
|
|
58
|
+
assert result["key"] == "test.key"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_get_property_with_filters(mock_adapter: JiraAdapter) -> None:
|
|
62
|
+
"""Test get_property with permission level and key filter."""
|
|
63
|
+
mock_adapter._client.get_property.return_value = {"key": "test.key", "value": "test_value"}
|
|
64
|
+
result = mock_adapter.get_property(key="test.key", permission_level="SYSTEM", key_filter="test.*")
|
|
65
|
+
mock_adapter._client.get_property.assert_called_once_with(key="test.key", permission_level="SYSTEM", key_filter="test.*")
|
|
66
|
+
assert result["key"] == "test.key"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_get_property_handles_error(mock_adapter: JiraAdapter) -> None:
|
|
70
|
+
"""Test that get_property handles errors from the SDK client."""
|
|
71
|
+
mock_adapter._client.get_property.side_effect = Exception("API Error")
|
|
72
|
+
with pytest.raises(JiraAdapterError, match="API Error"):
|
|
73
|
+
mock_adapter.get_property(key="test.key")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_set_property_calls_client(mock_adapter: JiraAdapter) -> None:
|
|
77
|
+
"""Test that set_property calls the SDK client."""
|
|
78
|
+
mock_adapter._client.set_property.return_value = None
|
|
79
|
+
mock_adapter.set_property("prop.id", "new_value")
|
|
80
|
+
mock_adapter._client.set_property.assert_called_once_with("prop.id", "new_value")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_set_property_handles_error(mock_adapter: JiraAdapter) -> None:
|
|
84
|
+
"""Test that set_property handles errors from the SDK client."""
|
|
85
|
+
mock_adapter._client.set_property.side_effect = Exception("API Error")
|
|
86
|
+
with pytest.raises(JiraAdapterError, match="API Error"):
|
|
87
|
+
mock_adapter.set_property("prop.id", "value")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_get_advanced_settings_calls_client(mock_adapter: JiraAdapter) -> None:
|
|
91
|
+
"""Test that get_advanced_settings calls the SDK client."""
|
|
92
|
+
mock_adapter._client.get_advanced_settings.return_value = {"setting1": "value1", "setting2": "value2"}
|
|
93
|
+
result = mock_adapter.get_advanced_settings()
|
|
94
|
+
mock_adapter._client.get_advanced_settings.assert_called_once()
|
|
95
|
+
assert "setting1" in result
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_get_advanced_settings_handles_error(mock_adapter: JiraAdapter) -> None:
|
|
99
|
+
"""Test that get_advanced_settings handles errors from the SDK client."""
|
|
100
|
+
mock_adapter._client.get_advanced_settings.side_effect = Exception("API Error")
|
|
101
|
+
with pytest.raises(JiraAdapterError, match="API Error"):
|
|
102
|
+
mock_adapter.get_advanced_settings()
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
# CLI command tests
|
|
107
|
+
# ---------------------------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_property_get_success(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
111
|
+
"""Test successful property get."""
|
|
112
|
+
mock_adapter = MagicMock()
|
|
113
|
+
mock_adapter_class.return_value = mock_adapter
|
|
114
|
+
mock_adapter.get_property.return_value = {"key": "test.key", "value": "test_value"}
|
|
115
|
+
|
|
116
|
+
result = runner.invoke(app, ["property", "get", "--key", "test.key"])
|
|
117
|
+
assert result.exit_code == 0
|
|
118
|
+
assert "test.key" in result.stdout
|
|
119
|
+
mock_adapter.get_property.assert_called_once_with(key="test.key", permission_level=None, key_filter=None)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_property_get_with_filters(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
123
|
+
"""Test property get with filters."""
|
|
124
|
+
mock_adapter = MagicMock()
|
|
125
|
+
mock_adapter_class.return_value = mock_adapter
|
|
126
|
+
mock_adapter.get_property.return_value = {"key": "test.key", "value": "test_value"}
|
|
127
|
+
|
|
128
|
+
result = runner.invoke(app, ["property", "get", "--key", "test.key", "--permission-level", "SYSTEM", "--key-filter", "test.*"])
|
|
129
|
+
assert result.exit_code == 0
|
|
130
|
+
mock_adapter.get_property.assert_called_once_with(key="test.key", permission_level="SYSTEM", key_filter="test.*")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_property_set_success(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
134
|
+
"""Test successful property set."""
|
|
135
|
+
mock_adapter = MagicMock()
|
|
136
|
+
mock_adapter_class.return_value = mock_adapter
|
|
137
|
+
mock_adapter.set_property.return_value = None
|
|
138
|
+
|
|
139
|
+
result = runner.invoke(app, ["property", "set", "--property-id", "prop.id", "--value", "new_value", "--yes"])
|
|
140
|
+
assert result.exit_code == 0
|
|
141
|
+
assert "set" in result.stdout
|
|
142
|
+
mock_adapter.set_property.assert_called_once_with("prop.id", "new_value")
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def test_property_set_requires_yes(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
146
|
+
"""Test property set requires --yes flag."""
|
|
147
|
+
result = runner.invoke(app, ["property", "set", "--property-id", "prop.id", "--value", "new_value"])
|
|
148
|
+
assert result.exit_code != 0
|
|
149
|
+
assert "Refusing to set property without --yes" in result.stderr
|
|
150
|
+
mock_adapter_class.return_value.set_property.assert_not_called()
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def test_property_set_requires_property_id(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
154
|
+
"""Test property set requires --property-id."""
|
|
155
|
+
result = runner.invoke(app, ["property", "set", "--value", "new_value", "--yes"])
|
|
156
|
+
assert result.exit_code != 0
|
|
157
|
+
assert "--property-id required for set" in result.stderr
|
|
158
|
+
mock_adapter_class.return_value.set_property.assert_not_called()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def test_property_set_requires_value(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
162
|
+
"""Test property set requires --value."""
|
|
163
|
+
result = runner.invoke(app, ["property", "set", "--property-id", "prop.id", "--yes"])
|
|
164
|
+
assert result.exit_code != 0
|
|
165
|
+
assert "--value required for set" in result.stderr
|
|
166
|
+
mock_adapter_class.return_value.set_property.assert_not_called()
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def test_settings_advanced_success(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
170
|
+
"""Test successful advanced settings get."""
|
|
171
|
+
mock_adapter = MagicMock()
|
|
172
|
+
mock_adapter_class.return_value = mock_adapter
|
|
173
|
+
mock_adapter.get_advanced_settings.return_value = {"setting1": "value1", "setting2": "value2"}
|
|
174
|
+
|
|
175
|
+
result = runner.invoke(app, ["settings", "advanced"])
|
|
176
|
+
assert result.exit_code == 0
|
|
177
|
+
assert "setting1" in result.stdout
|
|
178
|
+
mock_adapter.get_advanced_settings.assert_called_once()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def test_property_invalid_action(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
182
|
+
"""Test property command with invalid action."""
|
|
183
|
+
result = runner.invoke(app, ["property", "invalid"])
|
|
184
|
+
assert result.exit_code != 0
|
|
185
|
+
assert "Unknown action: invalid" in result.stderr
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def test_settings_invalid_action(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
189
|
+
"""Test settings command with invalid action."""
|
|
190
|
+
result = runner.invoke(app, ["settings", "invalid"])
|
|
191
|
+
assert result.exit_code != 0
|
|
192
|
+
assert "Unknown action: invalid" in result.stderr
|
|
193
|
+
|