diff --git a/confsync/tests/test_manifest.py b/confsync/tests/test_manifest.py new file mode 100644 index 0000000..8b5fc4e --- /dev/null +++ b/confsync/tests/test_manifest.py @@ -0,0 +1,280 @@ +"""Tests for manifest operations.""" + +import pytest +import tempfile +import os + +from confsync.core.manifest import ManifestBuilder +from confsync.models.config_models import ( + Manifest, + ManifestEntry, + ConfigFile, + ConfigCategory, +) + + +class TestManifestBuilder: + """Tests for ManifestBuilder.""" + + def test_default_ignore_patterns(self): + """Test default ignore patterns are loaded.""" + builder = ManifestBuilder() + + assert ".git" in builder.ignore_patterns + assert "*.swp" in builder.ignore_patterns + assert "__pycache__" in builder.ignore_patterns + + def test_custom_ignore_patterns(self): + """Test custom ignore patterns.""" + builder = ManifestBuilder(ignore_patterns=["custom_pattern", "*.custom"]) + + assert "custom_pattern" in builder.ignore_patterns + assert "*.custom" in builder.ignore_patterns + assert ".git" in builder.ignore_patterns + + def test_should_ignore(self): + """Test ignore pattern matching.""" + builder = ManifestBuilder() + + assert builder.should_ignore("/path/to/.git/config") + assert builder.should_ignore("/path/to/file.swp") + assert builder.should_ignore("/path/to/__pycache__/file.py") + assert not builder.should_ignore("/path/to/normal/file.txt") + + def test_build_from_detected(self): + """Test building manifest from detected configs.""" + builder = ManifestBuilder() + + configs = [ + ConfigFile( + path="/test/.vimrc", + name=".vimrc", + category=ConfigCategory.EDITOR, + tool_name="vim", + content="set nocompatible", + ), + ConfigFile( + path="/test/.bashrc", + name=".bashrc", + category=ConfigCategory.SHELL, + tool_name="bash", + content="export PATH=$PATH", + ), + ] + + manifest = builder.build_from_detected(configs) + + assert len(manifest.entries) == 2 + assert manifest.version == "1.0.0" + + def test_save_and_load_manifest(self): + """Test saving and loading manifest.""" + builder = ManifestBuilder() + + config = ConfigFile( + path="/test/.vimrc", + name=".vimrc", + category=ConfigCategory.EDITOR, + tool_name="vim", + content="set nocompatible", + ) + + manifest = builder.build_from_detected([config]) + + with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: + temp_path = f.name + + try: + builder.save_manifest(manifest, temp_path) + + loaded = builder.load_manifest(temp_path) + + assert len(loaded.entries) == 1 + entry_id = list(loaded.entries.keys())[0] + assert loaded.entries[entry_id].config_file.tool_name == "vim" + finally: + os.unlink(temp_path) + + def test_get_default_priority(self): + """Test default priority by category.""" + builder = ManifestBuilder() + + assert builder._get_default_priority(ConfigCategory.GIT) == 10 + assert builder._get_default_priority(ConfigCategory.SHELL) == 9 + assert builder._get_default_priority(ConfigCategory.EDITOR) == 7 + assert builder._get_default_priority(ConfigCategory.MISC) == 1 + + def test_get_default_merge_strategy(self): + """Test default merge strategy by file type.""" + builder = ManifestBuilder() + + assert builder._get_default_merge_strategy( + ConfigFile(path="/test.json", name="test.json", category=ConfigCategory.EDITOR, tool_name="test") + ) == "three_way" + + assert builder._get_default_merge_strategy( + ConfigFile(path="/test.yaml", name="test.yaml", category=ConfigCategory.EDITOR, tool_name="test") + ) == "three_way" + + assert builder._get_default_merge_strategy( + ConfigFile(path="/test.ini", name="test.ini", category=ConfigCategory.MISC, tool_name="test") + ) == "keep_local" + + def test_get_summary(self): + """Test manifest summary generation.""" + builder = ManifestBuilder() + + configs = [ + ConfigFile( + path="/home/user/.vimrc", + name=".vimrc", + category=ConfigCategory.EDITOR, + tool_name="vim", + content="vim content", + ), + ConfigFile( + path="/home/user/.bashrc", + name=".bashrc", + category=ConfigCategory.SHELL, + tool_name="bash", + content="bash content", + ), + ConfigFile( + path="/home/user/.gitconfig", + name=".gitconfig", + category=ConfigCategory.GIT, + tool_name="git", + content="git content", + ), + ] + + manifest = builder.build_from_detected(configs) + summary = builder.get_summary(manifest) + + assert summary["total_files"] == 3 + assert summary["by_category"]["editor"] == 1 + assert summary["by_category"]["shell"] == 1 + assert summary["by_category"]["git"] == 1 + + +class TestManifestMerging: + """Tests for manifest merging.""" + + def test_merge_manifests_local_wins(self): + """Test merging manifests with local wins strategy.""" + builder = ManifestBuilder() + + shared_id = "test-shared-id" + + local_config = ConfigFile( + id=shared_id, + path="/test/.vimrc", + name=".vimrc", + category=ConfigCategory.EDITOR, + tool_name="vim", + content="local content", + file_hash="local_hash", + ) + + remote_config = ConfigFile( + id=shared_id, + path="/test/.vimrc", + name=".vimrc", + category=ConfigCategory.EDITOR, + tool_name="vim", + content="remote content", + file_hash="remote_hash", + ) + + local_manifest = builder.build_from_detected([local_config]) + remote_manifest = builder.build_from_detected([remote_config]) + + merged = builder.merge_manifests( + Manifest(), + remote_manifest, + local_manifest, + strategy="local_wins" + ) + + entry_id = list(merged.entries.keys())[0] + assert merged.entries[entry_id].config_file.content == "local content" + + def test_merge_manifests_remote_wins(self): + """Test merging manifests with remote wins strategy.""" + builder = ManifestBuilder() + + shared_id = "test-shared-id" + + local_config = ConfigFile( + id=shared_id, + path="/test/.vimrc", + name=".vimrc", + category=ConfigCategory.EDITOR, + tool_name="vim", + content="local content", + file_hash="local_hash", + ) + + remote_config = ConfigFile( + id=shared_id, + path="/test/.vimrc", + name=".vimrc", + category=ConfigCategory.EDITOR, + tool_name="vim", + content="remote content", + file_hash="remote_hash", + ) + + local_manifest = builder.build_from_detected([local_config]) + remote_manifest = builder.build_from_detected([remote_config]) + + merged = builder.merge_manifests( + Manifest(), + remote_manifest, + local_manifest, + strategy="remote_wins" + ) + + entry_id = list(merged.entries.keys())[0] + assert merged.entries[entry_id].config_file.content == "remote content" + + +class TestManifestSerialization: + """Tests for manifest serialization.""" + + def test_manifest_to_dict(self): + """Test manifest serialization to dictionary.""" + config = ConfigFile( + path="/test/.vimrc", + name=".vimrc", + category=ConfigCategory.EDITOR, + tool_name="vim", + ) + entry = ManifestEntry(config_file=config) + + manifest = Manifest() + manifest.add_entry(entry) + + data = manifest.to_dict() + + assert data["version"] == "1.0.0" + assert len(data["entries"]) == 1 + + def test_manifest_from_dict(self): + """Test manifest deserialization from dictionary.""" + data = { + "version": "1.0.0", + "created_at": "2024-01-01T00:00:00", + "updated_at": "2024-01-01T00:00:00", + "entries": {}, + "metadata": {}, + } + + manifest = Manifest.from_dict(data) + + assert manifest.version == "1.0.0" + assert len(manifest.entries) == 0 + + +if __name__ == "__main__": + pytest.main([__file__, "-v"])