""" Unit tests for ChangeDetector. Tests change detection, recording, change types, and no-change cases. """ import pytest import tempfile from pathlib import Path from markitect.prompts.models import Artifact, ArtifactType, calculate_content_digest from markitect.prompts.incremental.detector import ChangeDetector from markitect.prompts.incremental.models import ChangeType @pytest.fixture def temp_db(): """Create temporary database for testing.""" with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f: db_path = f.name yield db_path Path(db_path).unlink(missing_ok=True) @pytest.fixture def detector(temp_db): """Create ChangeDetector instance.""" return ChangeDetector(temp_db) def _make_artifact(content="original content"): """Helper to create an in-memory artifact.""" return Artifact.create( space_id="space-1", name="test-artifact", content=content, artifact_type=ArtifactType.CONTENT, ) class TestDetectChange: """Tests for detecting content changes.""" def test_detect_modification(self, detector): """Test detecting a content modification.""" artifact = _make_artifact("original content") change = detector.detect_change(artifact, "modified content") assert change is not None assert change.artifact_id == artifact.id assert change.old_digest == artifact.content_digest assert change.new_digest == calculate_content_digest("modified content") assert change.change_type == ChangeType.MODIFIED def test_no_change_returns_none(self, detector): """Test that identical content returns None.""" artifact = _make_artifact("same content") change = detector.detect_change(artifact, "same content") assert change is None def test_detect_whitespace_change(self, detector): """Test detecting whitespace-only changes.""" artifact = _make_artifact("content") change = detector.detect_change(artifact, "content ") assert change is not None assert change.change_type == ChangeType.MODIFIED def test_detect_empty_to_content(self, detector): """Test detecting change from empty to content.""" artifact = _make_artifact("") change = detector.detect_change(artifact, "new content") assert change is not None assert change.change_type == ChangeType.MODIFIED class TestDetectCreation: """Tests for recording artifact creation.""" def test_detect_creation(self, detector): """Test creation change record.""" change = detector.detect_creation("artifact-123", "new content") assert change.artifact_id == "artifact-123" assert change.old_digest is None assert change.new_digest == calculate_content_digest("new content") assert change.change_type == ChangeType.CREATED def test_creation_has_unique_id(self, detector): """Test that each creation gets a unique ID.""" change1 = detector.detect_creation("art-1", "content") change2 = detector.detect_creation("art-2", "content") assert change1.id != change2.id class TestDetectDeletion: """Tests for recording artifact deletion.""" def test_detect_deletion(self, detector): """Test deletion change record.""" artifact = _make_artifact("content to delete") change = detector.detect_deletion(artifact) assert change.artifact_id == artifact.id assert change.old_digest == artifact.content_digest assert change.change_type == ChangeType.DELETED class TestRecordChange: """Tests for persisting change records.""" def test_record_and_retrieve(self, detector): """Test recording a change and retrieving it.""" artifact = _make_artifact("original") change = detector.detect_change(artifact, "modified") assert change is not None detector.record_change(change) changes = detector.get_changes_for_artifact(artifact.id) assert len(changes) == 1 assert changes[0].id == change.id assert changes[0].artifact_id == artifact.id assert changes[0].change_type == ChangeType.MODIFIED def test_record_multiple_changes(self, detector): """Test recording multiple changes for same artifact.""" artifact = _make_artifact("v1") change1 = detector.detect_change(artifact, "v2") detector.record_change(change1) # Simulate artifact update artifact.update_content("v2") change2 = detector.detect_change(artifact, "v3") detector.record_change(change2) changes = detector.get_changes_for_artifact(artifact.id) assert len(changes) == 2 def test_get_changes_by_type(self, detector): """Test filtering changes by type.""" # Record a creation creation = detector.detect_creation("art-new", "content") detector.record_change(creation) # Record a modification artifact = _make_artifact("old") modification = detector.detect_change(artifact, "new") detector.record_change(modification) created_changes = detector.get_changes_by_type(ChangeType.CREATED) assert len(created_changes) == 1 assert created_changes[0].change_type == ChangeType.CREATED modified_changes = detector.get_changes_by_type(ChangeType.MODIFIED) assert len(modified_changes) == 1 assert modified_changes[0].change_type == ChangeType.MODIFIED def test_no_changes_returns_empty(self, detector): """Test querying changes for artifact with none recorded.""" changes = detector.get_changes_for_artifact("nonexistent") assert changes == []