@ngocsangairvds/vsaf 3.2.13 → 3.2.15
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/bin/vsaf.js +13 -0
- package/package.json +1 -1
- package/src/config.js +167 -0
- package/src/global.js +34 -51
- package/src/utils.js +44 -1
- package/tools/vds-scripts/Makefile +9 -31
- package/tools/vds-scripts/docker/docker-compose.cli.yml +1 -117
- package/tools/vds-scripts/docker/docker-compose.services.yml +1 -40
- package/tools/vds-scripts/docker/infrastructure/init-schemas.sql +0 -34
- package/tools/vds-scripts/docker/infrastructure/pgbouncer/pgbouncer.ini +2 -6
- package/tools/vds-scripts/pyproject.toml +1 -33
- package/tools/vds-scripts/uv.lock +80 -1651
- package/tools/vds-scripts/vds_cli/pyproject.toml +3 -0
- package/tools/vds-scripts/vds_cli/src/vds_cli/cli.py +1 -127
- package/tools/vds-scripts/vds_cli/src/vds_cli/commands/lint_cli.py +1 -20
- package/tools/vds-scripts/vds_cli/src/vds_cli/router.py +0 -100
- package/tools/vds-scripts/vds_cli/tests/conftest.py +0 -2
- package/tools/vds-scripts/vds_cli/tests/unit/test_cli.py +0 -25
- package/tools/vds-scripts/vds_cli/tests/unit/test_lint_cli.py +2 -2
- package/tools/vds-scripts/vds_cli/tests/unit/test_router.py +0 -2
- package/tools/vds-scripts/CLOSURE.md +0 -340
- package/tools/vds-scripts/ECOSYSTEM-CHANGELOG.md +0 -52
- package/tools/vds-scripts/ECOSYSTEM-DOCS.md +0 -602
- package/tools/vds-scripts/ECOSYSTEM_ALIGNMENT.md +0 -133
- package/tools/vds-scripts/ENV-HYGIENE-OPS-NOTE.md +0 -65
- package/tools/vds-scripts/INVESTIGATION-cloud-401.md +0 -103
- package/tools/vds-scripts/MEM0_2.0_API_REFERENCE.md +0 -238
- package/tools/vds-scripts/PACKAGE_P125B_IMPLEMENTATION_SUMMARY.md +0 -131
- package/tools/vds-scripts/PHASE-MERGE-SUMMARY.md +0 -121
- package/tools/vds-scripts/PHASES-3-ARCHIVE.md +0 -59
- package/tools/vds-scripts/PROJECT_COMPLETION_SUMMARY.md +0 -45
- package/tools/vds-scripts/SEARCH-CRASH-REPRO.md +0 -51
- package/tools/vds-scripts/analyze_hexagonal.py +0 -217
- package/tools/vds-scripts/analyze_profiles.py +0 -60
- package/tools/vds-scripts/audit-checklist.xlsx +0 -0
- package/tools/vds-scripts/audit_orchestrator/.audit_approvals/approvals_index.json +0 -1
- package/tools/vds-scripts/audit_orchestrator/.env.example +0 -85
- package/tools/vds-scripts/audit_orchestrator/.github/workflows/audit.yml +0 -47
- package/tools/vds-scripts/audit_orchestrator/Dockerfile +0 -92
- package/tools/vds-scripts/audit_orchestrator/GOOGLE_SHEETS_IMPLEMENTATION_SUMMARY.md +0 -218
- package/tools/vds-scripts/audit_orchestrator/PHASE3_INTEGRATION_SUMMARY.md +0 -268
- package/tools/vds-scripts/audit_orchestrator/PHASE7-MERGE-SUMMARY.md +0 -174
- package/tools/vds-scripts/audit_orchestrator/README.md +0 -1573
- package/tools/vds-scripts/audit_orchestrator/TSK-168-IMPLEMENTATION-SUMMARY.md +0 -191
- package/tools/vds-scripts/audit_orchestrator/TSK-196-IMPLEMENTATION-SUMMARY.md +0 -201
- package/tools/vds-scripts/audit_orchestrator/alembic/env.py +0 -37
- package/tools/vds-scripts/audit_orchestrator/alembic/script.py.mako +0 -28
- package/tools/vds-scripts/audit_orchestrator/alembic/versions/0001_initial_audit_state_schema.py +0 -1260
- package/tools/vds-scripts/audit_orchestrator/alembic.ini +0 -68
- package/tools/vds-scripts/audit_orchestrator/config/category-mapping.json +0 -81
- package/tools/vds-scripts/audit_orchestrator/config/profile-timeouts.yaml +0 -17
- package/tools/vds-scripts/audit_orchestrator/create_sample.py +0 -55
- package/tools/vds-scripts/audit_orchestrator/data/corpus_accuracy_report.json +0 -17
- package/tools/vds-scripts/audit_orchestrator/data/exemplar_quality_report.json +0 -1606
- package/tools/vds-scripts/audit_orchestrator/data/instruction_plan_fixtures.json +0 -163
- package/tools/vds-scripts/audit_orchestrator/data/requirement_exemplars.json +0 -3443
- package/tools/vds-scripts/audit_orchestrator/data/requirement_scope_fixtures.json +0 -172
- package/tools/vds-scripts/audit_orchestrator/debug_rg.py +0 -46
- package/tools/vds-scripts/audit_orchestrator/demo_code_pack.py +0 -127
- package/tools/vds-scripts/audit_orchestrator/docs/AGENT_SDK_SELECTION_SPEC.md +0 -720
- package/tools/vds-scripts/audit_orchestrator/docs/API.md +0 -804
- package/tools/vds-scripts/audit_orchestrator/docs/CONTENT_ANALYSIS_APPROACH.md +0 -1041
- package/tools/vds-scripts/audit_orchestrator/docs/CONTENT_SCORING_EVOLUTION_SPEC.md +0 -868
- package/tools/vds-scripts/audit_orchestrator/docs/DEPLOYMENT.md +0 -778
- package/tools/vds-scripts/audit_orchestrator/docs/LLM_AGENT_AUDIT_SPEC.md +0 -721
- package/tools/vds-scripts/audit_orchestrator/docs/LLM_CONTENT_ANALYSIS_SPEC.md +0 -1143
- package/tools/vds-scripts/audit_orchestrator/docs/LSP_SETUP_GUIDE.md +0 -221
- package/tools/vds-scripts/audit_orchestrator/docs/MULTI_REPO_AUDIT_SPEC.md +0 -951
- package/tools/vds-scripts/audit_orchestrator/docs/OLLAMA_EMBEDDINGS_SETUP.md +0 -119
- package/tools/vds-scripts/audit_orchestrator/docs/PHASE32_REAL_BENCHMARK_2026-02-08.md +0 -66
- package/tools/vds-scripts/audit_orchestrator/docs/PHASE_64_TO_92_HISTORICAL_SPEC.md +0 -1772
- package/tools/vds-scripts/audit_orchestrator/docs/TSK-193-flow-trace.md +0 -201
- package/tools/vds-scripts/audit_orchestrator/docs/TSK-193-verification.md +0 -124
- package/tools/vds-scripts/audit_orchestrator/docs/phase152-hierarchical-query-surface.md +0 -46
- package/tools/vds-scripts/audit_orchestrator/examples/bitbucket_metadata_example.json +0 -50
- package/tools/vds-scripts/audit_orchestrator/legacy/migrations/README.md +0 -68
- package/tools/vds-scripts/audit_orchestrator/legacy/migrations/phase117_phase118_shared_state.sql +0 -64
- package/tools/vds-scripts/audit_orchestrator/legacy/migrations/phase154_published_pages.sql +0 -28
- package/tools/vds-scripts/audit_orchestrator/legacy/migrations/phase157_dispatch_tables.sql +0 -94
- package/tools/vds-scripts/audit_orchestrator/legacy/migrations/phase157_events.sql +0 -91
- package/tools/vds-scripts/audit_orchestrator/legacy/migrations/phase157_scope_snapshots.sql +0 -24
- package/tools/vds-scripts/audit_orchestrator/legacy/migrations/phase157_status_view.sql +0 -22
- package/tools/vds-scripts/audit_orchestrator/legacy/migrations/phase169_dispatch_observability.sql +0 -55
- package/tools/vds-scripts/audit_orchestrator/legacy/migrations/state_repair_hardening.sql +0 -24
- package/tools/vds-scripts/audit_orchestrator/pyproject.toml +0 -211
- package/tools/vds-scripts/audit_orchestrator/pyrightconfig.json +0 -51
- package/tools/vds-scripts/audit_orchestrator/pytest.ini +0 -37
- package/tools/vds-scripts/audit_orchestrator/reproduce_scanner.py +0 -40
- package/tools/vds-scripts/audit_orchestrator/scripts/README.md +0 -116
- package/tools/vds-scripts/audit_orchestrator/scripts/benchmark_crawl_modes.py +0 -455
- package/tools/vds-scripts/audit_orchestrator/scripts/benchmark_dspy.py +0 -513
- package/tools/vds-scripts/audit_orchestrator/scripts/benchmark_nlp_accuracy.py +0 -138
- package/tools/vds-scripts/audit_orchestrator/scripts/benchmark_retrieval_modes.py +0 -176
- package/tools/vds-scripts/audit_orchestrator/scripts/benchmark_upload_update_mode.py +0 -167
- package/tools/vds-scripts/audit_orchestrator/scripts/build_check.py +0 -76
- package/tools/vds-scripts/audit_orchestrator/scripts/check_live_progress.py +0 -61
- package/tools/vds-scripts/audit_orchestrator/scripts/cli_integration_test.py +0 -400
- package/tools/vds-scripts/audit_orchestrator/scripts/index_workspace.py +0 -178
- package/tools/vds-scripts/audit_orchestrator/scripts/inspect_route_conformance.py +0 -196
- package/tools/vds-scripts/audit_orchestrator/scripts/monitor_postgres.py +0 -145
- package/tools/vds-scripts/audit_orchestrator/scripts/optimize_audit.py +0 -462
- package/tools/vds-scripts/audit_orchestrator/scripts/verify.py +0 -673
- package/tools/vds-scripts/audit_orchestrator/scripts/verify_phase111_requirement_analysis.py +0 -375
- package/tools/vds-scripts/audit_orchestrator/scripts/verify_phase117_cross_repo_evidence.py +0 -77
- package/tools/vds-scripts/audit_orchestrator/scripts/verify_phase121_short_circuit.py +0 -680
- package/tools/vds-scripts/audit_orchestrator/scripts/verify_phase122_instruction_handling.py +0 -478
- package/tools/vds-scripts/audit_orchestrator/scripts/verify_phase125_skill_integration.py +0 -832
- package/tools/vds-scripts/audit_orchestrator/scripts/verify_phase_36.py +0 -394
- package/tools/vds-scripts/audit_orchestrator/scripts/verify_phase_37.py +0 -58
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/__init__.py +0 -17
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/__init__.py +0 -29
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/_langchain_warnings.py +0 -17
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/agentic_investigator.py +0 -4130
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/approval.py +0 -490
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/audit_loop_hooks.py +0 -107
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/audit_state.py +0 -50
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/base.py +0 -4035
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/code_agent.py +0 -667
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/code_analysis_helpers.py +0 -236
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/code_analysis_prompts.py +0 -146
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/docs_agent.py +0 -1234
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/langgraph_workflow.py +0 -2002
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/pydantic_base.py +0 -1227
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/requirement_analysis_agent.py +0 -593
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/security_agent.py +0 -1829
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/security_scanner.py +0 -686
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/skill_tools.py +0 -204
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/synthesis_agent.py +0 -1463
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/tool_efficiency_guard.py +0 -609
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/tool_registry.py +0 -3822
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/toolsets/__init__.py +0 -52
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/toolsets/evidence_corpus.py +0 -385
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/toolsets/filesystem.py +0 -1134
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/toolsets/lsp.py +0 -458
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/toolsets/mcp_toolset.py +0 -491
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/toolsets/skills_toolset.py +0 -997
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/toolsets/vector_evidence.py +0 -842
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/usage_tracker.py +0 -682
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/agents/visualization.py +0 -303
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/analyze_cmds.py +0 -892
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checklist_query/__init__.py +0 -15
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checklist_query/service.py +0 -171
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checks/__init__.py +0 -20
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checks/base.py +0 -60
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checks/bitbucket/__init__.py +0 -6
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checks/bitbucket/checks.py +0 -257
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checks/confluence/__init__.py +0 -10
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checks/confluence/checks.py +0 -78
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checks/git/__init__.py +0 -6
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checks/git/file_checks.py +0 -133
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checks/llm_checks/__init__.py +0 -17
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checks/llm_checks/api_docs_check.py +0 -80
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checks/llm_checks/readme_check.py +0 -76
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checks/llm_checks/security_docs_check.py +0 -78
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checks/registry.py +0 -402
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checks/sonarqube/__init__.py +0 -10
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/checks/sonarqube/checks.py +0 -276
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/cli.py +0 -12
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/cli_common.py +0 -128
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/cli_impl.py +0 -9826
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/clients/bitbucket_cli_client.py +0 -187
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/clients/confluence_cli_client.py +0 -977
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/clients/sonarqube_cli_client.py +0 -28
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/__init__.py +0 -21
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/base.py +0 -25
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/bitbucket_downloader.py +0 -644
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/bitbucket_metadata.py +0 -133
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/checklist_parser.py +0 -180
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/completeness/__init__.py +0 -31
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/completeness/bitbucket_probe.py +0 -443
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/completeness/confluence_probe.py +0 -365
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/completeness/freshness_evaluator.py +0 -330
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/completeness/material_completeness_service.py +0 -1079
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/confluence_collector.py +0 -259
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/diagram_extractor.py +0 -280
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/enrichment_extractor.py +0 -200
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/evidence_cache.py +0 -35
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/git_collector.py +0 -148
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/graphify_collector.py +0 -171
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/image_extractor.py +0 -359
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/linked_page_tracker.py +0 -120
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/markdown_converter.py +0 -344
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/material_cache.py +0 -1252
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/material_downloader.py +0 -1165
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/orchestrator.py +0 -168
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/registry_parser.py +0 -3063
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/requirements.py +0 -70
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/runner.py +0 -119
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/collectors/sonarqube_collector.py +0 -113
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/config.py +0 -1943
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/config_resolution/__init__.py +0 -23
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/config_resolution/discovery.py +0 -90
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/config_resolution/environment_resolver.py +0 -56
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/config_resolution/evidence.py +0 -78
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/config_resolution/models.py +0 -73
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/config_resolution/precedence.py +0 -10
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/config_resolution/redaction.py +0 -20
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/confluence_connectivity.py +0 -140
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/corpus_cmds.py +0 -278
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/db/__init__.py +0 -7
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/db/alembic_filters.py +0 -57
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/docs/__init__.py +0 -29
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/docs/diataxis_validator.py +0 -687
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/doctor_cmds.py +0 -3295
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/dspy_modules/__init__.py +0 -5
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/dspy_modules/evaluation.py +0 -301
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/dspy_modules/modules.py +0 -172
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/dspy_modules/runtime.py +0 -836
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/dspy_modules/signatures.py +0 -406
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/__init__.py +0 -192
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/ad_hoc_analyzer.py +0 -399
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/aggregator.py +0 -220
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/auditor.py +0 -504
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/batch_evidence_cache.py +0 -111
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/batch_processor.py +0 -4776
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/calibration.py +0 -217
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/checklist_generator.py +0 -1201
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/checklist_projection.py +0 -192
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/checklist_scoping.py +0 -221
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/checkpoint.py +0 -159
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/cl003_shared_lib_guard.py +0 -194
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/companion_context_service.py +0 -445
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/confluence_checklist_contract.py +0 -7425
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/cross_check_rules.py +0 -213
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/deterministic_evaluator.py +0 -237
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/drift_detector.py +0 -157
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/dspy_requirement_classifier.py +0 -640
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/evidence_assembler.py +0 -407
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/evidence_collector.py +0 -119
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/evidence_diversity.py +0 -101
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/gap_analyzer.py +0 -549
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/graduated.py +0 -185
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/grounding_validator.py +0 -287
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/instruction_analyzer.py +0 -882
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/instruction_compliance.py +0 -172
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/llm_row_evaluator.py +0 -9270
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/loader.py +0 -1070
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/manual_check_config.py +0 -136
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/mapping.py +0 -269
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/multi_judge.py +0 -65
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/phase120_checklist_update.py +0 -416
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/profile_scorer.py +0 -427
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/project_evidence_context.py +0 -449
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/project_knowledge_query_service.py +0 -155
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/project_knowledge_store.py +0 -383
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/project_topology.py +0 -1920
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/provider_failure_classifier.py +0 -778
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/readiness_cli_helpers.py +0 -341
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/readiness_extractor.py +0 -303
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/readiness_synthesizer.py +0 -730
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/regression_guard.py +0 -138
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/repo_type_classifier.py +0 -297
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/requirement_analysis.py +0 -1433
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/requirement_classification.py +0 -1725
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/result_merger.py +0 -814
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/route_matrix.py +0 -267
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/row_evaluator.py +0 -9437
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/row_evaluator_runtime.py +0 -1270
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/row_evaluator_types.py +0 -2102
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/rubric.py +0 -592
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/scorer.py +0 -1239
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/section_packs.py +0 -645
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/skill_recommendation.py +0 -1183
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/stability_harness.py +0 -207
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/target_selector.py +0 -841
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/telemetry.py +0 -347
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/template_analyzer.py +0 -469
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/token_tracker.py +0 -111
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/tool_first_planner.py +0 -7905
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/topology_query_service.py +0 -80
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/validator.py +0 -449
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/engine/weight_policy.py +0 -464
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/errors.py +0 -430
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/extract_cmds.py +0 -4887
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/identity.py +0 -146
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/incremental/__init__.py +0 -52
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/incremental/baseline.py +0 -378
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/incremental/change_analyzer.py +0 -407
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/incremental/delta_report.py +0 -189
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/incremental/diff_detector.py +0 -301
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/integrations/__init__.py +0 -3
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/llm/__init__.py +0 -50
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/llm/audit_schemas.py +0 -459
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/llm/codex_oauth.py +0 -340
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/llm/cost_tracker.py +0 -288
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/llm/engine.py +0 -751
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/llm/evaluator.py +0 -245
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/llm/prompts/__init__.py +0 -32
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/llm/prompts/api_docs_evaluation.py +0 -25
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/llm/prompts/gap_analysis.py +0 -31
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/llm/prompts/instruction_templates.py +0 -634
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/llm/prompts/readme_evaluation.py +0 -25
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/llm/prompts/row_evaluation.py +0 -247
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/llm/prompts/security_docs_evaluation.py +0 -25
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/llm/prompts/template_analysis.py +0 -25
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/llm/prompts.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/llm/provider.py +0 -626
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/logging_config.py +0 -577
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/mappings/__init__.py +0 -58
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/mappings/default_checklist_mapping.json +0 -18
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/mappings/vietnamese_checklist_mapping.json +0 -38
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/misc_cmds.py +0 -4689
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/models/__init__.py +0 -153
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/models/calibration.py +0 -98
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/models/checklist.py +0 -921
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/models/completeness.py +0 -309
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/models/enrichment.py +0 -58
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/models/enums.py +0 -97
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/models/evidence.py +0 -351
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/models/findings.py +0 -381
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/models/gaps.py +0 -299
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/models/graph.py +0 -42
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/models/multi_judge.py +0 -50
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/models/readiness.py +0 -309
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/models/registry.py +0 -386
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/models/reporting.py +0 -32
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/models/task.py +0 -549
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/models/template.py +0 -477
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/observability/__init__.py +0 -31
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/observability/metrics.py +0 -404
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/parse_cmds.py +0 -608
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/pdf_cmds.py +0 -208
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/performance_gates.py +0 -224
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/phase151_projection.py +0 -84
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/profiles/__init__.py +0 -65
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/profiles/detection.py +0 -842
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/profiles/models.py +0 -474
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/__init__.py +0 -1
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/_confluence_macros.py +0 -145
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/_field_sanitizer.py +0 -25
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/_table_builder.py +0 -63
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/_vietnamese_templates.py +0 -103
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/bitbucket_link_resolver.py +0 -34
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/checklist_renderer.py +0 -483
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/confluence_publisher.py +0 -3048
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/hierarchy_publisher.py +0 -213
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/live_data_injector.py +0 -152
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/macro_builder.py +0 -101
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/markdown_converter.py +0 -154
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/priority_renderer.py +0 -133
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/project_aggregate_renderer.py +0 -423
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/readiness_renderer.py +0 -186
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/system_doc_hierarchy_renderer.py +0 -382
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/publishers/system_doc_renderer.py +0 -683
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/report_cmds.py +0 -788
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/__init__.py +0 -13
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/aggregation_report.py +0 -86
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/checklist_generator.py +0 -425
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/excel_generator.py +0 -599
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/gap_report.py +0 -131
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/json_generator.py +0 -188
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/markdown_generator.py +0 -595
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/pdf/__init__.py +0 -154
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/pdf/collector.py +0 -61
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/pdf/department_builder.py +0 -77
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/pdf/errors.py +0 -9
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/pdf/md_renderer.py +0 -386
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/pdf/pdf_models.py +0 -95
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/pdf/pdf_writer.py +0 -27
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/pdf/repo_project_builders.py +0 -274
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/readiness_report.py +0 -447
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/reporting.py +0 -94
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/reports/sarif_generator.py +0 -519
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/runtime_profiles.py +0 -98
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/seed/__init__.py +0 -29
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/seed/seed_loader.py +0 -561
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/skills/__init__.py +0 -5
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/skills/skill_routing.py +0 -312
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/sources/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/sources/base.py +0 -110
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/sources/bitbucket.py +0 -129
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/sources/git_url.py +0 -60
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/sources/github.py +0 -75
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/sources/local.py +0 -58
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/spec_sync_validator.py +0 -15
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/state/__init__.py +0 -6285
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/state/readiness_helpers.py +0 -74
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/state/skill_readiness.py +0 -487
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/state/store.py +0 -12927
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/state_cmds.py +0 -1868
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/sync/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/sync/repo_sync.py +0 -409
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/sync_cmds.py +0 -1247
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/utils/__init__.py +0 -3
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/utils/debug_bundle.py +0 -214
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/validators/checklist_validator.py +0 -342
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/workflow_cmds.py +0 -19147
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/workflows/__init__.py +0 -9
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/workflows/_test_audit_daily_batch.py +0 -192
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/workflows/audit_daily_batch.py +0 -308
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/workflows/audit_deep_monthly.py +0 -193
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/workflows/audit_drift_scan.py +0 -178
- package/tools/vds-scripts/audit_orchestrator/src/vds_audit_orchestrator/workflows/audit_security_daily.py +0 -183
- package/tools/vds-scripts/audit_orchestrator/templates/sample_audit_template.xlsx +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/_helpers.py +0 -32
- package/tools/vds-scripts/audit_orchestrator/tests/collectors/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/collectors/completeness/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/collectors/completeness/test_bitbucket_probe.py +0 -403
- package/tools/vds-scripts/audit_orchestrator/tests/collectors/completeness/test_confluence_probe.py +0 -423
- package/tools/vds-scripts/audit_orchestrator/tests/collectors/test_bitbucket_downloader.py +0 -289
- package/tools/vds-scripts/audit_orchestrator/tests/collectors/test_image_extractor.py +0 -260
- package/tools/vds-scripts/audit_orchestrator/tests/collectors/test_markdown_converter.py +0 -57
- package/tools/vds-scripts/audit_orchestrator/tests/collectors/test_material_cache.py +0 -197
- package/tools/vds-scripts/audit_orchestrator/tests/collectors/test_material_downloader.py +0 -550
- package/tools/vds-scripts/audit_orchestrator/tests/collectors/test_registry_parser.py +0 -3514
- package/tools/vds-scripts/audit_orchestrator/tests/collectors/test_registry_parser_department_entry.py +0 -214
- package/tools/vds-scripts/audit_orchestrator/tests/collectors/test_registry_parser_flow.py +0 -200
- package/tools/vds-scripts/audit_orchestrator/tests/conftest.py +0 -988
- package/tools/vds-scripts/audit_orchestrator/tests/engine/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/engine/test_calibration.py +0 -48
- package/tools/vds-scripts/audit_orchestrator/tests/engine/test_confluence_checklist_phase22_helpers.py +0 -6065
- package/tools/vds-scripts/audit_orchestrator/tests/engine/test_multi_judge.py +0 -62
- package/tools/vds-scripts/audit_orchestrator/tests/engine/test_stability_harness.py +0 -61
- package/tools/vds-scripts/audit_orchestrator/tests/engine/test_structured_metadata.py +0 -419
- package/tools/vds-scripts/audit_orchestrator/tests/factories/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/factories/models.py +0 -534
- package/tools/vds-scripts/audit_orchestrator/tests/factories/templates.py +0 -241
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/diagrams/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/diagrams/compressed.drawio +0 -2
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/diagrams/mockup.bmpr +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/diagrams/simple.drawio +0 -26
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/golden/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/golden/bitbucket/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/golden/bitbucket/branch_permissions_cli.json +0 -26
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/golden/bitbucket/branch_permissions_direct.json +0 -24
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/golden/bitbucket/repo_conditions_cli.json +0 -14
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/golden/bitbucket/repo_conditions_direct.json +0 -12
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/golden/confluence/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/golden/confluence/page_cli.json +0 -7
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/golden/confluence/page_direct.json +0 -7
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/golden/confluence/search_cli.json +0 -11
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/golden/confluence/search_direct.json +0 -7
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/golden/sonarqube/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/golden/sonarqube/quality_gate_cli.json +0 -12
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/golden/sonarqube/quality_gate_direct.json +0 -12
- package/tools/vds-scripts/audit_orchestrator/tests/fixtures/requirement_strategy_phase115.json +0 -118
- package/tools/vds-scripts/audit_orchestrator/tests/integration/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/integration/conftest.py +0 -107
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/expected_outcomes.md +0 -50
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_audit_repo/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_audit_repo/auth.py +0 -27
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_audit_repo/config.py +0 -16
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_audit_repo/db.py +0 -24
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_audit_repo/main.py +0 -18
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_audit_repo/src/__init__.py +0 -1
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_audit_repo/src/utils.py +0 -22
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_checklist_template.json +0 -110
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_evidence/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_evidence/code_evidence_pack.json +0 -40
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_evidence/manifest.json +0 -49
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_evidence/projects/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_evidence/projects/mock-audit-project/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_evidence/projects/mock-audit-project/brd.md +0 -19
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_evidence/projects/mock-audit-project/design.md +0 -32
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_evidence/projects/mock-audit-project/security.md +0 -23
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_evidence/projects/mock-audit-project/srs.md +0 -25
- package/tools/vds-scripts/audit_orchestrator/tests/integration/fixtures/mock_evidence/projects/mock-audit-project/test.md +0 -30
- package/tools/vds-scripts/audit_orchestrator/tests/integration/test_checkpoint_merge.py +0 -1371
- package/tools/vds-scripts/audit_orchestrator/tests/integration/test_decoupling_route_p149.py +0 -176
- package/tools/vds-scripts/audit_orchestrator/tests/integration/test_gap_analyzer_batch_p149.py +0 -151
- package/tools/vds-scripts/audit_orchestrator/tests/integration/test_hybrid_search.py +0 -799
- package/tools/vds-scripts/audit_orchestrator/tests/integration/test_mcp_integration.py +0 -741
- package/tools/vds-scripts/audit_orchestrator/tests/integration/test_merge_ranking_p149.py +0 -98
- package/tools/vds-scripts/audit_orchestrator/tests/integration/test_modality_mismatch_p149.py +0 -171
- package/tools/vds-scripts/audit_orchestrator/tests/integration/test_phase117_118_storage.py +0 -350
- package/tools/vds-scripts/audit_orchestrator/tests/integration/test_phase121_short_circuit.py +0 -732
- package/tools/vds-scripts/audit_orchestrator/tests/integration/test_phase18_workflow.py +0 -223
- package/tools/vds-scripts/audit_orchestrator/tests/integration/test_phase48_e2e_verification.py +0 -763
- package/tools/vds-scripts/audit_orchestrator/tests/integration/test_phase81_doc_anchor_regression.py +0 -252
- package/tools/vds-scripts/audit_orchestrator/tests/integration/test_provider_failure_finding_p149.py +0 -339
- package/tools/vds-scripts/audit_orchestrator/tests/integration/test_readiness_e2e.py +0 -430
- package/tools/vds-scripts/audit_orchestrator/tests/integration/test_refined_workflow.py +0 -1180
- package/tools/vds-scripts/audit_orchestrator/tests/pdf/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/pdf/snapshots/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/pdf/snapshots/department_renderer.md +0 -24
- package/tools/vds-scripts/audit_orchestrator/tests/pdf/snapshots/project_renderer.md +0 -8
- package/tools/vds-scripts/audit_orchestrator/tests/pdf/snapshots/repo_renderer.md +0 -10
- package/tools/vds-scripts/audit_orchestrator/tests/pdf/test_department_pdf.py +0 -112
- package/tools/vds-scripts/audit_orchestrator/tests/pdf/test_e2e_pdf.py +0 -135
- package/tools/vds-scripts/audit_orchestrator/tests/pdf/test_idempotency.py +0 -45
- package/tools/vds-scripts/audit_orchestrator/tests/pdf/test_md_renderer.py +0 -46
- package/tools/vds-scripts/audit_orchestrator/tests/pdf/test_pdf_cmds.py +0 -97
- package/tools/vds-scripts/audit_orchestrator/tests/pdf/test_pdf_snapshot.py +0 -77
- package/tools/vds-scripts/audit_orchestrator/tests/pdf/test_pdf_writer.py +0 -65
- package/tools/vds-scripts/audit_orchestrator/tests/pdf/test_project_builder.py +0 -199
- package/tools/vds-scripts/audit_orchestrator/tests/pdf/test_public_api.py +0 -135
- package/tools/vds-scripts/audit_orchestrator/tests/pdf/test_repo_builder.py +0 -246
- package/tools/vds-scripts/audit_orchestrator/tests/pdf/test_workflow_pdf_flags.py +0 -36
- package/tools/vds-scripts/audit_orchestrator/tests/property/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/property/test_properties.py +0 -807
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_agent_error_compat.py +0 -38
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_agentic_skill_policy_skip.py +0 -234
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_base_event_stream_logging.py +0 -785
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_base_timeout_policy.py +0 -277
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_base_trace_payload_sanitization.py +0 -92
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_code_agent.py +0 -2311
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_code_agent_re_exports.py +0 -25
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_code_analysis_helpers.py +0 -94
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_create_audit_agent_reasoning_effort.py +0 -69
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_docs_agent.py +0 -2044
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_langgraph_workflow_efficiency_metrics.py +0 -71
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_output_validators.py +0 -317
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_phase41_toolsets.py +0 -6427
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_pydantic_ai_models.py +0 -1219
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_pydantic_base_url_resolution.py +0 -84
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_security_agent.py +0 -2069
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_skill_manager_focus.py +0 -439
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_synthesis_agent.py +0 -1195
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_tool_efficiency_guard_fr120.py +0 -683
- package/tools/vds-scripts/audit_orchestrator/tests/test_agents/test_toolsets.py +0 -716
- package/tools/vds-scripts/audit_orchestrator/tests/test_aggregator_p149.py +0 -171
- package/tools/vds-scripts/audit_orchestrator/tests/test_alembic_migrations.py +0 -287
- package/tools/vds-scripts/audit_orchestrator/tests/test_anchor_allowlist_p149.py +0 -273
- package/tools/vds-scripts/audit_orchestrator/tests/test_audit_otel.py +0 -283
- package/tools/vds-scripts/audit_orchestrator/tests/test_checklist_models.py +0 -583
- package/tools/vds-scripts/audit_orchestrator/tests/test_checks/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_checks/test_base_check.py +0 -211
- package/tools/vds-scripts/audit_orchestrator/tests/test_checks/test_llm_checks.py +0 -126
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_analyze_command.py +0 -400
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_archive_stale_page_cli.py +0 -217
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_bitbucket_metadata_cli.py +0 -354
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_cli_impl_profile_availability.py +0 -114
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_codex_profile.py +0 -174
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_compare_backends_cli.py +0 -449
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_confluence_parent_auto_resolve.py +0 -451
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_corpus_purge_cli.py +0 -290
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_credentials_preflight.py +0 -106
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_debug_bundle.py +0 -37
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_deprecation_phase157.py +0 -484
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_dispatch_concurrency_diagnostics.py +0 -758
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_doctor_check_confluence_cli.py +0 -320
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_doctor_codex.py +0 -187
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_doctor_corpus_status_cli.py +0 -236
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_doctor_correlation_cli.py +0 -128
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_doctor_crawl_status_cli.py +0 -192
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_doctor_credentials_cli.py +0 -86
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_doctor_dispatch_status_cli.py +0 -421
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_doctor_heartbeat_phase169.py +0 -173
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_doctor_hierarchy_status_cli.py +0 -199
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_doctor_locks_cli.py +0 -134
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_doctor_logs_follow_cli.py +0 -305
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_doctor_migration.py +0 -333
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_doctor_profile_availability_cli.py +0 -151
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_doctor_skills_policy_cli.py +0 -153
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_evidence_quality_cli.py +0 -307
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_export_debug_bundle_phase36.py +0 -60
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_export_git_manifest_cli.py +0 -172
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_file_removal_phase157e.py +0 -770
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_grounding_classifier.py +0 -226
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_logging.py +0 -49
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_materials_cli.py +0 -9127
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_metadata_completeness_phase92.py +0 -364
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_parent_dispatch_finalization_phase168f.py +0 -111
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_parse_cli.py +0 -590
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_phase117_118_feature_flags.py +0 -219
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_phase164_control_plane.py +0 -718
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_phase165_runner_scripts.py +0 -230
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_preparation_classifications.py +0 -146
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_prepare_cli.py +0 -398
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_publication_quality_gate.py +0 -126
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_publish_system_doc_cli.py +0 -158
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_query_checklist_cli.py +0 -219
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_readiness_cli.py +0 -673
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_readiness_cli_integration.py +0 -689
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_removed_flags_phase92.py +0 -36
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_report_cmds.py +0 -1317
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_run_history_index.py +0 -57
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_run_management.py +0 -1194
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_runtime_profiles_cli.py +0 -1658
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_smart_run_selection.py +0 -1562
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_state_cli.py +0 -2467
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_state_migration.py +0 -339
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_sync_repos_debug_artifacts.py +0 -1109
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_upload_results_cli.py +0 -809
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_validate_checklist.py +0 -178
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_validate_checklist_cli.py +0 -110
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_validate_spec_sync_cli.py +0 -519
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_workflow_default_parameters_baseline.py +0 -101
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_workflow_options.py +0 -7896
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_workflow_project_db_modes.py +0 -6516
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_workflow_project_project_scope.py +0 -831
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_workflow_project_target.py +0 -611
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_workflow_projects_phase131_lifecycle.py +0 -2488
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_workflow_projects_phase131_scaffolding.py +0 -96
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_workflow_row_key_guard.py +0 -78
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli/test_workflow_summary_artifacts.py +0 -1872
- package/tools/vds-scripts/audit_orchestrator/tests/test_cli_paths_phase2.py +0 -45
- package/tools/vds-scripts/audit_orchestrator/tests/test_clients/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_clients/test_bitbucket_cli_client.py +0 -124
- package/tools/vds-scripts/audit_orchestrator/tests/test_clients/test_cli_parity.py +0 -110
- package/tools/vds-scripts/audit_orchestrator/tests/test_clients/test_confluence_cli_client.py +0 -1149
- package/tools/vds-scripts/audit_orchestrator/tests/test_clients/test_sonarqube_cli_client.py +0 -19
- package/tools/vds-scripts/audit_orchestrator/tests/test_collectors/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_collectors/test_linked_page_tracker.py +0 -118
- package/tools/vds-scripts/audit_orchestrator/tests/test_companion_context_service.py +0 -230
- package/tools/vds-scripts/audit_orchestrator/tests/test_dspy_modules/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_dspy_modules/conftest.py +0 -11
- package/tools/vds-scripts/audit_orchestrator/tests/test_dspy_modules/test_compile_artifact.py +0 -465
- package/tools/vds-scripts/audit_orchestrator/tests/test_dspy_modules/test_cross_provider_critique.py +0 -120
- package/tools/vds-scripts/audit_orchestrator/tests/test_dspy_modules/test_cross_provider_critique_e2e.py +0 -75
- package/tools/vds-scripts/audit_orchestrator/tests/test_dspy_modules/test_evaluation.py +0 -515
- package/tools/vds-scripts/audit_orchestrator/tests/test_dspy_modules/test_runtime_loader.py +0 -537
- package/tools/vds-scripts/audit_orchestrator/tests/test_dspy_modules/test_signatures_normalization.py +0 -172
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_auditor_applicability.py +0 -68
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_checklist_generator.py +0 -1252
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_checklist_projection.py +0 -54
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_confluence_checklist_projection_consistency.py +0 -1696
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_critique_merger_matrix.py +0 -120
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_cross_check_rules.py +0 -459
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_cross_provider_critique.py +0 -55
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_doc_loader.py +0 -73
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_drift_detector.py +0 -34
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_evidence_collectors.py +0 -93
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_lease_timeout.py +0 -114
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_loader.py +0 -350
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_loader_parity.py +0 -179
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_low_confidence_reeval.py +0 -691
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_phase145a_completion.py +0 -209
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_phase31_row_consistency_retry_benchmark.py +0 -150
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_profile_detector.py +0 -286
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_regression_guard.py +0 -53
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_result_merger.py +0 -619
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_row_evaluator.py +0 -15783
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_row_failover.py +0 -215
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_scorer.py +0 -597
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_skill_breakdown_telemetry_fr137.py +0 -421
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_targeted_auto_merge.py +0 -229
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_timeout_failover.py +0 -488
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_timeout_telemetry.py +0 -73
- package/tools/vds-scripts/audit_orchestrator/tests/test_engine/test_validator.py +0 -419
- package/tools/vds-scripts/audit_orchestrator/tests/test_incremental/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_incremental/test_diff_detector.py +0 -111
- package/tools/vds-scripts/audit_orchestrator/tests/test_infra_persistence.py +0 -291
- package/tools/vds-scripts/audit_orchestrator/tests/test_integration/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_integration/test_phase3_integration.py +0 -516
- package/tools/vds-scripts/audit_orchestrator/tests/test_llm/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_llm/test_cache.py +0 -670
- package/tools/vds-scripts/audit_orchestrator/tests/test_llm/test_codex_model_builder.py +0 -281
- package/tools/vds-scripts/audit_orchestrator/tests/test_llm/test_codex_oauth.py +0 -330
- package/tools/vds-scripts/audit_orchestrator/tests/test_llm/test_codex_streaming.py +0 -433
- package/tools/vds-scripts/audit_orchestrator/tests/test_llm/test_cost_tracker.py +0 -27
- package/tools/vds-scripts/audit_orchestrator/tests/test_llm/test_engine.py +0 -876
- package/tools/vds-scripts/audit_orchestrator/tests/test_llm/test_evaluator.py +0 -212
- package/tools/vds-scripts/audit_orchestrator/tests/test_llm/test_instruction_templates.py +0 -639
- package/tools/vds-scripts/audit_orchestrator/tests/test_llm/test_prompt_metadata.py +0 -97
- package/tools/vds-scripts/audit_orchestrator/tests/test_llm/test_prompts.py +0 -660
- package/tools/vds-scripts/audit_orchestrator/tests/test_llm/test_provider.py +0 -330
- package/tools/vds-scripts/audit_orchestrator/tests/test_llm/test_provider_contract_sync.py +0 -18
- package/tools/vds-scripts/audit_orchestrator/tests/test_llm/test_reasoning_effort_validation.py +0 -565
- package/tools/vds-scripts/audit_orchestrator/tests/test_llm/test_schemas.py +0 -827
- package/tools/vds-scripts/audit_orchestrator/tests/test_logging_config.py +0 -297
- package/tools/vds-scripts/audit_orchestrator/tests/test_models/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_models/test_enums.py +0 -185
- package/tools/vds-scripts/audit_orchestrator/tests/test_models/test_findings.py +0 -1159
- package/tools/vds-scripts/audit_orchestrator/tests/test_models/test_project_profile.py +0 -307
- package/tools/vds-scripts/audit_orchestrator/tests/test_models/test_registry.py +0 -532
- package/tools/vds-scripts/audit_orchestrator/tests/test_models/test_template.py +0 -708
- package/tools/vds-scripts/audit_orchestrator/tests/test_observability/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_observability/test_metrics.py +0 -60
- package/tools/vds-scripts/audit_orchestrator/tests/test_paths_config_phase2.py +0 -21
- package/tools/vds-scripts/audit_orchestrator/tests/test_performance/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_performance/test_fr79_performance_guardrails.py +0 -199
- package/tools/vds-scripts/audit_orchestrator/tests/test_phase156_hardening.py +0 -498
- package/tools/vds-scripts/audit_orchestrator/tests/test_phase93_regression_guards.py +0 -123
- package/tools/vds-scripts/audit_orchestrator/tests/test_pipeline_integration.py +0 -517
- package/tools/vds-scripts/audit_orchestrator/tests/test_profiles/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_profiles/test_detection.py +0 -146
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_bitbucket_link_resolver.py +0 -55
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_checklist_renderer.py +0 -84
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_checklist_renderer_projection.py +0 -97
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_confluence_macros.py +0 -58
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_confluence_publisher.py +0 -2171
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_evidence_links.py +0 -129
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_field_sanitizer.py +0 -108
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_hierarchy_publisher.py +0 -134
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_incremental_plan_parser.py +0 -62
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_live_data_injector.py +0 -48
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_macro_builder.py +0 -22
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_p161_confluence_optimization.py +0 -168
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_priority_renderer.py +0 -96
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_project_aggregate_renderer.py +0 -364
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_storage_validation.py +0 -273
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_summary_refactor.py +0 -118
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_system_doc_hierarchy.py +0 -50
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_table_builder.py +0 -23
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_vietnamese_templates.py +0 -37
- package/tools/vds-scripts/audit_orchestrator/tests/test_publishers/test_wiring_integration.py +0 -290
- package/tools/vds-scripts/audit_orchestrator/tests/test_reports/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_reports/test_aggregation_report.py +0 -181
- package/tools/vds-scripts/audit_orchestrator/tests/test_reports/test_checklist_generator.py +0 -258
- package/tools/vds-scripts/audit_orchestrator/tests/test_reports/test_gap_report.py +0 -73
- package/tools/vds-scripts/audit_orchestrator/tests/test_reports/test_json_generator.py +0 -317
- package/tools/vds-scripts/audit_orchestrator/tests/test_result_merger_p149.py +0 -347
- package/tools/vds-scripts/audit_orchestrator/tests/test_route_mode_p149.py +0 -178
- package/tools/vds-scripts/audit_orchestrator/tests/test_rubric_parser.py +0 -179
- package/tools/vds-scripts/audit_orchestrator/tests/test_scorer.py +0 -110
- package/tools/vds-scripts/audit_orchestrator/tests/test_state/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_state/test_sparse_coverage.py +0 -117
- package/tools/vds-scripts/audit_orchestrator/tests/test_workflow/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/test_workflow/test_langgraph_workflow.py +0 -2072
- package/tools/vds-scripts/audit_orchestrator/tests/test_workflow/test_p161_runtime_hardening.py +0 -341
- package/tools/vds-scripts/audit_orchestrator/tests/test_workflow_cmds_p149.py +0 -112
- package/tools/vds-scripts/audit_orchestrator/tests/test_workflow_cmds_p172.py +0 -126
- package/tools/vds-scripts/audit_orchestrator/tests/test_workflow_guidance_p150.py +0 -95
- package/tools/vds-scripts/audit_orchestrator/tests/unit/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_agentic_investigator_phase115.py +0 -42
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_requirement_analysis_agent.py +0 -412
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_security_agent_updates.py +0 -131
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_security_scanner.py +0 -397
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_skill_executor.py +0 -316
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_skill_fallback.py +0 -299
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_skill_policy.py +0 -520
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_skill_telemetry.py +0 -306
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_synthesis_fixes.py +0 -761
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_tool_argument_robustness.py +0 -272
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_tool_registry.py +0 -2548
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_tool_registry_ast_grep.py +0 -87
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_tool_registry_phase123_scoping.py +0 -353
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_tool_registry_phase94_ff.py +0 -445
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_tool_registry_vector_search_phase115.py +0 -35
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_utils.py +0 -1007
- package/tools/vds-scripts/audit_orchestrator/tests/unit/agents/test_vector_evidence_toolset.py +0 -622
- package/tools/vds-scripts/audit_orchestrator/tests/unit/cli/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/unit/cli/test_workflow_cli.py +0 -123
- package/tools/vds-scripts/audit_orchestrator/tests/unit/collectors/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/unit/collectors/test_cache_guard.py +0 -479
- package/tools/vds-scripts/audit_orchestrator/tests/unit/collectors/test_checklist_parser_phase120.py +0 -55
- package/tools/vds-scripts/audit_orchestrator/tests/unit/collectors/test_diagram_extractor.py +0 -467
- package/tools/vds-scripts/audit_orchestrator/tests/unit/collectors/test_enrichment_extractor.py +0 -59
- package/tools/vds-scripts/audit_orchestrator/tests/unit/collectors/test_graphify_collector.py +0 -158
- package/tools/vds-scripts/audit_orchestrator/tests/unit/completeness/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/unit/completeness/test_completeness.py +0 -563
- package/tools/vds-scripts/audit_orchestrator/tests/unit/completeness/test_freshness_evaluator.py +0 -493
- package/tools/vds-scripts/audit_orchestrator/tests/unit/completeness/test_material_cache_metrics.py +0 -365
- package/tools/vds-scripts/audit_orchestrator/tests/unit/completeness/test_material_completeness_service.py +0 -2736
- package/tools/vds-scripts/audit_orchestrator/tests/unit/config_resolution/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/unit/config_resolution/test_discovery.py +0 -47
- package/tools/vds-scripts/audit_orchestrator/tests/unit/config_resolution/test_redaction.py +0 -15
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_ad_hoc_analyzer.py +0 -576
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_agent_loop.py +0 -1896
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_anchor_filter_cl003.py +0 -181
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_batch_evidence_cache.py +0 -155
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_batch_processor.py +0 -3608
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_checklist_contract.py +0 -55
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_checklist_scoping.py +0 -371
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_config_companion_phase123.py +0 -142
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_config_evidence_phase123.py +0 -249
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_confluence_checklist_contract_export_parity.py +0 -813
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_cross_repo_config_phase122.py +0 -613
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_dspy_requirement_classifier.py +0 -517
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_evidence_diversity.py +0 -144
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_evidence_truncation.py +0 -108
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_grounding_validator.py +0 -127
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_guidance_injection_phase120.py +0 -105
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_instruction_analysis_phase122.py +0 -761
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_instruction_pre_filter_phase167.py +0 -334
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_llm_row_evaluator_retries.py +0 -3684
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_loader_phase123.py +0 -345
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_manual_check_gating_phase122.py +0 -474
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_parallel_eval.py +0 -263
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_phase122_verifier_phase122.py +0 -169
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_phase166_route_failover.py +0 -437
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_post_eval_cl003_shared_lib.py +0 -267
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_postproc_streaming.py +0 -194
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_pre_eval_gating_phase122.py +0 -362
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_prepare_topology_coverage.py +0 -247
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_private_dns_sanitization_phase104.py +0 -397
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_project_evidence_context.py +0 -450
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_project_knowledge_store.py +0 -487
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_project_topology.py +0 -1142
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_provider_failure_classifier.py +0 -195
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_readiness_extractor.py +0 -496
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_readiness_synthesizer.py +0 -653
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_repo_type_classifier.py +0 -303
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_requirement_analysis.py +0 -508
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_requirement_analysis_execution_scope.py +0 -239
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_requirement_analysis_phase114.py +0 -919
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_requirement_analysis_phase115.py +0 -97
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_requirement_analysis_shared_lib.py +0 -340
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_requirement_classification_drift.py +0 -729
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_requirement_classification_nlp.py +0 -670
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_requirement_scope_phase122.py +0 -615
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_route_matrix.py +0 -258
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_route_override.py +0 -141
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_routing_precision.py +0 -650
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_row_evaluator_dual_evidence.py +0 -2987
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_row_evaluator_instruction_runtime_phase122.py +0 -365
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_row_evaluator_runtime.py +0 -830
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_runtime_hardening_phase122.py +0 -225
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_scoped_na_skip.py +0 -107
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_scoring_enhancements.py +0 -404
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_shared_library_retrieval_phase123.py +0 -441
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_shared_library_routing_phase123.py +0 -279
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_shared_resource_indexing_phase122.py +0 -188
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_skill_recommendation.py +0 -225
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_skill_routing_cl003_shared_lib.py +0 -338
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_skills_toolset.py +0 -319
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_stability_metric.py +0 -60
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_target_selector.py +0 -958
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_token_tracker.py +0 -121
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_token_wiring.py +0 -119
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_tool_first_planner.py +0 -7103
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_topology_knowledge_persistence.py +0 -332
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_topology_query_service.py +0 -55
- package/tools/vds-scripts/audit_orchestrator/tests/unit/engine/test_unverified_ref_retry.py +0 -909
- package/tools/vds-scripts/audit_orchestrator/tests/unit/models/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/unit/models/test_evidence.py +0 -515
- package/tools/vds-scripts/audit_orchestrator/tests/unit/models/test_gaps.py +0 -422
- package/tools/vds-scripts/audit_orchestrator/tests/unit/models/test_readiness.py +0 -428
- package/tools/vds-scripts/audit_orchestrator/tests/unit/publishers/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/unit/publishers/test_confluence_hierarchy.py +0 -227
- package/tools/vds-scripts/audit_orchestrator/tests/unit/publishers/test_project_title_generation.py +0 -335
- package/tools/vds-scripts/audit_orchestrator/tests/unit/publishers/test_publisher_registry_helpers.py +0 -290
- package/tools/vds-scripts/audit_orchestrator/tests/unit/publishers/test_publisher_registry_integration.py +0 -557
- package/tools/vds-scripts/audit_orchestrator/tests/unit/publishers/test_readiness_renderer.py +0 -381
- package/tools/vds-scripts/audit_orchestrator/tests/unit/publishers/test_repo_title_consistency.py +0 -266
- package/tools/vds-scripts/audit_orchestrator/tests/unit/publishers/test_upload_hierarchy_integration.py +0 -470
- package/tools/vds-scripts/audit_orchestrator/tests/unit/scripts/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/unit/scripts/test_benchmark_dspy.py +0 -177
- package/tools/vds-scripts/audit_orchestrator/tests/unit/scripts/test_benchmark_nlp_accuracy.py +0 -72
- package/tools/vds-scripts/audit_orchestrator/tests/unit/scripts/test_benchmark_retrieval_modes.py +0 -123
- package/tools/vds-scripts/audit_orchestrator/tests/unit/scripts/test_verify_phase111_requirement_analysis.py +0 -409
- package/tools/vds-scripts/audit_orchestrator/tests/unit/seed/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/unit/seed/test_seed_chain_cli.py +0 -277
- package/tools/vds-scripts/audit_orchestrator/tests/unit/seed/test_seed_loader.py +0 -502
- package/tools/vds-scripts/audit_orchestrator/tests/unit/skills/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/unit/skills/test_skill_routing.py +0 -209
- package/tools/vds-scripts/audit_orchestrator/tests/unit/sources/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/unit/sources/test_bitbucket_source.py +0 -66
- package/tools/vds-scripts/audit_orchestrator/tests/unit/sources/test_non_retryable_markers.py +0 -88
- package/tools/vds-scripts/audit_orchestrator/tests/unit/sources/test_repo_info.py +0 -212
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_completeness.py +0 -598
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_dispatch_events_contract_phase169.py +0 -100
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_dispatch_hardening_phase158.py +0 -392
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_dispatch_persistence_phase157.py +0 -914
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_embedding_client.py +0 -64
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_get_latest_completed_run.py +0 -313
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_heartbeat_phase169.py +0 -109
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_hybrid_search.py +0 -398
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_normalize_url.py +0 -262
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_phase152_query_surface.py +0 -59
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_phase98_confluence_document_model.py +0 -202
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_published_pages.py +0 -754
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_readiness_helpers.py +0 -193
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_run_ledger.py +0 -522
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_run_management.py +0 -378
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_schema_contract_phase170.py +0 -755
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_state_cmds.py +0 -231
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_state_loaders.py +0 -2151
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_state_run_api.py +0 -2226
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_store.py +0 -1435
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_store_dispatch.py +0 -646
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_store_dispatch_status_view.py +0 -181
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_store_scope.py +0 -213
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_utilization_persist_phase169.py +0 -77
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_vds_search.py +0 -263
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_vector_index_api.py +0 -319
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_vector_index_runtime.py +0 -175
- package/tools/vds-scripts/audit_orchestrator/tests/unit/state/test_vector_index_store.py +0 -1756
- package/tools/vds-scripts/audit_orchestrator/tests/unit/sync/__init__.py +0 -0
- package/tools/vds-scripts/audit_orchestrator/tests/unit/sync/test_repo_sync.py +0 -257
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_artifact_exclusion.py +0 -119
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_auto_promote_phase158.py +0 -337
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_carry_forward_artifact_filtering.py +0 -317
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_checklist_precache_p160a.py +0 -416
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_cli_decomposition_fr219.py +0 -269
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_code_chunk_carry_forward.py +0 -203
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_config_coherence.py +0 -180
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_config_secret_policy.py +0 -522
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_corpus_project_id_migration.py +0 -318
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_corpus_status_diagnostics.py +0 -239
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_department_priority_ordering.py +0 -131
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_dispatch_coordinator_phase158.py +0 -402
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_dispatch_job_identity_p167a.py +0 -238
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_dispatch_ramp_up_phase171.py +0 -434
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_dispatcher.py +0 -911
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_doc_type_en_inference.py +0 -246
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_early_exit_unchunked_repos.py +0 -111
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_errors.py +0 -237
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_errors_taxonomy.py +0 -83
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_extract_chunking_config_phase98.py +0 -73
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_extract_cmds_state_helpers.py +0 -33
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_extract_docs_code_chunking.py +0 -260
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_finalize_dispatch_run_phase168.py +0 -341
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_identity.py +0 -221
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_infrastructure_detection.py +0 -441
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_junction_table_phase95.py +0 -259
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_late_binding_assignment_p167c.py +0 -286
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_misc_cmds_fr224_225_hardening.py +0 -194
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_p172_integration.py +0 -306
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_parent_provider_preflight.py +0 -118
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_performance_gates_phase92.py +0 -141
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_performance_gates_phase93.py +0 -50
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_phase115_search_strategy.py +0 -106
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_phase154_title_consistency.py +0 -117
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_phase155_param_forwarding.py +0 -304
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_phase158_concurrency_defaults.py +0 -207
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_phase170_doctor_schema.py +0 -319
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_phase170_regression.py +0 -334
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_phase94_corpus_lifecycle.py +0 -307
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_phase96_repo_key_migration.py +0 -305
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_pipelined_scheduling.py +0 -130
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_profile_availability_probe.py +0 -616
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_profile_aware_row_timeout.py +0 -102
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_profile_timeout_stagger_p160cd.py +0 -205
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_progress_summary_phase169.py +0 -96
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_registry_checklist_diagnostics.py +0 -124
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_resume_manifest_p167b.py +0 -268
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_risk_mitigations_p160e1.py +0 -348
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_single_row_shards_p160b.py +0 -357
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_state_repo_discovery.py +0 -504
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_sync_metadata_entries.py +0 -57
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_task_models.py +0 -1796
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_utilization_telemetry_p167e.py +0 -259
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_vietnamese_fts_hardening.py +0 -160
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_workflow_phase98_enrichment.py +0 -92
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_workflow_project_merge_materialization.py +0 -322
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_workflow_row_key_migration_guard.py +0 -88
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_workflow_short_circuit_phase121.py +0 -564
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_workflow_single_target_row_context.py +0 -49
- package/tools/vds-scripts/audit_orchestrator/tests/unit/test_zero_result_messaging.py +0 -76
- package/tools/vds-scripts/bandit-report.json +0 -2974
- package/tools/vds-scripts/brd_orchestrator/README.md +0 -29
- package/tools/vds-scripts/brd_orchestrator/pyproject.toml +0 -63
- package/tools/vds-scripts/brd_orchestrator/src/vds_brd_orchestrator/__init__.py +0 -17
- package/tools/vds-scripts/brd_orchestrator/src/vds_brd_orchestrator/cli.py +0 -187
- package/tools/vds-scripts/brd_orchestrator/src/vds_brd_orchestrator/validator.py +0 -121
- package/tools/vds-scripts/brd_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/brd_orchestrator/tests/test_cli.py +0 -62
- package/tools/vds-scripts/brd_orchestrator/tests/test_validator.py +0 -33
- package/tools/vds-scripts/circular_dependency_orchestrator/README.md +0 -30
- package/tools/vds-scripts/circular_dependency_orchestrator/pyproject.toml +0 -43
- package/tools/vds-scripts/circular_dependency_orchestrator/src/vds_circular_dependency_orchestrator/__init__.py +0 -16
- package/tools/vds-scripts/circular_dependency_orchestrator/src/vds_circular_dependency_orchestrator/cli.py +0 -904
- package/tools/vds-scripts/circular_dependency_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/circular_dependency_orchestrator/tests/unit/__init__.py +0 -0
- package/tools/vds-scripts/circular_dependency_orchestrator/tests/unit/test_cli.py +0 -354
- package/tools/vds-scripts/coverage.json +0 -1
- package/tools/vds-scripts/create_pr.py +0 -57
- package/tools/vds-scripts/diagram_generator/README.md +0 -663
- package/tools/vds-scripts/diagram_generator/ci_validate.sh +0 -16
- package/tools/vds-scripts/diagram_generator/docs-nttc/projects/INSURANCE/analysis/current-state/insurance-claim-business/insurance-claim-business-component.png +0 -0
- package/tools/vds-scripts/diagram_generator/docs-nttc/projects/INSURANCE/analysis/current-state/insurance-claim-business/insurance-claim-business-component.puml +0 -23
- package/tools/vds-scripts/diagram_generator/docs-nttc/projects/INSURANCE/analysis/current-state/insurance-claim-business/insurance-claim-business-sequence.png +0 -0
- package/tools/vds-scripts/diagram_generator/docs-nttc/projects/INSURANCE/analysis/current-state/insurance-claim-business/insurance-claim-business-sequence.puml +0 -21
- package/tools/vds-scripts/diagram_generator/docs-nttc/projects/INSURANCE/analysis/current-state/insurance-claim-business/insurance-claim-business-usecase.png +0 -0
- package/tools/vds-scripts/diagram_generator/docs-nttc/projects/INSURANCE/analysis/current-state/insurance-claim-business/insurance-claim-business-usecase.puml +0 -14
- package/tools/vds-scripts/diagram_generator/examples/github-actions-validate.yml +0 -39
- package/tools/vds-scripts/diagram_generator/generate_all_diagrams.py +0 -827
- package/tools/vds-scripts/diagram_generator/generate_insurance_c4_diagrams.py +0 -261
- package/tools/vds-scripts/diagram_generator/generate_insurance_c4_quick.py +0 -486
- package/tools/vds-scripts/diagram_generator/pyproject.toml +0 -28
- package/tools/vds-scripts/diagram_generator/render_png.py +0 -59
- package/tools/vds-scripts/diagram_generator/src/vds_diagram_generator/__init__.py +0 -3
- package/tools/vds-scripts/diagram_generator/src/vds_diagram_generator/cli.py +0 -50
- package/tools/vds-scripts/diagram_generator/test_c4_hierarchical.py +0 -142
- package/tools/vds-scripts/diagram_generator/test_c4_quick.py +0 -131
- package/tools/vds-scripts/diagram_generator/tests/__init__.py +0 -0
- package/tools/vds-scripts/diagram_generator/tests/test_analyzer_completeness.py +0 -260
- package/tools/vds-scripts/diagram_generator/tests/test_c4_syntax_correctness.py +0 -138
- package/tools/vds-scripts/diagram_generator/tests/test_component_coverage.py +0 -182
- package/tools/vds-scripts/diagram_generator/tests/test_mermaid_output.py +0 -80
- package/tools/vds-scripts/diagram_generator/tests/test_png_generation.py +0 -112
- package/tools/vds-scripts/diagram_generator/tests/test_scenario_templates.py +0 -15
- package/tools/vds-scripts/diagram_generator/tests/test_sequence_accuracy.py +0 -93
- package/tools/vds-scripts/diagram_generator/tests/test_structurizr_export.py +0 -177
- package/tools/vds-scripts/diagram_generator/tests/test_style_consistency.py +0 -174
- package/tools/vds-scripts/diagram_generator/tests/test_usecase_generator.py +0 -201
- package/tools/vds-scripts/diagram_generator/tests/test_usecase_integration.py +0 -124
- package/tools/vds-scripts/docker/compose.phase2-verification.yml +0 -31
- package/tools/vds-scripts/docker-compose.openapi-validator.yml +0 -14
- package/tools/vds-scripts/excel_orchestrator/README.md +0 -288
- package/tools/vds-scripts/excel_orchestrator/RESEARCH_BASED_UPDATES_REPORT.md +0 -261
- package/tools/vds-scripts/excel_orchestrator/add_essential_missing_effort.py +0 -255
- package/tools/vds-scripts/excel_orchestrator/adjust_effort_complexity.py +0 -184
- package/tools/vds-scripts/excel_orchestrator/brd_analysis_and_task_breakdown.py +0 -632
- package/tools/vds-scripts/excel_orchestrator/brd_analysis_comprehensive.py +0 -1029
- package/tools/vds-scripts/excel_orchestrator/check_overlaps_and_brd_coverage.py +0 -570
- package/tools/vds-scripts/excel_orchestrator/clean_remarks_column.py +0 -127
- package/tools/vds-scripts/excel_orchestrator/comprehensive_brd_check.py +0 -322
- package/tools/vds-scripts/excel_orchestrator/create_buffered_summary.py +0 -119
- package/tools/vds-scripts/excel_orchestrator/create_service_totals_sheet.py +0 -118
- package/tools/vds-scripts/excel_orchestrator/examples/basic_operations.py +0 -85
- package/tools/vds-scripts/excel_orchestrator/expand_all_tasks.py +0 -341
- package/tools/vds-scripts/excel_orchestrator/expand_tasks.py +0 -304
- package/tools/vds-scripts/excel_orchestrator/fill_brd_references.py +0 -347
- package/tools/vds-scripts/excel_orchestrator/fill_remarks_and_colors.py +0 -132
- package/tools/vds-scripts/excel_orchestrator/finalize_brd_and_cleanup.py +0 -295
- package/tools/vds-scripts/excel_orchestrator/finalize_brd_coverage.py +0 -327
- package/tools/vds-scripts/excel_orchestrator/fix_all_formulas.py +0 -99
- package/tools/vds-scripts/excel_orchestrator/fix_detail_presentation.py +0 -113
- package/tools/vds-scripts/excel_orchestrator/fix_presentation_and_effort.py +0 -116
- package/tools/vds-scripts/excel_orchestrator/fix_presentation_consistency.py +0 -231
- package/tools/vds-scripts/excel_orchestrator/fix_remarks_matching.py +0 -179
- package/tools/vds-scripts/excel_orchestrator/group_tasks_by_service_id.py +0 -210
- package/tools/vds-scripts/excel_orchestrator/increase_brd_coverage.py +0 -497
- package/tools/vds-scripts/excel_orchestrator/increase_effort_complexity.py +0 -155
- package/tools/vds-scripts/excel_orchestrator/organize_and_deduplicate.py +0 -273
- package/tools/vds-scripts/excel_orchestrator/pyproject.toml +0 -64
- package/tools/vds-scripts/excel_orchestrator/rebuild_all_formulas.py +0 -146
- package/tools/vds-scripts/excel_orchestrator/remove_base_multiplier_and_check_duplicates.py +0 -310
- package/tools/vds-scripts/excel_orchestrator/remove_duplicate_brd_tasks.py +0 -137
- package/tools/vds-scripts/excel_orchestrator/research_based_updates.py +0 -457
- package/tools/vds-scripts/excel_orchestrator/restore_e_values.py +0 -172
- package/tools/vds-scripts/excel_orchestrator/src/vds_excel_orchestrator/__init__.py +0 -5
- package/tools/vds-scripts/excel_orchestrator/src/vds_excel_orchestrator/cli.py +0 -746
- package/tools/vds-scripts/excel_orchestrator/src/vds_excel_orchestrator/config.py +0 -74
- package/tools/vds-scripts/excel_orchestrator/src/vds_excel_orchestrator/converters.py +0 -226
- package/tools/vds-scripts/excel_orchestrator/src/vds_excel_orchestrator/errors.py +0 -88
- package/tools/vds-scripts/excel_orchestrator/src/vds_excel_orchestrator/excel_client.py +0 -443
- package/tools/vds-scripts/excel_orchestrator/src/vds_excel_orchestrator/formatters.py +0 -211
- package/tools/vds-scripts/excel_orchestrator/src/vds_excel_orchestrator/logging.py +0 -57
- package/tools/vds-scripts/excel_orchestrator/src/vds_excel_orchestrator/source_contract.py +0 -29
- package/tools/vds-scripts/excel_orchestrator/src/vds_excel_orchestrator/target_state_status.py +0 -837
- package/tools/vds-scripts/excel_orchestrator/src/vds_excel_orchestrator/ulnc_alignment.py +0 -1291
- package/tools/vds-scripts/excel_orchestrator/src/vds_excel_orchestrator/validators.py +0 -164
- package/tools/vds-scripts/excel_orchestrator/sync_detail_and_total_sheets.py +0 -211
- package/tools/vds-scripts/excel_orchestrator/tests/__init__.py +0 -1
- package/tools/vds-scripts/excel_orchestrator/tests/conftest.py +0 -36
- package/tools/vds-scripts/excel_orchestrator/tests/test_cli.py +0 -383
- package/tools/vds-scripts/excel_orchestrator/tests/test_excel_client.py +0 -129
- package/tools/vds-scripts/excel_orchestrator/tests/test_ulnc_alignment.py +0 -373
- package/tools/vds-scripts/excel_orchestrator/tests/test_validators.py +0 -64
- package/tools/vds-scripts/excel_orchestrator/update_api_database_effort.py +0 -261
- package/tools/vds-scripts/excel_orchestrator/update_buffers_inline.py +0 -115
- package/tools/vds-scripts/excel_orchestrator/update_complex_services_and_add_new.py +0 -336
- package/tools/vds-scripts/excel_orchestrator/update_responsibility_and_fix_rows.py +0 -208
- package/tools/vds-scripts/excel_orchestrator/update_task_breakdown_vietnamese.py +0 -309
- package/tools/vds-scripts/excel_orchestrator/update_vietnamese_and_responsibility.py +0 -415
- package/tools/vds-scripts/excel_orchestrator/verify_brd_coverage_comprehensive.py +0 -401
- package/tools/vds-scripts/hexagonal_orchestrator/README.md +0 -530
- package/tools/vds-scripts/hexagonal_orchestrator/pyproject.toml +0 -48
- package/tools/vds-scripts/hexagonal_orchestrator/src/vds_hexagonal_orchestrator/__init__.py +0 -39
- package/tools/vds-scripts/hexagonal_orchestrator/src/vds_hexagonal_orchestrator/analyzers/__init__.py +0 -19
- package/tools/vds-scripts/hexagonal_orchestrator/src/vds_hexagonal_orchestrator/analyzers/base.py +0 -95
- package/tools/vds-scripts/hexagonal_orchestrator/src/vds_hexagonal_orchestrator/analyzers/fallback.py +0 -614
- package/tools/vds-scripts/hexagonal_orchestrator/src/vds_hexagonal_orchestrator/analyzers/java.py +0 -372
- package/tools/vds-scripts/hexagonal_orchestrator/src/vds_hexagonal_orchestrator/analyzers/python.py +0 -437
- package/tools/vds-scripts/hexagonal_orchestrator/src/vds_hexagonal_orchestrator/cache.py +0 -331
- package/tools/vds-scripts/hexagonal_orchestrator/src/vds_hexagonal_orchestrator/classifier.py +0 -263
- package/tools/vds-scripts/hexagonal_orchestrator/src/vds_hexagonal_orchestrator/cli.py +0 -554
- package/tools/vds-scripts/hexagonal_orchestrator/src/vds_hexagonal_orchestrator/config.py +0 -577
- package/tools/vds-scripts/hexagonal_orchestrator/src/vds_hexagonal_orchestrator/models.py +0 -159
- package/tools/vds-scripts/hexagonal_orchestrator/src/vds_hexagonal_orchestrator/profiler.py +0 -451
- package/tools/vds-scripts/hexagonal_orchestrator/test-config.yaml +0 -38
- package/tools/vds-scripts/hexagonal_orchestrator/tests/__init__.py +0 -1
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/__init__.py +0 -1
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/java-compliant/adapter/driven/persistence/InMemoryUserRepository.java +0 -62
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/java-compliant/adapter/driving/api/UserController.java +0 -101
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/java-compliant/application/port/EmailService.java +0 -33
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/java-compliant/application/port/UserRepository.java +0 -45
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/java-compliant/application/usecase/CreateUser.java +0 -58
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/java-compliant/domain/entity/Email.java +0 -80
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/java-compliant/domain/entity/User.java +0 -98
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/java-noncompliant/domain/User.java +0 -64
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/java-with-frameworks/domain/Menu.java +0 -13
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/java-with-frameworks/domain/Product.java +0 -16
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/python-compliant/__init__.py +0 -1
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/python-compliant/application/__init__.py +0 -1
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/python-compliant/application/ports/__init__.py +0 -1
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/python-compliant/application/ports/email_service.py +0 -60
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/python-compliant/application/ports/user_repository.py +0 -78
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/python-compliant/domain/__init__.py +0 -1
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/python-compliant/domain/entities/__init__.py +0 -1
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/python-compliant/domain/entities/user.py +0 -56
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/python-compliant/domain/value_objects/__init__.py +0 -1
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/python-compliant/domain/value_objects/email.py +0 -63
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/python-noncompliant/application/user_service.py +0 -1837
- package/tools/vds-scripts/hexagonal_orchestrator/tests/fixtures/python-noncompliant/domain/user.py +0 -43
- package/tools/vds-scripts/hexagonal_orchestrator/tests/test_cache.py +0 -458
- package/tools/vds-scripts/hexagonal_orchestrator/tests/test_cli_integration.py +0 -942
- package/tools/vds-scripts/hexagonal_orchestrator/tests/test_cli_unit.py +0 -557
- package/tools/vds-scripts/hexagonal_orchestrator/tests/test_cross_repo_pollution.py +0 -275
- package/tools/vds-scripts/hexagonal_orchestrator/tests/test_foundation.py +0 -129
- package/tools/vds-scripts/hexagonal_orchestrator/tests/test_integration.py +0 -1524
- package/tools/vds-scripts/hexagonal_orchestrator/tests/test_java_analyzer.py +0 -642
- package/tools/vds-scripts/hexagonal_orchestrator/tests/test_timing_unit.py +0 -60
- package/tools/vds-scripts/intellij_orchestrator/README.md +0 -55
- package/tools/vds-scripts/intellij_orchestrator/pyproject.toml +0 -64
- package/tools/vds-scripts/intellij_orchestrator/src/vds_intellij_orchestrator/__init__.py +0 -17
- package/tools/vds-scripts/intellij_orchestrator/src/vds_intellij_orchestrator/cli.py +0 -210
- package/tools/vds-scripts/intellij_orchestrator/src/vds_intellij_orchestrator/core.py +0 -260
- package/tools/vds-scripts/intellij_orchestrator/tests/__init__.py +0 -1
- package/tools/vds-scripts/intellij_orchestrator/tests/test_cli.py +0 -112
- package/tools/vds-scripts/intellij_orchestrator/tests/test_core.py +0 -83
- package/tools/vds-scripts/links_orchestrator/README.md +0 -63
- package/tools/vds-scripts/links_orchestrator/pyproject.toml +0 -64
- package/tools/vds-scripts/links_orchestrator/src/vds_links_orchestrator/__init__.py +0 -10
- package/tools/vds-scripts/links_orchestrator/src/vds_links_orchestrator/cli.py +0 -254
- package/tools/vds-scripts/links_orchestrator/src/vds_links_orchestrator/validator.py +0 -244
- package/tools/vds-scripts/links_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/links_orchestrator/tests/test_cli.py +0 -128
- package/tools/vds-scripts/links_orchestrator/tests/test_validator.py +0 -76
- package/tools/vds-scripts/lsp_orchestrator/.dockerignore +0 -69
- package/tools/vds-scripts/lsp_orchestrator/ARCHITECTURE.md +0 -383
- package/tools/vds-scripts/lsp_orchestrator/CODE_QUALITY_IMPROVEMENTS.md +0 -196
- package/tools/vds-scripts/lsp_orchestrator/COMMANDS.md +0 -870
- package/tools/vds-scripts/lsp_orchestrator/Dockerfile +0 -59
- package/tools/vds-scripts/lsp_orchestrator/IMPLEMENTATION_SUMMARY.md +0 -490
- package/tools/vds-scripts/lsp_orchestrator/LSP_ISSUES_AND_FINDINGS.md +0 -380
- package/tools/vds-scripts/lsp_orchestrator/README.md +0 -616
- package/tools/vds-scripts/lsp_orchestrator/SETUP.md +0 -143
- package/tools/vds-scripts/lsp_orchestrator/TEST_COVERAGE_SUMMARY.md +0 -255
- package/tools/vds-scripts/lsp_orchestrator/VERIFICATION_CHECKLIST.md +0 -814
- package/tools/vds-scripts/lsp_orchestrator/docker-compose.yml +0 -102
- package/tools/vds-scripts/lsp_orchestrator/docs/FOR_LLMS.md +0 -401
- package/tools/vds-scripts/lsp_orchestrator/docs/explanation/lsp-response-matching.md +0 -79
- package/tools/vds-scripts/lsp_orchestrator/docs/how-to-guides/automate-with-json.md +0 -159
- package/tools/vds-scripts/lsp_orchestrator/docs/how-to-guides/docker-mode.md +0 -256
- package/tools/vds-scripts/lsp_orchestrator/docs/how-to-guides/navigate-code.md +0 -116
- package/tools/vds-scripts/lsp_orchestrator/docs/how-to-guides/parallel-processing.md +0 -179
- package/tools/vds-scripts/lsp_orchestrator/docs/how-to-guides/project-tool-detection.md +0 -320
- package/tools/vds-scripts/lsp_orchestrator/docs/how-to-guides/type-check-code.md +0 -46
- package/tools/vds-scripts/lsp_orchestrator/docs/how-to-guides/use-daemon-mode.md +0 -78
- package/tools/vds-scripts/lsp_orchestrator/docs/how-to-guides/wsl2-optimization.md +0 -227
- package/tools/vds-scripts/lsp_orchestrator/docs/index.md +0 -88
- package/tools/vds-scripts/lsp_orchestrator/docs/operator-hover-definition.md +0 -143
- package/tools/vds-scripts/lsp_orchestrator/docs/reference/commands.md +0 -581
- package/tools/vds-scripts/lsp_orchestrator/docs/reference/configuration.md +0 -422
- package/tools/vds-scripts/lsp_orchestrator/docs/tutorials/00-quick-start.md +0 -169
- package/tools/vds-scripts/lsp_orchestrator/pyproject.toml +0 -63
- package/tools/vds-scripts/lsp_orchestrator/src/test_file.py +0 -5
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/__init__.py +0 -3
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/aggregator.py +0 -340
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/basedpyright_runner.py +0 -167
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/cli.py +0 -3370
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/code_actions.py +0 -79
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/core.py +0 -3295
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/daemon_client.py +0 -672
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/daemon_manager.py +0 -577
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/daemon_server.py +0 -1040
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/detectors/__init__.py +0 -9
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/detectors/project_detector.py +0 -537
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/formatters.py +0 -141
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/ipc_protocol.py +0 -225
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/lsp_client.py +0 -957
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/lsp_router.py +0 -335
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/mcp_server.py +0 -181
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/models/__init__.py +0 -201
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/models/project_detector.py +0 -646
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/models/project_tools.py +0 -114
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/models.py +0 -399
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/mypy_runner.py +0 -209
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/protocols.py +0 -52
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/ruff_lsp_client.py +0 -109
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/ruff_runner.py +0 -44
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/utils.py +0 -959
- package/tools/vds-scripts/lsp_orchestrator/src/vds_lsp_orchestrator/workspace_indexer.py +0 -1037
- package/tools/vds-scripts/lsp_orchestrator/test_workspace_lsp.py +0 -6
- package/tools/vds-scripts/lsp_orchestrator/tests/__init__.py +0 -1
- package/tools/vds-scripts/lsp_orchestrator/tests/conftest.py +0 -6
- package/tools/vds-scripts/lsp_orchestrator/tests/test_aggregator.py +0 -59
- package/tools/vds-scripts/lsp_orchestrator/tests/test_cli.py +0 -111
- package/tools/vds-scripts/lsp_orchestrator/tests/test_detect_tools_command.py +0 -186
- package/tools/vds-scripts/lsp_orchestrator/tests/test_formatter_linter_detection.py +0 -519
- package/tools/vds-scripts/lsp_orchestrator/tests/test_integration_phase9_10_11.py +0 -367
- package/tools/vds-scripts/lsp_orchestrator/tests/test_mypy_runner.py +0 -482
- package/tools/vds-scripts/lsp_orchestrator/tests/test_package_manager_detection.py +0 -399
- package/tools/vds-scripts/lsp_orchestrator/tests/test_phase10.py +0 -389
- package/tools/vds-scripts/lsp_orchestrator/tests/test_phase11.py +0 -327
- package/tools/vds-scripts/lsp_orchestrator/tests/test_phase12_integration.py +0 -634
- package/tools/vds-scripts/lsp_orchestrator/tests/test_phase9.py +0 -196
- package/tools/vds-scripts/lsp_orchestrator/tests/test_project_detector.py +0 -377
- package/tools/vds-scripts/lsp_orchestrator/tests/test_test_runner_detection.py +0 -549
- package/tools/vds-scripts/lsp_orchestrator/tests/test_type_checker_routing.py +0 -362
- package/tools/vds-scripts/lsp_orchestrator/tests/test_workspace_indexer.py +0 -144
- package/tools/vds-scripts/markdown_orchestrator/README.md +0 -72
- package/tools/vds-scripts/markdown_orchestrator/pyproject.toml +0 -39
- package/tools/vds-scripts/markdown_orchestrator/src/vds_markdown_orchestrator/__init__.py +0 -5
- package/tools/vds-scripts/markdown_orchestrator/src/vds_markdown_orchestrator/cli.py +0 -102
- package/tools/vds-scripts/multi_agent_orchestrator/Dockerfile +0 -65
- package/tools/vds-scripts/multi_agent_orchestrator/README.md +0 -306
- package/tools/vds-scripts/multi_agent_orchestrator/postman/README.md +0 -264
- package/tools/vds-scripts/multi_agent_orchestrator/postman/TEST_RESULTS_SUMMARY.md +0 -197
- package/tools/vds-scripts/multi_agent_orchestrator/postman/VDS-Multi-Agent-Orchestrator-API.postman_collection.json +0 -1010
- package/tools/vds-scripts/multi_agent_orchestrator/postman/environments/local-development.postman_environment.json +0 -55
- package/tools/vds-scripts/multi_agent_orchestrator/postman/test-results.json +0 -24146
- package/tools/vds-scripts/multi_agent_orchestrator/pyproject.toml +0 -63
- package/tools/vds-scripts/multi_agent_orchestrator/run_api.py +0 -9
- package/tools/vds-scripts/multi_agent_orchestrator/run_mock_api.py +0 -9
- package/tools/vds-scripts/multi_agent_orchestrator/simple_test.py +0 -53
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/__init__.py +0 -25
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/agent_pool.py +0 -433
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/api/__init__.py +0 -5
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/api/main.py +0 -722
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/api/mock_main.py +0 -812
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/change_log.py +0 -515
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/cli.py +0 -424
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/config.py +0 -220
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/conflict_resolver.py +0 -462
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/coordinator.py +0 -627
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/models.py +0 -389
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/progress_dashboard.py +0 -380
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/redis_client.py +0 -245
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/scheduler_subscriber.py +0 -272
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/task_manager.py +0 -536
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/task_tracking.py +0 -550
- package/tools/vds-scripts/multi_agent_orchestrator/src/vds_multi_agent_orchestrator/vds_ai_memory_client.py +0 -352
- package/tools/vds-scripts/multi_agent_orchestrator/test_complete_system.py +0 -149
- package/tools/vds-scripts/multi_agent_orchestrator/test_infrastructure_only.py +0 -194
- package/tools/vds-scripts/multi_agent_orchestrator/test_integration.py +0 -108
- package/tools/vds-scripts/multi_agent_orchestrator/tests/__init__.py +0 -1
- package/tools/vds-scripts/multi_agent_orchestrator/tests/test_agent_registration_credential_validator.py +0 -223
- package/tools/vds-scripts/multi_agent_orchestrator/tests/test_config.py +0 -210
- package/tools/vds-scripts/multi_agent_orchestrator/tests/test_models.py +0 -195
- package/tools/vds-scripts/multi_agent_orchestrator/tests/test_w9_agent_routes.py +0 -321
- package/tools/vds-scripts/openapi_orchestrator/README.md +0 -197
- package/tools/vds-scripts/openapi_orchestrator/pyproject.toml +0 -106
- package/tools/vds-scripts/openapi_orchestrator/src/vds_openapi_orchestrator/__init__.py +0 -29
- package/tools/vds-scripts/openapi_orchestrator/src/vds_openapi_orchestrator/cli.py +0 -345
- package/tools/vds-scripts/openapi_orchestrator/src/vds_openapi_orchestrator/full_validator.py +0 -183
- package/tools/vds-scripts/openapi_orchestrator/src/vds_openapi_orchestrator/spec_validator.py +0 -197
- package/tools/vds-scripts/openapi_orchestrator/tests/__init__.py +0 -1
- package/tools/vds-scripts/openapi_orchestrator/tests/test_cli.py +0 -234
- package/tools/vds-scripts/openapi_orchestrator/tests/test_full_validator.py +0 -203
- package/tools/vds-scripts/openapi_orchestrator/tests/test_spec_validator.py +0 -295
- package/tools/vds-scripts/pdf_orchestrator/.dockerignore +0 -93
- package/tools/vds-scripts/pdf_orchestrator/.env.example +0 -40
- package/tools/vds-scripts/pdf_orchestrator/.ruff_rules.py +0 -350
- package/tools/vds-scripts/pdf_orchestrator/.yamllint.yml +0 -43
- package/tools/vds-scripts/pdf_orchestrator/DEVELOPMENT_PLAN.md +0 -80
- package/tools/vds-scripts/pdf_orchestrator/Dockerfile +0 -87
- package/tools/vds-scripts/pdf_orchestrator/README.md +0 -608
- package/tools/vds-scripts/pdf_orchestrator/cli_verification_test/test.md +0 -6
- package/tools/vds-scripts/pdf_orchestrator/cli_verification_test/test.pdf +0 -0
- package/tools/vds-scripts/pdf_orchestrator/config/alertmanager.yml +0 -83
- package/tools/vds-scripts/pdf_orchestrator/config/prometheus.prod.yml +0 -98
- package/tools/vds-scripts/pdf_orchestrator/config/prometheus.yml +0 -40
- package/tools/vds-scripts/pdf_orchestrator/config/redis.conf +0 -78
- package/tools/vds-scripts/pdf_orchestrator/docs/COMPETITIVE_ANALYSIS_REPORT.md +0 -309
- package/tools/vds-scripts/pdf_orchestrator/docs/FEATURES_GUIDE.md +0 -518
- package/tools/vds-scripts/pdf_orchestrator/docs/MULTI_USER_DEPLOYMENT_GUIDE.md +0 -615
- package/tools/vds-scripts/pdf_orchestrator/docs/USER_GUIDE.md +0 -829
- package/tools/vds-scripts/pdf_orchestrator/pyproject.toml +0 -87
- package/tools/vds-scripts/pdf_orchestrator/pytest.ini +0 -71
- package/tools/vds-scripts/pdf_orchestrator/ruff.toml +0 -6
- package/tools/vds-scripts/pdf_orchestrator/scripts/debug_security_report.py +0 -59
- package/tools/vds-scripts/pdf_orchestrator/scripts/demo_library_selector.py +0 -109
- package/tools/vds-scripts/pdf_orchestrator/scripts/generate_project_stats.py +0 -52
- package/tools/vds-scripts/pdf_orchestrator/scripts/generate_styled_pdf.py +0 -95
- package/tools/vds-scripts/pdf_orchestrator/scripts/migrate_render_pdfs.py +0 -285
- package/tools/vds-scripts/pdf_orchestrator/scripts/setup_team.bat +0 -283
- package/tools/vds-scripts/pdf_orchestrator/scripts/setup_team.sh +0 -324
- package/tools/vds-scripts/pdf_orchestrator/src/vds_pdf_orchestrator/__init__.py +0 -5
- package/tools/vds-scripts/pdf_orchestrator/src/vds_pdf_orchestrator/cli.py +0 -542
- package/tools/vds-scripts/pdf_orchestrator/src/vds_pdf_orchestrator/config.py +0 -33
- package/tools/vds-scripts/pdf_orchestrator/tests/README.md +0 -650
- package/tools/vds-scripts/pdf_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/pdf_orchestrator/tests/conftest.py +0 -520
- package/tools/vds-scripts/pdf_orchestrator/tests/requirements.txt +0 -51
- package/tools/vds-scripts/pdf_orchestrator/tests/run_tests.py +0 -659
- package/tools/vds-scripts/pdf_orchestrator/tests/test_config.py +0 -36
- package/tools/vds-scripts/progress_report_orchestrator/Dockerfile +0 -77
- package/tools/vds-scripts/progress_report_orchestrator/README.md +0 -39
- package/tools/vds-scripts/progress_report_orchestrator/alembic/env.py +0 -42
- package/tools/vds-scripts/progress_report_orchestrator/alembic/script.py.mako +0 -28
- package/tools/vds-scripts/progress_report_orchestrator/alembic/versions/0001_initial_progress_schema.py +0 -180
- package/tools/vds-scripts/progress_report_orchestrator/alembic.ini +0 -67
- package/tools/vds-scripts/progress_report_orchestrator/pyproject.toml +0 -67
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/__init__.py +0 -3
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/analyzers/__init__.py +0 -1
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/analyzers/endpoint_scanner.py +0 -238
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/analyzers/git_activity.py +0 -159
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/analyzers/hexagonal.py +0 -100
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/analyzers/test_scanner.py +0 -136
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/cli.py +0 -743
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/config.py +0 -50
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/db/__init__.py +0 -12
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/db/alembic_filters.py +0 -64
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/memory.py +0 -82
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/models/__init__.py +0 -1
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/models/analysis.py +0 -84
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/models/report.py +0 -117
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/models/topology.py +0 -101
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/parsers/__init__.py +0 -1
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/parsers/kg_parser.py +0 -252
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/parsers/uc_reader.py +0 -159
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/pipeline/__init__.py +0 -1
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/pipeline/concurrency.py +0 -39
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/pipeline/llm_eval.py +0 -570
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/pipeline/report.py +0 -1256
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/pipeline/structural.py +0 -384
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/pipeline/sync.py +0 -143
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/recommendations/__init__.py +0 -5
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/recommendations/engine.py +0 -105
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/recommendations/templates.py +0 -236
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/scheduler_subscriber.py +0 -238
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/skills/README.md +0 -56
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/skills/__init__.py +0 -1
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/skills/srs-architecture-reviewer/SKILL.md +0 -67
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/skills/srs-endpoint-matcher/SKILL.md +0 -67
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/state/__init__.py +0 -1
- package/tools/vds-scripts/progress_report_orchestrator/src/progress_report_orchestrator/state/schema.py +0 -625
- package/tools/vds-scripts/progress_report_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/progress_report_orchestrator/tests/fixtures/__init__.py +0 -0
- package/tools/vds-scripts/progress_report_orchestrator/tests/fixtures/kg/.gitkeep +0 -0
- package/tools/vds-scripts/progress_report_orchestrator/tests/fixtures/kg/__init__.py +0 -0
- package/tools/vds-scripts/progress_report_orchestrator/tests/fixtures/kg/doc-dependencies.yaml +0 -79
- package/tools/vds-scripts/progress_report_orchestrator/tests/fixtures/kg/fr-to-docs.yaml +0 -478
- package/tools/vds-scripts/progress_report_orchestrator/tests/fixtures/kg/fr-to-services.yaml +0 -18
- package/tools/vds-scripts/progress_report_orchestrator/tests/fixtures/kg/registry.yaml +0 -346
- package/tools/vds-scripts/progress_report_orchestrator/tests/fixtures/phase3_baseline_standard.md +0 -564
- package/tools/vds-scripts/progress_report_orchestrator/tests/integration/__init__.py +0 -0
- package/tools/vds-scripts/progress_report_orchestrator/tests/integration/test_checkpoint.py +0 -276
- package/tools/vds-scripts/progress_report_orchestrator/tests/test_alembic_migrations.py +0 -265
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/__init__.py +0 -0
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_analyzers.py +0 -267
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_bounded_gather.py +0 -176
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_cli_phase_report.py +0 -119
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_delta.py +0 -169
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_error_handling.py +0 -150
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_gate_exit_codes.py +0 -230
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_git_activity.py +0 -215
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_kg_parser.py +0 -267
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_llm_autodetect.py +0 -183
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_llm_eval.py +0 -529
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_memory_integration.py +0 -151
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_migration_contract.py +0 -254
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_mode_rendering.py +0 -576
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_models.py +0 -251
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_progress_llm_config.py +0 -67
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_recommendations.py +0 -480
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_report_enhancements.py +0 -415
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_resume_reload.py +0 -343
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_trend_regression.py +0 -294
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_uc_reader.py +0 -169
- package/tools/vds-scripts/progress_report_orchestrator/tests/unit/test_valence_gap.py +0 -293
- package/tools/vds-scripts/project-cycle-report.json +0 -14
- package/tools/vds-scripts/project-dependency-graph.json +0 -11361
- package/tools/vds-scripts/project-topology.json +0 -99
- package/tools/vds-scripts/public_interface_boundary_orchestrator/pyproject.toml +0 -18
- package/tools/vds-scripts/public_interface_boundary_orchestrator/src/vds_public_interface_boundary_orchestrator/__init__.py +0 -0
- package/tools/vds-scripts/public_interface_boundary_orchestrator/src/vds_public_interface_boundary_orchestrator/cli.py +0 -232
- package/tools/vds-scripts/public_interface_boundary_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/public_interface_boundary_orchestrator/tests/test_cli.py +0 -108
- package/tools/vds-scripts/research_orchestrator/README.md +0 -68
- package/tools/vds-scripts/research_orchestrator/py.typed +0 -0
- package/tools/vds-scripts/research_orchestrator/pyproject.toml +0 -95
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/__init__.py +0 -3
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/_env.py +0 -11
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/cli.py +0 -335
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/config.py +0 -43
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/evidence/__init__.py +0 -0
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/evidence/models.py +0 -89
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/evidence/scoring.py +0 -102
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/exceptions.py +0 -78
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/http_client.py +0 -160
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/logging.py +0 -49
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/output/__init__.py +0 -0
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/output/formatters.py +0 -93
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/py.typed +0 -1
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/report/__init__.py +0 -0
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/report/build.py +0 -156
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/report/format.py +0 -147
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/tools/__init__.py +0 -0
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/tools/health.py +0 -66
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/tools/health_graph.py +0 -52
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/tools/registry.py +0 -127
- package/tools/vds-scripts/research_orchestrator/src/vds_research_orchestrator/tools/search.py +0 -230
- package/tools/vds-scripts/research_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/research_orchestrator/tests/conftest.py +0 -53
- package/tools/vds-scripts/research_orchestrator/tests/test_cli.py +0 -222
- package/tools/vds-scripts/research_orchestrator/tests/test_config.py +0 -23
- package/tools/vds-scripts/research_orchestrator/tests/test_exceptions.py +0 -62
- package/tools/vds-scripts/research_orchestrator/tests/test_formatters.py +0 -89
- package/tools/vds-scripts/research_orchestrator/tests/test_graph_integration.py +0 -149
- package/tools/vds-scripts/research_orchestrator/tests/test_http_client.py +0 -134
- package/tools/vds-scripts/research_orchestrator/tests/test_report_build.py +0 -128
- package/tools/vds-scripts/research_orchestrator/tests/test_report_format.py +0 -91
- package/tools/vds-scripts/research_orchestrator/tests/test_scoring.py +0 -95
- package/tools/vds-scripts/research_orchestrator/tests/vds_research_orchestrator/test_tools/__init__.py +0 -1
- package/tools/vds-scripts/research_orchestrator/tests/vds_research_orchestrator/test_tools/test_health.py +0 -139
- package/tools/vds-scripts/research_orchestrator/tests/vds_research_orchestrator/test_tools/test_registry.py +0 -135
- package/tools/vds-scripts/research_orchestrator/tests/vds_research_orchestrator/test_tools/test_search.py +0 -238
- package/tools/vds-scripts/run-history.json +0 -26
- package/tools/vds-scripts/schema_converter/README.md +0 -109
- package/tools/vds-scripts/schema_converter/pyproject.toml +0 -37
- package/tools/vds-scripts/schema_converter/src/vds_schema_converter/__init__.py +0 -3
- package/tools/vds-scripts/schema_converter/src/vds_schema_converter/cli.py +0 -50
- package/tools/vds-scripts/schema_converter/tests/__init__.py +0 -0
- package/tools/vds-scripts/schema_converter/tests/test_json_schema_generator.py +0 -115
- package/tools/vds-scripts/schema_converter/tests/test_mermaid_generator.py +0 -112
- package/tools/vds-scripts/schema_converter/tests/test_parser.py +0 -111
- package/tools/vds-scripts/schema_converter/tests/test_plantuml_generator.py +0 -112
- package/tools/vds-scripts/schema_converter/tests/test_plantuml_validator.py +0 -69
- package/tools/vds-scripts/schema_converter/tests/test_prisma_generator.py +0 -113
- package/tools/vds-scripts/schema_converter/tests/test_sql_generator.py +0 -138
- package/tools/vds-scripts/schema_converter/tests/test_typeorm_generator.py +0 -110
- package/tools/vds-scripts/schema_converter/tests/test_validators.py +0 -96
- package/tools/vds-scripts/spec_orchestrator/README.md +0 -13
- package/tools/vds-scripts/spec_orchestrator/pyproject.toml +0 -40
- package/tools/vds-scripts/spec_orchestrator/src/vds_spec_orchestrator/__init__.py +0 -5
- package/tools/vds-scripts/spec_orchestrator/src/vds_spec_orchestrator/cli.py +0 -162
- package/tools/vds-scripts/spec_orchestrator/src/vds_spec_orchestrator/core.py +0 -575
- package/tools/vds-scripts/spec_orchestrator/src/vds_spec_orchestrator/sync.py +0 -306
- package/tools/vds-scripts/spec_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/spec_orchestrator/tests/test_frontmatter_drift.py +0 -243
- package/tools/vds-scripts/spec_orchestrator/tests/test_sync.py +0 -342
- package/tools/vds-scripts/structure_orchestrator/README.md +0 -60
- package/tools/vds-scripts/structure_orchestrator/pyproject.toml +0 -103
- package/tools/vds-scripts/structure_orchestrator/src/vds_structure_orchestrator/__init__.py +0 -13
- package/tools/vds-scripts/structure_orchestrator/src/vds_structure_orchestrator/cli.py +0 -308
- package/tools/vds-scripts/structure_orchestrator/src/vds_structure_orchestrator/validator.py +0 -257
- package/tools/vds-scripts/structure_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/structure_orchestrator/tests/test_cli.py +0 -161
- package/tools/vds-scripts/structure_orchestrator/tests/test_helpers.py +0 -115
- package/tools/vds-scripts/structure_orchestrator/tests/test_validator.py +0 -104
- package/tools/vds-scripts/task_orchestrator/README.md +0 -50
- package/tools/vds-scripts/task_orchestrator/__init__.py +0 -18
- package/tools/vds-scripts/task_orchestrator/pyproject.toml +0 -43
- package/tools/vds-scripts/task_orchestrator/scripts/run_excel_sync.py +0 -36
- package/tools/vds-scripts/task_orchestrator/src/vds_task_orchestrator/__init__.py +0 -13
- package/tools/vds-scripts/task_orchestrator/src/vds_task_orchestrator/audit.py +0 -134
- package/tools/vds-scripts/task_orchestrator/src/vds_task_orchestrator/cli.py +0 -127
- package/tools/vds-scripts/task_orchestrator/src/vds_task_orchestrator/debug.py +0 -133
- package/tools/vds-scripts/task_orchestrator/src/vds_task_orchestrator/normalize.py +0 -113
- package/tools/vds-scripts/task_orchestrator/src/vds_task_orchestrator/refine.py +0 -201
- package/tools/vds-scripts/task_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/task_orchestrator/tests/test_task_orchestrator.py +0 -84
- package/tools/vds-scripts/temp_query_projects.py +0 -2
- package/tools/vds-scripts/test_small.md +0 -1
- package/tools/vds-scripts/text_utils_orchestrator/pyproject.toml +0 -20
- package/tools/vds-scripts/text_utils_orchestrator/src/vds_text_utils/__init__.py +0 -7
- package/tools/vds-scripts/text_utils_orchestrator/src/vds_text_utils/i18n.py +0 -143
- package/tools/vds-scripts/text_utils_orchestrator/tests/__init__.py +0 -0
- package/tools/vds-scripts/text_utils_orchestrator/tests/test_i18n.py +0 -53
- package/tools/vds-scripts/upgrade_major.py +0 -61
- package/tools/vds-scripts/upgrade_major_v2.py +0 -64
- package/tools/vds-scripts/verify_violations.py +0 -57
- package/tools/vds-scripts/workflow-summary.json +0 -325
- package/tools/vds-scripts/workflow-summary.md +0 -8
|
@@ -1,3514 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import inspect
|
|
4
|
-
import json
|
|
5
|
-
from collections import Counter
|
|
6
|
-
from unittest.mock import AsyncMock
|
|
7
|
-
|
|
8
|
-
import pytest
|
|
9
|
-
from vds_audit_orchestrator.collectors.registry_parser import RegistryPageParser
|
|
10
|
-
from vds_audit_orchestrator.errors import DataSourceError
|
|
11
|
-
from vds_audit_orchestrator.models.registry import (
|
|
12
|
-
DocumentRole,
|
|
13
|
-
LinkSource,
|
|
14
|
-
RootChildType,
|
|
15
|
-
SkippedNodeReason,
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@pytest.fixture
|
|
20
|
-
def mock_client():
|
|
21
|
-
return AsyncMock()
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
@pytest.fixture
|
|
25
|
-
def parser(mock_client):
|
|
26
|
-
return RegistryPageParser(mock_client)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@pytest.fixture
|
|
30
|
-
def parser_checklist_first(mock_client):
|
|
31
|
-
"""Parser configured with checklist-first mode."""
|
|
32
|
-
return RegistryPageParser(mock_client)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@pytest.fixture
|
|
36
|
-
def sample_project_page_html():
|
|
37
|
-
return """
|
|
38
|
-
<table>
|
|
39
|
-
<tr>
|
|
40
|
-
<th>Vai trò</th>
|
|
41
|
-
<th>Loại tài liệu</th>
|
|
42
|
-
<th>Doc Type EN</th>
|
|
43
|
-
<th>Link</th>
|
|
44
|
-
</tr>
|
|
45
|
-
<tr>
|
|
46
|
-
<td>PO</td>
|
|
47
|
-
<td>1. Tài liệu BRD</td>
|
|
48
|
-
<td>BRD Document</td>
|
|
49
|
-
<td><a href="/display/TDOV/BRD+Doc">Link</a></td>
|
|
50
|
-
</tr>
|
|
51
|
-
<tr>
|
|
52
|
-
<td>DEV</td>
|
|
53
|
-
<td>8. Source code</td>
|
|
54
|
-
<td>Source Code</td>
|
|
55
|
-
<td><a href="http://bitbucket.digital.vn/projects/PAR">Repo</a></td>
|
|
56
|
-
</tr>
|
|
57
|
-
</table>
|
|
58
|
-
"""
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def test_extract_links_from_html(parser, sample_project_page_html):
|
|
62
|
-
# Mock base URL
|
|
63
|
-
base_url = "http://10.254.136.35:8090"
|
|
64
|
-
|
|
65
|
-
links = parser._extract_links_from_html(sample_project_page_html, base_url)
|
|
66
|
-
|
|
67
|
-
assert len(links) == 2
|
|
68
|
-
|
|
69
|
-
# Check BRD link
|
|
70
|
-
brd = next(link for link in links if "BRD" in link.url)
|
|
71
|
-
assert brd.source == LinkSource.CONFLUENCE
|
|
72
|
-
assert brd.url == "http://10.254.136.35:8090/display/TDOV/BRD+Doc"
|
|
73
|
-
assert brd.role == DocumentRole.PO
|
|
74
|
-
assert brd.doc_number == 1
|
|
75
|
-
assert brd.doc_type == "Tài liệu BRD"
|
|
76
|
-
assert brd.doc_type_en == "BRD Document"
|
|
77
|
-
assert brd.page_id is None
|
|
78
|
-
|
|
79
|
-
# Check Repo link
|
|
80
|
-
repo = next(link for link in links if "bitbucket" in link.url)
|
|
81
|
-
assert repo.source == LinkSource.BITBUCKET
|
|
82
|
-
assert repo.url == "http://bitbucket.digital.vn/projects/PAR"
|
|
83
|
-
assert repo.role == DocumentRole.DEV
|
|
84
|
-
assert repo.doc_number == 8
|
|
85
|
-
assert repo.doc_type == "Source code"
|
|
86
|
-
assert repo.doc_type_en == "Source Code"
|
|
87
|
-
assert repo.page_id is None
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def test_confluence_client_for_url_selects_server_scoped_client(mock_client, monkeypatch):
|
|
91
|
-
# When the active client is "internal" and the URL points to an external host
|
|
92
|
-
# (10.254.136.35 or 10.254.136.32 — both map to "external"), a scoped external
|
|
93
|
-
# client should be created and returned.
|
|
94
|
-
parser = RegistryPageParser(mock_client)
|
|
95
|
-
mock_client._server = "internal"
|
|
96
|
-
scoped_client = AsyncMock()
|
|
97
|
-
calls: dict[str, object] = {}
|
|
98
|
-
|
|
99
|
-
from vds_audit_orchestrator.clients.confluence_cli_client import ConfluenceCliClient
|
|
100
|
-
|
|
101
|
-
def _fake_for_registry_discovery(cls, project_path=None, *, server=None, timeout=120):
|
|
102
|
-
calls["server"] = server
|
|
103
|
-
calls["timeout"] = timeout
|
|
104
|
-
return scoped_client
|
|
105
|
-
|
|
106
|
-
monkeypatch.setattr(
|
|
107
|
-
ConfluenceCliClient,
|
|
108
|
-
"for_registry_discovery",
|
|
109
|
-
classmethod(_fake_for_registry_discovery),
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
selected = parser._confluence_client_for_url("http://10.254.136.35:8090/display/TMP/2.1.+PYC")
|
|
113
|
-
|
|
114
|
-
assert selected is scoped_client
|
|
115
|
-
assert calls["server"] == "external"
|
|
116
|
-
assert int(calls["timeout"]) == parser._server_scoped_timeout
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
def test_confluence_client_for_url_returns_existing_when_server_matches(mock_client):
|
|
120
|
-
# When the active client already matches the server inferred from the URL,
|
|
121
|
-
# no scoped client is created — the existing client is returned as-is.
|
|
122
|
-
parser = RegistryPageParser(mock_client)
|
|
123
|
-
mock_client._server = "external"
|
|
124
|
-
|
|
125
|
-
selected = parser._confluence_client_for_url("http://10.254.136.35:8090/display/TMP/2.1.+PYC")
|
|
126
|
-
assert selected is mock_client
|
|
127
|
-
|
|
128
|
-
selected = parser._confluence_client_for_url("http://10.254.136.32:8090/display/TMP/2.1.+PYC")
|
|
129
|
-
assert selected is mock_client
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
@pytest.mark.asyncio
|
|
133
|
-
async def test_parse_project_page(parser, mock_client, sample_project_page_html):
|
|
134
|
-
page_id = "12345"
|
|
135
|
-
mock_client.get_page.return_value = {
|
|
136
|
-
"id": page_id,
|
|
137
|
-
"title": "Test Project",
|
|
138
|
-
"body": {"storage": {"value": sample_project_page_html}},
|
|
139
|
-
"_links": {"base": "http://10.254.136.35:8090", "webui": "/pages/viewpage.action?pageId=12345"},
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
result = await parser.parse_project_page(page_id, "Test Project")
|
|
143
|
-
|
|
144
|
-
assert result is not None
|
|
145
|
-
assert result.project_name == "Test Project"
|
|
146
|
-
assert len(result.documents) == 2
|
|
147
|
-
assert len(result.confluence_links) == 1
|
|
148
|
-
assert len(result.bitbucket_links) == 1
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
@pytest.mark.asyncio
|
|
152
|
-
async def test_parse_project_page_resolves_display_link_to_page_id(parser, mock_client, sample_project_page_html):
|
|
153
|
-
page_id = "12345"
|
|
154
|
-
mock_client.get_page.return_value = {
|
|
155
|
-
"id": page_id,
|
|
156
|
-
"title": "Test Project",
|
|
157
|
-
"body": {"storage": {"value": sample_project_page_html}},
|
|
158
|
-
"_links": {"base": "http://10.254.136.35:8090", "webui": "/pages/viewpage.action?pageId=12345"},
|
|
159
|
-
}
|
|
160
|
-
mock_client.search_cql.return_value = [{"id": "33916401"}]
|
|
161
|
-
|
|
162
|
-
result = await parser.parse_project_page(page_id, "Test Project")
|
|
163
|
-
|
|
164
|
-
assert result is not None
|
|
165
|
-
brd = next(link for link in result.documents if "/display/" in link.url)
|
|
166
|
-
assert brd.page_id == "33916401"
|
|
167
|
-
assert mock_client.search_cql.await_count == 1
|
|
168
|
-
assert any('space="TDOV"' in str(call.args[0]) for call in mock_client.search_cql.await_args_list)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
@pytest.mark.asyncio
|
|
172
|
-
async def test_parse_project_page_reuses_display_link_resolution_cache(parser, mock_client):
|
|
173
|
-
html = """
|
|
174
|
-
<table>
|
|
175
|
-
<tr>
|
|
176
|
-
<th>Vai trò</th>
|
|
177
|
-
<th>Loại tài liệu</th>
|
|
178
|
-
<th>Link</th>
|
|
179
|
-
</tr>
|
|
180
|
-
<tr>
|
|
181
|
-
<td>BA</td>
|
|
182
|
-
<td>2. Tổng quan Nghiệp vụ</td>
|
|
183
|
-
<td><a href="/display/TMP/2.1.+PYC">Doc</a></td>
|
|
184
|
-
</tr>
|
|
185
|
-
</table>
|
|
186
|
-
"""
|
|
187
|
-
mock_client.get_page.side_effect = [
|
|
188
|
-
{
|
|
189
|
-
"id": "11111",
|
|
190
|
-
"title": "Project A",
|
|
191
|
-
"body": {"storage": {"value": html}},
|
|
192
|
-
"_links": {"base": "http://10.254.136.35:8090", "webui": "/pages/viewpage.action?pageId=11111"},
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
"id": "22222",
|
|
196
|
-
"title": "Project B",
|
|
197
|
-
"body": {"storage": {"value": html}},
|
|
198
|
-
"_links": {"base": "http://10.254.136.35:8090", "webui": "/pages/viewpage.action?pageId=22222"},
|
|
199
|
-
},
|
|
200
|
-
]
|
|
201
|
-
mock_client.search_cql.return_value = [{"id": "33916401"}]
|
|
202
|
-
|
|
203
|
-
first = await parser.parse_project_page("11111", "Project A")
|
|
204
|
-
second = await parser.parse_project_page("22222", "Project B")
|
|
205
|
-
|
|
206
|
-
assert first is not None
|
|
207
|
-
assert second is not None
|
|
208
|
-
assert first.documents[0].page_id == "33916401"
|
|
209
|
-
assert second.documents[0].page_id == "33916401"
|
|
210
|
-
assert mock_client.search_cql.await_count == 1
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
@pytest.mark.asyncio
|
|
214
|
-
async def test_parse_project_page_display_page_id_resolution_fails_fast_on_cql_error(parser, mock_client):
|
|
215
|
-
html = """
|
|
216
|
-
<table>
|
|
217
|
-
<tr>
|
|
218
|
-
<th>Vai trò</th>
|
|
219
|
-
<th>Loại tài liệu</th>
|
|
220
|
-
<th>Link</th>
|
|
221
|
-
</tr>
|
|
222
|
-
<tr>
|
|
223
|
-
<td>BA</td>
|
|
224
|
-
<td>2. Tổng quan Nghiệp vụ</td>
|
|
225
|
-
<td><a href="/display/TMP/2.1.+PYC">Doc</a></td>
|
|
226
|
-
</tr>
|
|
227
|
-
</table>
|
|
228
|
-
"""
|
|
229
|
-
mock_client.get_page.return_value = {
|
|
230
|
-
"id": "11111",
|
|
231
|
-
"title": "Project A",
|
|
232
|
-
"body": {"storage": {"value": html}},
|
|
233
|
-
"_links": {"base": "http://10.254.136.35:8090", "webui": "/pages/viewpage.action?pageId=11111"},
|
|
234
|
-
}
|
|
235
|
-
mock_client.search_cql.side_effect = DataSourceError(
|
|
236
|
-
"API error during cql_search: java.lang.IllegalArgumentException: parameters should not be empty"
|
|
237
|
-
)
|
|
238
|
-
|
|
239
|
-
result = await parser.parse_project_page("11111", "Project A")
|
|
240
|
-
|
|
241
|
-
assert result is not None
|
|
242
|
-
assert result.documents[0].page_id is None
|
|
243
|
-
assert mock_client.search_cql.await_count == 1
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
@pytest.mark.asyncio
|
|
247
|
-
async def test_parse_project_page_uses_prefetched_child_payload(parser, mock_client, sample_project_page_html):
|
|
248
|
-
page_id = "12345"
|
|
249
|
-
payload = {
|
|
250
|
-
"id": page_id,
|
|
251
|
-
"title": "Prefetched Project",
|
|
252
|
-
"body": {"storage": {"value": sample_project_page_html}},
|
|
253
|
-
"_links": {
|
|
254
|
-
"self": "http://confluence.digital.vn/rest/api/content/12345",
|
|
255
|
-
"webui": "/pages/viewpage.action?pageId=12345",
|
|
256
|
-
},
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
result = await parser.parse_project_page(page_id, "Ignored title", page_payload=payload)
|
|
260
|
-
|
|
261
|
-
assert result is not None
|
|
262
|
-
assert result.project_name == "Prefetched Project"
|
|
263
|
-
assert result.page_url == "http://confluence.digital.vn/pages/viewpage.action?pageId=12345"
|
|
264
|
-
assert len(result.documents) == 2
|
|
265
|
-
mock_client.get_page.assert_not_called()
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
@pytest.mark.asyncio
|
|
269
|
-
async def test_parse_project_page_prefetch_mode_skips_display_page_id_resolution(mock_client, tmp_path):
|
|
270
|
-
parser = RegistryPageParser(mock_client, prefetch_dir=tmp_path)
|
|
271
|
-
page_id = "12345"
|
|
272
|
-
payload = {
|
|
273
|
-
"id": page_id,
|
|
274
|
-
"title": "Prefetched Project",
|
|
275
|
-
"body": {
|
|
276
|
-
"storage": {
|
|
277
|
-
"value": "<table><tr><th>Link</th></tr><tr><td><a href='/display/TDOV/BRD+Doc'>Doc</a></td></tr></table>"
|
|
278
|
-
}
|
|
279
|
-
},
|
|
280
|
-
"_links": {
|
|
281
|
-
"self": "http://confluence.digital.vn/rest/api/content/12345",
|
|
282
|
-
"webui": "/pages/viewpage.action?pageId=12345",
|
|
283
|
-
},
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
result = await parser.parse_project_page(page_id, "Ignored title", page_payload=payload)
|
|
287
|
-
|
|
288
|
-
assert result is not None
|
|
289
|
-
assert result.documents[0].page_id is None
|
|
290
|
-
mock_client.search_cql.assert_not_called()
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
@pytest.mark.asyncio
|
|
294
|
-
async def test_parse_project_page_extracts_plain_text_bitbucket_links(parser, mock_client):
|
|
295
|
-
page_id = "99999"
|
|
296
|
-
html = """
|
|
297
|
-
<table>
|
|
298
|
-
<tr>
|
|
299
|
-
<th>Vai trò</th>
|
|
300
|
-
<th>Loại tài liệu</th>
|
|
301
|
-
<th>Link tài liệu</th>
|
|
302
|
-
</tr>
|
|
303
|
-
<tr>
|
|
304
|
-
<td>DEV</td>
|
|
305
|
-
<td>8. Source code</td>
|
|
306
|
-
<td>
|
|
307
|
-
<span class="nolink">http://bitbucket.digital.vn/projects/WEBAPPV2/repos/miniapp-core/browse</span>
|
|
308
|
-
http://bitbucket.digital.vn/projects/WEBAPPV2/repos/miniapp-lib/browse
|
|
309
|
-
</td>
|
|
310
|
-
</tr>
|
|
311
|
-
</table>
|
|
312
|
-
"""
|
|
313
|
-
mock_client.get_page.return_value = {
|
|
314
|
-
"id": page_id,
|
|
315
|
-
"title": "Mini App",
|
|
316
|
-
"body": {"storage": {"value": html}},
|
|
317
|
-
"_links": {"base": "http://confluence.digital.vn", "webui": "/pages/viewpage.action?pageId=99999"},
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
result = await parser.parse_project_page(page_id, "Mini App")
|
|
321
|
-
|
|
322
|
-
assert result is not None
|
|
323
|
-
assert len(result.bitbucket_links) == 2
|
|
324
|
-
assert "http://bitbucket.digital.vn/projects/WEBAPPV2/repos/miniapp-core/browse" in result.bitbucket_links
|
|
325
|
-
assert "http://bitbucket.digital.vn/projects/WEBAPPV2/repos/miniapp-lib/browse" in result.bitbucket_links
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
# Phase 24: Tests for checklist seed discovery (TSK-240)
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
@pytest.mark.asyncio
|
|
332
|
-
async def test_parse_registry_discovers_checklist_page(parser, mock_client):
|
|
333
|
-
"""Registry parser should discover and populate checklist seed metadata."""
|
|
334
|
-
root_id = "88716667"
|
|
335
|
-
|
|
336
|
-
# Mock child pages including a checklist page
|
|
337
|
-
mock_client.get_child_pages.return_value = [
|
|
338
|
-
{
|
|
339
|
-
"id": "88716673",
|
|
340
|
-
"title": "3.1 Checklist đánh giá chất lượng",
|
|
341
|
-
"_links": {
|
|
342
|
-
"base": "http://confluence.digital.vn",
|
|
343
|
-
"webui": "/pages/viewpage.action?pageId=88716673",
|
|
344
|
-
},
|
|
345
|
-
},
|
|
346
|
-
{
|
|
347
|
-
"id": "88716675",
|
|
348
|
-
"title": "3.2 Các hệ thống, nền tảng sản phẩm",
|
|
349
|
-
"_links": {
|
|
350
|
-
"base": "http://confluence.digital.vn",
|
|
351
|
-
"webui": "/pages/viewpage.action?pageId=88716675",
|
|
352
|
-
},
|
|
353
|
-
},
|
|
354
|
-
]
|
|
355
|
-
|
|
356
|
-
registry = await parser.parse_registry(root_id)
|
|
357
|
-
|
|
358
|
-
assert registry.checklist_page_id == "88716673"
|
|
359
|
-
assert registry.checklist_title == "3.1 Checklist đánh giá chất lượng"
|
|
360
|
-
assert registry.checklist_page_url == "http://confluence.digital.vn/pages/viewpage.action?pageId=88716673"
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
@pytest.mark.asyncio
|
|
364
|
-
async def test_parse_registry_without_checklist_page(parser, mock_client):
|
|
365
|
-
"""Registry parser should handle missing checklist page gracefully."""
|
|
366
|
-
root_id = "88716667"
|
|
367
|
-
|
|
368
|
-
# Mock child pages without a checklist page
|
|
369
|
-
mock_client.get_child_pages.return_value = [
|
|
370
|
-
{
|
|
371
|
-
"id": "88716675",
|
|
372
|
-
"title": "3.2 Các hệ thống, nền tảng sản phẩm",
|
|
373
|
-
"_links": {
|
|
374
|
-
"base": "http://confluence.digital.vn",
|
|
375
|
-
"webui": "/pages/viewpage.action?pageId=88716675",
|
|
376
|
-
},
|
|
377
|
-
},
|
|
378
|
-
]
|
|
379
|
-
|
|
380
|
-
registry = await parser.parse_registry(root_id)
|
|
381
|
-
|
|
382
|
-
assert registry.checklist_page_id is None
|
|
383
|
-
assert registry.checklist_title is None
|
|
384
|
-
assert registry.checklist_page_url is None
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
@pytest.mark.asyncio
|
|
388
|
-
async def test_parse_registry_checklist_fallback_detection(parser, mock_client):
|
|
389
|
-
"""Registry parser should detect checklist via fallback when no 3.1 prefix."""
|
|
390
|
-
root_id = "88716667"
|
|
391
|
-
|
|
392
|
-
# Mock child pages with checklist in title but no 3.1 prefix
|
|
393
|
-
mock_client.get_child_pages.return_value = [
|
|
394
|
-
{
|
|
395
|
-
"id": "99999",
|
|
396
|
-
"title": "Audit Checklist Template",
|
|
397
|
-
"_links": {
|
|
398
|
-
"base": "http://confluence.digital.vn",
|
|
399
|
-
"webui": "/pages/viewpage.action?pageId=99999",
|
|
400
|
-
},
|
|
401
|
-
},
|
|
402
|
-
{
|
|
403
|
-
"id": "88716675",
|
|
404
|
-
"title": "3.2 Các hệ thống, nền tảng sản phẩm",
|
|
405
|
-
"_links": {
|
|
406
|
-
"base": "http://confluence.digital.vn",
|
|
407
|
-
"webui": "/pages/viewpage.action?pageId=88716675",
|
|
408
|
-
},
|
|
409
|
-
},
|
|
410
|
-
]
|
|
411
|
-
|
|
412
|
-
registry = await parser.parse_registry(root_id)
|
|
413
|
-
|
|
414
|
-
assert registry.checklist_page_id == "99999"
|
|
415
|
-
assert registry.checklist_title == "Audit Checklist Template"
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
@pytest.mark.asyncio
|
|
419
|
-
async def test_find_projects_root_and_checklist_deterministic_returns_both(parser, mock_client):
|
|
420
|
-
"""Deterministic root-child discovery should return projects root and checklist info."""
|
|
421
|
-
root_id = "88716667"
|
|
422
|
-
|
|
423
|
-
mock_client.get_child_pages.return_value = [
|
|
424
|
-
{
|
|
425
|
-
"id": "88716673",
|
|
426
|
-
"title": "3.1 Checklist đánh giá",
|
|
427
|
-
"_links": {
|
|
428
|
-
"self": "http://confluence.digital.vn/rest/api/content/88716673",
|
|
429
|
-
"webui": "/pages/viewpage.action?pageId=88716673",
|
|
430
|
-
},
|
|
431
|
-
},
|
|
432
|
-
{
|
|
433
|
-
"id": "88716675",
|
|
434
|
-
"title": "3.2 Các hệ thống",
|
|
435
|
-
"_links": {
|
|
436
|
-
"self": "http://confluence.digital.vn/rest/api/content/88716675",
|
|
437
|
-
"webui": "/pages/viewpage.action?pageId=88716675",
|
|
438
|
-
},
|
|
439
|
-
},
|
|
440
|
-
]
|
|
441
|
-
|
|
442
|
-
projects_root_id, checklist_candidates = await parser._find_projects_root_and_checklist_deterministic(root_id)
|
|
443
|
-
|
|
444
|
-
assert projects_root_id == "88716675"
|
|
445
|
-
assert checklist_candidates
|
|
446
|
-
assert checklist_candidates[0]["id"] == "88716673"
|
|
447
|
-
assert checklist_candidates[0]["title"] == "3.1 Checklist đánh giá"
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
# Phase 24 TSK-241: Deterministic root-child classification tests
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
class TestClassifyRootChild:
|
|
454
|
-
"""Tests for deterministic root-child classification (TSK-241.2.1)."""
|
|
455
|
-
|
|
456
|
-
def test_classify_checklist_with_3_1_prefix(self):
|
|
457
|
-
"""3.1 prefix should classify as CHECKLIST."""
|
|
458
|
-
assert RegistryPageParser.classify_root_child("3.1 Checklist đánh giá") == RootChildType.CHECKLIST
|
|
459
|
-
assert RegistryPageParser.classify_root_child("3.1 Some other title") == RootChildType.CHECKLIST
|
|
460
|
-
assert RegistryPageParser.classify_root_child("3.1") == RootChildType.CHECKLIST
|
|
461
|
-
|
|
462
|
-
def test_classify_checklist_with_keyword(self):
|
|
463
|
-
"""'checklist' keyword should classify as CHECKLIST."""
|
|
464
|
-
assert RegistryPageParser.classify_root_child("Audit Checklist Template") == RootChildType.CHECKLIST
|
|
465
|
-
assert RegistryPageParser.classify_root_child("My Checklist") == RootChildType.CHECKLIST
|
|
466
|
-
assert RegistryPageParser.classify_root_child("CHECKLIST") == RootChildType.CHECKLIST
|
|
467
|
-
|
|
468
|
-
def test_classify_project_container_with_3_2_prefix(self):
|
|
469
|
-
"""3.2 prefix should classify as PROJECT_CONTAINER."""
|
|
470
|
-
assert RegistryPageParser.classify_root_child("3.2 Các hệ thống") == RootChildType.PROJECT_CONTAINER
|
|
471
|
-
assert RegistryPageParser.classify_root_child("3.2 Projects") == RootChildType.PROJECT_CONTAINER
|
|
472
|
-
assert RegistryPageParser.classify_root_child("3.2") == RootChildType.PROJECT_CONTAINER
|
|
473
|
-
|
|
474
|
-
def test_classify_project_container_with_vietnamese_keywords(self):
|
|
475
|
-
"""Vietnamese keywords should classify as PROJECT_CONTAINER."""
|
|
476
|
-
assert RegistryPageParser.classify_root_child("Các hệ thống sản phẩm") == RootChildType.PROJECT_CONTAINER
|
|
477
|
-
assert RegistryPageParser.classify_root_child("Nền tảng dịch vụ") == RootChildType.PROJECT_CONTAINER
|
|
478
|
-
assert RegistryPageParser.classify_root_child("Hệ thống và nền tảng") == RootChildType.PROJECT_CONTAINER
|
|
479
|
-
|
|
480
|
-
def test_classify_audit_history(self):
|
|
481
|
-
"""'audit history' should classify as AUDIT_HISTORY."""
|
|
482
|
-
assert RegistryPageParser.classify_root_child("Audit History") == RootChildType.AUDIT_HISTORY
|
|
483
|
-
assert RegistryPageParser.classify_root_child("audit history") == RootChildType.AUDIT_HISTORY
|
|
484
|
-
assert RegistryPageParser.classify_root_child("Project Audit History") == RootChildType.AUDIT_HISTORY
|
|
485
|
-
|
|
486
|
-
def test_classify_unknown(self):
|
|
487
|
-
"""Unrecognized titles should classify as UNKNOWN."""
|
|
488
|
-
assert RegistryPageParser.classify_root_child("Random Page") == RootChildType.UNKNOWN
|
|
489
|
-
assert RegistryPageParser.classify_root_child("Documentation") == RootChildType.UNKNOWN
|
|
490
|
-
assert RegistryPageParser.classify_root_child("") == RootChildType.UNKNOWN
|
|
491
|
-
assert RegistryPageParser.classify_root_child(" ") == RootChildType.UNKNOWN
|
|
492
|
-
|
|
493
|
-
def test_classify_case_insensitive(self):
|
|
494
|
-
"""Classification should be case-insensitive."""
|
|
495
|
-
assert RegistryPageParser.classify_root_child("CHECKLIST") == RootChildType.CHECKLIST
|
|
496
|
-
assert RegistryPageParser.classify_root_child("Checklist") == RootChildType.CHECKLIST
|
|
497
|
-
assert RegistryPageParser.classify_root_child("AUDIT HISTORY") == RootChildType.AUDIT_HISTORY
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
class TestClassifyChecklistProfileId:
|
|
501
|
-
"""Tests for checklist profile keyword classification (Phase 110)."""
|
|
502
|
-
|
|
503
|
-
def test_vietnamese_frontend_keyword_maps_to_frontend(self) -> None:
|
|
504
|
-
profile_id, match_count = RegistryPageParser._classify_checklist_profile_id("Checklist giao diện người dùng")
|
|
505
|
-
assert profile_id == "frontend"
|
|
506
|
-
assert match_count == 1
|
|
507
|
-
|
|
508
|
-
def test_english_frontend_keyword_maps_to_frontend(self) -> None:
|
|
509
|
-
profile_id, match_count = RegistryPageParser._classify_checklist_profile_id("Frontend Architecture Checklist")
|
|
510
|
-
assert profile_id == "frontend"
|
|
511
|
-
assert match_count == 1
|
|
512
|
-
|
|
513
|
-
def test_space_separated_front_end_keyword_maps_to_frontend(self) -> None:
|
|
514
|
-
"""Regression: actual Confluence title uses 'Front end' (space-separated)."""
|
|
515
|
-
profile_id, match_count = RegistryPageParser._classify_checklist_profile_id(
|
|
516
|
-
"3.2. Checklist đóng gói hệ thống - Front end"
|
|
517
|
-
)
|
|
518
|
-
assert profile_id == "frontend"
|
|
519
|
-
assert match_count == 1
|
|
520
|
-
|
|
521
|
-
def test_vietnamese_backend_keyword_maps_to_backend(self) -> None:
|
|
522
|
-
profile_id, match_count = RegistryPageParser._classify_checklist_profile_id("Checklist phía sau hệ thống")
|
|
523
|
-
assert profile_id == "backend"
|
|
524
|
-
assert match_count == 1
|
|
525
|
-
|
|
526
|
-
def test_ambiguous_profile_keywords_fall_back_to_default(self) -> None:
|
|
527
|
-
profile_id, match_count = RegistryPageParser._classify_checklist_profile_id(
|
|
528
|
-
"Backend frontend consolidated checklist"
|
|
529
|
-
)
|
|
530
|
-
assert profile_id == "default"
|
|
531
|
-
assert match_count == 2
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
class TestExtractChecklistVersion:
|
|
535
|
-
"""Tests for _extract_checklist_version (Phase 112 version-aware dedup)."""
|
|
536
|
-
|
|
537
|
-
def test_version_suffix_extracted(self) -> None:
|
|
538
|
-
base, ver = RegistryPageParser._extract_checklist_version("3.1 Checklist đánh giá các hệ thống nền tảng v1.1")
|
|
539
|
-
assert base == "3.1 Checklist đánh giá các hệ thống nền tảng"
|
|
540
|
-
assert ver == (1, 1)
|
|
541
|
-
|
|
542
|
-
def test_no_version_suffix_returns_zero(self) -> None:
|
|
543
|
-
base, ver = RegistryPageParser._extract_checklist_version("3.1 Checklist đánh giá các hệ thống nền tảng")
|
|
544
|
-
assert base == "3.1 Checklist đánh giá các hệ thống nền tảng"
|
|
545
|
-
assert ver == (0,)
|
|
546
|
-
|
|
547
|
-
def test_major_only_version(self) -> None:
|
|
548
|
-
base, ver = RegistryPageParser._extract_checklist_version("Checklist v2")
|
|
549
|
-
assert base == "Checklist"
|
|
550
|
-
assert ver == (2,)
|
|
551
|
-
|
|
552
|
-
def test_three_part_version(self) -> None:
|
|
553
|
-
base, ver = RegistryPageParser._extract_checklist_version("Checklist v2.3.1")
|
|
554
|
-
assert base == "Checklist"
|
|
555
|
-
assert ver == (2, 3, 1)
|
|
556
|
-
|
|
557
|
-
def test_case_insensitive_v(self) -> None:
|
|
558
|
-
base, ver = RegistryPageParser._extract_checklist_version("Checklist V1.0")
|
|
559
|
-
assert base == "Checklist"
|
|
560
|
-
assert ver == (1, 0)
|
|
561
|
-
|
|
562
|
-
def test_empty_title(self) -> None:
|
|
563
|
-
base, ver = RegistryPageParser._extract_checklist_version("")
|
|
564
|
-
assert base == ""
|
|
565
|
-
assert ver == (0,)
|
|
566
|
-
|
|
567
|
-
def test_version_ordering(self) -> None:
|
|
568
|
-
"""v1.1 should sort higher than implicit v0 (no suffix)."""
|
|
569
|
-
_, v0 = RegistryPageParser._extract_checklist_version("Checklist đánh giá")
|
|
570
|
-
_, v11 = RegistryPageParser._extract_checklist_version("Checklist đánh giá v1.1")
|
|
571
|
-
assert v11 > v0
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
class TestBuildChecklistProfilesVersionDedup:
|
|
575
|
-
"""Tests for version-aware deduplication in _build_checklist_profiles (Phase 112).
|
|
576
|
-
|
|
577
|
-
Regression: the live registry (88716667) contains both legacy 88716673
|
|
578
|
-
("3.1 Checklist đánh giá các hệ thống nền tảng") and current 88722450
|
|
579
|
-
("3.1 Checklist đánh giá các hệ thống nền tảng v1.1"). Both classify as
|
|
580
|
-
profile_id=default. Only 88722450 (v1.1) should survive dedup.
|
|
581
|
-
"""
|
|
582
|
-
|
|
583
|
-
def test_legacy_page_superseded_by_versioned_successor(self) -> None:
|
|
584
|
-
"""88716673 (no version) should be superseded by 88722450 (v1.1)."""
|
|
585
|
-
candidates = [
|
|
586
|
-
{
|
|
587
|
-
"id": "88716673",
|
|
588
|
-
"title": "3.1 Checklist đánh giá các hệ thống nền tảng",
|
|
589
|
-
"url": "/pages/viewpage.action?pageId=88716673",
|
|
590
|
-
"profile_id": "default",
|
|
591
|
-
},
|
|
592
|
-
{
|
|
593
|
-
"id": "88722450",
|
|
594
|
-
"title": "3.1 Checklist đánh giá các hệ thống nền tảng v1.1",
|
|
595
|
-
"url": "/pages/viewpage.action?pageId=88722450",
|
|
596
|
-
"profile_id": "default",
|
|
597
|
-
},
|
|
598
|
-
]
|
|
599
|
-
profiles, diag = RegistryPageParser._build_checklist_profiles(candidates)
|
|
600
|
-
default_profiles = [p for p in profiles if p.profile_id == "default"]
|
|
601
|
-
assert len(default_profiles) == 1
|
|
602
|
-
assert default_profiles[0].page_ids == ["88722450"]
|
|
603
|
-
assert "88716673" not in default_profiles[0].page_ids
|
|
604
|
-
assert diag["superseded_count"] == 1
|
|
605
|
-
|
|
606
|
-
def test_full_registry_produces_two_profiles(self) -> None:
|
|
607
|
-
"""Simulates the actual 88716667 registry with 3 checklist pages."""
|
|
608
|
-
candidates = [
|
|
609
|
-
{
|
|
610
|
-
"id": "88716673",
|
|
611
|
-
"title": "3.1 Checklist đánh giá các hệ thống nền tảng",
|
|
612
|
-
"url": "/pages/viewpage.action?pageId=88716673",
|
|
613
|
-
"profile_id": "default",
|
|
614
|
-
},
|
|
615
|
-
{
|
|
616
|
-
"id": "88722450",
|
|
617
|
-
"title": "3.1 Checklist đánh giá các hệ thống nền tảng v1.1",
|
|
618
|
-
"url": "/pages/viewpage.action?pageId=88722450",
|
|
619
|
-
"profile_id": "default",
|
|
620
|
-
},
|
|
621
|
-
{
|
|
622
|
-
"id": "88729031",
|
|
623
|
-
"title": "3.2. Checklist đóng gói hệ thống - Front end",
|
|
624
|
-
"url": "/pages/viewpage.action?pageId=88729031",
|
|
625
|
-
"profile_id": "frontend",
|
|
626
|
-
},
|
|
627
|
-
]
|
|
628
|
-
profiles, diag = RegistryPageParser._build_checklist_profiles(candidates)
|
|
629
|
-
profile_ids = [p.profile_id for p in profiles]
|
|
630
|
-
assert "frontend" in profile_ids
|
|
631
|
-
assert "default" in profile_ids
|
|
632
|
-
assert len(profiles) == 2
|
|
633
|
-
|
|
634
|
-
default_p = next(p for p in profiles if p.profile_id == "default")
|
|
635
|
-
frontend_p = next(p for p in profiles if p.profile_id == "frontend")
|
|
636
|
-
# Default profile should contain only v1.1, not legacy
|
|
637
|
-
assert default_p.page_ids == ["88722450"]
|
|
638
|
-
assert frontend_p.page_ids == ["88729031"]
|
|
639
|
-
assert diag["superseded_count"] == 1
|
|
640
|
-
|
|
641
|
-
def test_no_dedup_when_titles_differ(self) -> None:
|
|
642
|
-
"""Pages with genuinely different base titles should all be kept."""
|
|
643
|
-
candidates = [
|
|
644
|
-
{
|
|
645
|
-
"id": "100",
|
|
646
|
-
"title": "Backend Quality Checklist",
|
|
647
|
-
"url": "/pages/viewpage.action?pageId=100",
|
|
648
|
-
"profile_id": "default",
|
|
649
|
-
},
|
|
650
|
-
{
|
|
651
|
-
"id": "200",
|
|
652
|
-
"title": "Security Audit Checklist",
|
|
653
|
-
"url": "/pages/viewpage.action?pageId=200",
|
|
654
|
-
"profile_id": "default",
|
|
655
|
-
},
|
|
656
|
-
]
|
|
657
|
-
profiles, diag = RegistryPageParser._build_checklist_profiles(candidates)
|
|
658
|
-
default_p = next(p for p in profiles if p.profile_id == "default")
|
|
659
|
-
assert len(default_p.page_ids) == 2
|
|
660
|
-
assert "100" in default_p.page_ids
|
|
661
|
-
assert "200" in default_p.page_ids
|
|
662
|
-
assert diag["superseded_count"] == 0
|
|
663
|
-
|
|
664
|
-
def test_single_candidate_no_dedup(self) -> None:
|
|
665
|
-
"""Single candidate should pass through with no dedup."""
|
|
666
|
-
candidates = [
|
|
667
|
-
{
|
|
668
|
-
"id": "88722450",
|
|
669
|
-
"title": "3.1 Checklist đánh giá v1.1",
|
|
670
|
-
"url": "/pages/viewpage.action?pageId=88722450",
|
|
671
|
-
"profile_id": "default",
|
|
672
|
-
},
|
|
673
|
-
]
|
|
674
|
-
profiles, diag = RegistryPageParser._build_checklist_profiles(candidates)
|
|
675
|
-
assert len(profiles) == 1
|
|
676
|
-
assert profiles[0].page_ids == ["88722450"]
|
|
677
|
-
assert diag["superseded_count"] == 0
|
|
678
|
-
|
|
679
|
-
def test_higher_version_wins(self) -> None:
|
|
680
|
-
"""When v1.1 and v2.0 exist, v2.0 wins."""
|
|
681
|
-
candidates = [
|
|
682
|
-
{
|
|
683
|
-
"id": "AAA",
|
|
684
|
-
"title": "Checklist đánh giá v1.1",
|
|
685
|
-
"url": "",
|
|
686
|
-
"profile_id": "default",
|
|
687
|
-
},
|
|
688
|
-
{
|
|
689
|
-
"id": "BBB",
|
|
690
|
-
"title": "Checklist đánh giá v2.0",
|
|
691
|
-
"url": "",
|
|
692
|
-
"profile_id": "default",
|
|
693
|
-
},
|
|
694
|
-
]
|
|
695
|
-
profiles, diag = RegistryPageParser._build_checklist_profiles(candidates)
|
|
696
|
-
default_p = next(p for p in profiles if p.profile_id == "default")
|
|
697
|
-
assert default_p.page_ids == ["BBB"]
|
|
698
|
-
assert diag["superseded_count"] == 1
|
|
699
|
-
|
|
700
|
-
def test_empty_candidates(self) -> None:
|
|
701
|
-
profiles, diag = RegistryPageParser._build_checklist_profiles([])
|
|
702
|
-
assert profiles == []
|
|
703
|
-
assert diag["superseded_count"] == 0
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
class TestParserConfiguration:
|
|
707
|
-
"""Tests for parser configuration (TSK-241.2.2)."""
|
|
708
|
-
|
|
709
|
-
def test_parser_initializes_with_defaults(self, mock_client):
|
|
710
|
-
parser = RegistryPageParser(mock_client)
|
|
711
|
-
assert parser.adaptive_depth is True
|
|
712
|
-
assert parser.adaptive_max_depth == 2
|
|
713
|
-
|
|
714
|
-
def test_checklist_page_override(self, mock_client):
|
|
715
|
-
"""Should store checklist page override."""
|
|
716
|
-
parser = RegistryPageParser(mock_client, checklist_page="12345")
|
|
717
|
-
assert parser.checklist_page_override == "12345"
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
@pytest.mark.asyncio
|
|
721
|
-
async def test_parse_registry_checklist_first_mode(parser_checklist_first, mock_client):
|
|
722
|
-
"""Checklist-first mode should use deterministic classification."""
|
|
723
|
-
root_id = "88716667"
|
|
724
|
-
|
|
725
|
-
mock_client.get_child_pages.return_value = [
|
|
726
|
-
{
|
|
727
|
-
"id": "88716673",
|
|
728
|
-
"title": "3.1 Checklist đánh giá chất lượng",
|
|
729
|
-
"_links": {
|
|
730
|
-
"base": "http://confluence.digital.vn",
|
|
731
|
-
"webui": "/pages/viewpage.action?pageId=88716673",
|
|
732
|
-
},
|
|
733
|
-
},
|
|
734
|
-
{
|
|
735
|
-
"id": "88716675",
|
|
736
|
-
"title": "3.2 Các hệ thống, nền tảng sản phẩm",
|
|
737
|
-
"_links": {
|
|
738
|
-
"base": "http://confluence.digital.vn",
|
|
739
|
-
"webui": "/pages/viewpage.action?pageId=88716675",
|
|
740
|
-
},
|
|
741
|
-
},
|
|
742
|
-
]
|
|
743
|
-
|
|
744
|
-
registry = await parser_checklist_first.parse_registry(root_id)
|
|
745
|
-
assert registry.checklist_page_id == "88716673"
|
|
746
|
-
assert registry.checklist_title == "3.1 Checklist đánh giá chất lượng"
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
@pytest.mark.asyncio
|
|
750
|
-
async def test_checklist_first_uses_metadata_only_discovery(mock_client):
|
|
751
|
-
"""Checklist-first should keep root discovery lightweight and expand only candidate level."""
|
|
752
|
-
parser = RegistryPageParser(
|
|
753
|
-
mock_client,
|
|
754
|
-
recursive_depth=1,
|
|
755
|
-
)
|
|
756
|
-
root_id = "88716667"
|
|
757
|
-
expand_calls: list[tuple[str, str | None]] = []
|
|
758
|
-
|
|
759
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
760
|
-
expand_calls.append((str(page_id), expand))
|
|
761
|
-
if page_id == root_id:
|
|
762
|
-
return [
|
|
763
|
-
{"id": "88716673", "title": "3.1 Checklist đánh giá", "_links": {}},
|
|
764
|
-
{"id": "88716675", "title": "3.2 Các hệ thống", "_links": {}},
|
|
765
|
-
]
|
|
766
|
-
if page_id == "88716675":
|
|
767
|
-
return [
|
|
768
|
-
{
|
|
769
|
-
"id": "proj_1",
|
|
770
|
-
"title": "Project 1",
|
|
771
|
-
"body": {
|
|
772
|
-
"storage": {
|
|
773
|
-
"value": "<table><tr><th>Link</th></tr><tr><td><a href='http://example.com/doc'>Doc</a></td></tr></table>"
|
|
774
|
-
}
|
|
775
|
-
},
|
|
776
|
-
"_links": {"base": "http://confluence.digital.vn", "webui": "/pages/viewpage.action?pageId=proj_1"},
|
|
777
|
-
},
|
|
778
|
-
{"id": "audit", "title": "Audit History", "_links": {}},
|
|
779
|
-
]
|
|
780
|
-
return []
|
|
781
|
-
|
|
782
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
783
|
-
registry = await parser.parse_registry(root_id)
|
|
784
|
-
|
|
785
|
-
assert len(registry.projects) == 1
|
|
786
|
-
assert registry.projects[0].page_id == "proj_1"
|
|
787
|
-
assert expand_calls == [(root_id, None), ("88716675", "body.storage")]
|
|
788
|
-
mock_client.get_page.assert_not_called()
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
@pytest.mark.asyncio
|
|
792
|
-
async def test_parse_registry_persists_checklist_mode(parser, mock_client):
|
|
793
|
-
"""Registry output should persist checklist-first mode."""
|
|
794
|
-
root_id = "88716667"
|
|
795
|
-
|
|
796
|
-
mock_client.get_child_pages.return_value = [
|
|
797
|
-
{
|
|
798
|
-
"id": "88716675",
|
|
799
|
-
"title": "3.2 Các hệ thống",
|
|
800
|
-
"_links": {},
|
|
801
|
-
},
|
|
802
|
-
]
|
|
803
|
-
|
|
804
|
-
registry = await parser.parse_registry(root_id)
|
|
805
|
-
assert registry.mapping_version is not None
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
@pytest.mark.asyncio
|
|
809
|
-
async def test_parse_registry_derives_department_from_ancestry_lineage(mock_client):
|
|
810
|
-
parser = RegistryPageParser(mock_client, recursive_depth=3)
|
|
811
|
-
root_id = "88716667"
|
|
812
|
-
valid_html = """
|
|
813
|
-
<table>
|
|
814
|
-
<tr><th>Link</th></tr>
|
|
815
|
-
<tr><td><a href='http://example.com/doc'>Doc</a></td></tr>
|
|
816
|
-
</table>
|
|
817
|
-
"""
|
|
818
|
-
|
|
819
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
820
|
-
if page_id == root_id:
|
|
821
|
-
return [{"id": "projects_root", "title": "3.2 Các hệ thống", "_links": {}}]
|
|
822
|
-
if page_id == "projects_root":
|
|
823
|
-
return [{"id": "dept_a", "title": "Payments Department", "_links": {}}]
|
|
824
|
-
if page_id == "dept_a":
|
|
825
|
-
return [
|
|
826
|
-
{
|
|
827
|
-
"id": "proj_1",
|
|
828
|
-
"title": "Project 1",
|
|
829
|
-
"body": {"storage": {"value": valid_html}},
|
|
830
|
-
"_links": {
|
|
831
|
-
"base": "http://confluence.digital.vn",
|
|
832
|
-
"webui": "/pages/viewpage.action?pageId=proj_1",
|
|
833
|
-
},
|
|
834
|
-
}
|
|
835
|
-
]
|
|
836
|
-
return []
|
|
837
|
-
|
|
838
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
839
|
-
mock_client.get_page.return_value = None
|
|
840
|
-
|
|
841
|
-
registry = await parser.parse_registry(root_id)
|
|
842
|
-
|
|
843
|
-
assert len(registry.projects) == 1
|
|
844
|
-
project = registry.projects[0]
|
|
845
|
-
assert project.department == "Payments Department"
|
|
846
|
-
assert project.department_page_id == "dept_a"
|
|
847
|
-
assert project.department_storage_key == "payments-department"
|
|
848
|
-
assert project.department_assignment_reason == "derived_from_projects_root_lineage"
|
|
849
|
-
assert [node.page_id for node in project.confluence_ancestry_path] == ["projects_root", "dept_a", "proj_1"]
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
@pytest.mark.asyncio
|
|
853
|
-
async def test_parse_registry_marks_unassigned_when_department_node_missing(mock_client):
|
|
854
|
-
parser = RegistryPageParser(mock_client, recursive_depth=2)
|
|
855
|
-
root_id = "88716667"
|
|
856
|
-
valid_html = """
|
|
857
|
-
<table>
|
|
858
|
-
<tr><th>Link</th></tr>
|
|
859
|
-
<tr><td><a href='http://example.com/doc'>Doc</a></td></tr>
|
|
860
|
-
</table>
|
|
861
|
-
"""
|
|
862
|
-
|
|
863
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
864
|
-
if page_id == root_id:
|
|
865
|
-
return [{"id": "projects_root", "title": "3.2 Các hệ thống", "_links": {}}]
|
|
866
|
-
if page_id == "projects_root":
|
|
867
|
-
return [
|
|
868
|
-
{
|
|
869
|
-
"id": "proj_1",
|
|
870
|
-
"title": "Project 1",
|
|
871
|
-
"body": {"storage": {"value": valid_html}},
|
|
872
|
-
"_links": {
|
|
873
|
-
"base": "http://confluence.digital.vn",
|
|
874
|
-
"webui": "/pages/viewpage.action?pageId=proj_1",
|
|
875
|
-
},
|
|
876
|
-
}
|
|
877
|
-
]
|
|
878
|
-
return []
|
|
879
|
-
|
|
880
|
-
async def mock_get_page(page_id, expand=None):
|
|
881
|
-
if page_id == "projects_root":
|
|
882
|
-
return {"id": "projects_root", "title": "3.2 Các hệ thống", "_links": {}}
|
|
883
|
-
return None
|
|
884
|
-
|
|
885
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
886
|
-
mock_client.get_page.side_effect = mock_get_page
|
|
887
|
-
|
|
888
|
-
registry = await parser.parse_registry(root_id)
|
|
889
|
-
|
|
890
|
-
assert len(registry.projects) == 1
|
|
891
|
-
project = registry.projects[0]
|
|
892
|
-
assert project.department == "3.2 Các hệ thống"
|
|
893
|
-
assert project.department_page_id == "projects_root"
|
|
894
|
-
assert project.department_storage_key == "3-2-cac-he-thong"
|
|
895
|
-
assert project.department_assignment_reason == "derived_from_projects_root_anchor"
|
|
896
|
-
assert [node.page_id for node in project.confluence_ancestry_path] == ["projects_root", "proj_1"]
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
@pytest.mark.asyncio
|
|
900
|
-
async def test_parse_registry_derives_department_from_registry_root_anchor_when_root_is_project_container(mock_client):
|
|
901
|
-
parser = RegistryPageParser(mock_client, recursive_depth=2)
|
|
902
|
-
root_id = "88716694"
|
|
903
|
-
valid_html = """
|
|
904
|
-
<table>
|
|
905
|
-
<tr><th>Link</th></tr>
|
|
906
|
-
<tr><td><a href='http://bitbucket.digital.vn/projects/PAY'>Repo</a></td></tr>
|
|
907
|
-
</table>
|
|
908
|
-
"""
|
|
909
|
-
|
|
910
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
911
|
-
if page_id == root_id:
|
|
912
|
-
return [
|
|
913
|
-
{
|
|
914
|
-
"id": "proj_1",
|
|
915
|
-
"title": "Core Payment",
|
|
916
|
-
"body": {"storage": {"value": valid_html}},
|
|
917
|
-
"_links": {
|
|
918
|
-
"base": "http://confluence.digital.vn",
|
|
919
|
-
"webui": "/pages/viewpage.action?pageId=proj_1",
|
|
920
|
-
},
|
|
921
|
-
}
|
|
922
|
-
]
|
|
923
|
-
return []
|
|
924
|
-
|
|
925
|
-
async def mock_get_page(page_id, expand=None):
|
|
926
|
-
if page_id == root_id:
|
|
927
|
-
return {
|
|
928
|
-
"id": root_id,
|
|
929
|
-
"title": "3.2.4 Nền tảng thanh toán",
|
|
930
|
-
"_links": {
|
|
931
|
-
"base": "http://confluence.digital.vn",
|
|
932
|
-
"webui": f"/pages/viewpage.action?pageId={root_id}",
|
|
933
|
-
},
|
|
934
|
-
}
|
|
935
|
-
return None
|
|
936
|
-
|
|
937
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
938
|
-
mock_client.get_page.side_effect = mock_get_page
|
|
939
|
-
|
|
940
|
-
registry = await parser.parse_registry(root_id)
|
|
941
|
-
|
|
942
|
-
assert len(registry.projects) == 1
|
|
943
|
-
project = registry.projects[0]
|
|
944
|
-
assert project.department == "3.2.4 Nền tảng thanh toán"
|
|
945
|
-
assert project.department_page_id == "88716694"
|
|
946
|
-
assert project.root_page_id == "88716694"
|
|
947
|
-
assert project.root_title == "3.2.4 Nền tảng thanh toán"
|
|
948
|
-
assert project.root_assignment_reason == "derived_from_registry_root_lineage_head"
|
|
949
|
-
assert project.department_storage_key == "3-2-4-nen-tang-thanh-toan"
|
|
950
|
-
assert project.department_assignment_reason == "derived_from_registry_root_anchor"
|
|
951
|
-
assert [node.page_id for node in project.confluence_ancestry_path] == ["88716694", "proj_1"]
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
@pytest.mark.asyncio
|
|
955
|
-
async def test_parse_registry_atomic_project_entry_collects_repository_child_pages(mock_client, monkeypatch):
|
|
956
|
-
parser = RegistryPageParser(mock_client, recursive_depth=1)
|
|
957
|
-
root_id = "88727304"
|
|
958
|
-
monkeypatch.setattr(RegistryPageParser, "_local_repo_path_for_slug", staticmethod(lambda **_kwargs: None))
|
|
959
|
-
|
|
960
|
-
async def mock_get_page(page_id, expand=None):
|
|
961
|
-
if page_id == root_id:
|
|
962
|
-
return {
|
|
963
|
-
"id": root_id,
|
|
964
|
-
"title": "PAR",
|
|
965
|
-
"ancestors": [
|
|
966
|
-
{
|
|
967
|
-
"id": "88716667",
|
|
968
|
-
"title": "Khối CNTT",
|
|
969
|
-
"_links": {"webui": "/pages/viewpage.action?pageId=88716667"},
|
|
970
|
-
},
|
|
971
|
-
{
|
|
972
|
-
"id": "88716694",
|
|
973
|
-
"title": "3.2.4 Nền tảng thanh toán",
|
|
974
|
-
"_links": {"webui": "/pages/viewpage.action?pageId=88716694"},
|
|
975
|
-
},
|
|
976
|
-
],
|
|
977
|
-
"_links": {
|
|
978
|
-
"base": "http://confluence.digital.vn",
|
|
979
|
-
"webui": f"/pages/viewpage.action?pageId={root_id}",
|
|
980
|
-
},
|
|
981
|
-
}
|
|
982
|
-
return None
|
|
983
|
-
|
|
984
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
985
|
-
if page_id == root_id:
|
|
986
|
-
return [
|
|
987
|
-
{
|
|
988
|
-
"id": "91337278",
|
|
989
|
-
"title": "Repository - par-merchant-report",
|
|
990
|
-
"_links": {
|
|
991
|
-
"base": "http://confluence.digital.vn",
|
|
992
|
-
"webui": "/pages/viewpage.action?pageId=91337278",
|
|
993
|
-
},
|
|
994
|
-
},
|
|
995
|
-
{
|
|
996
|
-
"id": "91337281",
|
|
997
|
-
"title": "Repository - par-merchant-transaction",
|
|
998
|
-
"_links": {
|
|
999
|
-
"base": "http://confluence.digital.vn",
|
|
1000
|
-
"webui": "/pages/viewpage.action?pageId=91337281",
|
|
1001
|
-
},
|
|
1002
|
-
},
|
|
1003
|
-
]
|
|
1004
|
-
return []
|
|
1005
|
-
|
|
1006
|
-
mock_client.get_page.side_effect = mock_get_page
|
|
1007
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
1008
|
-
|
|
1009
|
-
registry = await parser.parse_registry(root_id)
|
|
1010
|
-
|
|
1011
|
-
assert len(registry.projects) == 1
|
|
1012
|
-
project = registry.projects[0]
|
|
1013
|
-
assert project.page_id == root_id
|
|
1014
|
-
assert project.department_page_id == "88716694"
|
|
1015
|
-
assert project.root_page_id == "88716667"
|
|
1016
|
-
assert [child.page_id for child in project.child_pages] == ["91337278", "91337281"]
|
|
1017
|
-
assert project.bitbucket_projects == [
|
|
1018
|
-
{
|
|
1019
|
-
"bitbucket_project_key": "PAR",
|
|
1020
|
-
"display_name": "PAR",
|
|
1021
|
-
"source": "registry_child_repository_pages",
|
|
1022
|
-
"repos": [
|
|
1023
|
-
{
|
|
1024
|
-
"default_branch": "main",
|
|
1025
|
-
"fetch_reason": "registry_child_repository_page",
|
|
1026
|
-
"local_code_path": None,
|
|
1027
|
-
"path": "http://confluence.digital.vn/pages/viewpage.action?pageId=91337278",
|
|
1028
|
-
"repo_slug": "merchant-report",
|
|
1029
|
-
"repo_storage_key": "par:merchant-report",
|
|
1030
|
-
"repo_url": None,
|
|
1031
|
-
"source_page_id": "91337278",
|
|
1032
|
-
"source_title": "Repository - par-merchant-report",
|
|
1033
|
-
"status": "discovered",
|
|
1034
|
-
},
|
|
1035
|
-
{
|
|
1036
|
-
"default_branch": "main",
|
|
1037
|
-
"fetch_reason": "registry_child_repository_page",
|
|
1038
|
-
"local_code_path": None,
|
|
1039
|
-
"path": "http://confluence.digital.vn/pages/viewpage.action?pageId=91337281",
|
|
1040
|
-
"repo_slug": "merchant-transaction",
|
|
1041
|
-
"repo_storage_key": "par:merchant-transaction",
|
|
1042
|
-
"repo_url": None,
|
|
1043
|
-
"source_page_id": "91337281",
|
|
1044
|
-
"source_title": "Repository - par-merchant-transaction",
|
|
1045
|
-
"status": "discovered",
|
|
1046
|
-
},
|
|
1047
|
-
],
|
|
1048
|
-
}
|
|
1049
|
-
]
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
@pytest.mark.asyncio
|
|
1053
|
-
async def test_parse_registry_prefers_nearest_department_container_under_top_level_root(mock_client):
|
|
1054
|
-
parser = RegistryPageParser(mock_client, recursive_depth=3)
|
|
1055
|
-
root_id = "88716667"
|
|
1056
|
-
valid_html = """
|
|
1057
|
-
<table>
|
|
1058
|
-
<tr><th>Link</th></tr>
|
|
1059
|
-
<tr><td><a href='http://bitbucket.digital.vn/projects/PAY'>Repo</a></td></tr>
|
|
1060
|
-
</table>
|
|
1061
|
-
"""
|
|
1062
|
-
|
|
1063
|
-
async def mock_get_page(page_id, expand=None):
|
|
1064
|
-
if page_id == root_id:
|
|
1065
|
-
return {
|
|
1066
|
-
"id": root_id,
|
|
1067
|
-
"title": "3.2 Các hệ thống, nền tảng sản phẩm",
|
|
1068
|
-
"_links": {
|
|
1069
|
-
"base": "http://confluence.digital.vn",
|
|
1070
|
-
"webui": f"/pages/viewpage.action?pageId={root_id}",
|
|
1071
|
-
},
|
|
1072
|
-
}
|
|
1073
|
-
if page_id == "88716677":
|
|
1074
|
-
return {
|
|
1075
|
-
"id": "88716677",
|
|
1076
|
-
"title": "3.2.1 Sản phẩm quản trị",
|
|
1077
|
-
"_links": {
|
|
1078
|
-
"base": "http://confluence.digital.vn",
|
|
1079
|
-
"webui": "/pages/viewpage.action?pageId=88716677",
|
|
1080
|
-
},
|
|
1081
|
-
}
|
|
1082
|
-
return None
|
|
1083
|
-
|
|
1084
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
1085
|
-
if page_id == root_id:
|
|
1086
|
-
return [{"id": "88716675", "title": "3.2 Các hệ thống", "_links": {}}]
|
|
1087
|
-
if page_id == "88716675":
|
|
1088
|
-
return [{"id": "88716694", "title": "3.2.4 Nền tảng thanh toán", "_links": {}}]
|
|
1089
|
-
if page_id == "88716694":
|
|
1090
|
-
return [
|
|
1091
|
-
{
|
|
1092
|
-
"id": "88718977",
|
|
1093
|
-
"title": "Auto Payment",
|
|
1094
|
-
"body": {"storage": {"value": valid_html}},
|
|
1095
|
-
"_links": {
|
|
1096
|
-
"base": "http://confluence.digital.vn",
|
|
1097
|
-
"webui": "/pages/viewpage.action?pageId=88718977",
|
|
1098
|
-
},
|
|
1099
|
-
}
|
|
1100
|
-
]
|
|
1101
|
-
return []
|
|
1102
|
-
|
|
1103
|
-
mock_client.get_page.side_effect = mock_get_page
|
|
1104
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
1105
|
-
|
|
1106
|
-
registry = await parser.parse_registry(root_id)
|
|
1107
|
-
|
|
1108
|
-
assert len(registry.projects) == 1
|
|
1109
|
-
project = registry.projects[0]
|
|
1110
|
-
assert project.department == "3.2.4 Nền tảng thanh toán"
|
|
1111
|
-
assert project.department_page_id == "88716694"
|
|
1112
|
-
assert project.root_page_id == root_id
|
|
1113
|
-
assert project.root_title == "3.2 Các hệ thống, nền tảng sản phẩm"
|
|
1114
|
-
assert project.root_assignment_reason == "derived_from_registry_root_lineage_head"
|
|
1115
|
-
assert project.department_storage_key == "3-2-4-nen-tang-thanh-toan"
|
|
1116
|
-
assert project.department_assignment_reason == "derived_from_projects_root_lineage"
|
|
1117
|
-
assert [node.page_id for node in project.confluence_ancestry_path] == [root_id, "88716675", "88716694", "88718977"]
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
@pytest.mark.asyncio
|
|
1121
|
-
async def test_parse_registry_skips_container_page_as_atomic_project_leaf(mock_client):
|
|
1122
|
-
parser = RegistryPageParser(mock_client, recursive_depth=1)
|
|
1123
|
-
root_id = "88716675"
|
|
1124
|
-
valid_html = """
|
|
1125
|
-
<table>
|
|
1126
|
-
<tr><th>Link</th></tr>
|
|
1127
|
-
<tr><td><a href='http://bitbucket.digital.vn/projects/PAR'>Repo</a></td></tr>
|
|
1128
|
-
</table>
|
|
1129
|
-
"""
|
|
1130
|
-
|
|
1131
|
-
async def mock_get_page(page_id, expand=None):
|
|
1132
|
-
if page_id == root_id:
|
|
1133
|
-
return {
|
|
1134
|
-
"id": root_id,
|
|
1135
|
-
"title": "3.2 Các hệ thống, nền tảng sản phẩm",
|
|
1136
|
-
"_links": {
|
|
1137
|
-
"base": "http://confluence.digital.vn",
|
|
1138
|
-
"webui": f"/pages/viewpage.action?pageId={root_id}",
|
|
1139
|
-
},
|
|
1140
|
-
}
|
|
1141
|
-
return None
|
|
1142
|
-
|
|
1143
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
1144
|
-
if page_id == root_id:
|
|
1145
|
-
return [{"id": "88716677", "title": "3.2.1 Sản phẩm quản trị", "_links": {}}]
|
|
1146
|
-
if page_id == "88716677":
|
|
1147
|
-
return [
|
|
1148
|
-
{
|
|
1149
|
-
"id": "88727304",
|
|
1150
|
-
"title": "PAR",
|
|
1151
|
-
"body": {"storage": {"value": valid_html}},
|
|
1152
|
-
"_links": {
|
|
1153
|
-
"base": "http://confluence.digital.vn",
|
|
1154
|
-
"webui": "/pages/viewpage.action?pageId=88727304",
|
|
1155
|
-
},
|
|
1156
|
-
}
|
|
1157
|
-
]
|
|
1158
|
-
return []
|
|
1159
|
-
|
|
1160
|
-
mock_client.get_page.side_effect = mock_get_page
|
|
1161
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
1162
|
-
|
|
1163
|
-
registry = await parser.parse_registry(root_id)
|
|
1164
|
-
|
|
1165
|
-
assert [project.page_id for project in registry.projects] == ["88727304"]
|
|
1166
|
-
project = registry.projects[0]
|
|
1167
|
-
assert project.root_page_id == root_id
|
|
1168
|
-
assert project.page_id != "88716677"
|
|
1169
|
-
assert any(node.reason == SkippedNodeReason.PROJECT_CONTAINER for node in registry.skipped_nodes)
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
@pytest.mark.asyncio
|
|
1173
|
-
async def test_parse_registry_project_container_filter_uses_candidate_page_lineage(mock_client):
|
|
1174
|
-
parser = RegistryPageParser(mock_client, recursive_depth=2)
|
|
1175
|
-
root_id = "88716675"
|
|
1176
|
-
valid_html = """
|
|
1177
|
-
<table>
|
|
1178
|
-
<tr><th>Link</th></tr>
|
|
1179
|
-
<tr><td><a href='http://bitbucket.digital.vn/projects/PAR'>Repo</a></td></tr>
|
|
1180
|
-
</table>
|
|
1181
|
-
"""
|
|
1182
|
-
|
|
1183
|
-
async def mock_get_page(page_id, expand=None):
|
|
1184
|
-
payloads = {
|
|
1185
|
-
root_id: {
|
|
1186
|
-
"id": root_id,
|
|
1187
|
-
"title": "3.2 Các hệ thống",
|
|
1188
|
-
"_links": {
|
|
1189
|
-
"base": "http://confluence.digital.vn",
|
|
1190
|
-
"webui": f"/pages/viewpage.action?pageId={root_id}",
|
|
1191
|
-
},
|
|
1192
|
-
},
|
|
1193
|
-
"88716677": {
|
|
1194
|
-
"id": "88716677",
|
|
1195
|
-
"title": "3.2.1 Sản phẩm quản trị",
|
|
1196
|
-
"parentId": root_id,
|
|
1197
|
-
"_links": {
|
|
1198
|
-
"base": "http://confluence.digital.vn",
|
|
1199
|
-
"webui": "/pages/viewpage.action?pageId=88716677",
|
|
1200
|
-
},
|
|
1201
|
-
},
|
|
1202
|
-
"88716679": {
|
|
1203
|
-
"id": "88716679",
|
|
1204
|
-
"title": "3.2.2 Nền tảng ứng dụng",
|
|
1205
|
-
"parentId": root_id,
|
|
1206
|
-
"_links": {
|
|
1207
|
-
"base": "http://confluence.digital.vn",
|
|
1208
|
-
"webui": "/pages/viewpage.action?pageId=88716679",
|
|
1209
|
-
},
|
|
1210
|
-
},
|
|
1211
|
-
"88727304": {
|
|
1212
|
-
"id": "88727304",
|
|
1213
|
-
"title": "PAR",
|
|
1214
|
-
"parentId": "88716679",
|
|
1215
|
-
"body": {"storage": {"value": valid_html}},
|
|
1216
|
-
"_links": {
|
|
1217
|
-
"base": "http://confluence.digital.vn",
|
|
1218
|
-
"webui": "/pages/viewpage.action?pageId=88727304",
|
|
1219
|
-
},
|
|
1220
|
-
},
|
|
1221
|
-
}
|
|
1222
|
-
return payloads.get(page_id)
|
|
1223
|
-
|
|
1224
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
1225
|
-
if page_id == root_id:
|
|
1226
|
-
return [
|
|
1227
|
-
{"id": "88716677", "title": "3.2.1 Sản phẩm quản trị", "_links": {}},
|
|
1228
|
-
{"id": "88716679", "title": "3.2.2 Nền tảng ứng dụng", "_links": {}},
|
|
1229
|
-
]
|
|
1230
|
-
if page_id == "88716677":
|
|
1231
|
-
return []
|
|
1232
|
-
if page_id == "88716679":
|
|
1233
|
-
return [
|
|
1234
|
-
{
|
|
1235
|
-
"id": "88727304",
|
|
1236
|
-
"title": "PAR",
|
|
1237
|
-
"body": {"storage": {"value": valid_html}},
|
|
1238
|
-
"_links": {
|
|
1239
|
-
"base": "http://confluence.digital.vn",
|
|
1240
|
-
"webui": "/pages/viewpage.action?pageId=88727304",
|
|
1241
|
-
},
|
|
1242
|
-
}
|
|
1243
|
-
]
|
|
1244
|
-
return []
|
|
1245
|
-
|
|
1246
|
-
mock_client.get_page.side_effect = mock_get_page
|
|
1247
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
1248
|
-
|
|
1249
|
-
registry = await parser.parse_registry(root_id)
|
|
1250
|
-
|
|
1251
|
-
assert [project.page_id for project in registry.projects] == ["88727304"]
|
|
1252
|
-
skipped_container_ids = {
|
|
1253
|
-
node.page_id for node in registry.skipped_nodes if node.reason == SkippedNodeReason.PROJECT_CONTAINER
|
|
1254
|
-
}
|
|
1255
|
-
assert "88716677" in skipped_container_ids
|
|
1256
|
-
assert "88727304" not in skipped_container_ids
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
@pytest.mark.asyncio
|
|
1260
|
-
async def test_parse_registry_direct_project_page_reconstructs_department_from_parent_chain(mock_client):
|
|
1261
|
-
parser = RegistryPageParser(mock_client, recursive_depth=2)
|
|
1262
|
-
project_id = "88718977"
|
|
1263
|
-
valid_html = """
|
|
1264
|
-
<table>
|
|
1265
|
-
<tr><th>Link</th></tr>
|
|
1266
|
-
<tr><td><a href='http://bitbucket.digital.vn/projects/PAY'>Repo</a></td></tr>
|
|
1267
|
-
</table>
|
|
1268
|
-
"""
|
|
1269
|
-
|
|
1270
|
-
async def mock_get_page(page_id, expand=None):
|
|
1271
|
-
if page_id == project_id:
|
|
1272
|
-
return {
|
|
1273
|
-
"id": project_id,
|
|
1274
|
-
"title": "pay",
|
|
1275
|
-
"parentId": "88716694",
|
|
1276
|
-
"body": {"storage": {"value": valid_html}},
|
|
1277
|
-
"_links": {
|
|
1278
|
-
"base": "http://confluence.digital.vn",
|
|
1279
|
-
"webui": f"/pages/viewpage.action?pageId={project_id}",
|
|
1280
|
-
},
|
|
1281
|
-
}
|
|
1282
|
-
if page_id == "88716694":
|
|
1283
|
-
return {
|
|
1284
|
-
"id": "88716694",
|
|
1285
|
-
"title": "3.2.4 Nền tảng thanh toán",
|
|
1286
|
-
"parentId": "88716675",
|
|
1287
|
-
"_links": {
|
|
1288
|
-
"base": "http://confluence.digital.vn",
|
|
1289
|
-
"webui": "/pages/viewpage.action?pageId=88716694",
|
|
1290
|
-
},
|
|
1291
|
-
}
|
|
1292
|
-
if page_id == "88716675":
|
|
1293
|
-
return {
|
|
1294
|
-
"id": "88716675",
|
|
1295
|
-
"title": "3.2 Các hệ thống",
|
|
1296
|
-
"_links": {
|
|
1297
|
-
"base": "http://confluence.digital.vn",
|
|
1298
|
-
"webui": "/pages/viewpage.action?pageId=88716675",
|
|
1299
|
-
},
|
|
1300
|
-
}
|
|
1301
|
-
return None
|
|
1302
|
-
|
|
1303
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
1304
|
-
if page_id == project_id:
|
|
1305
|
-
return []
|
|
1306
|
-
return []
|
|
1307
|
-
|
|
1308
|
-
mock_client.get_page.side_effect = mock_get_page
|
|
1309
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
1310
|
-
|
|
1311
|
-
registry = await parser.parse_registry(project_id)
|
|
1312
|
-
|
|
1313
|
-
assert len(registry.projects) == 1
|
|
1314
|
-
project = registry.projects[0]
|
|
1315
|
-
assert project.page_id == project_id
|
|
1316
|
-
assert project.department == "3.2.4 Nền tảng thanh toán"
|
|
1317
|
-
assert project.department_page_id == "88716694"
|
|
1318
|
-
assert project.root_page_id == "88716675"
|
|
1319
|
-
assert project.root_title == "3.2 Các hệ thống"
|
|
1320
|
-
assert project.root_assignment_reason == "derived_from_registry_root_lineage_head"
|
|
1321
|
-
assert project.department_storage_key == "3-2-4-nen-tang-thanh-toan"
|
|
1322
|
-
assert project.department_assignment_reason == "derived_from_candidate_root_anchor"
|
|
1323
|
-
assert [node.page_id for node in project.confluence_ancestry_path] == ["88716675", "88716694", project_id]
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
@pytest.mark.asyncio
|
|
1327
|
-
async def test_parse_registry_department_page_reconstructs_parent_root_from_parent_chain(mock_client):
|
|
1328
|
-
parser = RegistryPageParser(mock_client, recursive_depth=2)
|
|
1329
|
-
department_id = "88716694"
|
|
1330
|
-
valid_html = """
|
|
1331
|
-
<table>
|
|
1332
|
-
<tr><th>Link</th></tr>
|
|
1333
|
-
<tr><td><a href='http://bitbucket.digital.vn/projects/PAY'>Repo</a></td></tr>
|
|
1334
|
-
</table>
|
|
1335
|
-
"""
|
|
1336
|
-
|
|
1337
|
-
async def mock_get_page(page_id, expand=None):
|
|
1338
|
-
if page_id == department_id:
|
|
1339
|
-
return {
|
|
1340
|
-
"id": department_id,
|
|
1341
|
-
"title": "3.2.4 Nền tảng thanh toán",
|
|
1342
|
-
"parentId": "88716675",
|
|
1343
|
-
"_links": {
|
|
1344
|
-
"base": "http://confluence.digital.vn",
|
|
1345
|
-
"webui": f"/pages/viewpage.action?pageId={department_id}",
|
|
1346
|
-
},
|
|
1347
|
-
}
|
|
1348
|
-
if page_id == "88716675":
|
|
1349
|
-
return {
|
|
1350
|
-
"id": "88716675",
|
|
1351
|
-
"title": "3.2 Các hệ thống",
|
|
1352
|
-
"_links": {
|
|
1353
|
-
"base": "http://confluence.digital.vn",
|
|
1354
|
-
"webui": "/pages/viewpage.action?pageId=88716675",
|
|
1355
|
-
},
|
|
1356
|
-
}
|
|
1357
|
-
return None
|
|
1358
|
-
|
|
1359
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
1360
|
-
if page_id == department_id:
|
|
1361
|
-
return [
|
|
1362
|
-
{
|
|
1363
|
-
"id": "88718977",
|
|
1364
|
-
"title": "Auto Payment",
|
|
1365
|
-
"body": {"storage": {"value": valid_html}},
|
|
1366
|
-
"_links": {
|
|
1367
|
-
"base": "http://confluence.digital.vn",
|
|
1368
|
-
"webui": "/pages/viewpage.action?pageId=88718977",
|
|
1369
|
-
},
|
|
1370
|
-
}
|
|
1371
|
-
]
|
|
1372
|
-
return []
|
|
1373
|
-
|
|
1374
|
-
mock_client.get_page.side_effect = mock_get_page
|
|
1375
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
1376
|
-
|
|
1377
|
-
registry = await parser.parse_registry(department_id)
|
|
1378
|
-
|
|
1379
|
-
assert len(registry.projects) == 1
|
|
1380
|
-
project = registry.projects[0]
|
|
1381
|
-
assert project.page_id == "88718977"
|
|
1382
|
-
assert project.department == "3.2.4 Nền tảng thanh toán"
|
|
1383
|
-
assert project.department_page_id == department_id
|
|
1384
|
-
assert project.root_page_id == "88716675"
|
|
1385
|
-
assert project.root_title == "3.2 Các hệ thống"
|
|
1386
|
-
assert project.root_assignment_reason == "derived_from_registry_root_lineage_head"
|
|
1387
|
-
assert [node.page_id for node in project.confluence_ancestry_path] == ["88716675", department_id, "88718977"]
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
@pytest.mark.asyncio
|
|
1391
|
-
async def test_parse_registry_department_page_reconstructs_parent_root_from_ancestors_expand(mock_client):
|
|
1392
|
-
parser = RegistryPageParser(mock_client, recursive_depth=2)
|
|
1393
|
-
department_id = "88716694"
|
|
1394
|
-
valid_html = """
|
|
1395
|
-
<table>
|
|
1396
|
-
<tr><th>Link</th></tr>
|
|
1397
|
-
<tr><td><a href='http://bitbucket.digital.vn/projects/PAY'>Repo</a></td></tr>
|
|
1398
|
-
</table>
|
|
1399
|
-
"""
|
|
1400
|
-
|
|
1401
|
-
async def mock_get_page(page_id, expand=None):
|
|
1402
|
-
if page_id == department_id and expand == "ancestors":
|
|
1403
|
-
return {
|
|
1404
|
-
"id": department_id,
|
|
1405
|
-
"title": "3.2.4 Nền tảng thanh toán",
|
|
1406
|
-
"ancestors": [
|
|
1407
|
-
{
|
|
1408
|
-
"id": "88716667",
|
|
1409
|
-
"title": "3. TTCN - Đóng gói nền tảng Sản phẩm",
|
|
1410
|
-
"_links": {
|
|
1411
|
-
"webui": "/pages/viewpage.action?pageId=88716667",
|
|
1412
|
-
"base": "http://confluence.digital.vn",
|
|
1413
|
-
},
|
|
1414
|
-
},
|
|
1415
|
-
{
|
|
1416
|
-
"id": "88716675",
|
|
1417
|
-
"title": "3.2 Các hệ thống",
|
|
1418
|
-
"_links": {
|
|
1419
|
-
"webui": "/pages/viewpage.action?pageId=88716675",
|
|
1420
|
-
"base": "http://confluence.digital.vn",
|
|
1421
|
-
},
|
|
1422
|
-
},
|
|
1423
|
-
],
|
|
1424
|
-
"_links": {
|
|
1425
|
-
"base": "http://confluence.digital.vn",
|
|
1426
|
-
"webui": f"/pages/viewpage.action?pageId={department_id}",
|
|
1427
|
-
},
|
|
1428
|
-
}
|
|
1429
|
-
if page_id == department_id:
|
|
1430
|
-
return {
|
|
1431
|
-
"id": department_id,
|
|
1432
|
-
"title": "3.2.4 Nền tảng thanh toán",
|
|
1433
|
-
"_links": {
|
|
1434
|
-
"base": "http://confluence.digital.vn",
|
|
1435
|
-
"webui": f"/pages/viewpage.action?pageId={department_id}",
|
|
1436
|
-
},
|
|
1437
|
-
}
|
|
1438
|
-
return None
|
|
1439
|
-
|
|
1440
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
1441
|
-
if page_id == department_id:
|
|
1442
|
-
return [
|
|
1443
|
-
{
|
|
1444
|
-
"id": "88718977",
|
|
1445
|
-
"title": "Auto Payment",
|
|
1446
|
-
"body": {"storage": {"value": valid_html}},
|
|
1447
|
-
"_links": {
|
|
1448
|
-
"base": "http://confluence.digital.vn",
|
|
1449
|
-
"webui": "/pages/viewpage.action?pageId=88718977",
|
|
1450
|
-
},
|
|
1451
|
-
}
|
|
1452
|
-
]
|
|
1453
|
-
return []
|
|
1454
|
-
|
|
1455
|
-
mock_client.get_page.side_effect = mock_get_page
|
|
1456
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
1457
|
-
|
|
1458
|
-
registry = await parser.parse_registry(department_id)
|
|
1459
|
-
|
|
1460
|
-
assert len(registry.projects) == 1
|
|
1461
|
-
project = registry.projects[0]
|
|
1462
|
-
assert project.department_page_id == department_id
|
|
1463
|
-
assert project.root_page_id == "88716675"
|
|
1464
|
-
assert project.root_title == "3.2 Các hệ thống"
|
|
1465
|
-
assert [node.page_id for node in project.confluence_ancestry_path] == ["88716675", department_id, "88718977"]
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
@pytest.mark.asyncio
|
|
1469
|
-
async def test_parse_registry_with_checklist_override(mock_client):
|
|
1470
|
-
"""Explicit checklist override should take precedence."""
|
|
1471
|
-
parser = RegistryPageParser(
|
|
1472
|
-
mock_client,
|
|
1473
|
-
checklist_page="99999",
|
|
1474
|
-
)
|
|
1475
|
-
root_id = "88716667"
|
|
1476
|
-
|
|
1477
|
-
mock_client.get_child_pages.return_value = [
|
|
1478
|
-
{
|
|
1479
|
-
"id": "88716673",
|
|
1480
|
-
"title": "3.1 Checklist đánh giá",
|
|
1481
|
-
"_links": {
|
|
1482
|
-
"base": "http://confluence.digital.vn",
|
|
1483
|
-
"webui": "/pages/viewpage.action?pageId=88716673",
|
|
1484
|
-
},
|
|
1485
|
-
},
|
|
1486
|
-
{
|
|
1487
|
-
"id": "88716675",
|
|
1488
|
-
"title": "3.2 Các hệ thống",
|
|
1489
|
-
"_links": {},
|
|
1490
|
-
},
|
|
1491
|
-
]
|
|
1492
|
-
|
|
1493
|
-
registry = await parser.parse_registry(root_id)
|
|
1494
|
-
|
|
1495
|
-
# Override should take precedence over discovered checklist.
|
|
1496
|
-
assert registry.checklist_page_id == "99999"
|
|
1497
|
-
assert registry.checklist_title == "Explicit override"
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
@pytest.mark.asyncio
|
|
1501
|
-
async def test_find_projects_root_and_checklist_deterministic(parser_checklist_first, mock_client):
|
|
1502
|
-
"""Deterministic discovery should classify root children correctly."""
|
|
1503
|
-
root_id = "88716667"
|
|
1504
|
-
|
|
1505
|
-
mock_client.get_child_pages.return_value = [
|
|
1506
|
-
{
|
|
1507
|
-
"id": "11111",
|
|
1508
|
-
"title": "Audit History",
|
|
1509
|
-
"_links": {},
|
|
1510
|
-
},
|
|
1511
|
-
{
|
|
1512
|
-
"id": "88716673",
|
|
1513
|
-
"title": "3.1 Checklist đánh giá",
|
|
1514
|
-
"_links": {
|
|
1515
|
-
"self": "http://confluence.digital.vn/rest/api/content/88716673",
|
|
1516
|
-
"webui": "/pages/viewpage.action?pageId=88716673",
|
|
1517
|
-
},
|
|
1518
|
-
},
|
|
1519
|
-
{
|
|
1520
|
-
"id": "88716675",
|
|
1521
|
-
"title": "3.2 Các hệ thống",
|
|
1522
|
-
"_links": {
|
|
1523
|
-
"self": "http://confluence.digital.vn/rest/api/content/88716675",
|
|
1524
|
-
"webui": "/pages/viewpage.action?pageId=88716675",
|
|
1525
|
-
},
|
|
1526
|
-
},
|
|
1527
|
-
{
|
|
1528
|
-
"id": "22222",
|
|
1529
|
-
"title": "Random Documentation",
|
|
1530
|
-
"_links": {},
|
|
1531
|
-
},
|
|
1532
|
-
]
|
|
1533
|
-
|
|
1534
|
-
(
|
|
1535
|
-
projects_root_id,
|
|
1536
|
-
checklist_candidates,
|
|
1537
|
-
) = await parser_checklist_first._find_projects_root_and_checklist_deterministic(root_id)
|
|
1538
|
-
|
|
1539
|
-
assert projects_root_id == "88716675"
|
|
1540
|
-
assert checklist_candidates
|
|
1541
|
-
assert checklist_candidates[0]["id"] == "88716673"
|
|
1542
|
-
assert checklist_candidates[0]["title"] == "3.1 Checklist đánh giá"
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
@pytest.mark.asyncio
|
|
1546
|
-
async def test_deterministic_discovery_without_checklist(parser_checklist_first, mock_client):
|
|
1547
|
-
"""Deterministic discovery should handle missing checklist gracefully."""
|
|
1548
|
-
root_id = "88716667"
|
|
1549
|
-
|
|
1550
|
-
mock_client.get_child_pages.return_value = [
|
|
1551
|
-
{
|
|
1552
|
-
"id": "88716675",
|
|
1553
|
-
"title": "3.2 Các hệ thống",
|
|
1554
|
-
"_links": {},
|
|
1555
|
-
},
|
|
1556
|
-
]
|
|
1557
|
-
|
|
1558
|
-
(
|
|
1559
|
-
projects_root_id,
|
|
1560
|
-
checklist_candidates,
|
|
1561
|
-
) = await parser_checklist_first._find_projects_root_and_checklist_deterministic(root_id)
|
|
1562
|
-
|
|
1563
|
-
assert projects_root_id == "88716675"
|
|
1564
|
-
assert checklist_candidates == []
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
@pytest.mark.asyncio
|
|
1568
|
-
async def test_deterministic_discovery_without_projects_root(parser_checklist_first, mock_client):
|
|
1569
|
-
"""Deterministic discovery should handle missing projects root gracefully."""
|
|
1570
|
-
root_id = "88716667"
|
|
1571
|
-
|
|
1572
|
-
mock_client.get_child_pages.return_value = [
|
|
1573
|
-
{
|
|
1574
|
-
"id": "88716673",
|
|
1575
|
-
"title": "3.1 Checklist đánh giá",
|
|
1576
|
-
"_links": {
|
|
1577
|
-
"base": "http://confluence.digital.vn",
|
|
1578
|
-
"webui": "/pages/viewpage.action?pageId=88716673",
|
|
1579
|
-
},
|
|
1580
|
-
},
|
|
1581
|
-
]
|
|
1582
|
-
|
|
1583
|
-
(
|
|
1584
|
-
projects_root_id,
|
|
1585
|
-
checklist_candidates,
|
|
1586
|
-
) = await parser_checklist_first._find_projects_root_and_checklist_deterministic(root_id)
|
|
1587
|
-
|
|
1588
|
-
assert projects_root_id is None
|
|
1589
|
-
assert checklist_candidates
|
|
1590
|
-
assert checklist_candidates[0]["id"] == "88716673"
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
# Phase 24 TSK-242: Project link extraction hardening tests
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
class TestProjectCandidateFiltering:
|
|
1597
|
-
"""Tests for enhanced project candidate filtering (TSK-242.3.1)."""
|
|
1598
|
-
|
|
1599
|
-
def test_filter_audit_history_exact(self):
|
|
1600
|
-
"""Audit history pages should be filtered."""
|
|
1601
|
-
is_candidate, reason = RegistryPageParser._is_project_candidate_title("Audit History")
|
|
1602
|
-
assert not is_candidate
|
|
1603
|
-
assert reason == SkippedNodeReason.AUDIT_HISTORY
|
|
1604
|
-
|
|
1605
|
-
def test_filter_audit_history_vietnamese(self):
|
|
1606
|
-
"""Vietnamese audit history pages should be filtered."""
|
|
1607
|
-
is_candidate, reason = RegistryPageParser._is_project_candidate_title("Lịch sử audit")
|
|
1608
|
-
assert not is_candidate
|
|
1609
|
-
assert reason == SkippedNodeReason.AUDIT_HISTORY
|
|
1610
|
-
|
|
1611
|
-
is_candidate, reason = RegistryPageParser._is_project_candidate_title("Lịch sử đánh giá")
|
|
1612
|
-
assert not is_candidate
|
|
1613
|
-
assert reason == SkippedNodeReason.AUDIT_HISTORY
|
|
1614
|
-
|
|
1615
|
-
def test_filter_checklist_pages(self):
|
|
1616
|
-
"""Checklist pages should be filtered."""
|
|
1617
|
-
is_candidate, reason = RegistryPageParser._is_project_candidate_title("Checklist đánh giá")
|
|
1618
|
-
assert not is_candidate
|
|
1619
|
-
assert reason == SkippedNodeReason.CHECKLIST_NODE
|
|
1620
|
-
|
|
1621
|
-
is_candidate, reason = RegistryPageParser._is_project_candidate_title("Template audit")
|
|
1622
|
-
assert not is_candidate
|
|
1623
|
-
assert reason == SkippedNodeReason.CHECKLIST_NODE
|
|
1624
|
-
|
|
1625
|
-
def test_filter_empty_title(self):
|
|
1626
|
-
"""Empty titles should be filtered."""
|
|
1627
|
-
is_candidate, reason = RegistryPageParser._is_project_candidate_title("")
|
|
1628
|
-
assert not is_candidate
|
|
1629
|
-
assert reason == SkippedNodeReason.EMPTY_TITLE
|
|
1630
|
-
|
|
1631
|
-
is_candidate, reason = RegistryPageParser._is_project_candidate_title(" ")
|
|
1632
|
-
assert not is_candidate
|
|
1633
|
-
assert reason == SkippedNodeReason.EMPTY_TITLE
|
|
1634
|
-
|
|
1635
|
-
def test_valid_project_titles(self):
|
|
1636
|
-
"""Valid project titles should pass filtering."""
|
|
1637
|
-
is_candidate, reason = RegistryPageParser._is_project_candidate_title("Insurance Core Service")
|
|
1638
|
-
assert is_candidate
|
|
1639
|
-
assert reason is None
|
|
1640
|
-
|
|
1641
|
-
is_candidate, reason = RegistryPageParser._is_project_candidate_title("Merchant Profile Service")
|
|
1642
|
-
assert is_candidate
|
|
1643
|
-
assert reason is None
|
|
1644
|
-
|
|
1645
|
-
is_candidate, reason = RegistryPageParser._is_project_candidate_title("Dịch vụ thanh toán")
|
|
1646
|
-
assert is_candidate
|
|
1647
|
-
assert reason is None
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
class TestUrlNormalization:
|
|
1651
|
-
"""Tests for URL normalization (TSK-242.3.2)."""
|
|
1652
|
-
|
|
1653
|
-
def test_normalize_trailing_slashes(self):
|
|
1654
|
-
"""Trailing slashes should be removed."""
|
|
1655
|
-
assert RegistryPageParser.normalize_url("http://example.com/path/") == "http://example.com/path"
|
|
1656
|
-
assert RegistryPageParser.normalize_url("http://example.com/path///") == "http://example.com/path"
|
|
1657
|
-
|
|
1658
|
-
def test_normalize_confluence_pageid_url(self):
|
|
1659
|
-
"""Confluence pageId URLs should be normalized."""
|
|
1660
|
-
url = "http://confluence.digital.vn/pages/viewpage.action?pageId=12345&extra=param"
|
|
1661
|
-
normalized = RegistryPageParser.normalize_url(url)
|
|
1662
|
-
assert normalized == "http://confluence.digital.vn/pages/viewpage.action?pageid=12345"
|
|
1663
|
-
|
|
1664
|
-
def test_normalize_confluence_display_url(self):
|
|
1665
|
-
"""Confluence display URLs should be normalized."""
|
|
1666
|
-
url = "http://confluence.digital.vn/display/SPACE/Page+Title?param=value"
|
|
1667
|
-
normalized = RegistryPageParser.normalize_url(url)
|
|
1668
|
-
assert normalized == "http://confluence.digital.vn/display/space/page+title"
|
|
1669
|
-
|
|
1670
|
-
def test_normalize_bitbucket_url(self):
|
|
1671
|
-
"""Bitbucket URLs should be normalized."""
|
|
1672
|
-
url = "http://bitbucket.digital.vn/projects/INS/repos/insurance-core/browse"
|
|
1673
|
-
normalized = RegistryPageParser.normalize_url(url)
|
|
1674
|
-
assert normalized == "http://bitbucket.digital.vn/projects/ins/repos/insurance-core"
|
|
1675
|
-
|
|
1676
|
-
def test_normalize_empty_url(self):
|
|
1677
|
-
"""Empty URLs should return empty string."""
|
|
1678
|
-
assert RegistryPageParser.normalize_url("") == ""
|
|
1679
|
-
assert RegistryPageParser.normalize_url(" ") == ""
|
|
1680
|
-
|
|
1681
|
-
def test_normalize_case_insensitive(self):
|
|
1682
|
-
"""URLs should be normalized to lowercase."""
|
|
1683
|
-
url = "HTTP://EXAMPLE.COM/PATH"
|
|
1684
|
-
normalized = RegistryPageParser.normalize_url(url)
|
|
1685
|
-
assert normalized == "http://example.com/path"
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
class TestExtractConfluenceTinyId:
|
|
1689
|
-
"""Tests for the _extract_confluence_tiny_id static method."""
|
|
1690
|
-
|
|
1691
|
-
def test_extracts_tiny_id_from_absolute_short_url(self):
|
|
1692
|
-
result = RegistryPageParser._extract_confluence_tiny_id("http://confluence.digital.vn/x/J2Q1")
|
|
1693
|
-
assert result == "J2Q1"
|
|
1694
|
-
|
|
1695
|
-
def test_extracts_tiny_id_from_relative_short_url(self):
|
|
1696
|
-
result = RegistryPageParser._extract_confluence_tiny_id("/x/J2Q1")
|
|
1697
|
-
assert result == "J2Q1"
|
|
1698
|
-
|
|
1699
|
-
def test_returns_none_for_pageid_url(self):
|
|
1700
|
-
result = RegistryPageParser._extract_confluence_tiny_id(
|
|
1701
|
-
"http://confluence.digital.vn/pages/viewpage.action?pageId=12345"
|
|
1702
|
-
)
|
|
1703
|
-
assert result is None
|
|
1704
|
-
|
|
1705
|
-
def test_returns_none_for_display_url(self):
|
|
1706
|
-
result = RegistryPageParser._extract_confluence_tiny_id(
|
|
1707
|
-
"http://confluence.digital.vn/display/TTCN24/Core+Transfer"
|
|
1708
|
-
)
|
|
1709
|
-
assert result is None
|
|
1710
|
-
|
|
1711
|
-
def test_returns_none_for_empty_string(self):
|
|
1712
|
-
assert RegistryPageParser._extract_confluence_tiny_id("") is None
|
|
1713
|
-
|
|
1714
|
-
def test_returns_none_for_non_confluence_url(self):
|
|
1715
|
-
assert RegistryPageParser._extract_confluence_tiny_id("http://example.com/x/abc") == "abc"
|
|
1716
|
-
|
|
1717
|
-
def test_handles_alphanumeric_and_hyphen_and_underscore(self):
|
|
1718
|
-
for tiny_id in ("abc123", "Ab-Cd_Ef", "ZZZZ", "a1-b2_c3"):
|
|
1719
|
-
result = RegistryPageParser._extract_confluence_tiny_id(f"http://confluence.digital.vn/x/{tiny_id}")
|
|
1720
|
-
assert result == tiny_id
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
class TestLinkDeduplication:
|
|
1724
|
-
"""Tests for link deduplication (TSK-242.3.2)."""
|
|
1725
|
-
|
|
1726
|
-
def test_deduplicate_exact_duplicates(self, parser):
|
|
1727
|
-
"""Exact duplicate links should be removed."""
|
|
1728
|
-
from vds_audit_orchestrator.models.registry import DocumentLink, DocumentRole, LinkSource
|
|
1729
|
-
|
|
1730
|
-
links = [
|
|
1731
|
-
DocumentLink(
|
|
1732
|
-
role=DocumentRole.PO,
|
|
1733
|
-
doc_type="BRD",
|
|
1734
|
-
url="http://confluence.digital.vn/pages/viewpage.action?pageId=12345",
|
|
1735
|
-
source=LinkSource.CONFLUENCE,
|
|
1736
|
-
),
|
|
1737
|
-
DocumentLink(
|
|
1738
|
-
role=DocumentRole.BA,
|
|
1739
|
-
doc_type="SRS",
|
|
1740
|
-
url="http://confluence.digital.vn/pages/viewpage.action?pageId=12345",
|
|
1741
|
-
source=LinkSource.CONFLUENCE,
|
|
1742
|
-
),
|
|
1743
|
-
]
|
|
1744
|
-
|
|
1745
|
-
deduplicated = parser.deduplicate_links(links)
|
|
1746
|
-
assert len(deduplicated) == 1
|
|
1747
|
-
assert deduplicated[0].role == DocumentRole.PO # First occurrence kept
|
|
1748
|
-
|
|
1749
|
-
def test_deduplicate_normalized_duplicates(self, parser):
|
|
1750
|
-
"""Links that normalize to the same URL should be deduplicated."""
|
|
1751
|
-
from vds_audit_orchestrator.models.registry import DocumentLink, DocumentRole, LinkSource
|
|
1752
|
-
|
|
1753
|
-
links = [
|
|
1754
|
-
DocumentLink(
|
|
1755
|
-
role=DocumentRole.PO,
|
|
1756
|
-
doc_type="BRD",
|
|
1757
|
-
url="http://confluence.digital.vn/pages/viewpage.action?pageId=12345",
|
|
1758
|
-
source=LinkSource.CONFLUENCE,
|
|
1759
|
-
),
|
|
1760
|
-
DocumentLink(
|
|
1761
|
-
role=DocumentRole.BA,
|
|
1762
|
-
doc_type="SRS",
|
|
1763
|
-
url="http://confluence.digital.vn/pages/viewpage.action?pageId=12345&extra=param",
|
|
1764
|
-
source=LinkSource.CONFLUENCE,
|
|
1765
|
-
),
|
|
1766
|
-
]
|
|
1767
|
-
|
|
1768
|
-
deduplicated = parser.deduplicate_links(links)
|
|
1769
|
-
assert len(deduplicated) == 1
|
|
1770
|
-
|
|
1771
|
-
def test_deduplicate_preserves_unique_links(self, parser):
|
|
1772
|
-
"""Unique links should be preserved."""
|
|
1773
|
-
from vds_audit_orchestrator.models.registry import DocumentLink, DocumentRole, LinkSource
|
|
1774
|
-
|
|
1775
|
-
links = [
|
|
1776
|
-
DocumentLink(
|
|
1777
|
-
role=DocumentRole.PO,
|
|
1778
|
-
doc_type="BRD",
|
|
1779
|
-
url="http://confluence.digital.vn/pages/viewpage.action?pageId=12345",
|
|
1780
|
-
source=LinkSource.CONFLUENCE,
|
|
1781
|
-
),
|
|
1782
|
-
DocumentLink(
|
|
1783
|
-
role=DocumentRole.DEV,
|
|
1784
|
-
doc_type="Source Code",
|
|
1785
|
-
url="http://bitbucket.digital.vn/projects/INS/repos/core",
|
|
1786
|
-
source=LinkSource.BITBUCKET,
|
|
1787
|
-
),
|
|
1788
|
-
]
|
|
1789
|
-
|
|
1790
|
-
deduplicated = parser.deduplicate_links(links)
|
|
1791
|
-
assert len(deduplicated) == 2
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
class TestSkippedNodeTelemetry:
|
|
1795
|
-
"""Tests for skipped-node telemetry (TSK-242.3.3)."""
|
|
1796
|
-
|
|
1797
|
-
@pytest.mark.asyncio
|
|
1798
|
-
async def test_skipped_nodes_tracked_for_audit_history(self, parser, mock_client):
|
|
1799
|
-
"""Audit history pages should be tracked in skipped_nodes."""
|
|
1800
|
-
root_id = "88716667"
|
|
1801
|
-
|
|
1802
|
-
# Mock get_child_pages to return different results for different calls
|
|
1803
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
1804
|
-
if page_id == root_id:
|
|
1805
|
-
return [
|
|
1806
|
-
{
|
|
1807
|
-
"id": "88716675",
|
|
1808
|
-
"title": "3.2 Các hệ thống",
|
|
1809
|
-
"_links": {},
|
|
1810
|
-
},
|
|
1811
|
-
]
|
|
1812
|
-
elif page_id == "88716675":
|
|
1813
|
-
return [
|
|
1814
|
-
{
|
|
1815
|
-
"id": "11111",
|
|
1816
|
-
"title": "Audit History",
|
|
1817
|
-
"body": {"storage": {"value": "<table></table>"}},
|
|
1818
|
-
"_links": {},
|
|
1819
|
-
},
|
|
1820
|
-
{
|
|
1821
|
-
"id": "22222",
|
|
1822
|
-
"title": "Valid Project",
|
|
1823
|
-
"body": {
|
|
1824
|
-
"storage": {
|
|
1825
|
-
"value": "<table><tr><th>Link</th></tr><tr><td><a href='http://example.com'>Link</a></td></tr></table>"
|
|
1826
|
-
}
|
|
1827
|
-
},
|
|
1828
|
-
"_links": {
|
|
1829
|
-
"base": "http://confluence.digital.vn",
|
|
1830
|
-
"webui": "/pages/viewpage.action?pageId=22222",
|
|
1831
|
-
},
|
|
1832
|
-
},
|
|
1833
|
-
]
|
|
1834
|
-
return []
|
|
1835
|
-
|
|
1836
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
1837
|
-
|
|
1838
|
-
registry = await parser.parse_registry(root_id)
|
|
1839
|
-
|
|
1840
|
-
# Should have skipped the audit history page
|
|
1841
|
-
audit_history_skipped = [n for n in registry.skipped_nodes if n.reason == SkippedNodeReason.AUDIT_HISTORY]
|
|
1842
|
-
assert len(audit_history_skipped) == 1
|
|
1843
|
-
assert audit_history_skipped[0].page_id == "11111"
|
|
1844
|
-
assert audit_history_skipped[0].title == "Audit History"
|
|
1845
|
-
|
|
1846
|
-
@pytest.mark.asyncio
|
|
1847
|
-
async def test_skipped_nodes_tracked_for_checklist(self, parser, mock_client):
|
|
1848
|
-
"""Checklist pages should be tracked in skipped_nodes."""
|
|
1849
|
-
root_id = "88716667"
|
|
1850
|
-
|
|
1851
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
1852
|
-
if page_id == root_id:
|
|
1853
|
-
return [
|
|
1854
|
-
{
|
|
1855
|
-
"id": "88716675",
|
|
1856
|
-
"title": "3.2 Các hệ thống",
|
|
1857
|
-
"_links": {},
|
|
1858
|
-
},
|
|
1859
|
-
]
|
|
1860
|
-
elif page_id == "88716675":
|
|
1861
|
-
return [
|
|
1862
|
-
{
|
|
1863
|
-
"id": "33333",
|
|
1864
|
-
"title": "Checklist Template",
|
|
1865
|
-
"body": {"storage": {"value": "<table></table>"}},
|
|
1866
|
-
"_links": {},
|
|
1867
|
-
},
|
|
1868
|
-
{
|
|
1869
|
-
"id": "44444",
|
|
1870
|
-
"title": "Valid Project",
|
|
1871
|
-
"body": {
|
|
1872
|
-
"storage": {
|
|
1873
|
-
"value": "<table><tr><th>Link</th></tr><tr><td><a href='http://example.com'>Link</a></td></tr></table>"
|
|
1874
|
-
}
|
|
1875
|
-
},
|
|
1876
|
-
"_links": {
|
|
1877
|
-
"base": "http://confluence.digital.vn",
|
|
1878
|
-
"webui": "/pages/viewpage.action?pageId=44444",
|
|
1879
|
-
},
|
|
1880
|
-
},
|
|
1881
|
-
]
|
|
1882
|
-
return []
|
|
1883
|
-
|
|
1884
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
1885
|
-
|
|
1886
|
-
registry = await parser.parse_registry(root_id)
|
|
1887
|
-
|
|
1888
|
-
checklist_skipped = [n for n in registry.skipped_nodes if n.reason == SkippedNodeReason.CHECKLIST_NODE]
|
|
1889
|
-
assert len(checklist_skipped) >= 1
|
|
1890
|
-
assert any(n.page_id == "33333" for n in checklist_skipped)
|
|
1891
|
-
|
|
1892
|
-
@pytest.mark.asyncio
|
|
1893
|
-
async def test_skipped_nodes_tracked_for_no_links(self, parser, mock_client):
|
|
1894
|
-
"""Pages with no links should be tracked in skipped_nodes."""
|
|
1895
|
-
root_id = "88716667"
|
|
1896
|
-
|
|
1897
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
1898
|
-
if page_id == root_id:
|
|
1899
|
-
return [
|
|
1900
|
-
{
|
|
1901
|
-
"id": "88716675",
|
|
1902
|
-
"title": "3.2 Các hệ thống",
|
|
1903
|
-
"_links": {},
|
|
1904
|
-
},
|
|
1905
|
-
]
|
|
1906
|
-
elif page_id == "88716675":
|
|
1907
|
-
return [
|
|
1908
|
-
{
|
|
1909
|
-
"id": "44444",
|
|
1910
|
-
"title": "Empty Project",
|
|
1911
|
-
"body": {"storage": {"value": "<p>No tables here</p>"}},
|
|
1912
|
-
"_links": {
|
|
1913
|
-
"base": "http://confluence.digital.vn",
|
|
1914
|
-
"webui": "/pages/viewpage.action?pageId=44444",
|
|
1915
|
-
},
|
|
1916
|
-
},
|
|
1917
|
-
{
|
|
1918
|
-
"id": "55555",
|
|
1919
|
-
"title": "Valid Project",
|
|
1920
|
-
"body": {
|
|
1921
|
-
"storage": {
|
|
1922
|
-
"value": "<table><tr><th>Link</th></tr><tr><td><a href='http://example.com'>Link</a></td></tr></table>"
|
|
1923
|
-
}
|
|
1924
|
-
},
|
|
1925
|
-
"_links": {
|
|
1926
|
-
"base": "http://confluence.digital.vn",
|
|
1927
|
-
"webui": "/pages/viewpage.action?pageId=55555",
|
|
1928
|
-
},
|
|
1929
|
-
},
|
|
1930
|
-
]
|
|
1931
|
-
return []
|
|
1932
|
-
|
|
1933
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
1934
|
-
|
|
1935
|
-
registry = await parser.parse_registry(root_id)
|
|
1936
|
-
|
|
1937
|
-
no_links_skipped = [n for n in registry.skipped_nodes if n.reason == SkippedNodeReason.NO_LINKS]
|
|
1938
|
-
assert len(no_links_skipped) >= 1
|
|
1939
|
-
assert any(n.page_id == "44444" for n in no_links_skipped)
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
class TestMixedVnEnTableHeaders:
|
|
1943
|
-
"""Tests for mixed Vietnamese/English table headers (TSK-242.3.4)."""
|
|
1944
|
-
|
|
1945
|
-
def test_extract_links_mixed_vn_en_headers(self, parser):
|
|
1946
|
-
"""Should handle tables with mixed VN/EN headers."""
|
|
1947
|
-
html = """
|
|
1948
|
-
<table>
|
|
1949
|
-
<tr>
|
|
1950
|
-
<th>Vai trò</th>
|
|
1951
|
-
<th>Loại tài liệu</th>
|
|
1952
|
-
<th>Doc Type EN</th>
|
|
1953
|
-
<th>Link tài liệu</th>
|
|
1954
|
-
</tr>
|
|
1955
|
-
<tr>
|
|
1956
|
-
<td>PO</td>
|
|
1957
|
-
<td>1. Tài liệu BRD</td>
|
|
1958
|
-
<td>BRD Document</td>
|
|
1959
|
-
<td><a href="/display/SPACE/BRD">BRD Link</a></td>
|
|
1960
|
-
</tr>
|
|
1961
|
-
</table>
|
|
1962
|
-
"""
|
|
1963
|
-
base_url = "http://confluence.digital.vn"
|
|
1964
|
-
|
|
1965
|
-
links = parser._extract_links_from_html(html, base_url)
|
|
1966
|
-
|
|
1967
|
-
assert len(links) == 1
|
|
1968
|
-
assert links[0].role == DocumentRole.PO
|
|
1969
|
-
assert links[0].doc_type == "Tài liệu BRD"
|
|
1970
|
-
assert links[0].doc_type_en == "BRD Document"
|
|
1971
|
-
|
|
1972
|
-
def test_extract_links_english_only_headers(self, parser):
|
|
1973
|
-
"""Should handle tables with English-only headers."""
|
|
1974
|
-
html = """
|
|
1975
|
-
<table>
|
|
1976
|
-
<tr>
|
|
1977
|
-
<th>Role</th>
|
|
1978
|
-
<th>Document Type</th>
|
|
1979
|
-
<th>Link</th>
|
|
1980
|
-
</tr>
|
|
1981
|
-
<tr>
|
|
1982
|
-
<td>Developer</td>
|
|
1983
|
-
<td>Source Code</td>
|
|
1984
|
-
<td><a href="http://bitbucket.digital.vn/projects/INS">Repo</a></td>
|
|
1985
|
-
</tr>
|
|
1986
|
-
</table>
|
|
1987
|
-
"""
|
|
1988
|
-
base_url = "http://confluence.digital.vn"
|
|
1989
|
-
|
|
1990
|
-
links = parser._extract_links_from_html(html, base_url)
|
|
1991
|
-
|
|
1992
|
-
assert len(links) == 1
|
|
1993
|
-
assert links[0].role == DocumentRole.DEV
|
|
1994
|
-
assert links[0].source == LinkSource.BITBUCKET
|
|
1995
|
-
|
|
1996
|
-
def test_extract_links_vietnamese_only_headers(self, parser):
|
|
1997
|
-
"""Should handle tables with Vietnamese-only headers."""
|
|
1998
|
-
html = """
|
|
1999
|
-
<table>
|
|
2000
|
-
<tr>
|
|
2001
|
-
<th>Phụ trách</th>
|
|
2002
|
-
<th>Mô tả</th>
|
|
2003
|
-
<th>Link</th>
|
|
2004
|
-
</tr>
|
|
2005
|
-
<tr>
|
|
2006
|
-
<td>BA</td>
|
|
2007
|
-
<td>Tài liệu phân tích</td>
|
|
2008
|
-
<td><a href="/pages/viewpage.action?pageId=12345">Link</a></td>
|
|
2009
|
-
</tr>
|
|
2010
|
-
</table>
|
|
2011
|
-
"""
|
|
2012
|
-
base_url = "http://confluence.digital.vn"
|
|
2013
|
-
|
|
2014
|
-
links = parser._extract_links_from_html(html, base_url)
|
|
2015
|
-
|
|
2016
|
-
assert len(links) == 1
|
|
2017
|
-
assert links[0].role == DocumentRole.BA
|
|
2018
|
-
assert links[0].source == LinkSource.CONFLUENCE
|
|
2019
|
-
assert links[0].page_id == "12345"
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
class TestLinkColumnFallback:
|
|
2023
|
-
"""Tests for link-column fallback behavior (TSK-242.3.4)."""
|
|
2024
|
-
|
|
2025
|
-
def test_fallback_to_all_cells_when_no_link_header(self, parser):
|
|
2026
|
-
"""Should search all cells for links when no 'Link' header is present."""
|
|
2027
|
-
html = """
|
|
2028
|
-
<table>
|
|
2029
|
-
<tr>
|
|
2030
|
-
<th>Vai trò</th>
|
|
2031
|
-
<th>Tài liệu</th>
|
|
2032
|
-
</tr>
|
|
2033
|
-
<tr>
|
|
2034
|
-
<td>PO</td>
|
|
2035
|
-
<td><a href="/display/SPACE/Doc">Document</a></td>
|
|
2036
|
-
</tr>
|
|
2037
|
-
</table>
|
|
2038
|
-
"""
|
|
2039
|
-
base_url = "http://confluence.digital.vn"
|
|
2040
|
-
|
|
2041
|
-
links = parser._extract_links_from_html(html, base_url)
|
|
2042
|
-
|
|
2043
|
-
assert len(links) == 1
|
|
2044
|
-
assert links[0].url == "http://confluence.digital.vn/display/SPACE/Doc"
|
|
2045
|
-
|
|
2046
|
-
def test_link_column_takes_priority(self, parser):
|
|
2047
|
-
"""When 'Link' header exists, only that column should be searched."""
|
|
2048
|
-
html = """
|
|
2049
|
-
<table>
|
|
2050
|
-
<tr>
|
|
2051
|
-
<th>Vai trò</th>
|
|
2052
|
-
<th>Tài liệu</th>
|
|
2053
|
-
<th>Link</th>
|
|
2054
|
-
</tr>
|
|
2055
|
-
<tr>
|
|
2056
|
-
<td><a href="/ignored">PO</a></td>
|
|
2057
|
-
<td>BRD</td>
|
|
2058
|
-
<td><a href="/display/SPACE/BRD">BRD Link</a></td>
|
|
2059
|
-
</tr>
|
|
2060
|
-
</table>
|
|
2061
|
-
"""
|
|
2062
|
-
base_url = "http://confluence.digital.vn"
|
|
2063
|
-
|
|
2064
|
-
links = parser._extract_links_from_html(html, base_url)
|
|
2065
|
-
|
|
2066
|
-
# Should only find the link in the Link column
|
|
2067
|
-
assert len(links) == 1
|
|
2068
|
-
assert "/display/SPACE/BRD" in links[0].url
|
|
2069
|
-
|
|
2070
|
-
def test_multiple_links_in_link_column(self, parser):
|
|
2071
|
-
"""Should extract multiple links from the Link column."""
|
|
2072
|
-
html = """
|
|
2073
|
-
<table>
|
|
2074
|
-
<tr>
|
|
2075
|
-
<th>Vai trò</th>
|
|
2076
|
-
<th>Link tài liệu</th>
|
|
2077
|
-
</tr>
|
|
2078
|
-
<tr>
|
|
2079
|
-
<td>PO</td>
|
|
2080
|
-
<td>
|
|
2081
|
-
<a href="/display/SPACE/BRD">BRD</a>
|
|
2082
|
-
<a href="/display/SPACE/PRD">PRD</a>
|
|
2083
|
-
</td>
|
|
2084
|
-
</tr>
|
|
2085
|
-
</table>
|
|
2086
|
-
"""
|
|
2087
|
-
base_url = "http://confluence.digital.vn"
|
|
2088
|
-
|
|
2089
|
-
links = parser._extract_links_from_html(html, base_url)
|
|
2090
|
-
|
|
2091
|
-
assert len(links) == 2
|
|
2092
|
-
urls = [link.url for link in links]
|
|
2093
|
-
assert any("BRD" in url for url in urls)
|
|
2094
|
-
assert any("PRD" in url for url in urls)
|
|
2095
|
-
|
|
2096
|
-
def test_extract_plain_text_urls_in_link_column(self, parser):
|
|
2097
|
-
"""Should extract plain-text URLs in link cells (nolink spans / raw text)."""
|
|
2098
|
-
html = """
|
|
2099
|
-
<table>
|
|
2100
|
-
<tr>
|
|
2101
|
-
<th>Vai trò</th>
|
|
2102
|
-
<th>Loại tài liệu</th>
|
|
2103
|
-
<th>Link tài liệu</th>
|
|
2104
|
-
</tr>
|
|
2105
|
-
<tr>
|
|
2106
|
-
<td>DEV</td>
|
|
2107
|
-
<td>8. Source code</td>
|
|
2108
|
-
<td>
|
|
2109
|
-
<span class="nolink">http://bitbucket.digital.vn/projects/MAP</span>
|
|
2110
|
-
http://bitbucket.digital.vn/projects/SUPERAPP/repos/miniapp-webplatform-android/browse
|
|
2111
|
-
</td>
|
|
2112
|
-
</tr>
|
|
2113
|
-
</table>
|
|
2114
|
-
"""
|
|
2115
|
-
base_url = "http://confluence.digital.vn"
|
|
2116
|
-
|
|
2117
|
-
links = parser._extract_links_from_html(html, base_url)
|
|
2118
|
-
|
|
2119
|
-
urls = {link.url for link in links}
|
|
2120
|
-
assert "http://bitbucket.digital.vn/projects/MAP" in urls
|
|
2121
|
-
assert "http://bitbucket.digital.vn/projects/SUPERAPP/repos/miniapp-webplatform-android/browse" in urls
|
|
2122
|
-
assert all(link.source == LinkSource.BITBUCKET for link in links)
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
# Phase 24 TSK-243: Timeout budgets and partial-success diagnostics tests
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
class TestParserBudgetControls:
|
|
2129
|
-
"""Tests for parser budget controls (TSK-243.4.1)."""
|
|
2130
|
-
|
|
2131
|
-
def test_max_projects_parameter(self, mock_client):
|
|
2132
|
-
"""Parser should accept max_projects parameter."""
|
|
2133
|
-
parser = RegistryPageParser(mock_client, max_projects=5)
|
|
2134
|
-
assert parser.max_projects == 5
|
|
2135
|
-
|
|
2136
|
-
def test_max_projects_none_means_no_limit(self, mock_client):
|
|
2137
|
-
"""max_projects=None should mean no limit."""
|
|
2138
|
-
parser = RegistryPageParser(mock_client, max_projects=None)
|
|
2139
|
-
assert parser.max_projects is None
|
|
2140
|
-
|
|
2141
|
-
def test_step_timeout_parameter(self, mock_client):
|
|
2142
|
-
"""Parser should accept step_timeout parameter."""
|
|
2143
|
-
parser = RegistryPageParser(mock_client, step_timeout=30.0)
|
|
2144
|
-
assert parser.step_timeout == 30.0
|
|
2145
|
-
|
|
2146
|
-
def test_step_timeout_none_means_no_limit(self, mock_client):
|
|
2147
|
-
"""step_timeout=None should mean no per-step timeout."""
|
|
2148
|
-
parser = RegistryPageParser(mock_client, step_timeout=None)
|
|
2149
|
-
assert parser.step_timeout is None
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
class TestMaxProjectsLimit:
|
|
2153
|
-
"""Tests for max_projects limit behavior (TSK-243.4.1)."""
|
|
2154
|
-
|
|
2155
|
-
@pytest.mark.asyncio
|
|
2156
|
-
async def test_max_projects_limits_parsing(self, mock_client):
|
|
2157
|
-
"""Parser should stop after max_projects limit is reached."""
|
|
2158
|
-
parser = RegistryPageParser(mock_client, max_projects=2)
|
|
2159
|
-
root_id = "88716667"
|
|
2160
|
-
|
|
2161
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
2162
|
-
if page_id == root_id:
|
|
2163
|
-
return [
|
|
2164
|
-
{"id": "88716675", "title": "3.2 Các hệ thống", "_links": {}},
|
|
2165
|
-
]
|
|
2166
|
-
elif page_id == "88716675":
|
|
2167
|
-
# Return 5 project candidates
|
|
2168
|
-
return [
|
|
2169
|
-
{
|
|
2170
|
-
"id": f"proj_{i}",
|
|
2171
|
-
"title": f"Project {i}",
|
|
2172
|
-
"body": {
|
|
2173
|
-
"storage": {
|
|
2174
|
-
"value": f"<table><tr><th>Link</th></tr><tr><td><a href='http://example.com/{i}'>Link</a></td></tr></table>"
|
|
2175
|
-
}
|
|
2176
|
-
},
|
|
2177
|
-
"_links": {
|
|
2178
|
-
"base": "http://confluence.digital.vn",
|
|
2179
|
-
"webui": f"/pages/viewpage.action?pageId=proj_{i}",
|
|
2180
|
-
},
|
|
2181
|
-
}
|
|
2182
|
-
for i in range(5)
|
|
2183
|
-
]
|
|
2184
|
-
return []
|
|
2185
|
-
|
|
2186
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
2187
|
-
|
|
2188
|
-
registry = await parser.parse_registry(root_id)
|
|
2189
|
-
|
|
2190
|
-
# Should only have 2 projects due to limit
|
|
2191
|
-
assert len(registry.projects) == 2
|
|
2192
|
-
# Should have diagnostics indicating max_projects was reached
|
|
2193
|
-
assert registry.diagnostics is not None
|
|
2194
|
-
assert registry.diagnostics.max_projects_reached is True
|
|
2195
|
-
# Should have skipped nodes for the remaining projects
|
|
2196
|
-
max_projects_skipped = [n for n in registry.skipped_nodes if n.reason == SkippedNodeReason.MAX_PROJECTS_REACHED]
|
|
2197
|
-
assert len(max_projects_skipped) == 3
|
|
2198
|
-
|
|
2199
|
-
@pytest.mark.asyncio
|
|
2200
|
-
async def test_no_limit_when_max_projects_none(self, mock_client):
|
|
2201
|
-
"""Parser should parse all projects when max_projects is None."""
|
|
2202
|
-
parser = RegistryPageParser(mock_client, max_projects=None)
|
|
2203
|
-
root_id = "88716667"
|
|
2204
|
-
|
|
2205
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
2206
|
-
if page_id == root_id:
|
|
2207
|
-
return [
|
|
2208
|
-
{"id": "88716675", "title": "3.2 Các hệ thống", "_links": {}},
|
|
2209
|
-
]
|
|
2210
|
-
elif page_id == "88716675":
|
|
2211
|
-
return [
|
|
2212
|
-
{
|
|
2213
|
-
"id": f"proj_{i}",
|
|
2214
|
-
"title": f"Project {i}",
|
|
2215
|
-
"body": {
|
|
2216
|
-
"storage": {
|
|
2217
|
-
"value": f"<table><tr><th>Link</th></tr><tr><td><a href='http://example.com/{i}'>Link</a></td></tr></table>"
|
|
2218
|
-
}
|
|
2219
|
-
},
|
|
2220
|
-
"_links": {
|
|
2221
|
-
"base": "http://confluence.digital.vn",
|
|
2222
|
-
"webui": f"/pages/viewpage.action?pageId=proj_{i}",
|
|
2223
|
-
},
|
|
2224
|
-
}
|
|
2225
|
-
for i in range(5)
|
|
2226
|
-
]
|
|
2227
|
-
return []
|
|
2228
|
-
|
|
2229
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
2230
|
-
|
|
2231
|
-
registry = await parser.parse_registry(root_id)
|
|
2232
|
-
|
|
2233
|
-
# Should have all 5 projects
|
|
2234
|
-
assert len(registry.projects) == 5
|
|
2235
|
-
assert registry.diagnostics is not None
|
|
2236
|
-
assert registry.diagnostics.max_projects_reached is False
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
class TestParseDiagnostics:
|
|
2240
|
-
"""Tests for parse diagnostics (TSK-243.4.2)."""
|
|
2241
|
-
|
|
2242
|
-
@pytest.mark.asyncio
|
|
2243
|
-
async def test_diagnostics_populated_on_success(self, mock_client):
|
|
2244
|
-
"""Diagnostics should be populated even on successful parse."""
|
|
2245
|
-
parser = RegistryPageParser(mock_client)
|
|
2246
|
-
root_id = "88716667"
|
|
2247
|
-
|
|
2248
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
2249
|
-
if page_id == root_id:
|
|
2250
|
-
return [
|
|
2251
|
-
{"id": "88716675", "title": "3.2 Các hệ thống", "_links": {}},
|
|
2252
|
-
]
|
|
2253
|
-
elif page_id == "88716675":
|
|
2254
|
-
return [
|
|
2255
|
-
{
|
|
2256
|
-
"id": "proj_1",
|
|
2257
|
-
"title": "Project 1",
|
|
2258
|
-
"body": {
|
|
2259
|
-
"storage": {
|
|
2260
|
-
"value": "<table><tr><th>Link</th></tr><tr><td><a href='http://example.com'>Link</a></td></tr></table>"
|
|
2261
|
-
}
|
|
2262
|
-
},
|
|
2263
|
-
"_links": {
|
|
2264
|
-
"base": "http://confluence.digital.vn",
|
|
2265
|
-
"webui": "/pages/viewpage.action?pageId=proj_1",
|
|
2266
|
-
},
|
|
2267
|
-
},
|
|
2268
|
-
]
|
|
2269
|
-
return []
|
|
2270
|
-
|
|
2271
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
2272
|
-
|
|
2273
|
-
registry = await parser.parse_registry(root_id)
|
|
2274
|
-
|
|
2275
|
-
assert registry.diagnostics is not None
|
|
2276
|
-
# Duration may be 0 for very fast tests, so just check it's non-negative
|
|
2277
|
-
assert registry.diagnostics.parse_duration_ms >= 0
|
|
2278
|
-
assert registry.diagnostics.total_pages_attempted == 1
|
|
2279
|
-
assert registry.diagnostics.total_pages_succeeded == 1
|
|
2280
|
-
assert registry.diagnostics.timed_out_pages == []
|
|
2281
|
-
assert registry.diagnostics.failed_pages == []
|
|
2282
|
-
assert registry.diagnostics.partial_success is False
|
|
2283
|
-
|
|
2284
|
-
@pytest.mark.asyncio
|
|
2285
|
-
async def test_diagnostics_tracks_failed_pages(self, mock_client):
|
|
2286
|
-
"""Diagnostics should track failed pages when parse_project_page raises an exception."""
|
|
2287
|
-
parser = RegistryPageParser(mock_client)
|
|
2288
|
-
root_id = "88716667"
|
|
2289
|
-
|
|
2290
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
2291
|
-
if page_id == root_id:
|
|
2292
|
-
return [
|
|
2293
|
-
{"id": "88716675", "title": "3.2 Các hệ thống", "_links": {}},
|
|
2294
|
-
]
|
|
2295
|
-
elif page_id == "88716675":
|
|
2296
|
-
return [
|
|
2297
|
-
{
|
|
2298
|
-
"id": "proj_1",
|
|
2299
|
-
"title": "Project 1",
|
|
2300
|
-
"body": {
|
|
2301
|
-
"storage": {
|
|
2302
|
-
"value": "<table><tr><th>Link</th></tr><tr><td><a href='http://example.com'>Link</a></td></tr></table>"
|
|
2303
|
-
}
|
|
2304
|
-
},
|
|
2305
|
-
"_links": {
|
|
2306
|
-
"base": "http://confluence.digital.vn",
|
|
2307
|
-
"webui": "/pages/viewpage.action?pageId=proj_1",
|
|
2308
|
-
},
|
|
2309
|
-
},
|
|
2310
|
-
{
|
|
2311
|
-
"id": "proj_2",
|
|
2312
|
-
"title": "Project 2",
|
|
2313
|
-
# No body.storage - will need to fetch via get_page
|
|
2314
|
-
"_links": {},
|
|
2315
|
-
},
|
|
2316
|
-
]
|
|
2317
|
-
return []
|
|
2318
|
-
|
|
2319
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
2320
|
-
# Make get_page raise an exception to simulate failure
|
|
2321
|
-
mock_client.get_page.side_effect = Exception("Simulated failure")
|
|
2322
|
-
|
|
2323
|
-
registry = await parser.parse_registry(root_id)
|
|
2324
|
-
|
|
2325
|
-
assert registry.diagnostics is not None
|
|
2326
|
-
assert registry.diagnostics.total_pages_attempted == 2
|
|
2327
|
-
# proj_2 should be in failed_pages since get_page raises an exception
|
|
2328
|
-
assert len(registry.diagnostics.failed_pages) == 1
|
|
2329
|
-
assert "proj_2" in registry.diagnostics.failed_pages
|
|
2330
|
-
assert registry.diagnostics.partial_success is True
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
class TestPartialSuccessBehavior:
|
|
2334
|
-
"""Tests for partial-success behavior (TSK-243.4.3)."""
|
|
2335
|
-
|
|
2336
|
-
@pytest.mark.asyncio
|
|
2337
|
-
async def test_returns_partial_results_on_timeout(self, mock_client):
|
|
2338
|
-
"""Parser should return partial results when some pages timeout."""
|
|
2339
|
-
# Use a very short step_timeout to trigger timeouts
|
|
2340
|
-
parser = RegistryPageParser(mock_client, step_timeout=0.001)
|
|
2341
|
-
root_id = "88716667"
|
|
2342
|
-
|
|
2343
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
2344
|
-
if page_id == root_id:
|
|
2345
|
-
return [
|
|
2346
|
-
{"id": "88716675", "title": "3.2 Các hệ thống", "_links": {}},
|
|
2347
|
-
]
|
|
2348
|
-
elif page_id == "88716675":
|
|
2349
|
-
return [
|
|
2350
|
-
{
|
|
2351
|
-
"id": "proj_fast",
|
|
2352
|
-
"title": "Fast Project",
|
|
2353
|
-
"body": {
|
|
2354
|
-
"storage": {
|
|
2355
|
-
"value": "<table><tr><th>Link</th></tr><tr><td><a href='http://example.com'>Link</a></td></tr></table>"
|
|
2356
|
-
}
|
|
2357
|
-
},
|
|
2358
|
-
"_links": {
|
|
2359
|
-
"base": "http://confluence.digital.vn",
|
|
2360
|
-
"webui": "/pages/viewpage.action?pageId=proj_fast",
|
|
2361
|
-
},
|
|
2362
|
-
},
|
|
2363
|
-
{
|
|
2364
|
-
"id": "proj_slow",
|
|
2365
|
-
"title": "Slow Project",
|
|
2366
|
-
# No body - will need to fetch via get_page
|
|
2367
|
-
"_links": {},
|
|
2368
|
-
},
|
|
2369
|
-
]
|
|
2370
|
-
return []
|
|
2371
|
-
|
|
2372
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
2373
|
-
|
|
2374
|
-
# Make get_page slow to trigger timeout
|
|
2375
|
-
async def slow_get_page(*args, **kwargs):
|
|
2376
|
-
import asyncio
|
|
2377
|
-
|
|
2378
|
-
await asyncio.sleep(1.0) # Much longer than step_timeout
|
|
2379
|
-
|
|
2380
|
-
mock_client.get_page.side_effect = slow_get_page
|
|
2381
|
-
|
|
2382
|
-
registry = await parser.parse_registry(root_id)
|
|
2383
|
-
|
|
2384
|
-
# Should have at least the fast project
|
|
2385
|
-
assert registry.diagnostics is not None
|
|
2386
|
-
# The slow project should be in timed_out_pages
|
|
2387
|
-
assert "proj_slow" in registry.diagnostics.timed_out_pages
|
|
2388
|
-
assert registry.diagnostics.partial_success is True
|
|
2389
|
-
|
|
2390
|
-
@pytest.mark.asyncio
|
|
2391
|
-
async def test_returns_partial_results_on_failure(self, mock_client):
|
|
2392
|
-
"""Parser should return partial results when some pages fail with exceptions."""
|
|
2393
|
-
parser = RegistryPageParser(mock_client)
|
|
2394
|
-
root_id = "88716667"
|
|
2395
|
-
|
|
2396
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
2397
|
-
if page_id == root_id:
|
|
2398
|
-
return [
|
|
2399
|
-
{"id": "88716675", "title": "3.2 Các hệ thống", "_links": {}},
|
|
2400
|
-
]
|
|
2401
|
-
elif page_id == "88716675":
|
|
2402
|
-
return [
|
|
2403
|
-
{
|
|
2404
|
-
"id": "proj_good",
|
|
2405
|
-
"title": "Good Project",
|
|
2406
|
-
"body": {
|
|
2407
|
-
"storage": {
|
|
2408
|
-
"value": "<table><tr><th>Link</th></tr><tr><td><a href='http://example.com'>Link</a></td></tr></table>"
|
|
2409
|
-
}
|
|
2410
|
-
},
|
|
2411
|
-
"_links": {
|
|
2412
|
-
"base": "http://confluence.digital.vn",
|
|
2413
|
-
"webui": "/pages/viewpage.action?pageId=proj_good",
|
|
2414
|
-
},
|
|
2415
|
-
},
|
|
2416
|
-
{
|
|
2417
|
-
"id": "proj_bad",
|
|
2418
|
-
"title": "Bad Project",
|
|
2419
|
-
# No body.storage - will need to fetch via get_page
|
|
2420
|
-
"_links": {},
|
|
2421
|
-
},
|
|
2422
|
-
]
|
|
2423
|
-
return []
|
|
2424
|
-
|
|
2425
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
2426
|
-
# Make get_page raise an exception to simulate failure
|
|
2427
|
-
mock_client.get_page.side_effect = Exception("Simulated failure")
|
|
2428
|
-
|
|
2429
|
-
registry = await parser.parse_registry(root_id)
|
|
2430
|
-
|
|
2431
|
-
# Should have the good project
|
|
2432
|
-
assert len(registry.projects) == 1
|
|
2433
|
-
assert registry.projects[0].project_name == "Good Project"
|
|
2434
|
-
# Bad project should be in failed_pages
|
|
2435
|
-
assert registry.diagnostics is not None
|
|
2436
|
-
assert "proj_bad" in registry.diagnostics.failed_pages
|
|
2437
|
-
assert registry.diagnostics.partial_success is True
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
class TestTimeoutSkippedNodeReason:
|
|
2441
|
-
"""Tests for TIMEOUT skipped node reason (TSK-243)."""
|
|
2442
|
-
|
|
2443
|
-
@pytest.mark.asyncio
|
|
2444
|
-
async def test_timeout_creates_skipped_node(self, mock_client):
|
|
2445
|
-
"""Timed out pages should create skipped nodes with TIMEOUT reason."""
|
|
2446
|
-
parser = RegistryPageParser(mock_client, step_timeout=0.001)
|
|
2447
|
-
root_id = "88716667"
|
|
2448
|
-
|
|
2449
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
2450
|
-
if page_id == root_id:
|
|
2451
|
-
return [
|
|
2452
|
-
{"id": "88716675", "title": "3.2 Các hệ thống", "_links": {}},
|
|
2453
|
-
]
|
|
2454
|
-
elif page_id == "88716675":
|
|
2455
|
-
return [
|
|
2456
|
-
{
|
|
2457
|
-
"id": "proj_slow",
|
|
2458
|
-
"title": "Slow Project",
|
|
2459
|
-
# No body.storage - will need to fetch via get_page
|
|
2460
|
-
"_links": {},
|
|
2461
|
-
},
|
|
2462
|
-
]
|
|
2463
|
-
return []
|
|
2464
|
-
|
|
2465
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
2466
|
-
|
|
2467
|
-
async def slow_get_page(*args, **kwargs):
|
|
2468
|
-
import asyncio
|
|
2469
|
-
|
|
2470
|
-
await asyncio.sleep(1.0)
|
|
2471
|
-
|
|
2472
|
-
mock_client.get_page.side_effect = slow_get_page
|
|
2473
|
-
|
|
2474
|
-
registry = await parser.parse_registry(root_id)
|
|
2475
|
-
|
|
2476
|
-
timeout_skipped = [
|
|
2477
|
-
n for n in registry.skipped_nodes if n.reason == SkippedNodeReason.TIMEOUT and n.page_id == "proj_slow"
|
|
2478
|
-
]
|
|
2479
|
-
# Should have at least one timeout for proj_slow
|
|
2480
|
-
assert len(timeout_skipped) >= 1
|
|
2481
|
-
assert timeout_skipped[0].title == "Slow Project"
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
class TestRetryWithTimeout:
|
|
2485
|
-
"""Tests for retry behavior with timeout (TSK-243)."""
|
|
2486
|
-
|
|
2487
|
-
@pytest.mark.asyncio
|
|
2488
|
-
async def test_retry_respects_step_timeout(self, mock_client):
|
|
2489
|
-
"""Retry should respect step_timeout for each attempt."""
|
|
2490
|
-
parser = RegistryPageParser(mock_client, step_timeout=0.1, retries=1)
|
|
2491
|
-
|
|
2492
|
-
call_count = 0
|
|
2493
|
-
|
|
2494
|
-
async def slow_fn():
|
|
2495
|
-
nonlocal call_count
|
|
2496
|
-
call_count += 1
|
|
2497
|
-
import asyncio
|
|
2498
|
-
|
|
2499
|
-
await asyncio.sleep(0.5) # Longer than step_timeout
|
|
2500
|
-
return "result"
|
|
2501
|
-
|
|
2502
|
-
result = await parser._retry(slow_fn, test_context="test")
|
|
2503
|
-
|
|
2504
|
-
# Should have tried twice (initial + 1 retry) and both timed out
|
|
2505
|
-
assert call_count == 2
|
|
2506
|
-
assert result is None
|
|
2507
|
-
|
|
2508
|
-
@pytest.mark.asyncio
|
|
2509
|
-
async def test_retry_succeeds_within_timeout(self, mock_client):
|
|
2510
|
-
"""Retry should succeed if function completes within timeout."""
|
|
2511
|
-
parser = RegistryPageParser(mock_client, step_timeout=1.0, retries=1)
|
|
2512
|
-
|
|
2513
|
-
async def fast_fn():
|
|
2514
|
-
return "result"
|
|
2515
|
-
|
|
2516
|
-
result = await parser._retry(fast_fn, test_context="test")
|
|
2517
|
-
|
|
2518
|
-
assert result == "result"
|
|
2519
|
-
|
|
2520
|
-
@pytest.mark.asyncio
|
|
2521
|
-
async def test_retry_with_explicit_timeout_override(self, mock_client):
|
|
2522
|
-
"""Explicit timeout parameter should override step_timeout."""
|
|
2523
|
-
parser = RegistryPageParser(mock_client, step_timeout=10.0, retries=0)
|
|
2524
|
-
|
|
2525
|
-
call_count = 0
|
|
2526
|
-
|
|
2527
|
-
async def slow_fn():
|
|
2528
|
-
nonlocal call_count
|
|
2529
|
-
call_count += 1
|
|
2530
|
-
import asyncio
|
|
2531
|
-
|
|
2532
|
-
await asyncio.sleep(0.5)
|
|
2533
|
-
return "result"
|
|
2534
|
-
|
|
2535
|
-
# Use explicit timeout that's shorter than step_timeout
|
|
2536
|
-
result = await parser._retry(slow_fn, timeout=0.1, test_context="test")
|
|
2537
|
-
|
|
2538
|
-
assert call_count == 1
|
|
2539
|
-
assert result is None # Should timeout
|
|
2540
|
-
|
|
2541
|
-
@pytest.mark.asyncio
|
|
2542
|
-
async def test_retry_tracks_rate_limit_and_retry_after(self, mock_client):
|
|
2543
|
-
parser = RegistryPageParser(mock_client, retries=1, backoff=0.0)
|
|
2544
|
-
call_count = 0
|
|
2545
|
-
|
|
2546
|
-
async def flaky_fn():
|
|
2547
|
-
nonlocal call_count
|
|
2548
|
-
call_count += 1
|
|
2549
|
-
if call_count == 1:
|
|
2550
|
-
raise DataSourceError(
|
|
2551
|
-
"rate limited",
|
|
2552
|
-
context={"stderr": "HTTP 429 Too Many Requests\nRetry-After: 7"},
|
|
2553
|
-
)
|
|
2554
|
-
return "ok"
|
|
2555
|
-
|
|
2556
|
-
result = await parser._retry(flaky_fn, test_context="test")
|
|
2557
|
-
|
|
2558
|
-
assert result == "ok"
|
|
2559
|
-
assert parser._diag_retry_count == 1
|
|
2560
|
-
assert parser._diag_rate_limit_events == 1
|
|
2561
|
-
assert parser._diag_retry_after_seconds == 7
|
|
2562
|
-
|
|
2563
|
-
@pytest.mark.asyncio
|
|
2564
|
-
async def test_retry_honors_retry_after_delay(self, mock_client, monkeypatch):
|
|
2565
|
-
parser = RegistryPageParser(mock_client, retries=1, backoff=0.1)
|
|
2566
|
-
call_count = 0
|
|
2567
|
-
sleep_delays: list[float] = []
|
|
2568
|
-
|
|
2569
|
-
async def fake_sleep(delay: float):
|
|
2570
|
-
sleep_delays.append(delay)
|
|
2571
|
-
|
|
2572
|
-
monkeypatch.setattr("vds_audit_orchestrator.collectors.registry_parser.asyncio.sleep", fake_sleep)
|
|
2573
|
-
|
|
2574
|
-
async def flaky_fn():
|
|
2575
|
-
nonlocal call_count
|
|
2576
|
-
call_count += 1
|
|
2577
|
-
if call_count == 1:
|
|
2578
|
-
raise DataSourceError(
|
|
2579
|
-
"rate limited",
|
|
2580
|
-
context={"stderr": "HTTP 429 Too Many Requests\nRetry-After: 3"},
|
|
2581
|
-
)
|
|
2582
|
-
return "ok"
|
|
2583
|
-
|
|
2584
|
-
result = await parser._retry(flaky_fn, test_context="test")
|
|
2585
|
-
|
|
2586
|
-
assert result == "ok"
|
|
2587
|
-
assert sleep_delays == [3.0]
|
|
2588
|
-
assert parser._diag_retry_count == 1
|
|
2589
|
-
assert parser._diag_rate_limit_events == 1
|
|
2590
|
-
assert parser._diag_retry_after_seconds == 3
|
|
2591
|
-
|
|
2592
|
-
@pytest.mark.asyncio
|
|
2593
|
-
async def test_retry_5xx_retries_without_rate_limit_event(self, mock_client):
|
|
2594
|
-
parser = RegistryPageParser(mock_client, retries=1, backoff=0.0)
|
|
2595
|
-
call_count = 0
|
|
2596
|
-
|
|
2597
|
-
async def flaky_5xx():
|
|
2598
|
-
nonlocal call_count
|
|
2599
|
-
call_count += 1
|
|
2600
|
-
if call_count == 1:
|
|
2601
|
-
raise DataSourceError("upstream error", context={"stderr": "HTTP 503 Service Unavailable"})
|
|
2602
|
-
return "ok"
|
|
2603
|
-
|
|
2604
|
-
result = await parser._retry(flaky_5xx, test_context="test")
|
|
2605
|
-
|
|
2606
|
-
assert result == "ok"
|
|
2607
|
-
assert parser._diag_retry_count == 1
|
|
2608
|
-
assert parser._diag_rate_limit_events == 0
|
|
2609
|
-
assert parser._diag_retry_after_seconds is None
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
class TestDiagnosticsModel:
|
|
2613
|
-
"""Tests for ParseDiagnostics model (TSK-243)."""
|
|
2614
|
-
|
|
2615
|
-
def test_parse_diagnostics_defaults(self):
|
|
2616
|
-
"""ParseDiagnostics should have sensible defaults."""
|
|
2617
|
-
from vds_audit_orchestrator.models.registry import ParseDiagnostics
|
|
2618
|
-
|
|
2619
|
-
diag = ParseDiagnostics()
|
|
2620
|
-
|
|
2621
|
-
assert diag.parse_duration_ms == 0
|
|
2622
|
-
assert diag.timed_out_pages == []
|
|
2623
|
-
assert diag.failed_pages == []
|
|
2624
|
-
assert diag.total_pages_attempted == 0
|
|
2625
|
-
assert diag.total_pages_succeeded == 0
|
|
2626
|
-
assert diag.max_projects_reached is False
|
|
2627
|
-
assert diag.partial_success is False
|
|
2628
|
-
assert diag.prefetch_hits == 0
|
|
2629
|
-
assert diag.prefetch_misses == 0
|
|
2630
|
-
assert diag.network_fetches == 0
|
|
2631
|
-
assert diag.fallback_fetches == 0
|
|
2632
|
-
assert diag.prefetch_pages_indexed == 0
|
|
2633
|
-
assert diag.step_timings_ms == {}
|
|
2634
|
-
assert diag.retry_count == 0
|
|
2635
|
-
assert diag.rate_limit_events == 0
|
|
2636
|
-
assert diag.retry_after_seconds is None
|
|
2637
|
-
assert diag.prefetch_insufficient_coverage is False
|
|
2638
|
-
assert diag.prefetch_hint is None
|
|
2639
|
-
assert diag.result_status == "success"
|
|
2640
|
-
assert diag.result_message is None
|
|
2641
|
-
assert diag.can_retry is False
|
|
2642
|
-
assert diag.effective_retries == 0
|
|
2643
|
-
assert diag.effective_backoff_seconds == 0.0
|
|
2644
|
-
assert diag.max_backoff_seconds == 0.0
|
|
2645
|
-
assert diag.effective_step_timeout_seconds is None
|
|
2646
|
-
|
|
2647
|
-
def test_parse_diagnostics_serialization(self):
|
|
2648
|
-
"""ParseDiagnostics should serialize correctly."""
|
|
2649
|
-
from vds_audit_orchestrator.models.registry import ParseDiagnostics
|
|
2650
|
-
|
|
2651
|
-
diag = ParseDiagnostics(
|
|
2652
|
-
parse_duration_ms=1500,
|
|
2653
|
-
timed_out_pages=["page1", "page2"],
|
|
2654
|
-
failed_pages=["page3"],
|
|
2655
|
-
total_pages_attempted=10,
|
|
2656
|
-
total_pages_succeeded=7,
|
|
2657
|
-
max_projects_reached=True,
|
|
2658
|
-
partial_success=True,
|
|
2659
|
-
prefetch_hits=5,
|
|
2660
|
-
prefetch_misses=1,
|
|
2661
|
-
network_fetches=3,
|
|
2662
|
-
fallback_fetches=2,
|
|
2663
|
-
prefetch_pages_indexed=12,
|
|
2664
|
-
step_timings_ms={"discover_root_and_checklist": 40, "find_projects": 80},
|
|
2665
|
-
retry_count=3,
|
|
2666
|
-
rate_limit_events=1,
|
|
2667
|
-
retry_after_seconds=9,
|
|
2668
|
-
prefetch_insufficient_coverage=True,
|
|
2669
|
-
prefetch_hint="Prefetch artifacts may be too shallow.",
|
|
2670
|
-
result_status="partial_success",
|
|
2671
|
-
result_message="Registry parsed partially.",
|
|
2672
|
-
can_retry=True,
|
|
2673
|
-
effective_retries=2,
|
|
2674
|
-
effective_backoff_seconds=1.0,
|
|
2675
|
-
max_backoff_seconds=60.0,
|
|
2676
|
-
effective_step_timeout_seconds=20.0,
|
|
2677
|
-
)
|
|
2678
|
-
|
|
2679
|
-
data = diag.model_dump()
|
|
2680
|
-
|
|
2681
|
-
assert data["parse_duration_ms"] == 1500
|
|
2682
|
-
assert data["timed_out_pages"] == ["page1", "page2"]
|
|
2683
|
-
assert data["failed_pages"] == ["page3"]
|
|
2684
|
-
assert data["total_pages_attempted"] == 10
|
|
2685
|
-
assert data["total_pages_succeeded"] == 7
|
|
2686
|
-
assert data["max_projects_reached"] is True
|
|
2687
|
-
assert data["partial_success"] is True
|
|
2688
|
-
assert data["prefetch_hits"] == 5
|
|
2689
|
-
assert data["prefetch_misses"] == 1
|
|
2690
|
-
assert data["network_fetches"] == 3
|
|
2691
|
-
assert data["fallback_fetches"] == 2
|
|
2692
|
-
assert data["prefetch_pages_indexed"] == 12
|
|
2693
|
-
assert data["step_timings_ms"]["discover_root_and_checklist"] == 40
|
|
2694
|
-
assert data["retry_count"] == 3
|
|
2695
|
-
assert data["rate_limit_events"] == 1
|
|
2696
|
-
assert data["retry_after_seconds"] == 9
|
|
2697
|
-
assert data["prefetch_insufficient_coverage"] is True
|
|
2698
|
-
assert data["prefetch_hint"] == "Prefetch artifacts may be too shallow."
|
|
2699
|
-
assert data["result_status"] == "partial_success"
|
|
2700
|
-
assert data["result_message"] == "Registry parsed partially."
|
|
2701
|
-
assert data["can_retry"] is True
|
|
2702
|
-
assert data["effective_retries"] == 2
|
|
2703
|
-
assert data["effective_backoff_seconds"] == 1.0
|
|
2704
|
-
assert data["max_backoff_seconds"] == 60.0
|
|
2705
|
-
assert data["effective_step_timeout_seconds"] == 20.0
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
class TestRegistryWithDiagnostics:
|
|
2709
|
-
"""Tests for ProjectRegistry with diagnostics field (TSK-243)."""
|
|
2710
|
-
|
|
2711
|
-
def test_registry_includes_diagnostics(self):
|
|
2712
|
-
"""ProjectRegistry should include diagnostics field."""
|
|
2713
|
-
from vds_audit_orchestrator.models.registry import ParseDiagnostics, ProjectRegistry
|
|
2714
|
-
|
|
2715
|
-
diag = ParseDiagnostics(parse_duration_ms=1000)
|
|
2716
|
-
registry = ProjectRegistry(
|
|
2717
|
-
registry_url="/pages/viewpage.action?pageId=12345",
|
|
2718
|
-
registry_page_id="12345",
|
|
2719
|
-
diagnostics=diag,
|
|
2720
|
-
)
|
|
2721
|
-
|
|
2722
|
-
assert registry.diagnostics is not None
|
|
2723
|
-
assert registry.diagnostics.parse_duration_ms == 1000
|
|
2724
|
-
|
|
2725
|
-
def test_registry_diagnostics_serialization(self):
|
|
2726
|
-
"""Registry with diagnostics should serialize correctly."""
|
|
2727
|
-
from vds_audit_orchestrator.models.registry import ParseDiagnostics, ProjectRegistry
|
|
2728
|
-
|
|
2729
|
-
diag = ParseDiagnostics(
|
|
2730
|
-
parse_duration_ms=2000,
|
|
2731
|
-
partial_success=True,
|
|
2732
|
-
timed_out_pages=["page1"],
|
|
2733
|
-
)
|
|
2734
|
-
registry = ProjectRegistry(
|
|
2735
|
-
registry_url="/pages/viewpage.action?pageId=12345",
|
|
2736
|
-
registry_page_id="12345",
|
|
2737
|
-
diagnostics=diag,
|
|
2738
|
-
)
|
|
2739
|
-
|
|
2740
|
-
data = registry.model_dump()
|
|
2741
|
-
|
|
2742
|
-
assert data["diagnostics"]["parse_duration_ms"] == 2000
|
|
2743
|
-
assert data["diagnostics"]["partial_success"] is True
|
|
2744
|
-
assert data["diagnostics"]["timed_out_pages"] == ["page1"]
|
|
2745
|
-
|
|
2746
|
-
def test_registry_diagnostics_optional(self):
|
|
2747
|
-
"""Registry diagnostics should be optional for backward compatibility."""
|
|
2748
|
-
from vds_audit_orchestrator.models.registry import ProjectRegistry
|
|
2749
|
-
|
|
2750
|
-
registry = ProjectRegistry(
|
|
2751
|
-
registry_url="/pages/viewpage.action?pageId=12345",
|
|
2752
|
-
registry_page_id="12345",
|
|
2753
|
-
)
|
|
2754
|
-
|
|
2755
|
-
assert registry.diagnostics is None
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
# Phase 24 TSK-243.4.5: Prefetch path tests
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
class TestPrefetchArtifacts:
|
|
2762
|
-
"""Tests for prefetch artifact loading (TSK-243.4.5)."""
|
|
2763
|
-
|
|
2764
|
-
def test_prefetch_dir_parameter(self, mock_client, tmp_path):
|
|
2765
|
-
"""Parser should accept prefetch_dir parameter."""
|
|
2766
|
-
parser = RegistryPageParser(mock_client, prefetch_dir=tmp_path)
|
|
2767
|
-
assert parser.prefetch_dir == tmp_path
|
|
2768
|
-
|
|
2769
|
-
def test_prefetch_dir_none_by_default(self, mock_client):
|
|
2770
|
-
"""prefetch_dir should be None by default."""
|
|
2771
|
-
parser = RegistryPageParser(mock_client)
|
|
2772
|
-
assert parser.prefetch_dir is None
|
|
2773
|
-
|
|
2774
|
-
def test_load_prefetch_artifacts_returns_false_when_no_dir(self, mock_client):
|
|
2775
|
-
"""_load_prefetch_artifacts should return False when no prefetch_dir."""
|
|
2776
|
-
parser = RegistryPageParser(mock_client)
|
|
2777
|
-
assert parser._load_prefetch_artifacts() is False
|
|
2778
|
-
|
|
2779
|
-
def test_load_prefetch_artifacts_returns_false_when_dir_missing(self, mock_client, tmp_path):
|
|
2780
|
-
"""_load_prefetch_artifacts should return False when dir doesn't exist."""
|
|
2781
|
-
parser = RegistryPageParser(mock_client, prefetch_dir=tmp_path / "nonexistent")
|
|
2782
|
-
assert parser._load_prefetch_artifacts() is False
|
|
2783
|
-
|
|
2784
|
-
def test_load_prefetch_artifacts_returns_false_when_manifest_missing(self, mock_client, tmp_path):
|
|
2785
|
-
"""_load_prefetch_artifacts should return False when manifest is missing."""
|
|
2786
|
-
parser = RegistryPageParser(mock_client, prefetch_dir=tmp_path)
|
|
2787
|
-
assert parser._load_prefetch_artifacts() is False
|
|
2788
|
-
|
|
2789
|
-
def test_load_prefetch_artifacts_loads_manifest(self, mock_client, tmp_path):
|
|
2790
|
-
"""_load_prefetch_artifacts should load manifest and index pages."""
|
|
2791
|
-
import json
|
|
2792
|
-
|
|
2793
|
-
manifest = {
|
|
2794
|
-
"root_page_id": "12345",
|
|
2795
|
-
"page": {"id": "12345", "title": "Root Page"},
|
|
2796
|
-
"child_pages": [
|
|
2797
|
-
{"id": "11111", "title": "Child 1", "parent_id": "12345"},
|
|
2798
|
-
{"id": "22222", "title": "Child 2", "parent_id": "12345"},
|
|
2799
|
-
],
|
|
2800
|
-
}
|
|
2801
|
-
(tmp_path / "crawl_manifest.json").write_text(json.dumps(manifest))
|
|
2802
|
-
|
|
2803
|
-
parser = RegistryPageParser(mock_client, prefetch_dir=tmp_path)
|
|
2804
|
-
result = parser._load_prefetch_artifacts()
|
|
2805
|
-
|
|
2806
|
-
assert result is True
|
|
2807
|
-
assert parser._prefetch_manifest is not None
|
|
2808
|
-
assert parser._prefetch_manifest["root_page_id"] == "12345"
|
|
2809
|
-
assert len(parser._prefetch_pages) == 3 # root + 2 children
|
|
2810
|
-
|
|
2811
|
-
def test_get_prefetch_page_returns_page(self, mock_client, tmp_path):
|
|
2812
|
-
"""_get_prefetch_page should return page data from cache."""
|
|
2813
|
-
import json
|
|
2814
|
-
|
|
2815
|
-
manifest = {
|
|
2816
|
-
"root_page_id": "12345",
|
|
2817
|
-
"page": {"id": "12345", "title": "Root Page"},
|
|
2818
|
-
"child_pages": [
|
|
2819
|
-
{"id": "11111", "title": "Child 1", "parent_id": "12345"},
|
|
2820
|
-
],
|
|
2821
|
-
}
|
|
2822
|
-
(tmp_path / "crawl_manifest.json").write_text(json.dumps(manifest))
|
|
2823
|
-
|
|
2824
|
-
parser = RegistryPageParser(mock_client, prefetch_dir=tmp_path)
|
|
2825
|
-
parser._load_prefetch_artifacts()
|
|
2826
|
-
|
|
2827
|
-
page = parser._get_prefetch_page("11111")
|
|
2828
|
-
assert page is not None
|
|
2829
|
-
assert page["title"] == "Child 1"
|
|
2830
|
-
|
|
2831
|
-
def test_get_prefetch_page_returns_none_for_unknown(self, mock_client, tmp_path):
|
|
2832
|
-
"""_get_prefetch_page should return None for unknown page ID."""
|
|
2833
|
-
import json
|
|
2834
|
-
|
|
2835
|
-
manifest = {
|
|
2836
|
-
"root_page_id": "12345",
|
|
2837
|
-
"page": {"id": "12345", "title": "Root Page"},
|
|
2838
|
-
"child_pages": [],
|
|
2839
|
-
}
|
|
2840
|
-
(tmp_path / "crawl_manifest.json").write_text(json.dumps(manifest))
|
|
2841
|
-
|
|
2842
|
-
parser = RegistryPageParser(mock_client, prefetch_dir=tmp_path)
|
|
2843
|
-
parser._load_prefetch_artifacts()
|
|
2844
|
-
|
|
2845
|
-
page = parser._get_prefetch_page("99999")
|
|
2846
|
-
assert page is None
|
|
2847
|
-
|
|
2848
|
-
def test_get_prefetch_children_returns_children_for_root(self, mock_client, tmp_path):
|
|
2849
|
-
"""_get_prefetch_children should return all children for root page."""
|
|
2850
|
-
import json
|
|
2851
|
-
|
|
2852
|
-
manifest = {
|
|
2853
|
-
"root_page_id": "12345",
|
|
2854
|
-
"page": {"id": "12345", "title": "Root Page"},
|
|
2855
|
-
"child_pages": [
|
|
2856
|
-
{"id": "11111", "title": "Child 1", "parent_id": "12345"},
|
|
2857
|
-
{"id": "22222", "title": "Child 2", "parent_id": "12345"},
|
|
2858
|
-
],
|
|
2859
|
-
}
|
|
2860
|
-
(tmp_path / "crawl_manifest.json").write_text(json.dumps(manifest))
|
|
2861
|
-
|
|
2862
|
-
parser = RegistryPageParser(mock_client, prefetch_dir=tmp_path)
|
|
2863
|
-
parser._load_prefetch_artifacts()
|
|
2864
|
-
|
|
2865
|
-
children = parser._get_prefetch_children("12345")
|
|
2866
|
-
assert children is not None
|
|
2867
|
-
assert len(children) == 2
|
|
2868
|
-
|
|
2869
|
-
def test_get_prefetch_children_returns_none_when_no_manifest(self, mock_client):
|
|
2870
|
-
"""_get_prefetch_children should return None when no manifest loaded."""
|
|
2871
|
-
parser = RegistryPageParser(mock_client)
|
|
2872
|
-
children = parser._get_prefetch_children("12345")
|
|
2873
|
-
assert children is None
|
|
2874
|
-
|
|
2875
|
-
def test_get_prefetch_children_returns_empty_for_known_leaf(self, mock_client, tmp_path):
|
|
2876
|
-
"""Known leaf pages in manifest should return empty list, not None."""
|
|
2877
|
-
import json
|
|
2878
|
-
|
|
2879
|
-
manifest = {
|
|
2880
|
-
"root_page_id": "12345",
|
|
2881
|
-
"page": {"page_id": "12345", "title": "Root Page"},
|
|
2882
|
-
"child_pages": [
|
|
2883
|
-
{"page_id": "11111", "title": "Leaf", "parent_page_id": "12345"},
|
|
2884
|
-
],
|
|
2885
|
-
}
|
|
2886
|
-
(tmp_path / "crawl_manifest.json").write_text(json.dumps(manifest))
|
|
2887
|
-
|
|
2888
|
-
parser = RegistryPageParser(mock_client, prefetch_dir=tmp_path)
|
|
2889
|
-
parser._load_prefetch_artifacts()
|
|
2890
|
-
|
|
2891
|
-
children = parser._get_prefetch_children("11111")
|
|
2892
|
-
assert children == []
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
class TestPrefetchIntegration:
|
|
2896
|
-
"""Integration tests for prefetch path (TSK-243.4.5)."""
|
|
2897
|
-
|
|
2898
|
-
@pytest.mark.asyncio
|
|
2899
|
-
async def test_parse_registry_uses_prefetch_when_available(self, mock_client, tmp_path):
|
|
2900
|
-
"""Parser should use prefetch artifacts instead of network calls for root children."""
|
|
2901
|
-
import json
|
|
2902
|
-
|
|
2903
|
-
manifest = {
|
|
2904
|
-
"root_page_id": "88716667",
|
|
2905
|
-
"page": {"id": "88716667", "title": "Root"},
|
|
2906
|
-
"child_pages": [
|
|
2907
|
-
{
|
|
2908
|
-
"id": "88716673",
|
|
2909
|
-
"title": "3.1 Checklist đánh giá",
|
|
2910
|
-
"parent_id": "88716667",
|
|
2911
|
-
"_links": {
|
|
2912
|
-
"base": "http://confluence.digital.vn",
|
|
2913
|
-
"webui": "/pages/viewpage.action?pageId=88716673",
|
|
2914
|
-
},
|
|
2915
|
-
},
|
|
2916
|
-
{
|
|
2917
|
-
"id": "88716675",
|
|
2918
|
-
"title": "3.2 Các hệ thống",
|
|
2919
|
-
"parent_id": "88716667",
|
|
2920
|
-
"_links": {},
|
|
2921
|
-
},
|
|
2922
|
-
{
|
|
2923
|
-
"id": "proj_1",
|
|
2924
|
-
"title": "Project 1",
|
|
2925
|
-
"parent_id": "88716675",
|
|
2926
|
-
"body": {
|
|
2927
|
-
"storage": {
|
|
2928
|
-
"value": "<table><tr><th>Link</th></tr><tr><td><a href='http://example.com'>Link</a></td></tr></table>"
|
|
2929
|
-
}
|
|
2930
|
-
},
|
|
2931
|
-
"_links": {"base": "http://confluence.digital.vn", "webui": "/pages/viewpage.action?pageId=proj_1"},
|
|
2932
|
-
},
|
|
2933
|
-
],
|
|
2934
|
-
}
|
|
2935
|
-
(tmp_path / "crawl_manifest.json").write_text(json.dumps(manifest))
|
|
2936
|
-
|
|
2937
|
-
# Mock get_child_pages to return empty for any calls (for nested traversal)
|
|
2938
|
-
mock_client.get_child_pages.return_value = []
|
|
2939
|
-
|
|
2940
|
-
parser = RegistryPageParser(mock_client, prefetch_dir=tmp_path)
|
|
2941
|
-
registry = await parser.parse_registry("88716667")
|
|
2942
|
-
|
|
2943
|
-
# Should have discovered checklist and projects from prefetch
|
|
2944
|
-
assert registry.checklist_page_id == "88716673"
|
|
2945
|
-
assert len(registry.projects) == 1
|
|
2946
|
-
assert registry.projects[0].project_name == "Project 1"
|
|
2947
|
-
|
|
2948
|
-
# The parser uses prefetch for root children discovery, but may still
|
|
2949
|
-
# make network calls for nested traversal (depth > 1). With recursive_depth=1
|
|
2950
|
-
# (default), it should only traverse direct children which are in prefetch.
|
|
2951
|
-
assert registry.diagnostics is not None
|
|
2952
|
-
assert registry.diagnostics.prefetch_hits >= 2
|
|
2953
|
-
assert registry.diagnostics.network_fetches == 0
|
|
2954
|
-
assert registry.diagnostics.fallback_fetches == 0
|
|
2955
|
-
assert registry.diagnostics.prefetch_pages_indexed == 4
|
|
2956
|
-
assert "prefetch_load" in registry.diagnostics.step_timings_ms
|
|
2957
|
-
mock_client.get_child_pages.assert_not_called()
|
|
2958
|
-
mock_client.get_page.assert_not_called()
|
|
2959
|
-
|
|
2960
|
-
@pytest.mark.asyncio
|
|
2961
|
-
async def test_parse_registry_prefetch_schema_fallbacks_work(self, mock_client, tmp_path):
|
|
2962
|
-
"""Prefetch-only parse should support page_id/id and page_url/url variants."""
|
|
2963
|
-
import json
|
|
2964
|
-
|
|
2965
|
-
manifest = {
|
|
2966
|
-
"root_page_id": "88716667",
|
|
2967
|
-
"page": {
|
|
2968
|
-
"page_id": "88716667",
|
|
2969
|
-
"title": "Root",
|
|
2970
|
-
"url": "http://confluence.digital.vn/pages/viewpage.action?pageId=88716667",
|
|
2971
|
-
},
|
|
2972
|
-
"child_pages": [
|
|
2973
|
-
{
|
|
2974
|
-
"page_id": "88716673",
|
|
2975
|
-
"title": "3.1 Checklist đánh giá",
|
|
2976
|
-
"parent_page_id": "88716667",
|
|
2977
|
-
"page_url": "http://confluence.digital.vn/pages/viewpage.action?pageId=88716673",
|
|
2978
|
-
},
|
|
2979
|
-
{
|
|
2980
|
-
"id": "88716675",
|
|
2981
|
-
"title": "3.2 Các hệ thống",
|
|
2982
|
-
"parent_id": "88716667",
|
|
2983
|
-
"url": "http://confluence.digital.vn/pages/viewpage.action?pageId=88716675",
|
|
2984
|
-
},
|
|
2985
|
-
{
|
|
2986
|
-
"page_id": "proj_1",
|
|
2987
|
-
"title": "Project 1",
|
|
2988
|
-
"parent_page_id": "88716675",
|
|
2989
|
-
"page_url": "http://confluence.digital.vn/pages/viewpage.action?pageId=proj_1",
|
|
2990
|
-
"body": {
|
|
2991
|
-
"storage": {
|
|
2992
|
-
"value": "<table><tr><th>Link</th></tr><tr><td><a href='http://example.com/doc'>Doc</a></td></tr></table>"
|
|
2993
|
-
}
|
|
2994
|
-
},
|
|
2995
|
-
},
|
|
2996
|
-
],
|
|
2997
|
-
}
|
|
2998
|
-
(tmp_path / "crawl_manifest.json").write_text(json.dumps(manifest))
|
|
2999
|
-
|
|
3000
|
-
parser = RegistryPageParser(
|
|
3001
|
-
mock_client,
|
|
3002
|
-
prefetch_dir=tmp_path,
|
|
3003
|
-
recursive_depth=1,
|
|
3004
|
-
)
|
|
3005
|
-
mock_client.get_page.return_value = {
|
|
3006
|
-
"id": "88716675",
|
|
3007
|
-
"title": "3.2 Các hệ thống",
|
|
3008
|
-
"body": {"storage": {"value": ""}},
|
|
3009
|
-
}
|
|
3010
|
-
registry = await parser.parse_registry("88716667")
|
|
3011
|
-
|
|
3012
|
-
assert registry.checklist_page_id == "88716673"
|
|
3013
|
-
assert registry.checklist_page_url == "http://confluence.digital.vn/pages/viewpage.action?pageId=88716673"
|
|
3014
|
-
assert len(registry.projects) == 1
|
|
3015
|
-
assert registry.projects[0].page_id == "proj_1"
|
|
3016
|
-
assert registry.projects[0].page_url == "http://confluence.digital.vn/pages/viewpage.action?pageId=proj_1"
|
|
3017
|
-
assert registry.diagnostics is not None
|
|
3018
|
-
assert registry.diagnostics.network_fetches == 0
|
|
3019
|
-
assert registry.diagnostics.fallback_fetches == 0
|
|
3020
|
-
mock_client.get_child_pages.assert_not_called()
|
|
3021
|
-
mock_client.get_page.assert_not_called()
|
|
3022
|
-
|
|
3023
|
-
@pytest.mark.asyncio
|
|
3024
|
-
async def test_parse_registry_prefetch_insufficient_coverage_hint(self, mock_client, tmp_path):
|
|
3025
|
-
"""Emit actionable hint when prefetch artifacts are too shallow for project discovery."""
|
|
3026
|
-
import json
|
|
3027
|
-
|
|
3028
|
-
manifest = {
|
|
3029
|
-
"root_page_id": "88716667",
|
|
3030
|
-
"page": {"id": "88716667", "title": "Root"},
|
|
3031
|
-
"child_pages": [
|
|
3032
|
-
{"id": "88716673", "title": "3.1 Checklist đánh giá", "parent_id": "88716667"},
|
|
3033
|
-
{"id": "88716675", "title": "3.2 Các hệ thống", "parent_id": "88716667"},
|
|
3034
|
-
],
|
|
3035
|
-
}
|
|
3036
|
-
(tmp_path / "crawl_manifest.json").write_text(json.dumps(manifest))
|
|
3037
|
-
|
|
3038
|
-
parser = RegistryPageParser(
|
|
3039
|
-
mock_client,
|
|
3040
|
-
prefetch_dir=tmp_path,
|
|
3041
|
-
recursive_depth=1,
|
|
3042
|
-
)
|
|
3043
|
-
registry = await parser.parse_registry("88716667")
|
|
3044
|
-
|
|
3045
|
-
assert registry.projects == []
|
|
3046
|
-
assert registry.diagnostics is not None
|
|
3047
|
-
assert registry.diagnostics.prefetch_pages_indexed == 3
|
|
3048
|
-
assert registry.diagnostics.prefetch_insufficient_coverage is True
|
|
3049
|
-
assert registry.diagnostics.prefetch_hint is not None
|
|
3050
|
-
assert "depth>=2" in registry.diagnostics.prefetch_hint
|
|
3051
|
-
|
|
3052
|
-
@pytest.mark.asyncio
|
|
3053
|
-
async def test_parse_project_page_uses_prefetch_html_path_without_network(self, mock_client, tmp_path):
|
|
3054
|
-
"""Project parse should use prefetch html_path without get_page fallback."""
|
|
3055
|
-
project_html = (
|
|
3056
|
-
"<table><tr><th>Link</th></tr><tr><td><a href='http://example.com/spec'>Spec</a></td></tr></table>"
|
|
3057
|
-
)
|
|
3058
|
-
project_html_path = tmp_path / "proj_1.html"
|
|
3059
|
-
project_html_path.write_text(project_html, encoding="utf-8")
|
|
3060
|
-
|
|
3061
|
-
parser = RegistryPageParser(
|
|
3062
|
-
mock_client,
|
|
3063
|
-
prefetch_dir=tmp_path,
|
|
3064
|
-
recursive_depth=1,
|
|
3065
|
-
)
|
|
3066
|
-
material = await parser.parse_project_page(
|
|
3067
|
-
"proj_1",
|
|
3068
|
-
"Project 1",
|
|
3069
|
-
page_payload={
|
|
3070
|
-
"page_id": "proj_1",
|
|
3071
|
-
"title": "Project 1",
|
|
3072
|
-
"page_url": "http://confluence.digital.vn/pages/viewpage.action?pageId=proj_1",
|
|
3073
|
-
"html_path": str(project_html_path),
|
|
3074
|
-
},
|
|
3075
|
-
)
|
|
3076
|
-
|
|
3077
|
-
assert material is not None
|
|
3078
|
-
assert material.page_id == "proj_1"
|
|
3079
|
-
assert material.documents
|
|
3080
|
-
assert material.documents[0].url == "http://example.com/spec"
|
|
3081
|
-
mock_client.get_page.assert_not_called()
|
|
3082
|
-
|
|
3083
|
-
@pytest.mark.asyncio
|
|
3084
|
-
async def test_parse_registry_falls_back_to_network_when_no_prefetch(self, mock_client):
|
|
3085
|
-
"""Parser should fall back to network when no prefetch available."""
|
|
3086
|
-
root_id = "88716667"
|
|
3087
|
-
|
|
3088
|
-
async def mock_get_child_pages(page_id, expand=None):
|
|
3089
|
-
if page_id == root_id:
|
|
3090
|
-
return [
|
|
3091
|
-
{"id": "88716675", "title": "3.2 Các hệ thống", "_links": {}},
|
|
3092
|
-
]
|
|
3093
|
-
elif page_id == "88716675":
|
|
3094
|
-
return [
|
|
3095
|
-
{
|
|
3096
|
-
"id": "proj_1",
|
|
3097
|
-
"title": "Project 1",
|
|
3098
|
-
"body": {
|
|
3099
|
-
"storage": {
|
|
3100
|
-
"value": "<table><tr><th>Link</th></tr><tr><td><a href='http://example.com'>Link</a></td></tr></table>"
|
|
3101
|
-
}
|
|
3102
|
-
},
|
|
3103
|
-
"_links": {
|
|
3104
|
-
"base": "http://confluence.digital.vn",
|
|
3105
|
-
"webui": "/pages/viewpage.action?pageId=proj_1",
|
|
3106
|
-
},
|
|
3107
|
-
},
|
|
3108
|
-
]
|
|
3109
|
-
return []
|
|
3110
|
-
|
|
3111
|
-
mock_client.get_child_pages.side_effect = mock_get_child_pages
|
|
3112
|
-
|
|
3113
|
-
parser = RegistryPageParser(mock_client) # No prefetch_dir
|
|
3114
|
-
registry = await parser.parse_registry(root_id)
|
|
3115
|
-
|
|
3116
|
-
# Should have made network calls
|
|
3117
|
-
assert mock_client.get_child_pages.call_count >= 1
|
|
3118
|
-
assert len(registry.projects) == 1
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
class TestAdaptiveDepthPhase35:
|
|
3122
|
-
"""Phase 35 adaptive-depth traversal tests."""
|
|
3123
|
-
|
|
3124
|
-
@staticmethod
|
|
3125
|
-
def _require_adaptive_depth_support() -> None:
|
|
3126
|
-
sig = inspect.signature(RegistryPageParser.__init__)
|
|
3127
|
-
missing = [name for name in ("adaptive_depth", "adaptive_max_depth") if name not in sig.parameters]
|
|
3128
|
-
if missing:
|
|
3129
|
-
pytest.xfail(f"Phase 35 parser options not available yet: {', '.join(missing)}")
|
|
3130
|
-
|
|
3131
|
-
@staticmethod
|
|
3132
|
-
def _hierarchy_html() -> str:
|
|
3133
|
-
return (
|
|
3134
|
-
"<table><tr><th>Vai trò</th><th>Loại tài liệu</th><th>Link</th></tr>"
|
|
3135
|
-
"<tr><td>PO</td><td>1. Tài liệu BRD</td><td>"
|
|
3136
|
-
"<a href='http://confluence.digital.vn/display/INS/BRD'>BRD</a>"
|
|
3137
|
-
"</td></tr></table>"
|
|
3138
|
-
)
|
|
3139
|
-
|
|
3140
|
-
@staticmethod
|
|
3141
|
-
def _mock_hierarchy(mock_client: AsyncMock, html: str) -> None:
|
|
3142
|
-
root_id = "88716667"
|
|
3143
|
-
projects_root_id = "88716675"
|
|
3144
|
-
department_id = "dep-1"
|
|
3145
|
-
project_id = "proj-1"
|
|
3146
|
-
|
|
3147
|
-
async def _get_child_pages(page_id: str, expand: str | None = None):
|
|
3148
|
-
if page_id == root_id:
|
|
3149
|
-
return [
|
|
3150
|
-
{"id": "88716673", "title": "3.1 Checklist đánh giá", "_links": {}},
|
|
3151
|
-
{"id": projects_root_id, "title": "3.2 Các hệ thống", "_links": {}},
|
|
3152
|
-
]
|
|
3153
|
-
if page_id == projects_root_id:
|
|
3154
|
-
return [{"id": department_id, "title": "Checklist Department", "_links": {}}]
|
|
3155
|
-
if page_id == department_id:
|
|
3156
|
-
return [
|
|
3157
|
-
{
|
|
3158
|
-
"id": project_id,
|
|
3159
|
-
"title": "Project Alpha",
|
|
3160
|
-
"body": {"storage": {"value": html}},
|
|
3161
|
-
"_links": {
|
|
3162
|
-
"base": "http://confluence.digital.vn",
|
|
3163
|
-
"webui": "/pages/viewpage.action?pageId=proj-1",
|
|
3164
|
-
},
|
|
3165
|
-
}
|
|
3166
|
-
]
|
|
3167
|
-
return []
|
|
3168
|
-
|
|
3169
|
-
mock_client.get_child_pages.side_effect = _get_child_pages
|
|
3170
|
-
|
|
3171
|
-
@staticmethod
|
|
3172
|
-
def _mock_mixed_hierarchy(mock_client: AsyncMock, html: str) -> None:
|
|
3173
|
-
root_id = "88716667"
|
|
3174
|
-
projects_root_id = "88716675"
|
|
3175
|
-
department_id = "dep-1"
|
|
3176
|
-
direct_project_id = "proj-direct"
|
|
3177
|
-
nested_project_id = "proj-nested"
|
|
3178
|
-
|
|
3179
|
-
async def _get_child_pages(page_id: str, expand: str | None = None):
|
|
3180
|
-
if page_id == root_id:
|
|
3181
|
-
return [
|
|
3182
|
-
{"id": "88716673", "title": "3.1 Checklist đánh giá", "_links": {}},
|
|
3183
|
-
{"id": projects_root_id, "title": "3.2 Các hệ thống", "_links": {}},
|
|
3184
|
-
]
|
|
3185
|
-
if page_id == projects_root_id:
|
|
3186
|
-
return [
|
|
3187
|
-
{
|
|
3188
|
-
"id": direct_project_id,
|
|
3189
|
-
"title": "Project Direct",
|
|
3190
|
-
"body": {"storage": {"value": html}},
|
|
3191
|
-
"_links": {
|
|
3192
|
-
"base": "http://confluence.digital.vn",
|
|
3193
|
-
"webui": "/pages/viewpage.action?pageId=proj-direct",
|
|
3194
|
-
},
|
|
3195
|
-
},
|
|
3196
|
-
{"id": department_id, "title": "Department A", "_links": {}},
|
|
3197
|
-
]
|
|
3198
|
-
if page_id == department_id:
|
|
3199
|
-
return [
|
|
3200
|
-
{
|
|
3201
|
-
"id": nested_project_id,
|
|
3202
|
-
"title": "Project Nested",
|
|
3203
|
-
"body": {"storage": {"value": html}},
|
|
3204
|
-
"_links": {
|
|
3205
|
-
"base": "http://confluence.digital.vn",
|
|
3206
|
-
"webui": "/pages/viewpage.action?pageId=proj-nested",
|
|
3207
|
-
},
|
|
3208
|
-
}
|
|
3209
|
-
]
|
|
3210
|
-
return []
|
|
3211
|
-
|
|
3212
|
-
mock_client.get_child_pages.side_effect = _get_child_pages
|
|
3213
|
-
|
|
3214
|
-
@pytest.mark.asyncio
|
|
3215
|
-
async def test_adaptive_depth_discovers_department_nested_project(self, mock_client):
|
|
3216
|
-
self._require_adaptive_depth_support()
|
|
3217
|
-
self._mock_hierarchy(mock_client, self._hierarchy_html())
|
|
3218
|
-
|
|
3219
|
-
parser = RegistryPageParser(
|
|
3220
|
-
mock_client,
|
|
3221
|
-
recursive_depth=1,
|
|
3222
|
-
adaptive_depth=True,
|
|
3223
|
-
adaptive_max_depth=3,
|
|
3224
|
-
)
|
|
3225
|
-
registry = await parser.parse_registry("88716667")
|
|
3226
|
-
|
|
3227
|
-
assert len(registry.projects) == 1
|
|
3228
|
-
assert registry.projects[0].page_id == "proj-1"
|
|
3229
|
-
|
|
3230
|
-
@pytest.mark.asyncio
|
|
3231
|
-
async def test_adaptive_depth_triggers_when_first_pass_has_zero_candidates(self, mock_client):
|
|
3232
|
-
self._require_adaptive_depth_support()
|
|
3233
|
-
root_id = "88716667"
|
|
3234
|
-
checklist_id = "88716673"
|
|
3235
|
-
projects_root_id = "88716675"
|
|
3236
|
-
department_id = "dep-1"
|
|
3237
|
-
project_id = "proj-1"
|
|
3238
|
-
project_html = "<table><tr><th>Link</th></tr><tr><td><a href='http://example.com/doc'>Doc</a></td></tr></table>"
|
|
3239
|
-
|
|
3240
|
-
async def _get_child_pages(page_id: str, expand: str | None = None):
|
|
3241
|
-
if page_id == root_id:
|
|
3242
|
-
return [
|
|
3243
|
-
{"id": checklist_id, "title": "3.1 Checklist đánh giá", "_links": {}},
|
|
3244
|
-
{"id": projects_root_id, "title": "3.2 Template container", "_links": {}},
|
|
3245
|
-
]
|
|
3246
|
-
if page_id == projects_root_id:
|
|
3247
|
-
return [{"id": department_id, "title": "", "_links": {}}]
|
|
3248
|
-
if page_id == department_id:
|
|
3249
|
-
return [
|
|
3250
|
-
{
|
|
3251
|
-
"id": project_id,
|
|
3252
|
-
"title": "3.2.1 CEP",
|
|
3253
|
-
"body": {"storage": {"value": project_html}},
|
|
3254
|
-
"_links": {
|
|
3255
|
-
"base": "http://confluence.digital.vn",
|
|
3256
|
-
"webui": "/pages/viewpage.action?pageId=proj-1",
|
|
3257
|
-
},
|
|
3258
|
-
}
|
|
3259
|
-
]
|
|
3260
|
-
return []
|
|
3261
|
-
|
|
3262
|
-
mock_client.get_child_pages.side_effect = _get_child_pages
|
|
3263
|
-
|
|
3264
|
-
async def _get_page(page_id: str, expand: str | None = None):
|
|
3265
|
-
if page_id == project_id:
|
|
3266
|
-
return {
|
|
3267
|
-
"id": project_id,
|
|
3268
|
-
"title": "3.2.1 CEP",
|
|
3269
|
-
"body": {"storage": {"value": project_html}},
|
|
3270
|
-
"_links": {
|
|
3271
|
-
"base": "http://confluence.digital.vn",
|
|
3272
|
-
"webui": "/pages/viewpage.action?pageId=proj-1",
|
|
3273
|
-
},
|
|
3274
|
-
}
|
|
3275
|
-
return {
|
|
3276
|
-
"id": page_id,
|
|
3277
|
-
"title": f"Node {page_id}",
|
|
3278
|
-
"body": {"storage": {"value": "<p>no links</p>"}},
|
|
3279
|
-
"_links": {
|
|
3280
|
-
"base": "http://confluence.digital.vn",
|
|
3281
|
-
"webui": f"/pages/viewpage.action?pageId={page_id}",
|
|
3282
|
-
},
|
|
3283
|
-
}
|
|
3284
|
-
|
|
3285
|
-
mock_client.get_page.side_effect = _get_page
|
|
3286
|
-
|
|
3287
|
-
parser = RegistryPageParser(
|
|
3288
|
-
mock_client,
|
|
3289
|
-
recursive_depth=1,
|
|
3290
|
-
adaptive_depth=True,
|
|
3291
|
-
adaptive_max_depth=3,
|
|
3292
|
-
)
|
|
3293
|
-
registry = await parser.parse_registry(root_id)
|
|
3294
|
-
|
|
3295
|
-
assert [project.page_id for project in registry.projects] == [project_id]
|
|
3296
|
-
assert registry.diagnostics is not None
|
|
3297
|
-
assert registry.diagnostics.adaptive_depth_applied is True
|
|
3298
|
-
assert registry.diagnostics.adaptive_trigger_reason == "zero_projects_with_zero_candidates"
|
|
3299
|
-
|
|
3300
|
-
@pytest.mark.asyncio
|
|
3301
|
-
async def test_adaptive_depth_can_be_disabled(self, mock_client):
|
|
3302
|
-
self._require_adaptive_depth_support()
|
|
3303
|
-
self._mock_hierarchy(mock_client, self._hierarchy_html())
|
|
3304
|
-
|
|
3305
|
-
parser = RegistryPageParser(
|
|
3306
|
-
mock_client,
|
|
3307
|
-
recursive_depth=1,
|
|
3308
|
-
adaptive_depth=False,
|
|
3309
|
-
adaptive_max_depth=3,
|
|
3310
|
-
)
|
|
3311
|
-
registry = await parser.parse_registry("88716667")
|
|
3312
|
-
|
|
3313
|
-
assert registry.projects == []
|
|
3314
|
-
|
|
3315
|
-
@pytest.mark.asyncio
|
|
3316
|
-
async def test_adaptive_depth_respects_max_depth_bound(self, mock_client):
|
|
3317
|
-
self._require_adaptive_depth_support()
|
|
3318
|
-
self._mock_hierarchy(mock_client, self._hierarchy_html())
|
|
3319
|
-
|
|
3320
|
-
parser = RegistryPageParser(
|
|
3321
|
-
mock_client,
|
|
3322
|
-
recursive_depth=1,
|
|
3323
|
-
adaptive_depth=True,
|
|
3324
|
-
adaptive_max_depth=1,
|
|
3325
|
-
)
|
|
3326
|
-
registry = await parser.parse_registry("88716667")
|
|
3327
|
-
|
|
3328
|
-
assert registry.projects == []
|
|
3329
|
-
|
|
3330
|
-
@pytest.mark.asyncio
|
|
3331
|
-
async def test_adaptive_depth_discovers_mixed_topology_without_duplicates(self, mock_client):
|
|
3332
|
-
self._require_adaptive_depth_support()
|
|
3333
|
-
self._mock_mixed_hierarchy(mock_client, self._hierarchy_html())
|
|
3334
|
-
|
|
3335
|
-
parser = RegistryPageParser(
|
|
3336
|
-
mock_client,
|
|
3337
|
-
recursive_depth=1,
|
|
3338
|
-
adaptive_depth=True,
|
|
3339
|
-
adaptive_max_depth=3,
|
|
3340
|
-
)
|
|
3341
|
-
registry = await parser.parse_registry("88716667")
|
|
3342
|
-
|
|
3343
|
-
assert sorted(project.page_id for project in registry.projects) == ["proj-direct", "proj-nested"]
|
|
3344
|
-
assert registry.diagnostics is not None
|
|
3345
|
-
assert registry.diagnostics.adaptive_depth_applied is True
|
|
3346
|
-
assert registry.diagnostics.adaptive_trigger_reason == "partial_projects_with_no_links_signal"
|
|
3347
|
-
|
|
3348
|
-
@pytest.mark.asyncio
|
|
3349
|
-
async def test_adaptive_depth_prefetch_nested_projects_without_network(self, mock_client, tmp_path):
|
|
3350
|
-
self._require_adaptive_depth_support()
|
|
3351
|
-
manifest = {
|
|
3352
|
-
"root_page_id": "88716667",
|
|
3353
|
-
"page": {"id": "88716667", "title": "Root"},
|
|
3354
|
-
"child_pages": [
|
|
3355
|
-
{"id": "88716673", "title": "3.1 Checklist đánh giá", "parent_id": "88716667"},
|
|
3356
|
-
{"id": "88716675", "title": "3.2 Các hệ thống", "parent_id": "88716667"},
|
|
3357
|
-
{"id": "dep-1", "title": "Department A", "parent_id": "88716675"},
|
|
3358
|
-
{
|
|
3359
|
-
"id": "proj-1",
|
|
3360
|
-
"title": "Project 1",
|
|
3361
|
-
"parent_id": "dep-1",
|
|
3362
|
-
"body": {
|
|
3363
|
-
"storage": {
|
|
3364
|
-
"value": "<table><tr><th>Link</th></tr><tr><td><a href='http://example.com/doc'>Doc</a></td></tr></table>"
|
|
3365
|
-
}
|
|
3366
|
-
},
|
|
3367
|
-
},
|
|
3368
|
-
],
|
|
3369
|
-
}
|
|
3370
|
-
(tmp_path / "crawl_manifest.json").write_text(json.dumps(manifest))
|
|
3371
|
-
|
|
3372
|
-
parser = RegistryPageParser(
|
|
3373
|
-
mock_client,
|
|
3374
|
-
prefetch_dir=tmp_path,
|
|
3375
|
-
recursive_depth=1,
|
|
3376
|
-
adaptive_depth=True,
|
|
3377
|
-
adaptive_max_depth=2,
|
|
3378
|
-
)
|
|
3379
|
-
registry = await parser.parse_registry("88716667")
|
|
3380
|
-
|
|
3381
|
-
assert [project.page_id for project in registry.projects] == ["proj-1"]
|
|
3382
|
-
assert registry.diagnostics is not None
|
|
3383
|
-
assert registry.diagnostics.adaptive_depth_applied is True
|
|
3384
|
-
assert registry.diagnostics.network_fetches == 0
|
|
3385
|
-
assert registry.diagnostics.fallback_fetches == 0
|
|
3386
|
-
mock_client.get_child_pages.assert_not_called()
|
|
3387
|
-
mock_client.get_page.assert_not_called()
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
@pytest.mark.asyncio
|
|
3391
|
-
async def test_parse_registry_robust_when_projects_root_not_classified(mock_client):
|
|
3392
|
-
"""Fallback candidate roots should still find root->container->department->project."""
|
|
3393
|
-
root_id = "88716667"
|
|
3394
|
-
checklist_id = "88716673"
|
|
3395
|
-
container_id = "container-x"
|
|
3396
|
-
department_id = "dep-1"
|
|
3397
|
-
project_id = "proj-1"
|
|
3398
|
-
project_html = "<table><tr><th>Link</th></tr><tr><td><a href='http://example.com/doc'>Doc</a></td></tr></table>"
|
|
3399
|
-
|
|
3400
|
-
async def _get_child_pages(page_id: str, expand: str | None = None):
|
|
3401
|
-
if page_id == root_id:
|
|
3402
|
-
return [
|
|
3403
|
-
{"id": checklist_id, "title": "3.1 Checklist đánh giá", "_links": {}},
|
|
3404
|
-
{"id": container_id, "title": "Các sản phẩm bảo hiểm", "_links": {}},
|
|
3405
|
-
]
|
|
3406
|
-
if page_id == container_id:
|
|
3407
|
-
return [{"id": department_id, "title": "Department A", "_links": {}}]
|
|
3408
|
-
if page_id == department_id:
|
|
3409
|
-
return [
|
|
3410
|
-
{
|
|
3411
|
-
"id": project_id,
|
|
3412
|
-
"title": "Project Alpha",
|
|
3413
|
-
"body": {"storage": {"value": project_html}},
|
|
3414
|
-
"_links": {
|
|
3415
|
-
"base": "http://confluence.digital.vn",
|
|
3416
|
-
"webui": "/pages/viewpage.action?pageId=proj-1",
|
|
3417
|
-
},
|
|
3418
|
-
}
|
|
3419
|
-
]
|
|
3420
|
-
if page_id == checklist_id:
|
|
3421
|
-
pytest.fail("Checklist branch should be pruned from project traversal")
|
|
3422
|
-
return []
|
|
3423
|
-
|
|
3424
|
-
mock_client.get_child_pages.side_effect = _get_child_pages
|
|
3425
|
-
|
|
3426
|
-
parser = RegistryPageParser(
|
|
3427
|
-
mock_client,
|
|
3428
|
-
recursive_depth=2,
|
|
3429
|
-
)
|
|
3430
|
-
registry = await parser.parse_registry(root_id)
|
|
3431
|
-
|
|
3432
|
-
assert [project.page_id for project in registry.projects] == [project_id]
|
|
3433
|
-
assert registry.checklist_page_id == checklist_id
|
|
3434
|
-
assert mock_client.get_child_pages.call_count == 3
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
@pytest.mark.asyncio
|
|
3438
|
-
async def test_adaptive_depth_reuses_network_cache_for_children_and_pages(mock_client):
|
|
3439
|
-
"""Adaptive pass should avoid re-downloading already fetched children/page bodies."""
|
|
3440
|
-
root_id = "88716667"
|
|
3441
|
-
projects_root_id = "88716675"
|
|
3442
|
-
direct_project_id = "proj-direct"
|
|
3443
|
-
empty_project_id = "proj-empty"
|
|
3444
|
-
department_id = "dep-1"
|
|
3445
|
-
nested_project_id = "proj-nested"
|
|
3446
|
-
project_html = "<table><tr><th>Link</th></tr><tr><td><a href='http://example.com/doc'>Doc</a></td></tr></table>"
|
|
3447
|
-
|
|
3448
|
-
async def _get_child_pages(page_id: str, expand: str | None = None):
|
|
3449
|
-
if page_id == root_id:
|
|
3450
|
-
return [
|
|
3451
|
-
{"id": "88716673", "title": "3.1 Checklist đánh giá", "_links": {}},
|
|
3452
|
-
{"id": projects_root_id, "title": "3.2 Các hệ thống", "_links": {}},
|
|
3453
|
-
]
|
|
3454
|
-
if page_id == projects_root_id:
|
|
3455
|
-
return [
|
|
3456
|
-
{"id": direct_project_id, "title": "Project Direct", "_links": {}},
|
|
3457
|
-
{
|
|
3458
|
-
"id": empty_project_id,
|
|
3459
|
-
"title": "Project Empty",
|
|
3460
|
-
"body": {"storage": {"value": "<p>No documents yet</p>"}},
|
|
3461
|
-
"_links": {},
|
|
3462
|
-
},
|
|
3463
|
-
{
|
|
3464
|
-
"id": department_id,
|
|
3465
|
-
"title": "Department A",
|
|
3466
|
-
"body": {"storage": {"value": "<p>Department container</p>"}},
|
|
3467
|
-
"_links": {},
|
|
3468
|
-
},
|
|
3469
|
-
]
|
|
3470
|
-
if page_id == department_id:
|
|
3471
|
-
return [
|
|
3472
|
-
{
|
|
3473
|
-
"id": nested_project_id,
|
|
3474
|
-
"title": "Project Nested",
|
|
3475
|
-
"body": {"storage": {"value": project_html}},
|
|
3476
|
-
"_links": {
|
|
3477
|
-
"base": "http://confluence.digital.vn",
|
|
3478
|
-
"webui": "/pages/viewpage.action?pageId=proj-nested",
|
|
3479
|
-
},
|
|
3480
|
-
}
|
|
3481
|
-
]
|
|
3482
|
-
return []
|
|
3483
|
-
|
|
3484
|
-
async def _get_page(page_id: str, expand: str | None = None):
|
|
3485
|
-
if page_id == direct_project_id:
|
|
3486
|
-
return {
|
|
3487
|
-
"id": direct_project_id,
|
|
3488
|
-
"title": "Project Direct",
|
|
3489
|
-
"body": {"storage": {"value": project_html}},
|
|
3490
|
-
"_links": {
|
|
3491
|
-
"base": "http://confluence.digital.vn",
|
|
3492
|
-
"webui": "/pages/viewpage.action?pageId=proj-direct",
|
|
3493
|
-
},
|
|
3494
|
-
}
|
|
3495
|
-
return {"id": page_id, "title": page_id, "body": {"storage": {"value": ""}}, "_links": {}}
|
|
3496
|
-
|
|
3497
|
-
mock_client.get_child_pages.side_effect = _get_child_pages
|
|
3498
|
-
mock_client.get_page.side_effect = _get_page
|
|
3499
|
-
|
|
3500
|
-
parser = RegistryPageParser(
|
|
3501
|
-
mock_client,
|
|
3502
|
-
recursive_depth=1,
|
|
3503
|
-
adaptive_depth=True,
|
|
3504
|
-
adaptive_max_depth=2,
|
|
3505
|
-
)
|
|
3506
|
-
registry = await parser.parse_registry(root_id)
|
|
3507
|
-
|
|
3508
|
-
assert sorted(project.page_id for project in registry.projects) == [direct_project_id, nested_project_id]
|
|
3509
|
-
assert registry.diagnostics is not None
|
|
3510
|
-
assert registry.diagnostics.adaptive_depth_applied is True
|
|
3511
|
-
assert mock_client.get_page.call_count == 1
|
|
3512
|
-
parent_call_counts = Counter(str(call.args[0]) for call in mock_client.get_child_pages.await_args_list)
|
|
3513
|
-
assert parent_call_counts[root_id] == 1
|
|
3514
|
-
assert parent_call_counts[projects_root_id] == 1
|