This repository has been archived on 2026-08-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
starmen1265 c9ca1b538d 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>
2026-03-17 00:17:10 +08:00

223 lines
8.2 KiB
Python

"""Tests for ArchiveProcessor
Tests that ArchiveProcessor:
- Copies md file to correct platform directory
- Uses correct filename format {YYYYMMDD}_{content_id}_analysis.md
- Skips when step already completed
- Detects existing archive by content_id
- Handles missing analysis file with error
"""
import pytest
from datetime import datetime
from pathlib import Path
from unittest.mock import patch, MagicMock
from kb_workflow.pipeline.processors.archive import ArchiveProcessor
from kb_workflow.pipeline.schemas import ArchiveInput, ArchiveOutput
from kb_workflow.pipeline.state import WorkflowStateManager
# =============================================================================
# Fixtures
# =============================================================================
@pytest.fixture
def archive_test_env(tmp_path):
"""Create test environment for archive tests"""
# Create intermediate content directory
content_dir = tmp_path / "intermediate" / "bilibili_BV1test"
content_dir.mkdir(parents=True)
# Create analysis file
analysis_file = content_dir / "analysis.md"
analysis_file.write_text("# Test Analysis\n\nContent here.")
# Create archive directory
archive_dir = tmp_path / "archive"
archive_dir.mkdir(parents=True)
# Create state manager
state_manager = WorkflowStateManager(content_dir)
return {
"content_dir": content_dir,
"analysis_file": analysis_file,
"archive_dir": archive_dir,
"state_manager": state_manager,
}
# =============================================================================
# ArchiveProcessor Tests
# =============================================================================
class TestArchiveProcessorCopyToPlatformDir:
"""Test that archive file is copied to correct platform directory"""
@pytest.mark.asyncio
async def test_copies_to_platform_dir(self, archive_test_env):
"""Test that archive file is copied to correct platform directory"""
mock_settings = MagicMock()
mock_settings.archive_path = str(archive_test_env["archive_dir"])
with patch('kb_workflow.pipeline.processors.archive.get_settings') as mock_get_settings:
mock_get_settings.return_value = mock_settings
processor = ArchiveProcessor()
# Mock _ask_overwrite to return True (user says yes) in case of collision
processor._ask_overwrite = lambda f: True
input_data = ArchiveInput(
content_id="BV1test",
platform="bilibili",
content_dir=str(archive_test_env["content_dir"]),
analysis_file=str(archive_test_env["analysis_file"]),
content_type="video"
)
result = await processor.process(input_data)
assert result.success is True
assert result.skipped is False
assert "bilibili" in result.archive_file
assert result.archive_file.endswith("_analysis.md")
class TestArchiveProcessorFilenameFormat:
"""Test that archive filename follows correct format"""
@pytest.mark.asyncio
async def test_filename_format(self, archive_test_env):
"""Test that archive filename follows {YYYYMMDD}_{content_id}_analysis.md format"""
mock_settings = MagicMock()
mock_settings.archive_path = str(archive_test_env["archive_dir"])
with patch('kb_workflow.pipeline.processors.archive.get_settings') as mock_get_settings:
mock_get_settings.return_value = mock_settings
processor = ArchiveProcessor()
# Mock _ask_overwrite to return True (user says yes) in case of collision
processor._ask_overwrite = lambda f: True
input_data = ArchiveInput(
content_id="BV1test",
platform="bilibili",
content_dir=str(archive_test_env["content_dir"]),
analysis_file=str(archive_test_env["analysis_file"]),
content_type="video"
)
result = await processor.process(input_data)
# Check filename format
archive_path = Path(result.archive_file)
filename = archive_path.name
today = datetime.now().strftime("%Y%m%d")
expected = f"{today}_BV1test_analysis.md"
assert filename == expected
class TestArchiveProcessorSkipsCompleted:
"""Test that archive step is skipped when already completed"""
@pytest.mark.asyncio
async def test_skips_completed(self, archive_test_env):
"""Test that archive step is skipped when already completed"""
mock_settings = MagicMock()
mock_settings.archive_path = str(archive_test_env["archive_dir"])
# Create state with archive already completed
state = archive_test_env["state_manager"].get_or_create(
content_id="BV1test",
platform="bilibili",
source_url="https://test.com"
)
state.mark_step_completed("archive")
archive_test_env["state_manager"].save(state)
with patch('kb_workflow.pipeline.processors.archive.get_settings') as mock_get_settings:
mock_get_settings.return_value = mock_settings
processor = ArchiveProcessor(state_manager=archive_test_env["state_manager"])
input_data = ArchiveInput(
content_id="BV1test",
platform="bilibili",
content_dir=str(archive_test_env["content_dir"]),
analysis_file=str(archive_test_env["analysis_file"]),
content_type="video"
)
result = await processor.process(input_data)
assert result.success is True
assert result.skipped is True
class TestArchiveProcessorDetectsExisting:
"""Test that archive processor detects existing archive by content_id"""
@pytest.mark.asyncio
async def test_detects_existing_archive(self, archive_test_env):
"""Test that archive processor detects existing archive by content_id"""
mock_settings = MagicMock()
mock_settings.archive_path = str(archive_test_env["archive_dir"])
# Create existing archive with different timestamp
platform_dir = archive_test_env["archive_dir"] / "bilibili"
platform_dir.mkdir(parents=True, exist_ok=True)
existing = platform_dir / "20260101_BV1test_analysis.md"
existing.write_text("Old content")
with patch('kb_workflow.pipeline.processors.archive.get_settings') as mock_get_settings:
mock_get_settings.return_value = mock_settings
processor = ArchiveProcessor()
# Mock _ask_overwrite to return False (user says no)
processor._ask_overwrite = lambda f: False
input_data = ArchiveInput(
content_id="BV1test",
platform="bilibili",
content_dir=str(archive_test_env["content_dir"]),
analysis_file=str(archive_test_env["analysis_file"]),
content_type="video"
)
result = await processor.process(input_data)
assert result.success is True
assert result.skipped is True
class TestArchiveProcessorHandlesMissingFile:
"""Test that archive processor handles missing analysis file"""
@pytest.mark.asyncio
async def test_handles_missing_file(self, archive_test_env):
"""Test that archive processor handles missing analysis file"""
mock_settings = MagicMock()
mock_settings.archive_path = str(archive_test_env["archive_dir"])
with patch('kb_workflow.pipeline.processors.archive.get_settings') as mock_get_settings:
mock_get_settings.return_value = mock_settings
processor = ArchiveProcessor()
# Mock _ask_overwrite to avoid stdin issues
processor._ask_overwrite = lambda f: True
input_data = ArchiveInput(
content_id="BV1test",
platform="bilibili",
content_dir=str(archive_test_env["content_dir"]),
analysis_file="/nonexistent/analysis.md", # Missing file
content_type="video"
)
result = await processor.process(input_data)
assert result.success is False
assert "not found" in result.error_message.lower()