- 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
178 lines
5.9 KiB
Python
178 lines
5.9 KiB
Python
"""Unit tests for configuration system."""
|
|
|
|
import pytest
|
|
import json
|
|
from pathlib import Path
|
|
from tagging_system.config import (
|
|
TaggingConfig,
|
|
DirectoryMapping,
|
|
TagHierarchy,
|
|
SensitivePatterns,
|
|
load_config,
|
|
save_config
|
|
)
|
|
|
|
|
|
class TestDirectoryMapping:
|
|
"""Test DirectoryMapping model."""
|
|
|
|
def test_directory_mapping_creation(self):
|
|
"""Test DirectoryMapping creation."""
|
|
mapping = DirectoryMapping(
|
|
pattern="100-project",
|
|
primary_tag="project",
|
|
hierarchical_tags=["tech"],
|
|
exclude_patterns=["*.tmp"]
|
|
)
|
|
|
|
assert mapping.pattern == "100-project"
|
|
assert mapping.primary_tag == "project"
|
|
assert mapping.hierarchical_tags == ["tech"]
|
|
assert mapping.exclude_patterns == ["*.tmp"]
|
|
|
|
|
|
class TestTagHierarchy:
|
|
"""Test TagHierarchy model."""
|
|
|
|
def test_tag_hierarchy_creation(self):
|
|
"""Test TagHierarchy creation."""
|
|
hierarchy = TagHierarchy(
|
|
root="tech",
|
|
children={"ai": TagHierarchy("ai")},
|
|
aliases=["technology"]
|
|
)
|
|
|
|
assert hierarchy.root == "tech"
|
|
assert "ai" in hierarchy.children
|
|
assert hierarchy.aliases == ["technology"]
|
|
|
|
def test_get_full_path(self):
|
|
"""Test get_full_path method."""
|
|
hierarchy = TagHierarchy("tech")
|
|
|
|
assert hierarchy.get_full_path() == "tech"
|
|
assert hierarchy.get_full_path("ai") == "tech/ai"
|
|
assert hierarchy.get_full_path("ai/llm") == "tech/ai/llm"
|
|
|
|
|
|
class TestSensitivePatterns:
|
|
"""Test SensitivePatterns model."""
|
|
|
|
def test_sensitive_patterns_defaults(self):
|
|
"""Test SensitivePatterns default values."""
|
|
patterns = SensitivePatterns()
|
|
|
|
assert len(patterns.credential_patterns) > 0
|
|
assert len(patterns.personal_patterns) > 0
|
|
assert len(patterns.financial_patterns) > 0
|
|
assert any("api" in pattern for pattern in patterns.credential_patterns)
|
|
|
|
|
|
class TestTaggingConfig:
|
|
"""Test TaggingConfig model."""
|
|
|
|
def test_tagging_config_defaults(self):
|
|
"""Test TaggingConfig default values."""
|
|
config = TaggingConfig()
|
|
|
|
assert len(config.directory_mappings) > 0
|
|
assert len(config.tag_hierarchies) > 0
|
|
assert len(config.excluded_directories) > 0
|
|
assert config.tag_format_rules["case"] == "kebab"
|
|
|
|
def test_get_directory_mapping(self):
|
|
"""Test get_directory_mapping method."""
|
|
config = TaggingConfig()
|
|
|
|
mapping = config.get_directory_mapping("100-project/AI/test.md")
|
|
assert mapping is not None
|
|
assert mapping.primary_tag == "project"
|
|
|
|
no_mapping = config.get_directory_mapping("unknown/path")
|
|
assert no_mapping is None
|
|
|
|
def test_get_tag_hierarchy(self):
|
|
"""Test get_tag_hierarchy method."""
|
|
config = TaggingConfig()
|
|
|
|
tech_hierarchy = config.get_tag_hierarchy("tech")
|
|
assert tech_hierarchy is not None
|
|
assert tech_hierarchy.root == "tech"
|
|
|
|
unknown_hierarchy = config.get_tag_hierarchy("unknown")
|
|
assert unknown_hierarchy is None
|
|
|
|
def test_is_excluded_directory(self):
|
|
"""Test is_excluded_directory method."""
|
|
config = TaggingConfig()
|
|
|
|
assert config.is_excluded_directory(".obsidian/plugins") is True
|
|
assert config.is_excluded_directory("100-project/AI") is False
|
|
|
|
def test_is_excluded_file(self):
|
|
"""Test is_excluded_file method."""
|
|
config = TaggingConfig()
|
|
|
|
assert config.is_excluded_file("test.pyc") is True
|
|
assert config.is_excluded_file(".DS_Store") is True
|
|
assert config.is_excluded_file("test.md") is False
|
|
|
|
|
|
class TestConfigLoading:
|
|
"""Test configuration loading and saving."""
|
|
|
|
def test_load_default_config(self):
|
|
"""Test loading default config when no file exists."""
|
|
config = load_config()
|
|
|
|
assert isinstance(config, TaggingConfig)
|
|
assert len(config.directory_mappings) > 0
|
|
|
|
def test_load_config_creates_default_file(self, tmp_path: Path):
|
|
"""Test that load_config creates default file when path doesn't exist."""
|
|
config_path = tmp_path / "config.yaml"
|
|
|
|
config = load_config(config_path)
|
|
|
|
assert isinstance(config, TaggingConfig)
|
|
assert config_path.exists()
|
|
|
|
def test_save_and_load_config(self, tmp_path: Path):
|
|
"""Test saving and loading configuration."""
|
|
config_path = tmp_path / "test_config.yaml"
|
|
original_config = TaggingConfig()
|
|
original_config.excluded_directories.append("test_exclude")
|
|
|
|
save_config(original_config, config_path)
|
|
loaded_config = load_config(config_path)
|
|
|
|
assert config_path.exists()
|
|
assert "test_exclude" in loaded_config.excluded_directories
|
|
|
|
def test_load_json_config(self, tmp_path: Path):
|
|
"""Test loading JSON configuration."""
|
|
config_path = tmp_path / "config.json"
|
|
config_data = {
|
|
"excluded_directories": [".test", ".custom"],
|
|
"tag_format_rules": {"case": "snake"}
|
|
}
|
|
|
|
with open(config_path, 'w') as f:
|
|
json.dump(config_data, f)
|
|
|
|
config = load_config(config_path)
|
|
|
|
assert ".test" in config.excluded_directories
|
|
assert ".custom" in config.excluded_directories
|
|
assert config.tag_format_rules["case"] == "snake"
|
|
|
|
def test_load_invalid_config_returns_default(self, tmp_path: Path):
|
|
"""Test that invalid config file returns default config."""
|
|
config_path = tmp_path / "invalid.yaml"
|
|
config_path.write_text("invalid: yaml: content: [")
|
|
|
|
config = load_config(config_path)
|
|
|
|
# Should return default config on error
|
|
assert isinstance(config, TaggingConfig)
|
|
assert len(config.directory_mappings) > 0 |