Files
gyxx-flow/tests/test_content_data_layout.py
T

123 lines
4.3 KiB
Python

from __future__ import annotations
from pathlib import Path
import pytest
from gyxx_flow.modules.content_marketing.runtime import runtime_paths
PROJECT_ROOT = Path(__file__).resolve().parents[1]
RUNTIME_ROOT = (
PROJECT_ROOT / "src" / "gyxx_flow" / "modules" / "content_marketing" / "runtime"
)
def _source(relative: str) -> str:
return (RUNTIME_ROOT / relative).read_text(encoding="utf-8-sig")
def test_content_output_resolver_keeps_paths_inside_the_selected_layer(
tmp_path: Path,
) -> None:
root = tmp_path / "exports" / "content_marketing"
relative = runtime_paths.resolve_layer_output(
"reports/monthly.md", layer_root=root, field="--output"
)
absolute = runtime_paths.resolve_layer_output(
root / "summary" / "daily.md", layer_root=root, field="--output"
)
assert relative == (root / "reports" / "monthly.md").resolve()
assert absolute == (root / "summary" / "daily.md").resolve()
with pytest.raises(ValueError, match="--output"):
runtime_paths.resolve_layer_output(
"../escape.md", layer_root=root, field="--output"
)
with pytest.raises(ValueError, match="--output"):
runtime_paths.resolve_layer_output(
tmp_path / "outside.md", layer_root=root, field="--output"
)
def test_dynamic_mapping_uses_normalized_cache_and_read_only_source_seed() -> None:
source = _source("feishu_mapping.py")
assert 'MAPPING_DIR = PATHS.normalized_root / "mappings"' in source
assert 'MAPPING_SEED_PATH = PATHS.config_root /' in source
assert 'SELF_MAPPING_SEED_PATH = PATHS.config_root /' in source
assert 'MAPPING_PATH = PATHS.config_root /' not in source
assert "if data_dir:" not in source
def test_v2_outputs_and_checkpoints_use_their_semantic_layers() -> None:
assert 'Path("data") / "v2_results"' not in _source("chanmama_scraper.py")
assert 'PATHS.normalized_root / "v2_results"' in _source("chanmama_scraper.py")
for relative in (
"bilibili_scraper.py",
"pgy_xhs_scraper_v2.py",
"xingtu_scraper_v2.py",
"self_bilibili_scraper.py",
"self_douyin_scraper.py",
):
assert 'PATHS.normalized_root / "v2_results"' in _source(relative), relative
assert 'PATHS.curated_root / "run_all"' in _source("run_all.py")
assert 'PATHS.state_root / "checkpoints" / "bilibili"' in _source(
"bilibili_scraper.py"
)
assert "PATHS.browser_storage_state_file.write_text" in _source(
"xingtu_scraper.py"
)
assert 'PATHS.state_root / "checkpoints" / "pgy"' in _source(
"pgy_xhs_scraper_v2.py"
)
for relative in (
"data/tools/check_status.py",
"data/tools/retry_failed.py",
"data/tools/sync_metrics_to_cmt_notes.py",
):
source = _source(relative)
assert 'PATHS.normalized_root / "v2_results"' in source, relative
assert 'PATHS.raw_root / "v2_results"' not in source, relative
def test_transient_content_artifacts_use_tmp_root() -> None:
doc_writer = _source("data/tools/feishu_doc_writer.py")
report_card = _source("data/tools/daily_report_card.py")
assert 'TMP_DIR = PATHS.tmp_root / "feishu_doc_writer"' in doc_writer
assert 'PATHS.tmp_root / "daily_report_card"' in report_card
assert 'chart_path.with_name(f"{chart_path.stem}_feishu.png")' not in report_card
def test_batch_launchers_never_fall_back_to_module_local_var() -> None:
for path in sorted((RUNTIME_ROOT / "data" / "tools").glob("*.bat")):
source = path.read_text(encoding="utf-8-sig")
if "GYXX_DATA_ROOT" not in source:
continue
assert '%PROJECT_DIR%\\var' not in source, path.name
assert '%GYXX_PROJECT_ROOT%\\var' in source, path.name
assert "if not defined GYXX_PROJECT_ROOT" in source, path.name
@pytest.mark.parametrize(
("relative", "layer_expression"),
(
("data/tools/analyze_comments.py", "PATHS.exports_root"),
("data/tools/analyze_note.py", "PATHS.exports_root"),
("data/tools/collect_note_metrics.py", "PATHS.normalized_root"),
("data/tools/generate_creator_report.py", "PATHS.exports_root"),
),
)
def test_cli_output_overrides_are_constrained_to_a_data_layer(
relative: str, layer_expression: str
) -> None:
source = _source(relative)
assert "resolve_layer_output(" in source
assert layer_expression in source