refactor(06-02): update CLI and test imports to kb_workflow.pipeline

- Remove sys.path manipulation from kb_workflow.py
- Update CLI imports to use kb_workflow.pipeline prefix
- Update all test file imports and patch paths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-17 00:17:10 +08:00
parent fdca690bd6
commit c9ca1b538d
20 changed files with 307 additions and 307 deletions
+40 -40
View File
@@ -20,7 +20,7 @@ class TestPipelineInput:
def test_schema_pipeline_input_validates_url_field(self):
"""PipelineInput must include url field (required)"""
from kb_pipeline.schemas import PipelineInput
from kb_workflow.pipeline.schemas import PipelineInput
# Missing url
with pytest.raises(ValidationError) as exc_info:
@@ -37,7 +37,7 @@ class TestPipelineInput:
def test_schema_pipeline_input_has_optional_output_dir(self):
"""PipelineInput has optional output_dir field"""
from kb_pipeline.schemas import PipelineInput
from kb_workflow.pipeline.schemas import PipelineInput
# Without output_dir
input_data = PipelineInput(url="https://www.bilibili.com/video/BV1xx")
@@ -52,7 +52,7 @@ class TestPipelineInput:
def test_schema_pipeline_input_has_skip_completed_default(self):
"""PipelineInput skip_completed defaults to True"""
from kb_pipeline.schemas import PipelineInput
from kb_workflow.pipeline.schemas import PipelineInput
input_data = PipelineInput(url="https://www.bilibili.com/video/BV1xx")
assert input_data.skip_completed is True
@@ -70,7 +70,7 @@ class TestPipelineOutput:
def test_schema_pipeline_output_contains_results_dict(self):
"""PipelineOutput contains results dict"""
from kb_pipeline.schemas import PipelineOutput
from kb_workflow.pipeline.schemas import PipelineOutput
output = PipelineOutput(success=True)
assert hasattr(output, "results")
@@ -79,7 +79,7 @@ class TestPipelineOutput:
def test_schema_pipeline_output_has_success_field(self):
"""PipelineOutput requires success field"""
from kb_pipeline.schemas import PipelineOutput
from kb_workflow.pipeline.schemas import PipelineOutput
with pytest.raises(ValidationError) as exc_info:
PipelineOutput()
@@ -87,7 +87,7 @@ class TestPipelineOutput:
def test_schema_pipeline_output_has_optional_error_message(self):
"""PipelineOutput has optional error_message field"""
from kb_pipeline.schemas import PipelineOutput
from kb_workflow.pipeline.schemas import PipelineOutput
output = PipelineOutput(success=True)
assert output.error_message is None
@@ -100,7 +100,7 @@ class TestPipelineOutput:
def test_schema_pipeline_output_has_content_dir(self):
"""PipelineOutput has optional content_dir field"""
from kb_pipeline.schemas import PipelineOutput
from kb_workflow.pipeline.schemas import PipelineOutput
output = PipelineOutput(success=True)
assert output.content_dir is None
@@ -117,7 +117,7 @@ class TestPipelineConfig:
def test_schema_pipeline_config_has_default_steps_list(self):
"""PipelineConfig has default steps list"""
from kb_pipeline.schemas import PipelineConfig
from kb_workflow.pipeline.schemas import PipelineConfig
config = PipelineConfig()
assert hasattr(config, "steps")
@@ -126,14 +126,14 @@ class TestPipelineConfig:
def test_schema_pipeline_config_allows_custom_steps(self):
"""PipelineConfig allows custom steps list"""
from kb_pipeline.schemas import PipelineConfig
from kb_workflow.pipeline.schemas import PipelineConfig
custom_config = PipelineConfig(steps=["parse", "download"])
assert custom_config.steps == ["parse", "download"]
def test_schema_pipeline_config_allows_custom_output_dir(self):
"""PipelineConfig allows custom output_dir"""
from kb_pipeline.schemas import PipelineConfig
from kb_workflow.pipeline.schemas import PipelineConfig
config = PipelineConfig(output_dir=Path("/custom/output"))
assert config.output_dir == Path("/custom/output")
@@ -144,14 +144,14 @@ class TestPipelineConfig:
def test_schema_pipeline_config_has_skip_completed_default(self):
"""PipelineConfig skip_completed defaults to True"""
from kb_pipeline.schemas import PipelineConfig
from kb_workflow.pipeline.schemas import PipelineConfig
config = PipelineConfig()
assert config.skip_completed is True
def test_schema_pipeline_config_has_max_retries_default(self):
"""PipelineConfig max_retries defaults to 3"""
from kb_pipeline.schemas import PipelineConfig
from kb_workflow.pipeline.schemas import PipelineConfig
config = PipelineConfig()
assert config.max_retries == 3
@@ -171,8 +171,8 @@ class TestPipelineInit:
def test_init_pipeline_with_processors(self):
"""Pipeline initializes with all processors"""
from kb_pipeline.pipeline import Pipeline
from kb_pipeline.schemas import PipelineConfig
from kb_workflow.pipeline.pipeline import Pipeline
from kb_workflow.pipeline.schemas import PipelineConfig
mock_processor1 = MagicMock()
mock_processor1.STEP_NAME = "parse"
@@ -180,7 +180,7 @@ class TestPipelineInit:
mock_processor2 = MagicMock()
mock_processor2.STEP_NAME = "download"
with patch("kb_pipeline.pipeline.get_settings") as mock_settings:
with patch("kb_workflow.pipeline.pipeline.get_settings") as mock_settings:
mock_settings.return_value = MagicMock()
pipeline = Pipeline(processors=[mock_processor1, mock_processor2])
@@ -192,15 +192,15 @@ class TestPipelineInit:
def test_init_pipeline_validates_processor_step_name(self):
"""Pipeline validates processor STEP_NAME matches config.steps"""
from kb_pipeline.pipeline import Pipeline
from kb_pipeline.schemas import PipelineConfig
from kb_workflow.pipeline.pipeline import Pipeline
from kb_workflow.pipeline.schemas import PipelineConfig
mock_processor = MagicMock()
mock_processor.STEP_NAME = "unknown_step"
config = PipelineConfig(steps=["parse", "download"])
with patch("kb_pipeline.pipeline.get_settings") as mock_settings:
with patch("kb_workflow.pipeline.pipeline.get_settings") as mock_settings:
mock_settings.return_value = MagicMock()
with pytest.raises(ValueError) as exc_info:
@@ -210,13 +210,13 @@ class TestPipelineInit:
def test_init_pipeline_handles_missing_processor_gracefully(self):
"""Pipeline handles missing processor gracefully"""
from kb_pipeline.pipeline import Pipeline
from kb_pipeline.schemas import PipelineConfig
from kb_workflow.pipeline.pipeline import Pipeline
from kb_workflow.pipeline.schemas import PipelineConfig
# Empty processors list should work
config = PipelineConfig(steps=[])
with patch("kb_pipeline.pipeline.get_settings") as mock_settings:
with patch("kb_workflow.pipeline.pipeline.get_settings") as mock_settings:
mock_settings.return_value = MagicMock()
pipeline = Pipeline(processors=[], config=config)
@@ -228,14 +228,14 @@ class TestPipelineParseUrl:
def test_parse_url_creates_correct_content_directory(self):
"""Pipeline._get_content_dir creates correct content directory"""
from kb_pipeline.pipeline import Pipeline
from kb_pipeline.link_parser import ParsedLink
from kb_workflow.pipeline.pipeline import Pipeline
from kb_workflow.pipeline.link_parser import ParsedLink
import tempfile
mock_processor = MagicMock()
mock_processor.STEP_NAME = "parse"
with patch("kb_pipeline.pipeline.get_settings") as mock_settings:
with patch("kb_workflow.pipeline.pipeline.get_settings") as mock_settings:
mock_settings.return_value = MagicMock()
with tempfile.TemporaryDirectory() as tmpdir:
@@ -258,12 +258,12 @@ class TestPipelineParseUrl:
def test_parse_url_returns_none_for_empty_url(self):
"""Pipeline._parse_url returns None for empty URL"""
from kb_pipeline.pipeline import Pipeline
from kb_workflow.pipeline.pipeline import Pipeline
mock_processor = MagicMock()
mock_processor.STEP_NAME = "parse"
with patch("kb_pipeline.pipeline.get_settings") as mock_settings:
with patch("kb_workflow.pipeline.pipeline.get_settings") as mock_settings:
mock_settings.return_value = MagicMock()
pipeline = Pipeline(processors=[mock_processor])
@@ -273,12 +273,12 @@ class TestPipelineParseUrl:
def test_parse_url_returns_none_for_invalid_url(self):
"""Pipeline._parse_url returns None for unrecognized URL"""
from kb_pipeline.pipeline import Pipeline
from kb_workflow.pipeline.pipeline import Pipeline
mock_processor = MagicMock()
mock_processor.STEP_NAME = "parse"
with patch("kb_pipeline.pipeline.get_settings") as mock_settings:
with patch("kb_workflow.pipeline.pipeline.get_settings") as mock_settings:
mock_settings.return_value = MagicMock()
pipeline = Pipeline(processors=[mock_processor])
@@ -288,12 +288,12 @@ class TestPipelineParseUrl:
def test_parse_url_parses_bilibili_url(self):
"""Pipeline._parse_url correctly parses Bilibili URL"""
from kb_pipeline.pipeline import Pipeline
from kb_workflow.pipeline.pipeline import Pipeline
mock_processor = MagicMock()
mock_processor.STEP_NAME = "parse"
with patch("kb_pipeline.pipeline.get_settings") as mock_settings:
with patch("kb_workflow.pipeline.pipeline.get_settings") as mock_settings:
mock_settings.return_value = MagicMock()
pipeline = Pipeline(processors=[mock_processor])
@@ -373,8 +373,8 @@ class TestPipelineArchiveStep:
@pytest.mark.asyncio
async def test_pipeline_includes_archive_step(self, mock_processors_with_archive, tmp_path):
"""Test that pipeline includes archive as final step"""
from kb_pipeline.pipeline import Pipeline
from kb_pipeline.schemas import PipelineConfig
from kb_workflow.pipeline.pipeline import Pipeline
from kb_workflow.pipeline.schemas import PipelineConfig
config = PipelineConfig(
steps=["parse", "download", "asr", "analyze", "archive"],
@@ -382,7 +382,7 @@ class TestPipelineArchiveStep:
skip_completed=False
)
with patch("kb_pipeline.pipeline.get_settings") as mock_settings:
with patch("kb_workflow.pipeline.pipeline.get_settings") as mock_settings:
mock_settings.return_value = MagicMock(intermediate_path=str(tmp_path))
pipeline = Pipeline(
@@ -395,7 +395,7 @@ class TestPipelineArchiveStep:
with patch.object(pipeline, '_parse_url') as mock_parse, \
patch.object(pipeline, '_get_content_dir') as mock_dir:
from kb_pipeline.link_parser import ParsedLink
from kb_workflow.pipeline.link_parser import ParsedLink
mock_parse.return_value = ParsedLink(
platform="bilibili",
content_type="video",
@@ -419,8 +419,8 @@ class TestArchivePlatformDirectory:
@pytest.mark.asyncio
async def test_archive_uses_platform_directory(self, tmp_path):
"""Test that archive files go to {archive_path}/{platform}/ directory"""
from kb_pipeline.processors.archive import ArchiveProcessor
from kb_pipeline.schemas import ArchiveInput
from kb_workflow.pipeline.processors.archive import ArchiveProcessor
from kb_workflow.pipeline.schemas import ArchiveInput
# Setup
archive_dir = tmp_path / "archive"
@@ -430,7 +430,7 @@ class TestArchivePlatformDirectory:
analysis_file.write_text("# Test")
# Mock settings - patch at base module level where get_settings is called in __init__
with patch('kb_pipeline.processors.base.get_settings') as mock_get_settings:
with patch('kb_workflow.pipeline.processors.base.get_settings') as mock_get_settings:
mock_settings = MagicMock()
mock_settings.archive_path = str(archive_dir)
mock_get_settings.return_value = mock_settings
@@ -461,8 +461,8 @@ class TestArchiveNoDateSubdirectories:
@pytest.mark.asyncio
async def test_archive_no_date_subdirectories(self, tmp_path):
"""Test that archive does not create date-based subdirectories"""
from kb_pipeline.processors.archive import ArchiveProcessor
from kb_pipeline.schemas import ArchiveInput
from kb_workflow.pipeline.processors.archive import ArchiveProcessor
from kb_workflow.pipeline.schemas import ArchiveInput
archive_dir = tmp_path / "archive"
content_dir = tmp_path / "intermediate" / "bilibili_BV1test"
@@ -470,7 +470,7 @@ class TestArchiveNoDateSubdirectories:
analysis_file = content_dir / "analysis.md"
analysis_file.write_text("# Test")
with patch('kb_pipeline.processors.base.get_settings') as mock_get_settings:
with patch('kb_workflow.pipeline.processors.base.get_settings') as mock_get_settings:
mock_settings = MagicMock()
mock_settings.archive_path = str(archive_dir)
mock_get_settings.return_value = mock_settings