232 lines
7.9 KiB
Python
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)
|