feat: expand module workflows, dynamic config, notifications and console API
This commit is contained in:
@@ -19,6 +19,26 @@ WORKERS = (tm_worker, dy_worker, jd_worker)
|
||||
BUSINESS_DATE = date(2026, 8, 4)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("worker", WORKERS)
|
||||
def test_missing_time_select_option_is_treated_as_no_existing_record(monkeypatch, worker) -> None:
|
||||
monkeypatch.setattr(
|
||||
worker.subprocess,
|
||||
"run",
|
||||
lambda *_a, **_k: SimpleNamespace(
|
||||
returncode=1,
|
||||
stdout="",
|
||||
stderr=json.dumps(
|
||||
{
|
||||
"ok": False,
|
||||
"error": {"code": 800030005, "message": "not_found"},
|
||||
}
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
assert worker.find_record_id_by_time("bas_test", "tbl_test", "8.13") is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("worker", WORKERS)
|
||||
def test_platform_collector_failure_forces_nonzero(worker) -> None:
|
||||
assert worker.combined_result_exit_code(
|
||||
@@ -78,6 +98,30 @@ def test_formal_base_upsert_accepts_verified_response_record_id(monkeypatch, wor
|
||||
assert message == "record_id=rec12345678"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("worker", WORKERS)
|
||||
def test_formal_base_update_reuses_valid_requested_record_id(monkeypatch, worker) -> None:
|
||||
monkeypatch.setattr(acceptance_policy, "skip_feishu_table_write", lambda *_a, **_k: False)
|
||||
monkeypatch.setattr(
|
||||
worker.subprocess,
|
||||
"run",
|
||||
lambda *_a, **_k: SimpleNamespace(
|
||||
returncode=0,
|
||||
stdout=json.dumps({"ok": True, "data": {"updated": True}}),
|
||||
stderr="",
|
||||
),
|
||||
)
|
||||
|
||||
ok, message = worker.upsert_record(
|
||||
"bas_test",
|
||||
"tbl_test",
|
||||
{"时间": "8.4", "男性比例": 0.5},
|
||||
record_id="rec_existing123",
|
||||
)
|
||||
|
||||
assert ok is True
|
||||
assert message == "record_id=rec_existing123"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("worker", WORKERS)
|
||||
def test_acceptance_skip_is_not_formal_success(monkeypatch, worker) -> None:
|
||||
monkeypatch.setattr(acceptance_policy, "skip_feishu_table_write", lambda *_a, **_k: True)
|
||||
@@ -265,6 +309,21 @@ def test_zero_distributions_are_not_valid_persona() -> None:
|
||||
) is False
|
||||
|
||||
|
||||
def test_dy_persistent_profile_is_not_overwritten_by_exported_cookies() -> None:
|
||||
assert dy_collector.persistent_profile_launch_options("bound-profile") == {
|
||||
"user_data_dir": "bound-profile"
|
||||
}
|
||||
|
||||
|
||||
def test_dy_profileless_fallback_can_restore_exported_cookies() -> None:
|
||||
cookies = [{"name": "session", "value": "new", "domain": ".jinritemai.com"}]
|
||||
|
||||
assert dy_collector.cookie_fallback_launch_options(cookies) == {
|
||||
"cookies": cookies
|
||||
}
|
||||
assert dy_collector.cookie_fallback_launch_options(None) == {}
|
||||
|
||||
|
||||
def test_dmp_skips_are_not_valid_style_results() -> None:
|
||||
styles = {"款A": ["123"]}
|
||||
assert dmp.records_exit_code(
|
||||
@@ -317,3 +376,24 @@ def test_tm_collector_command_redacts_credentials() -> None:
|
||||
assert "secret-user" not in rendered
|
||||
assert "secret-pass" not in rendered
|
||||
assert rendered.count("***") == 2
|
||||
|
||||
|
||||
def test_jd_persona_login_home_navigation_does_not_wait_for_networkidle() -> None:
|
||||
project_root = Path(__file__).resolve().parents[3]
|
||||
source_path = (
|
||||
project_root
|
||||
/ "src"
|
||||
/ "gyxx_flow"
|
||||
/ "modules"
|
||||
/ "product_commerce"
|
||||
/ "vendors"
|
||||
/ "jd-data-flow"
|
||||
/ "jd_data_collector.py"
|
||||
)
|
||||
source = source_path.read_text(encoding="utf-8")
|
||||
|
||||
step_login = source[source.index("def step_login") :]
|
||||
home_block = step_login[step_login.index("shop.jd.com/jdm/home") :]
|
||||
assert 'wait_until="networkidle"' not in home_block[:400]
|
||||
assert 'wait_until="domcontentloaded"' in home_block[:400]
|
||||
assert "timeout=60_000" in home_block[:400]
|
||||
|
||||
Reference in New Issue
Block a user