@ngocsangairvds/vsaf 3.1.26 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/global.js +70 -10
- package/tools/skills/vds-scripts-skill/.openskills.json +6 -0
- package/tools/skills/vds-scripts-skill/QUALITY.md +44 -0
- package/tools/skills/vds-scripts-skill/SKILL.md +135 -0
- package/tools/skills/vds-scripts-skill/references/audit-commands.md +171 -0
- package/tools/skills/vds-scripts-skill/references/capability-index.md +34 -0
- package/tools/skills/vds-scripts-skill/references/development-commands.md +12 -0
- package/tools/skills/vds-scripts-skill/references/google-sheets.md +73 -0
- package/tools/skills/vds-scripts-skill/references/integration-commands.md +17 -0
- package/tools/skills/vds-scripts-skill/references/platform-bootstrap.md +31 -0
- package/tools/skills/vds-scripts-skill/references/specialist-routing.md +14 -0
- package/tools/skills/vds-scripts-skill/references/validation-commands.md +15 -0
- package/tools/skills/vsaf-build/SKILL.md +32 -2
- package/tools/skills/vsaf-push-prd/SKILL.md +43 -40
- package/tools/skills/vsaf-push-srs/SKILL.md +44 -41
- package/tools/skills/vsaf-ship/SKILL.md +41 -10
- package/tools/skills/vsaf-test/SKILL.md +8 -0
- package/tools/vds-scripts/.mcp.json +11 -0
- package/tools/vds-scripts/.secrets.baseline +133 -0
- package/tools/vds-scripts/AGENTS.md +152 -0
- package/tools/vds-scripts/CLAUDE.md +101 -0
- package/tools/vds-scripts/CLI_COMMAND_OPTIMIZATION.md +156 -0
- package/tools/vds-scripts/PACKAGE_P125B_IMPLEMENTATION_SUMMARY.md +131 -0
- package/tools/vds-scripts/PROJECT_COMPLETION_SUMMARY.md +45 -0
- package/tools/vds-scripts/README.md +97 -0
- package/tools/vds-scripts/bitbucket_manifest_mapping.toml +34 -0
- package/tools/vds-scripts/bitbucket_orchestrator/ARCHITECTURE_ANALYSIS.md +258 -0
- package/tools/vds-scripts/bitbucket_orchestrator/BITBUCKET_API_PRACTICES.md +393 -0
- package/tools/vds-scripts/bitbucket_orchestrator/EVALUATION_REPORT.md +61 -0
- package/tools/vds-scripts/bitbucket_orchestrator/FEATURES.md +908 -0
- package/tools/vds-scripts/bitbucket_orchestrator/README.md +687 -0
- package/tools/vds-scripts/bitbucket_orchestrator/pyproject.toml +40 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/__init__.py +20 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/async_client.py +657 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/cli.py +2108 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/client.py +2534 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/config.py +171 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/errors.py +67 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/factory.py +185 -0
- package/tools/vds-scripts/bitbucket_orchestrator/src/vds_bitbucket_orchestrator/protocols.py +244 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/__init__.py +8 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/conftest.py +65 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_advanced_search.py +151 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_async_client.py +546 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_branch_permissions.py +145 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_cli.py +115 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client.py +157 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_branch_conditions.py +79 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_code_advanced.py +163 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_code_file.py +32 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_deployment_environments.py +194 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_issues.py +164 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_pipelines_advanced.py +179 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_pr_blockers.py +119 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_client_repository_variables.py +156 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code.py +98 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code_advanced.py +282 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_code_insights.py +335 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_conditions.py +147 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_config.py +131 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_deployment_env.py +352 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_factory.py +371 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_fork_operations.py +204 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_issue_cli.py +261 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_pipeline_advanced.py +270 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_pr_blocker.py +204 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_protocols.py +334 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_repo_settings.py +343 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_repo_variables.py +270 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_webhooks.py +189 -0
- package/tools/vds-scripts/bitbucket_orchestrator/tests/test_workspace.py +233 -0
- package/tools/vds-scripts/bitbucket_orchestrator/uv.lock +742 -0
- package/tools/vds-scripts/confluence_orchestrator/Dockerfile +19 -0
- package/tools/vds-scripts/confluence_orchestrator/README.md +412 -0
- package/tools/vds-scripts/confluence_orchestrator/SYNC_SCRIPTS.md +127 -0
- package/tools/vds-scripts/confluence_orchestrator/SYNC_STANDARDIZATION.md +108 -0
- package/tools/vds-scripts/confluence_orchestrator/pyproject.toml +48 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/__init__.py +20 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/cli.py +2532 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/config.py +175 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/content.py +290 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/content_v2.py +94 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/crawl_tree.py +1835 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/errors.py +80 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/eventing.py +109 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/http.py +1114 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/orchestration.py +165 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/reporting.py +78 -0
- package/tools/vds-scripts/confluence_orchestrator/src/confluence_orchestrator/tree.py +121 -0
- package/tools/vds-scripts/confluence_orchestrator/sync_pdfs_from_markdown.py +213 -0
- package/tools/vds-scripts/confluence_orchestrator/sync_pdfs_to_confluence.py +305 -0
- package/tools/vds-scripts/confluence_orchestrator/sync_png_attachments.py +305 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/conftest.py +8 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_advanced_content.py +224 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_advanced_search.py +188 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_cache_management.py +247 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_cli.py +499 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_config.py +83 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_content.py +186 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_content_flags.py +27 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_crawl_tree.py +2250 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_draft_management.py +223 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing.py +71 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_chaos.py +37 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_rate_limit.py +44 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_eventing_timeout.py +49 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_export.py +230 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_history.py +204 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_http.py +117 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_orchestration.py +91 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_reporting.py +24 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_search_cql.py +34 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_space_management.py +237 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_space_permissions.py +332 -0
- package/tools/vds-scripts/confluence_orchestrator/tests/test_user_group_management.py +388 -0
- package/tools/vds-scripts/confluence_orchestrator/uv.lock +1023 -0
- package/tools/vds-scripts/git_orchestrator/ENHANCEMENT_SUMMARY.md +119 -0
- package/tools/vds-scripts/git_orchestrator/README.md +280 -0
- package/tools/vds-scripts/git_orchestrator/VERIFICATION_REPORT.md +152 -0
- package/tools/vds-scripts/git_orchestrator/pyproject.toml +35 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/__init__.py +7 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/__main__.py +4 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/cli.py +847 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/logging_config.py +63 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/manifest.py +129 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/orchestrator.py +819 -0
- package/tools/vds-scripts/git_orchestrator/src/vds_git_orchestrator/reporting.py +53 -0
- package/tools/vds-scripts/git_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_cli_settings.py +21 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_integration.py +74 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_manifest.py +79 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_orchestrator.py +204 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_public_api.py +236 -0
- package/tools/vds-scripts/git_orchestrator/tests/test_resilience.py +345 -0
- package/tools/vds-scripts/git_orchestrator/uv.lock +271 -0
- package/tools/vds-scripts/jira_orchestrator/README.md +770 -0
- package/tools/vds-scripts/jira_orchestrator/pyproject.toml +39 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/__init__.py +1 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/adapter.py +1320 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/cli.py +2271 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/config.py +138 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/errors.py +67 -0
- package/tools/vds-scripts/jira_orchestrator/src/vds_jira_orchestrator/reporting.py +65 -0
- package/tools/vds-scripts/jira_orchestrator/tests/__init__.py +1 -0
- package/tools/vds-scripts/jira_orchestrator/tests/conftest.py +86 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_agile_list_payloads.py +54 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_bulk_operations.py +69 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_components.py +57 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_createmeta.py +45 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_dashboard.py +117 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_issue_properties.py +54 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_permissions_compat.py +42 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_reindex.py +42 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_remote_links.py +76 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_transitions.py +91 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_user_management.py +110 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_version_management.py +133 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_adapter_watchers.py +41 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_advanced_search.py +164 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_agile.py +256 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_application_properties.py +193 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_backlog.py +91 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_bulk_operations.py +277 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_cli.py +106 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_components.py +106 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_config.py +164 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_dashboard.py +122 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_discover_fields.py +207 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_filter_management.py +333 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_issue_archiving.py +164 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_issue_links.py +257 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_issue_properties.py +171 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_link_types.py +314 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_parse_set.py +37 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_permissions.py +273 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_reindex.py +81 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_remote_links.py +254 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_security_schemes.py +170 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_transitions_changelog.py +114 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_user_management.py +226 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_version_management.py +339 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_watchers.py +101 -0
- package/tools/vds-scripts/jira_orchestrator/tests/test_worklog.py +223 -0
- package/tools/vds-scripts/jira_orchestrator/uv.lock +738 -0
- package/tools/vds-scripts/mcp_server/Dockerfile +34 -0
- package/tools/vds-scripts/mcp_server/README.md +140 -0
- package/tools/vds-scripts/mcp_server/pyproject.toml +42 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/__init__.py +4 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/config.py +36 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/server.py +66 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/__init__.py +14 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/bitbucket_tools.py +47 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/confluence_tools.py +59 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/git_tools.py +71 -0
- package/tools/vds-scripts/mcp_server/src/vds_mcp_server/tools/jira_tools.py +63 -0
- package/tools/vds-scripts/mcp_server/tests/__init__.py +2 -0
- package/tools/vds-scripts/mcp_server/tests/conftest.py +29 -0
- package/tools/vds-scripts/mcp_server/tests/unit/__init__.py +2 -0
- package/tools/vds-scripts/mcp_server/tests/unit/test_bitbucket_tools.py +25 -0
- package/tools/vds-scripts/mcp_server/tests/unit/test_confluence_tools.py +25 -0
- package/tools/vds-scripts/mcp_server/tests/unit/test_git_tools.py +32 -0
- package/tools/vds-scripts/mcp_server/tests/unit/test_jira_tools.py +32 -0
- package/tools/vds-scripts/mcp_server/tests/verification/__init__.py +2 -0
- package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_confluence_tools.py +40 -0
- package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_jira_tools.py +37 -0
- package/tools/vds-scripts/mcp_server/tests/verification/test_mcp_tool_registration.py +47 -0
- package/tools/vds-scripts/mcp_server/uv.lock +1032 -0
- package/tools/vds-scripts/mypy.ini +5 -0
- package/tools/vds-scripts/pyproject.toml +29 -0
- package/tools/vds-scripts/repo-manifest.yaml +273 -0
- package/tools/vds-scripts/repo-manifest.yaml.example +25 -0
- package/tools/vds-scripts/scripts/BRD-Validation-API.postman_collection.json +706 -0
- package/tools/vds-scripts/scripts/BRD-Validation-README.md +308 -0
- package/tools/vds-scripts/scripts/README.md +162 -0
- package/tools/vds-scripts/scripts/bootstrap_uv.sh +30 -0
- package/tools/vds-scripts/scripts/brd-validation-environment.json +51 -0
- package/tools/vds-scripts/scripts/brd-validation-test-results.json +13023 -0
- package/tools/vds-scripts/scripts/brd_coverage_report.json +276 -0
- package/tools/vds-scripts/scripts/create_memory_session.py +35 -0
- package/tools/vds-scripts/scripts/deployment/load_docker_images_offline.sh +90 -0
- package/tools/vds-scripts/scripts/final_completion_report.md +139 -0
- package/tools/vds-scripts/scripts/folder_structure_report.json +321 -0
- package/tools/vds-scripts/scripts/generate_completion_report.py +125 -0
- package/tools/vds-scripts/scripts/generate_intellij_modules.py +150 -0
- package/tools/vds-scripts/scripts/link_integrity_report.json +807 -0
- package/tools/vds-scripts/scripts/move_audit_artifact_pages.py +255 -0
- package/tools/vds-scripts/scripts/move_audit_artifact_pages_rest.py +165 -0
- package/tools/vds-scripts/scripts/move_wrong_dept_pages.py +216 -0
- package/tools/vds-scripts/scripts/save_intellij_memories.py +120 -0
- package/tools/vds-scripts/scripts/save_memories_to_vds_ai.py +83 -0
- package/tools/vds-scripts/scripts/save_memories_vds_style.py +129 -0
- package/tools/vds-scripts/scripts/search_intellij_memories.py +50 -0
- package/tools/vds-scripts/scripts/setup_intellij_workspace.py +65 -0
- package/tools/vds-scripts/scripts/target-state-automation/README.md +89 -0
- package/tools/vds-scripts/scripts/target-state-automation/confluence_sync_coordinator.sh +27 -0
- package/tools/vds-scripts/scripts/target-state-automation/coordination.sh +114 -0
- package/tools/vds-scripts/scripts/target-state-automation/diagram_coordinator.sh +25 -0
- package/tools/vds-scripts/scripts/target-state-automation/docs_root.sh +22 -0
- package/tools/vds-scripts/scripts/target-state-automation/generate_diagrams.sh +22 -0
- package/tools/vds-scripts/scripts/target-state-automation/markdown_coordinator.sh +25 -0
- package/tools/vds-scripts/scripts/target-state-automation/progress_dashboard.sh +17 -0
- package/tools/vds-scripts/scripts/target-state-automation/schema_coordinator.sh +25 -0
- package/tools/vds-scripts/scripts/target-state-automation/sync_confluence.sh +30 -0
- package/tools/vds-scripts/scripts/target-state-automation/update_dependencies.sh +19 -0
- package/tools/vds-scripts/scripts/target-state-automation/validate_links.sh +86 -0
- package/tools/vds-scripts/scripts/target-state-automation/validate_markdown.sh +52 -0
- package/tools/vds-scripts/scripts/target-state-automation/validate_schemas.sh +26 -0
- package/tools/vds-scripts/scripts/target-state-automation/validate_structure.sh +98 -0
- package/tools/vds-scripts/scripts/update_modules_xml.py +190 -0
- package/tools/vds-scripts/scripts/uv-workspace-alignment-verification-2026-03-25.md +128 -0
- package/tools/vds-scripts/scripts/validate_brd_coverage.py +179 -0
- package/tools/vds-scripts/scripts/validate_folder_structure.py +240 -0
- package/tools/vds-scripts/scripts/validate_link_integrity.py +272 -0
- package/tools/vds-scripts/scripts/vds_sh_helpers.sh +180 -0
- package/tools/vds-scripts/scripts/verification/phase2_portable_paths_ubuntu_docker.sh +26 -0
- package/tools/vds-scripts/scripts/worktree_uv.sh +48 -0
- package/tools/vds-scripts/uv.lock +8 -0
- package/tools/vds-scripts/vds_cli/README.md +126 -0
- package/tools/vds-scripts/vds_cli/VERIFICATION_REPORT.md +41 -0
- package/tools/vds-scripts/vds_cli/pyproject.toml +38 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/__init__.py +3 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/cli.py +173 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/docs_sync.py +1203 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/env.py +41 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/google_sheets_orchestrator/__init__.py +3 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/google_sheets_orchestrator/google_sheets_orchestrator.py +198 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/router.py +93 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/sync_api.py +647 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/sync_service.py +266 -0
- package/tools/vds-scripts/vds_cli/tests/__init__.py +2 -0
- package/tools/vds-scripts/vds_cli/tests/conftest.py +49 -0
- package/tools/vds-scripts/vds_cli/tests/unit/__init__.py +2 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_cli.py +143 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_docs_sync.py +422 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_env.py +51 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_router.py +72 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_sync_api.py +357 -0
- package/tools/vds-scripts/vds_cli/tests/unit/test_sync_service.py +160 -0
- package/tools/vds-scripts/vds_cli/tests/verification/__init__.py +2 -0
- package/tools/vds-scripts/vds_cli/tests/verification/test_bitbucket_real.py +33 -0
- package/tools/vds-scripts/vds_cli/tests/verification/test_confluence_real.py +35 -0
- package/tools/vds-scripts/vds_cli/tests/verification/test_jira_real.py +41 -0
- package/tools/vds-scripts/vds_cli/uv.lock +524 -0
- package/tools/vds-scripts/vds_cli_common/README.md +190 -0
- package/tools/vds-scripts/vds_cli_common/pyproject.toml +92 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/__init__.py +34 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/completers.py +139 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/context.py +201 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/env.py +119 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/errors.py +318 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/output.py +284 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/paths.py +78 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/testing.py +213 -0
- package/tools/vds-scripts/vds_cli_common/src/vds_cli_common/version.py +85 -0
- package/tools/vds-scripts/vds_cli_common/tests/__init__.py +1 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_completers.py +148 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_context.py +192 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_env.py +102 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_errors.py +186 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_output.py +229 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_paths.py +61 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_testing.py +138 -0
- package/tools/vds-scripts/vds_cli_common/tests/test_version.py +64 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
"""Tests for JIRA filter management."""
|
|
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, JiraNotFound
|
|
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_get_filters_calls_client(mock_jira: MagicMock) -> None:
|
|
22
|
+
"""Test that get_filters calls the SDK client."""
|
|
23
|
+
client = MagicMock()
|
|
24
|
+
client.resource_url.return_value = "https://jira.example.com/rest/api/2/filter"
|
|
25
|
+
client.get.return_value = {"values": [{"id": 10000, "name": "Filter 1"}, {"id": 10001, "name": "Filter 2"}]}
|
|
26
|
+
mock_jira.return_value = client
|
|
27
|
+
|
|
28
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
29
|
+
result = adapter.get_filters()
|
|
30
|
+
|
|
31
|
+
assert len(result) == 2
|
|
32
|
+
assert result[0]["id"] == 10000
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
36
|
+
def test_get_filters_with_owner(mock_jira: MagicMock) -> None:
|
|
37
|
+
"""Test that get_filters filters by owner."""
|
|
38
|
+
client = MagicMock()
|
|
39
|
+
client.resource_url.return_value = "https://jira.example.com/rest/api/2/filter"
|
|
40
|
+
client.get.return_value = {"values": [{"id": 10000, "name": "My Filter", "owner": {"name": "user"}}]}
|
|
41
|
+
mock_jira.return_value = client
|
|
42
|
+
|
|
43
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
44
|
+
adapter.get_filters(owner="user")
|
|
45
|
+
|
|
46
|
+
client.get.assert_called_once()
|
|
47
|
+
assert "username" in str(client.get.call_args)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
51
|
+
def test_get_filter_calls_client(mock_jira: MagicMock) -> None:
|
|
52
|
+
"""Test that get_filter calls the SDK client."""
|
|
53
|
+
client = MagicMock()
|
|
54
|
+
client.get_filter.return_value = {"id": 10000, "name": "Test Filter", "jql": "project = TEST"}
|
|
55
|
+
mock_jira.return_value = client
|
|
56
|
+
|
|
57
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
58
|
+
result = adapter.get_filter(10000)
|
|
59
|
+
|
|
60
|
+
client.get_filter.assert_called_once_with(10000)
|
|
61
|
+
assert result["id"] == 10000
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
65
|
+
def test_get_filter_not_found(mock_jira: MagicMock) -> None:
|
|
66
|
+
"""Test that get_filter raises JiraNotFound when filter is not found."""
|
|
67
|
+
client = MagicMock()
|
|
68
|
+
client.get_filter.return_value = None
|
|
69
|
+
mock_jira.return_value = client
|
|
70
|
+
|
|
71
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
72
|
+
|
|
73
|
+
with pytest.raises(JiraNotFound, match="Filter not found: 99999"):
|
|
74
|
+
adapter.get_filter(99999)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
78
|
+
def test_create_filter_calls_client(mock_jira: MagicMock) -> None:
|
|
79
|
+
"""Test that create_filter calls the SDK client."""
|
|
80
|
+
client = MagicMock()
|
|
81
|
+
client.create_filter.return_value = {"id": 10000, "name": "New Filter", "jql": "project = TEST"}
|
|
82
|
+
mock_jira.return_value = client
|
|
83
|
+
|
|
84
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
85
|
+
result = adapter.create_filter(name="New Filter", jql="project = TEST", description="Test filter")
|
|
86
|
+
|
|
87
|
+
client.create_filter.assert_called_once_with(name="New Filter", jql="project = TEST", description="Test filter", favourite=False)
|
|
88
|
+
assert result["id"] == 10000
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
92
|
+
def test_create_filter_with_favourite(mock_jira: MagicMock) -> None:
|
|
93
|
+
"""Test that create_filter can mark filter as favourite."""
|
|
94
|
+
client = MagicMock()
|
|
95
|
+
client.create_filter.return_value = {"id": 10000, "name": "Favourite Filter", "favourite": True}
|
|
96
|
+
mock_jira.return_value = client
|
|
97
|
+
|
|
98
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
99
|
+
result = adapter.create_filter(name="Favourite Filter", jql="project = TEST", favourite=True)
|
|
100
|
+
|
|
101
|
+
client.create_filter.assert_called_once_with(name="Favourite Filter", jql="project = TEST", description=None, favourite=True)
|
|
102
|
+
assert result["favourite"] is True
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
106
|
+
def test_update_filter_calls_client(mock_jira: MagicMock) -> None:
|
|
107
|
+
"""Test that update_filter calls the SDK client."""
|
|
108
|
+
client = MagicMock()
|
|
109
|
+
client.get_filter.return_value = {"id": 10000, "name": "Old Name", "jql": "project = TEST"}
|
|
110
|
+
client.update_filter.return_value = {"id": 10000, "name": "New Name", "jql": "project = TEST"}
|
|
111
|
+
mock_jira.return_value = client
|
|
112
|
+
|
|
113
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
114
|
+
result = adapter.update_filter(filter_id=10000, name="New Name")
|
|
115
|
+
|
|
116
|
+
client.update_filter.assert_called_once()
|
|
117
|
+
assert result["name"] == "New Name"
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
121
|
+
def test_update_filter_with_jql(mock_jira: MagicMock) -> None:
|
|
122
|
+
"""Test that update_filter can update JQL."""
|
|
123
|
+
client = MagicMock()
|
|
124
|
+
client.update_filter.return_value = {"id": 10000, "name": "Filter", "jql": "project = NEW"}
|
|
125
|
+
mock_jira.return_value = client
|
|
126
|
+
|
|
127
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
128
|
+
result = adapter.update_filter(filter_id=10000, jql="project = NEW")
|
|
129
|
+
|
|
130
|
+
client.update_filter.assert_called_once()
|
|
131
|
+
assert result["jql"] == "project = NEW"
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
135
|
+
def test_delete_filter_calls_client(mock_jira: MagicMock) -> None:
|
|
136
|
+
"""Test that delete_filter calls the SDK client."""
|
|
137
|
+
client = MagicMock()
|
|
138
|
+
client.delete_filter.return_value = None
|
|
139
|
+
mock_jira.return_value = client
|
|
140
|
+
|
|
141
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
142
|
+
adapter.delete_filter(10000)
|
|
143
|
+
|
|
144
|
+
client.delete_filter.assert_called_once_with(10000)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@patch("vds_jira_orchestrator.adapter.Jira")
|
|
148
|
+
def test_get_filters_handles_list_response(mock_jira: MagicMock) -> None:
|
|
149
|
+
"""Test that get_filters handles list response format."""
|
|
150
|
+
client = MagicMock()
|
|
151
|
+
client.resource_url.return_value = "https://jira.example.com/rest/api/2/filter"
|
|
152
|
+
client.get.return_value = [{"id": 10000, "name": "Filter 1"}]
|
|
153
|
+
mock_jira.return_value = client
|
|
154
|
+
|
|
155
|
+
adapter = JiraAdapter(url="https://jira.example.com", username="user", password="pass")
|
|
156
|
+
result = adapter.get_filters()
|
|
157
|
+
|
|
158
|
+
assert len(result) == 1
|
|
159
|
+
assert result[0]["id"] == 10000
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# ---------------------------------------------------------------------------
|
|
163
|
+
# CLI command tests
|
|
164
|
+
# ---------------------------------------------------------------------------
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
168
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
169
|
+
def test_cli_filter_list_success(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
170
|
+
"""Test filter list CLI command success."""
|
|
171
|
+
mock_adapter = MagicMock()
|
|
172
|
+
mock_adapter.get_filters.return_value = [{"id": 10000, "name": "Filter 1"}]
|
|
173
|
+
mock_adapter_class.return_value = mock_adapter
|
|
174
|
+
|
|
175
|
+
result = runner.invoke(app, ["filter", "list"])
|
|
176
|
+
|
|
177
|
+
assert result.exit_code == 0
|
|
178
|
+
assert "filters" in result.stdout
|
|
179
|
+
mock_adapter.get_filters.assert_called_once_with(owner=None)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
183
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
184
|
+
def test_cli_filter_list_with_owner(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
185
|
+
"""Test filter list CLI command with owner filter."""
|
|
186
|
+
mock_adapter = MagicMock()
|
|
187
|
+
mock_adapter.get_filters.return_value = [{"id": 10000, "name": "My Filter"}]
|
|
188
|
+
mock_adapter_class.return_value = mock_adapter
|
|
189
|
+
|
|
190
|
+
result = runner.invoke(app, ["filter", "list", "--owner", "user"])
|
|
191
|
+
|
|
192
|
+
assert result.exit_code == 0
|
|
193
|
+
mock_adapter.get_filters.assert_called_once_with(owner="user")
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
197
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
198
|
+
def test_cli_filter_get_success(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
199
|
+
"""Test filter get CLI command success."""
|
|
200
|
+
mock_adapter = MagicMock()
|
|
201
|
+
mock_adapter.get_filter.return_value = {"id": 10000, "name": "Test Filter", "jql": "project = TEST"}
|
|
202
|
+
mock_adapter_class.return_value = mock_adapter
|
|
203
|
+
|
|
204
|
+
result = runner.invoke(app, ["filter", "get", "--filter-id", "10000"])
|
|
205
|
+
|
|
206
|
+
assert result.exit_code == 0
|
|
207
|
+
assert "10000" in result.stdout
|
|
208
|
+
mock_adapter.get_filter.assert_called_once_with(10000)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
212
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
213
|
+
def test_cli_filter_get_requires_id(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
214
|
+
"""Test filter get CLI command requires filter ID."""
|
|
215
|
+
result = runner.invoke(app, ["filter", "get"])
|
|
216
|
+
|
|
217
|
+
assert result.exit_code != 0
|
|
218
|
+
assert "--filter-id required" in result.stdout or "--filter-id required" in result.stderr
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
222
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
223
|
+
def test_cli_filter_create_success(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
224
|
+
"""Test filter create CLI command success."""
|
|
225
|
+
mock_adapter = MagicMock()
|
|
226
|
+
mock_adapter.create_filter.return_value = {"id": 10000, "name": "New Filter", "jql": "project = TEST"}
|
|
227
|
+
mock_adapter_class.return_value = mock_adapter
|
|
228
|
+
|
|
229
|
+
result = runner.invoke(app, ["filter", "create", "--name", "New Filter", "--jql", "project = TEST", "--yes"])
|
|
230
|
+
|
|
231
|
+
assert result.exit_code == 0
|
|
232
|
+
mock_adapter.create_filter.assert_called_once()
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
236
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
237
|
+
def test_cli_filter_create_requires_yes(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
238
|
+
"""Test filter create CLI command requires --yes flag."""
|
|
239
|
+
result = runner.invoke(app, ["filter", "create", "--name", "New Filter", "--jql", "project = TEST"])
|
|
240
|
+
|
|
241
|
+
assert result.exit_code != 0
|
|
242
|
+
assert "--yes" in result.stdout or "--yes" in result.stderr or "without --yes" in result.stdout or "without --yes" in result.stderr or "Refusing" in result.stdout or "Refusing" in result.stderr
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
246
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
247
|
+
def test_cli_filter_create_requires_name(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
248
|
+
"""Test filter create CLI command requires name."""
|
|
249
|
+
result = runner.invoke(app, ["filter", "create", "--jql", "project = TEST", "--yes"])
|
|
250
|
+
|
|
251
|
+
assert result.exit_code != 0
|
|
252
|
+
assert "--name required" in result.stdout or "--name required" in result.stderr
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
256
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
257
|
+
def test_cli_filter_create_requires_jql(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
258
|
+
"""Test filter create CLI command requires JQL."""
|
|
259
|
+
result = runner.invoke(app, ["filter", "create", "--name", "New Filter", "--yes"])
|
|
260
|
+
|
|
261
|
+
assert result.exit_code != 0
|
|
262
|
+
assert "--jql required" in result.stdout or "--jql required" in result.stderr
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
266
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
267
|
+
def test_cli_filter_update_success(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
268
|
+
"""Test filter update CLI command success."""
|
|
269
|
+
mock_adapter = MagicMock()
|
|
270
|
+
mock_adapter.get_filter.return_value = {"id": 10000, "name": "Old Name", "jql": "project = TEST"}
|
|
271
|
+
mock_adapter.update_filter.return_value = {"id": 10000, "name": "New Name", "jql": "project = TEST"}
|
|
272
|
+
mock_adapter_class.return_value = mock_adapter
|
|
273
|
+
|
|
274
|
+
result = runner.invoke(app, ["filter", "update", "--filter-id", "10000", "--name", "New Name", "--yes"])
|
|
275
|
+
|
|
276
|
+
assert result.exit_code == 0
|
|
277
|
+
mock_adapter.update_filter.assert_called_once()
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
281
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
282
|
+
def test_cli_filter_update_requires_yes(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
283
|
+
"""Test filter update CLI command requires --yes flag."""
|
|
284
|
+
result = runner.invoke(app, ["filter", "update", "--filter-id", "10000", "--name", "New Name"])
|
|
285
|
+
|
|
286
|
+
assert result.exit_code != 0
|
|
287
|
+
assert "--yes" in result.stdout or "--yes" in result.stderr or "without --yes" in result.stdout or "without --yes" in result.stderr or "Refusing" in result.stdout or "Refusing" in result.stderr
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
291
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
292
|
+
def test_cli_filter_update_requires_field(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
293
|
+
"""Test filter update CLI command requires at least one field."""
|
|
294
|
+
result = runner.invoke(app, ["filter", "update", "--filter-id", "10000", "--yes"])
|
|
295
|
+
|
|
296
|
+
assert result.exit_code != 0
|
|
297
|
+
assert "At least one" in result.stdout or "At least one" in result.stderr
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
301
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
302
|
+
def test_cli_filter_delete_success(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
303
|
+
"""Test filter delete CLI command success."""
|
|
304
|
+
mock_adapter = MagicMock()
|
|
305
|
+
mock_adapter.delete_filter.return_value = None
|
|
306
|
+
mock_adapter_class.return_value = mock_adapter
|
|
307
|
+
|
|
308
|
+
result = runner.invoke(app, ["filter", "delete", "--filter-id", "10000", "--yes"])
|
|
309
|
+
|
|
310
|
+
assert result.exit_code == 0
|
|
311
|
+
assert "success" in result.stdout
|
|
312
|
+
mock_adapter.delete_filter.assert_called_once_with(10000)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
316
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
317
|
+
def test_cli_filter_delete_requires_yes(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
318
|
+
"""Test filter delete CLI command requires --yes flag."""
|
|
319
|
+
result = runner.invoke(app, ["filter", "delete", "--filter-id", "10000"])
|
|
320
|
+
|
|
321
|
+
assert result.exit_code != 0
|
|
322
|
+
assert "--yes" in result.stdout or "--yes" in result.stderr or "without --yes" in result.stdout or "without --yes" in result.stderr or "Refusing" in result.stdout or "Refusing" in result.stderr
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@patch("vds_jira_orchestrator.cli.JiraAdapter")
|
|
326
|
+
@patch("vds_jira_orchestrator.cli.JiraSettings")
|
|
327
|
+
def test_cli_filter_invalid_action(mock_settings: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
328
|
+
"""Test filter CLI command with invalid action."""
|
|
329
|
+
result = runner.invoke(app, ["filter", "invalid"])
|
|
330
|
+
|
|
331
|
+
assert result.exit_code != 0
|
|
332
|
+
assert "Unknown action" in result.stdout or "Unknown action" in result.stderr
|
|
333
|
+
|
|
@@ -0,0 +1,164 @@
|
|
|
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_issue_archive_calls_client(mock_adapter: JiraAdapter) -> None:
|
|
54
|
+
"""Test that issue_archive calls the SDK client."""
|
|
55
|
+
mock_adapter._client.issue_archive.return_value = None
|
|
56
|
+
mock_adapter.issue_archive("PROJ-1")
|
|
57
|
+
mock_adapter._client.issue_archive.assert_called_once_with("PROJ-1")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_issue_archive_handles_error(mock_adapter: JiraAdapter) -> None:
|
|
61
|
+
"""Test that issue_archive handles errors from the SDK client."""
|
|
62
|
+
mock_adapter._client.issue_archive.side_effect = Exception("API Error")
|
|
63
|
+
with pytest.raises(JiraAdapterError, match="API Error"):
|
|
64
|
+
mock_adapter.issue_archive("PROJ-1")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_issue_restore_calls_client(mock_adapter: JiraAdapter) -> None:
|
|
68
|
+
"""Test that issue_restore calls the SDK client."""
|
|
69
|
+
mock_adapter._client.issue_restore.return_value = None
|
|
70
|
+
mock_adapter.issue_restore("PROJ-1")
|
|
71
|
+
mock_adapter._client.issue_restore.assert_called_once_with("PROJ-1")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_issue_restore_handles_error(mock_adapter: JiraAdapter) -> None:
|
|
75
|
+
"""Test that issue_restore handles errors from the SDK client."""
|
|
76
|
+
mock_adapter._client.issue_restore.side_effect = Exception("API Error")
|
|
77
|
+
with pytest.raises(JiraAdapterError, match="API Error"):
|
|
78
|
+
mock_adapter.issue_restore("PROJ-1")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# ---------------------------------------------------------------------------
|
|
82
|
+
# CLI command tests
|
|
83
|
+
# ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_issue_archive_success(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
87
|
+
"""Test successful issue archive."""
|
|
88
|
+
mock_adapter = MagicMock()
|
|
89
|
+
mock_adapter_class.return_value = mock_adapter
|
|
90
|
+
mock_adapter.issue_archive.return_value = None
|
|
91
|
+
|
|
92
|
+
result = runner.invoke(app, ["issue-archive", "--issue-key", "PROJ-1", "--yes"])
|
|
93
|
+
assert result.exit_code == 0
|
|
94
|
+
assert "archived" in result.stdout
|
|
95
|
+
assert "PROJ-1" in result.stdout
|
|
96
|
+
mock_adapter.issue_archive.assert_called_once_with("PROJ-1")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_issue_archive_requires_yes(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
100
|
+
"""Test issue archive requires --yes flag."""
|
|
101
|
+
result = runner.invoke(app, ["issue-archive", "--issue-key", "PROJ-1"])
|
|
102
|
+
assert result.exit_code != 0
|
|
103
|
+
assert "Refusing to archive issue without --yes" in result.stderr
|
|
104
|
+
mock_adapter_class.return_value.issue_archive.assert_not_called()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_issue_archive_requires_issue_key(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
108
|
+
"""Test issue archive requires --issue-key."""
|
|
109
|
+
result = runner.invoke(app, ["issue-archive", "--yes"])
|
|
110
|
+
assert result.exit_code != 0
|
|
111
|
+
# Typer will show error about missing required option
|
|
112
|
+
mock_adapter_class.return_value.issue_archive.assert_not_called()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_issue_restore_success(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
116
|
+
"""Test successful issue restore."""
|
|
117
|
+
mock_adapter = MagicMock()
|
|
118
|
+
mock_adapter_class.return_value = mock_adapter
|
|
119
|
+
mock_adapter.issue_restore.return_value = None
|
|
120
|
+
|
|
121
|
+
result = runner.invoke(app, ["issue-restore", "--issue-key", "PROJ-1", "--yes"])
|
|
122
|
+
assert result.exit_code == 0
|
|
123
|
+
assert "restored" in result.stdout
|
|
124
|
+
assert "PROJ-1" in result.stdout
|
|
125
|
+
mock_adapter.issue_restore.assert_called_once_with("PROJ-1")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_issue_restore_requires_yes(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
129
|
+
"""Test issue restore requires --yes flag."""
|
|
130
|
+
result = runner.invoke(app, ["issue-restore", "--issue-key", "PROJ-1"])
|
|
131
|
+
assert result.exit_code != 0
|
|
132
|
+
assert "Refusing to restore issue without --yes" in result.stderr
|
|
133
|
+
mock_adapter_class.return_value.issue_restore.assert_not_called()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def test_issue_restore_requires_issue_key(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
137
|
+
"""Test issue restore requires --issue-key."""
|
|
138
|
+
result = runner.invoke(app, ["issue-restore", "--yes"])
|
|
139
|
+
assert result.exit_code != 0
|
|
140
|
+
# Typer will show error about missing required option
|
|
141
|
+
mock_adapter_class.return_value.issue_restore.assert_not_called()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_issue_archive_cli_handles_error(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
145
|
+
"""Test issue archive CLI handles adapter errors."""
|
|
146
|
+
mock_adapter = MagicMock()
|
|
147
|
+
mock_adapter_class.return_value = mock_adapter
|
|
148
|
+
mock_adapter.issue_archive.side_effect = JiraAdapterError("Issue not found")
|
|
149
|
+
|
|
150
|
+
result = runner.invoke(app, ["issue-archive", "--issue-key", "PROJ-999", "--yes"])
|
|
151
|
+
assert result.exit_code != 0
|
|
152
|
+
assert "Issue not found" in result.stderr
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def test_issue_restore_cli_handles_error(runner: CliRunner, mock_settings_class: MagicMock, mock_adapter_class: MagicMock) -> None:
|
|
156
|
+
"""Test issue restore CLI handles adapter errors."""
|
|
157
|
+
mock_adapter = MagicMock()
|
|
158
|
+
mock_adapter_class.return_value = mock_adapter
|
|
159
|
+
mock_adapter.issue_restore.side_effect = JiraAdapterError("Issue not found")
|
|
160
|
+
|
|
161
|
+
result = runner.invoke(app, ["issue-restore", "--issue-key", "PROJ-999", "--yes"])
|
|
162
|
+
assert result.exit_code != 0
|
|
163
|
+
assert "Issue not found" in result.stderr
|
|
164
|
+
|