初始提交:飞球 ETL 系统全量代码

This commit is contained in:
Neo
2026-02-13 08:05:34 +08:00
commit 3c51f5485d
441 changed files with 117631 additions and 0 deletions

View File

@@ -0,0 +1,140 @@
# 单元测试清单280 passed / 1 skipped
> 最后更新2026-02-12基于 `pytest tests/unit -v` 输出。
## 概览
| 分类 | 测试文件 | 测试数 | 说明 |
|------|---------|--------|------|
| ETL 任务(在线) | `test_etl_tasks_online.py` | 14 | FakeAPI 模拟在线抓取,验证 14 个 ODS 任务 E/T/L |
| ETL 任务(离线) | `test_etl_tasks_offline.py` | 14 | 本地 JSON 回放,验证离线入库链路 |
| ETL 任务(分阶段) | `test_etl_tasks_stages.py` | 42 | 14 个任务 × 3 阶段Extract/Transform/Load |
| ODS 通用任务 | `test_ods_tasks.py` | ~20+ | ODS 通用加载器任务测试 |
| 解析器 | `test_parsers.py` | ~10+ | 数据类型解析(日期/金额/枚举) |
| 配置管理 | `test_config.py` | ~10+ | AppConfig 加载、点号路径、分层覆盖 |
| 接口路由 | `test_endpoint_routing.py` | ~5+ | 近期/历史接口路由规则 |
| 报告工具 | `test_reporting.py` | ~5+ | 汇总格式化工具 |
| 审计扫描 | `test_audit_*.py`6 个文件) | ~40+ | 仓库审计:文件清单、流程树、文档对齐、报告属性 |
| 关系指数 | `test_relation_index_base.py` | ~5+ | RS/OS/MS/ML 指数基础逻辑 |
| **调度器重构(新增)** | 见下方 | **51** | TaskRegistry / TaskExecutor / PipelineRunner / CLI / E2E |
## 调度器重构新增测试51 个)
### `test_task_registry.py` — TaskRegistry 单元测试16 个)
| 测试类 | 测试方法 | 验证内容 |
|--------|---------|---------|
| `TestRegisterAndMetadata` | `test_register_with_defaults` | 仅传 task_code + task_class 时使用默认元数据 |
| | `test_register_with_full_metadata` | 完整元数据注册layer/task_type |
| | `test_register_utility_task` | 工具类任务 requires_db_config=False |
| | `test_case_insensitive_lookup` | task_code 大小写不敏感 |
| | `test_get_metadata_unknown_returns_none` | 未注册任务返回 None |
| `TestCreateTask` | `test_create_task_returns_instance` | 创建任务实例(接口不变) |
| | `test_create_task_unknown_raises` | 未知任务抛 ValueError |
| `TestGetTasksByLayer` | `test_returns_matching_tasks` | 按层查询返回匹配任务 |
| | `test_case_insensitive_layer` | 层名大小写不敏感 |
| | `test_no_match_returns_empty` | 无匹配返回空列表 |
| | `test_none_layer_excluded` | layer=None 不被任何层查询返回 |
| `TestIsUtilityTask` | `test_utility_task` | requires_db_config=False → True |
| | `test_normal_task` | requires_db_config=True → False |
| | `test_unknown_task` | 未注册任务 → False |
| `TestGetAllTaskCodes` | `test_returns_all_codes` | 返回所有已注册代码 |
| | `test_empty_registry` | 空注册表返回空列表 |
### `test_task_registry_properties.py` — TaskRegistry 属性测试3 个类,~300 次迭代)
| 测试类 | 测试方法 | Property | 验证内容 | 迭代次数 |
|--------|---------|----------|---------|---------|
| `TestProperty8MetadataRoundTrip` | `test_metadata_round_trip` | P8 | 任意 task_code/requires_db/layer/task_type 组合注册后get_metadata 返回完全相同的值 | 100 |
| `TestProperty9BackwardCompatibleDefaults` | `test_legacy_register_uses_defaults` | P9 | 仅传 task_code + task_class 时,默认 requires_db_config=True、layer=None、task_type="etl" | 100 |
| `TestProperty10GetTasksByLayer` | `test_get_tasks_by_layer_matches_manual_filter` | P10 | 注册一组任务后,按层查询结果与手动过滤完全一致 | 100 |
### `test_config_properties.py` — 配置映射属性测试1 个类100 次迭代)
| 测试类 | 测试方法 | Property | 验证内容 | 迭代次数 |
|--------|---------|----------|---------|---------|
| `TestProperty11FlowToDataSourceMapping` | `test_pipeline_flow_maps_to_data_source` | P11 | pipeline_flowFULL/FETCH_ONLY/INGEST_ONLY→ data_sourcehybrid/online/offline映射一致 | 100 |
### `test_task_executor_properties.py` — TaskExecutor 属性测试4 个类7 个方法,~700 次迭代)
| 测试类 | 测试方法 | Property | 验证内容 | 迭代次数 |
|--------|---------|----------|---------|---------|
| `TestProperty1DataSourceDeterminesPath` | `test_flow_includes_fetch` | P1 | data_source 为 online/hybrid 时 fetch=Trueoffline 时 fetch=False | 100 |
| | `test_flow_includes_ingest` | P1 | data_source 为 offline/hybrid 时 ingest=Trueonline 时 ingest=False | 100 |
| | `test_fetch_and_ingest_consistency` | P1 | hybrid 两者皆 Trueonline 仅 fetchoffline 仅 ingest | 100 |
| `TestProperty2SuccessAdvancesCursor` | `test_success_with_window_advances_cursor` | P2 | 成功任务调用 cursor_mgr.advance传入正确的 window_start/window_end | 100 |
| `TestProperty3FailureMarksFailAndReraises` | `test_exception_marks_fail_and_reraises` | P3 | 异常时 run_tracker.update_run(status="FAIL") 并重新抛出原始异常 | 100 |
| `TestProperty4UtilityTaskDeterminedByMetadata` | `test_utility_task_skips_cursor_and_run_tracker` | P4 | 工具类任务requires_db_config=False跳过游标和运行记录 | 100 |
| | `test_non_utility_task_uses_cursor_and_run_tracker` | P4 | 非工具类任务使用游标和运行记录 | 100 |
### `test_pipeline_runner_properties.py` — PipelineRunner 属性测试3 个类8 个方法,~800 次迭代)
| 测试类 | 测试方法 | Property | 验证内容 | 迭代次数 |
|--------|---------|----------|---------|---------|
| `TestProperty5PipelineNameToLayers` | `test_layers_match_pipeline_definition` | P5 | run() 返回的 layers 与 PIPELINE_LAYERS[pipeline] 完全一致 | 100 |
| | `test_resolve_tasks_called_with_correct_layers` | P5 | _resolve_tasks 接收的层列表与定义一致 | 100 |
| `TestProperty6ProcessingModeControlsFlow` | `test_increment_executes_iff_mode_contains_increment` | P6 | 增量 ETL 执行当且仅当 mode 包含 "increment" | 100 |
| | `test_verification_executes_iff_mode_contains_verify` | P6 | 校验流程执行当且仅当 mode 包含 "verify" | 100 |
| `TestProperty7PipelineSummaryCompleteness` | `test_summary_has_required_fields` | P7 | 返回字典包含 status/pipeline/layers/results/verification_summary | 100 |
| | `test_results_length_equals_executed_tasks` | P7 | results 长度等于实际执行的任务数 | 100 |
| | `test_pipeline_and_layers_match_input` | P7 | 返回的 pipeline 和 layers 与输入一致 | 100 |
| | `test_increment_only_has_no_verification` | P7 | increment_only 模式下 verification_summary 为 None | 100 |
### `test_filter_verify_tables.py` — 校验表过滤单元测试9 个)
| 测试类 | 测试方法 | 验证内容 |
|--------|---------|---------|
| `TestFilterVerifyTables` | `test_none_input_returns_none` | 输入 None 返回 None |
| | `test_empty_list_returns_none` | 空列表返回 None |
| | `test_dwd_layer_filters_correctly` | DWD 层过滤:保留 dwd_/dim_/fact_ 前缀 |
| | `test_dws_layer_filters_correctly` | DWS 层过滤:保留 dws_ 前缀 |
| | `test_index_layer_filters_correctly` | INDEX 层过滤:保留 v_/wbi_/nci_ 等前缀 |
| | `test_ods_layer_filters_correctly` | ODS 层过滤:保留 ods_ 前缀 |
| | `test_unknown_layer_returns_normalized` | 未知层返回归一化后的全部表名 |
| | `test_layer_case_insensitive` | 层名大小写不敏感 |
| | `test_whitespace_and_empty_entries_stripped` | 空白和空条目被过滤 |
### `test_cli_args.py` — CLI 参数解析单元测试14 个)
| 测试类 | 测试方法 | 验证内容 |
|--------|---------|---------|
| `TestDataSourceArg` | `test_data_source_valid_values` (×3) | --data-source 接受 online/offline/hybrid |
| | `test_data_source_default_is_none` | 未指定时默认 None |
| `TestResolveDataSource` | `test_explicit_data_source_returns_directly` | 显式 --data-source 直接返回 |
| | `test_data_source_takes_priority_over_pipeline_flow` | --data-source 优先于 --pipeline-flow |
| | `test_pipeline_flow_maps_with_deprecation_warning` (×3) | 旧参数映射 + 弃用警告 |
| | `test_neither_arg_defaults_to_hybrid` | 两者都未指定时默认 hybrid |
| `TestBuildCliOverrides` | `test_data_source_online_sets_run_key` | --data-source 写入 run.data_source |
| | `test_pipeline_flow_sets_both_keys` | 旧参数同时写入 pipeline.flow 和 run.data_source |
| | `test_default_data_source_is_hybrid` | 默认 run.data_source 为 hybrid |
| `TestPipelineAndTasks` | `test_pipeline_and_tasks_both_parsed` | --pipeline + --tasks 同时解析 |
### `test_e2e_flow.py` — 端到端流程集成测试4 个)
| 测试类 | 测试方法 | 验证内容 |
|--------|---------|---------|
| `TestTraditionalModeE2E` | `test_run_tasks_executes_utility_task_and_returns_results` | TaskExecutor.run_tasks 工具类任务端到端 |
| `TestPipelineModeE2E` | `test_pipeline_delegates_to_executor_and_returns_structure` | PipelineRunner → TaskExecutor 委托 + 返回结构 |
| | `test_pipeline_verify_only_skips_increment` | verify_only 模式跳过增量 ETL |
| `TestSchedulerThinWrapper` | `test_scheduler_delegates_run_tasks` | ETLScheduler 薄包装层正确委托 TaskExecutor/PipelineRunner |
---
## 属性测试PBT汇总
| Property | 所属组件 | 验证需求 | 迭代次数 |
|----------|---------|---------|---------|
| P1 | TaskExecutor | data_source 参数决定执行路径Req 1.2 | 300 |
| P2 | TaskExecutor | 成功任务推进游标Req 1.3 | 100 |
| P3 | TaskExecutor | 失败任务标记 FAIL 并重新抛出Req 1.4 | 100 |
| P4 | TaskExecutor | 工具类任务由元数据决定Req 1.6, 4.2 | 200 |
| P5 | PipelineRunner | 管道名称→层列表映射Req 2.1 | 200 |
| P6 | PipelineRunner | processing_mode 控制执行流程Req 2.3, 2.4 | 200 |
| P7 | PipelineRunner | 管道结果汇总完整性Req 2.6 | 400 |
| P8 | TaskRegistry | 元数据 round-tripReq 4.1 | 100 |
| P9 | TaskRegistry | 向后兼容默认值Req 4.4 | 100 |
| P10 | TaskRegistry | 按层查询任务Req 4.3 | 100 |
| P11 | AppConfig | pipeline_flow → data_source 映射一致性Req 8.1-8.4, 5.2 | 100 |
总计11 个属性,~1900 次迭代。