138 lines
5.4 KiB
Python
138 lines
5.4 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""CLI 参数解析单元测试
|
||
|
||
验证 --data-source 新参数、--pipeline-flow 弃用映射、
|
||
--pipeline + --tasks 同时使用、以及 build_cli_overrides 集成行为。
|
||
|
||
需求: 3.1, 3.3, 3.5
|
||
"""
|
||
import warnings
|
||
from argparse import Namespace
|
||
from unittest.mock import patch
|
||
|
||
import pytest
|
||
|
||
from cli.main import parse_args, resolve_data_source, build_cli_overrides
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 1. --data-source 新参数解析
|
||
# ---------------------------------------------------------------------------
|
||
class TestDataSourceArg:
|
||
"""--data-source 新参数测试"""
|
||
|
||
@pytest.mark.parametrize("value", ["online", "offline", "hybrid"])
|
||
def test_data_source_valid_values(self, value):
|
||
with patch("sys.argv", ["cli", "--data-source", value]):
|
||
args = parse_args()
|
||
assert args.data_source == value
|
||
|
||
def test_data_source_default_is_none(self):
|
||
with patch("sys.argv", ["cli"]):
|
||
args = parse_args()
|
||
assert args.data_source is None
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 2. resolve_data_source() 弃用映射
|
||
# ---------------------------------------------------------------------------
|
||
class TestResolveDataSource:
|
||
"""resolve_data_source() 弃用映射测试"""
|
||
|
||
def test_explicit_data_source_returns_directly(self):
|
||
args = Namespace(data_source="online", pipeline_flow=None)
|
||
assert resolve_data_source(args) == "online"
|
||
|
||
def test_data_source_takes_priority_over_pipeline_flow(self):
|
||
"""--data-source 优先于 --pipeline-flow"""
|
||
args = Namespace(data_source="online", pipeline_flow="FULL")
|
||
assert resolve_data_source(args) == "online"
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
"flow, expected",
|
||
[
|
||
("FULL", "hybrid"),
|
||
("FETCH_ONLY", "online"),
|
||
("INGEST_ONLY", "offline"),
|
||
],
|
||
)
|
||
def test_pipeline_flow_maps_with_deprecation_warning(self, flow, expected):
|
||
"""旧参数 --pipeline-flow 映射到正确的 data_source 并发出弃用警告"""
|
||
args = Namespace(data_source=None, pipeline_flow=flow)
|
||
with pytest.warns(DeprecationWarning, match="--pipeline-flow 已弃用"):
|
||
result = resolve_data_source(args)
|
||
assert result == expected
|
||
|
||
def test_neither_arg_defaults_to_hybrid(self):
|
||
"""两个参数都未指定时,默认返回 hybrid"""
|
||
args = Namespace(data_source=None, pipeline_flow=None)
|
||
assert resolve_data_source(args) == "hybrid"
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 3. build_cli_overrides() 集成
|
||
# ---------------------------------------------------------------------------
|
||
class TestBuildCliOverrides:
|
||
"""build_cli_overrides() 集成测试"""
|
||
|
||
def _make_args(self, **kwargs):
|
||
"""构造最小 Namespace,未指定的参数设为 None/False"""
|
||
defaults = dict(
|
||
store_id=None, tasks=None, dry_run=False,
|
||
pipeline=None, processing_mode="increment_only",
|
||
fetch_before_verify=False, verify_tables=None,
|
||
window_split="none", lookback_hours=24, overlap_seconds=3600,
|
||
pg_dsn=None, pg_host=None, pg_port=None, pg_name=None,
|
||
pg_user=None, pg_password=None,
|
||
api_base=None, api_token=None, api_timeout=None,
|
||
api_page_size=None, api_retry_max=None,
|
||
window_start=None, window_end=None,
|
||
force_window_override=False,
|
||
window_split_unit=None, window_split_days=None,
|
||
window_compensation_hours=None,
|
||
export_root=None, log_root=None,
|
||
data_source=None, pipeline_flow=None,
|
||
fetch_root=None, ingest_source=None, write_pretty_json=False,
|
||
idle_start=None, idle_end=None, allow_empty_advance=False,
|
||
)
|
||
defaults.update(kwargs)
|
||
return Namespace(**defaults)
|
||
|
||
def test_data_source_online_sets_run_key(self):
|
||
args = self._make_args(data_source="online")
|
||
overrides = build_cli_overrides(args)
|
||
assert overrides["run"]["data_source"] == "online"
|
||
|
||
def test_pipeline_flow_sets_both_keys(self):
|
||
"""旧参数同时写入 pipeline.flow 和 run.data_source"""
|
||
args = self._make_args(pipeline_flow="FULL")
|
||
with warnings.catch_warnings():
|
||
warnings.simplefilter("ignore", DeprecationWarning)
|
||
overrides = build_cli_overrides(args)
|
||
assert overrides["pipeline"]["flow"] == "FULL"
|
||
assert overrides["run"]["data_source"] == "hybrid"
|
||
|
||
def test_default_data_source_is_hybrid(self):
|
||
"""无 --data-source 也无 --pipeline-flow 时,run.data_source 默认 hybrid"""
|
||
args = self._make_args()
|
||
overrides = build_cli_overrides(args)
|
||
assert overrides["run"]["data_source"] == "hybrid"
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 4. --pipeline + --tasks 同时使用
|
||
# ---------------------------------------------------------------------------
|
||
class TestPipelineAndTasks:
|
||
"""--pipeline + --tasks 同时使用时的行为"""
|
||
|
||
def test_pipeline_and_tasks_both_parsed(self):
|
||
with patch("sys.argv", [
|
||
"cli",
|
||
"--pipeline", "api_full",
|
||
"--tasks", "ODS_MEMBER,ODS_ORDER",
|
||
]):
|
||
args = parse_args()
|
||
assert args.pipeline == "api_full"
|
||
assert args.tasks == "ODS_MEMBER,ODS_ORDER"
|