123 lines
4.5 KiB
Python
123 lines
4.5 KiB
Python
"""M7 调度与日报模块单元测试。"""
|
||
|
||
from unittest.mock import patch
|
||
|
||
from scheduler.pipeline import PipelineResult, StepResult
|
||
|
||
# --------------------------------------------------------------------------- #
|
||
# StepResult
|
||
# --------------------------------------------------------------------------- #
|
||
|
||
|
||
class TestStepResult:
|
||
"""StepResult 测试。"""
|
||
|
||
def test_success_step(self):
|
||
sr = StepResult(name="extract", success=True, elapsed_sec=5.0, message="17 篇")
|
||
assert sr.name == "extract"
|
||
assert sr.success
|
||
assert sr.message == "17 篇"
|
||
|
||
def test_failed_step(self):
|
||
sr = StepResult(name="translate", success=False, elapsed_sec=30.0,
|
||
message="API timeout")
|
||
assert not sr.success
|
||
assert "timeout" in sr.message
|
||
|
||
|
||
# --------------------------------------------------------------------------- #
|
||
# PipelineResult
|
||
# --------------------------------------------------------------------------- #
|
||
|
||
|
||
class TestPipelineResult:
|
||
"""PipelineResult 测试。"""
|
||
|
||
def test_all_success(self):
|
||
result = PipelineResult(steps=[
|
||
StepResult(name="extract", success=True, elapsed_sec=1.0),
|
||
StepResult(name="dedup", success=True, elapsed_sec=0.5),
|
||
])
|
||
assert result.all_success
|
||
assert result.success_count == 2
|
||
|
||
def test_partial_failure(self):
|
||
result = PipelineResult(steps=[
|
||
StepResult(name="extract", success=True, elapsed_sec=1.0),
|
||
StepResult(name="translate", success=False, elapsed_sec=30.0),
|
||
StepResult(name="embed", success=True, elapsed_sec=2.0),
|
||
])
|
||
assert not result.all_success
|
||
assert result.success_count == 2
|
||
|
||
def test_empty(self):
|
||
result = PipelineResult()
|
||
assert result.all_success # 空集合 vacuously true
|
||
assert result.success_count == 0
|
||
|
||
|
||
# --------------------------------------------------------------------------- #
|
||
# run_pipeline(mock 各模块)
|
||
# --------------------------------------------------------------------------- #
|
||
|
||
|
||
class TestRunPipeline:
|
||
"""run_pipeline 测试(mock 各步骤)。"""
|
||
|
||
@patch("scheduler.pipeline.run_step_extract")
|
||
@patch("scheduler.pipeline.run_step_dedup")
|
||
@patch("scheduler.pipeline.run_step_embed")
|
||
@patch("scheduler.pipeline.run_step_index")
|
||
def test_pipeline_runs_all_steps(self, mock_idx, mock_emb, mock_dedup, mock_ext):
|
||
from scheduler.pipeline import run_pipeline
|
||
|
||
mock_ext.return_value = StepResult(name="extract", success=True, elapsed_sec=1)
|
||
mock_dedup.return_value = StepResult(name="dedup", success=True, elapsed_sec=1)
|
||
mock_emb.return_value = StepResult(name="embed", success=True, elapsed_sec=1)
|
||
mock_idx.return_value = StepResult(name="index", success=True, elapsed_sec=1)
|
||
|
||
result = run_pipeline("20260621", skip_report=True)
|
||
assert result.success_count >= 4
|
||
|
||
@patch("scheduler.pipeline.run_step_extract")
|
||
@patch("scheduler.pipeline.run_step_dedup")
|
||
def test_pipeline_continues_on_failure(self, mock_dedup, mock_ext):
|
||
from scheduler.pipeline import run_pipeline
|
||
|
||
mock_ext.return_value = StepResult(name="extract", success=False, elapsed_sec=1,
|
||
message="error")
|
||
mock_dedup.return_value = StepResult(name="dedup", success=True, elapsed_sec=1)
|
||
|
||
result = run_pipeline("20260621", steps=["extract", "dedup"], skip_report=True)
|
||
# extract 失败但 dedup 仍然执行
|
||
assert result.success_count == 1
|
||
|
||
def test_unknown_step_skipped(self):
|
||
from scheduler.pipeline import run_pipeline
|
||
result = run_pipeline("20260621", steps=["nonexistent_step"], skip_report=True)
|
||
assert len(result.steps) == 1
|
||
assert not result.steps[0].success
|
||
|
||
|
||
# --------------------------------------------------------------------------- #
|
||
# generate_report(无数据场景)
|
||
# --------------------------------------------------------------------------- #
|
||
|
||
|
||
class TestGenerateReport:
|
||
"""generate_report 测试。"""
|
||
|
||
@patch("scheduler.reporter._load_events_window")
|
||
@patch("scheduler.reporter._collect_stats_window")
|
||
def test_no_data_returns_none(self, mock_stats, mock_events):
|
||
from scheduler.reporter import generate_report
|
||
|
||
mock_events.return_value = []
|
||
mock_stats.return_value = {
|
||
"proc": 0, "deduped": 0, "emb_count": 0,
|
||
"qdrant_count": 0, "raw_total": 0, "raw_by_source": {},
|
||
}
|
||
|
||
result = generate_report()
|
||
assert result is None
|