Files
vault-para/tests/test_interfaces.py
windyboy f99056a099 feat(tagging-system): Add comprehensive tagging system implementation
- Add tagging system Python package with modular architecture (config, core, impl)
- Create core interfaces for file discovery, content analysis, and tag generation
- Implement content analyzer with language detection and topic extraction
- Implement file discovery engine with directory scanning and filtering
- Implement tag generator with hierarchical tag creation and consolidation
- Add configuration management with example config and validation
- Create comprehensive design and requirements documentation in .kiro/specs
- Add pytest test suite with unit tests for models, interfaces, and config
- Add setup.py, requirements.txt, and pytest.ini for package management
- Add README.md with project overview and usage instructions
- Update workspace.json with new project structure
- Add Excalidraw diagram for system architecture visualization
- Establish foundation for automated vault tagging and metadata management
2025-12-31 08:37:16 +08:00

232 lines
7.9 KiB
Python

"""Unit tests for core interfaces and protocols."""
import pytest
from typing import List
from tagging_system.core.interfaces import (
FileDiscovery,
ContentAnalyzer,
TagGenerator,
FrontmatterManager,
BaseFileDiscovery,
BaseContentAnalyzer,
BaseTagGenerator,
BaseFrontmatterManager
)
from tagging_system.core.models import (
FileInfo,
ContentAnalysis,
TagStructure,
FrontmatterData,
ValidationResult,
ContentType,
LanguageInfo
)
class MockFileDiscovery(BaseFileDiscovery):
"""Mock implementation of FileDiscovery for testing."""
def scan_directory(self, path: str) -> List[FileInfo]:
"""Mock directory scanning."""
from datetime import datetime
return [
FileInfo(
path=f"{path}/test.md",
name="test.md",
directory=path,
extension=".md",
size=100,
created=datetime.now(),
modified=datetime.now()
)
]
def filter_by_type(self, files: List[FileInfo], types: List[str]) -> List[FileInfo]:
"""Mock file filtering."""
return [f for f in files if f.extension in types]
def exclude_sensitive(self, files: List[FileInfo]) -> List[FileInfo]:
"""Mock sensitive file exclusion."""
return [f for f in files if "sensitive" not in f.path]
class MockContentAnalyzer(BaseContentAnalyzer):
"""Mock implementation of ContentAnalyzer for testing."""
def analyze_content(self, content: str) -> ContentAnalysis:
"""Mock content analysis."""
return ContentAnalysis(
language=LanguageInfo.ENGLISH,
content_type=ContentType.NOTE,
topics=["test"],
mentions={'tools': [], 'technologies': [], 'people': [], 'organizations': []}
)
def detect_language(self, content: str) -> str:
"""Mock language detection."""
return "en"
def extract_topics(self, content: str) -> List[str]:
"""Mock topic extraction."""
return ["test", "mock"]
def classify_content_type(self, content: str, filename: str) -> str:
"""Mock content type classification."""
return "note"
class MockTagGenerator(BaseTagGenerator):
"""Mock implementation of TagGenerator for testing."""
def generate_directory_tags(self, filepath: str) -> List[str]:
"""Mock directory tag generation."""
if "100-project" in filepath:
return ["project"]
return ["unknown"]
def generate_content_tags(self, analysis: ContentAnalysis) -> List[str]:
"""Mock content tag generation."""
return analysis.topics
def generate_hierarchical_tags(self, topics: List[str]) -> List[str]:
"""Mock hierarchical tag generation."""
return [f"topic/{topic}" for topic in topics]
def consolidate_tags(self, tags: List[str]) -> List[str]:
"""Mock tag consolidation."""
return list(set(tags)) # Remove duplicates
class MockFrontmatterManager(BaseFrontmatterManager):
"""Mock implementation of FrontmatterManager for testing."""
def parse_frontmatter(self, content: str) -> FrontmatterData:
"""Mock frontmatter parsing."""
return FrontmatterData(
title="Test",
tags=["test"],
created="2024-01-01"
)
def update_frontmatter(self, content: str, updates: FrontmatterData) -> str:
"""Mock frontmatter updating."""
return f"---\ntitle: {updates.title}\ntags: {updates.tags}\n---\n{content}"
def validate_frontmatter(self, data: FrontmatterData) -> ValidationResult:
"""Mock frontmatter validation."""
result = ValidationResult(is_valid=True)
if not data.title:
result.add_error("Title is required")
return result
class TestProtocolCompliance:
"""Test that mock implementations comply with protocols."""
def test_file_discovery_protocol_compliance(self):
"""Test that MockFileDiscovery implements FileDiscovery protocol."""
mock = MockFileDiscovery()
assert isinstance(mock, FileDiscovery)
# Test method calls
files = mock.scan_directory("test")
assert len(files) == 1
assert files[0].name == "test.md"
filtered = mock.filter_by_type(files, [".md"])
assert len(filtered) == 1
non_sensitive = mock.exclude_sensitive(files)
assert len(non_sensitive) == 1
def test_content_analyzer_protocol_compliance(self):
"""Test that MockContentAnalyzer implements ContentAnalyzer protocol."""
mock = MockContentAnalyzer()
assert isinstance(mock, ContentAnalyzer)
# Test method calls
analysis = mock.analyze_content("test content")
assert analysis.language == LanguageInfo.ENGLISH
assert analysis.content_type == ContentType.NOTE
language = mock.detect_language("test content")
assert language == "en"
topics = mock.extract_topics("test content")
assert "test" in topics
content_type = mock.classify_content_type("test content", "test.md")
assert content_type == "note"
def test_tag_generator_protocol_compliance(self):
"""Test that MockTagGenerator implements TagGenerator protocol."""
mock = MockTagGenerator()
assert isinstance(mock, TagGenerator)
# Test method calls
dir_tags = mock.generate_directory_tags("100-project/test.md")
assert "project" in dir_tags
analysis = ContentAnalysis(
language=LanguageInfo.ENGLISH,
content_type=ContentType.NOTE,
topics=["ai", "ml"]
)
content_tags = mock.generate_content_tags(analysis)
assert "ai" in content_tags
hierarchical = mock.generate_hierarchical_tags(["ai", "ml"])
assert "topic/ai" in hierarchical
consolidated = mock.consolidate_tags(["tag1", "tag1", "tag2"])
assert len(consolidated) == 2
def test_frontmatter_manager_protocol_compliance(self):
"""Test that MockFrontmatterManager implements FrontmatterManager protocol."""
mock = MockFrontmatterManager()
assert isinstance(mock, FrontmatterManager)
# Test method calls
frontmatter = mock.parse_frontmatter("---\ntitle: Test\n---\nContent")
assert frontmatter.title == "Test"
updated = mock.update_frontmatter("Content", frontmatter)
assert "title: Test" in updated
validation = mock.validate_frontmatter(frontmatter)
assert validation.is_valid is True
class TestAbstractBaseClasses:
"""Test abstract base class behavior."""
def test_base_classes_cannot_be_instantiated(self):
"""Test that abstract base classes cannot be instantiated directly."""
with pytest.raises(TypeError):
BaseFileDiscovery()
with pytest.raises(TypeError):
BaseContentAnalyzer()
with pytest.raises(TypeError):
BaseTagGenerator()
with pytest.raises(TypeError):
BaseFrontmatterManager()
def test_concrete_implementations_work(self):
"""Test that concrete implementations of base classes work."""
file_discovery = MockFileDiscovery()
content_analyzer = MockContentAnalyzer()
tag_generator = MockTagGenerator()
frontmatter_manager = MockFrontmatterManager()
# All should be instances of their respective base classes
assert isinstance(file_discovery, BaseFileDiscovery)
assert isinstance(content_analyzer, BaseContentAnalyzer)
assert isinstance(tag_generator, BaseTagGenerator)
assert isinstance(frontmatter_manager, BaseFrontmatterManager)