""" Unit tests for dependency repository. Tests CRUD operations, duplicate handling, and query by source, target, run, and type. """ import pytest import tempfile from pathlib import Path from markitect.prompts.dependencies.models import DependencyEdge, EdgeType from markitect.prompts.dependencies.repository import ( SQLiteDependencyRepository, DuplicateDependencyError, ) @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 repository(temp_db): """Create repository instance with temp database.""" return SQLiteDependencyRepository(temp_db) @pytest.fixture def sample_edge(): """Create sample dependency edge for testing.""" return DependencyEdge.create( source_artifact_id="artifact-1", target_artifact_id="artifact-2", run_id="run-1", edge_type=EdgeType.REQUIRES, ) class TestSQLiteDependencyRepository: """Tests for SQLiteDependencyRepository.""" def test_create_edge(self, repository, sample_edge): """Test creating a dependency edge.""" created = repository.create(sample_edge) assert created.id == sample_edge.id assert created.source_artifact_id == "artifact-1" assert created.target_artifact_id == "artifact-2" def test_create_duplicate_edge_raises_error(self, repository): """Test creating duplicate edge raises error.""" edge1 = DependencyEdge.create( source_artifact_id="a", target_artifact_id="b", run_id="r1", edge_type=EdgeType.REQUIRES, ) repository.create(edge1) edge2 = DependencyEdge.create( source_artifact_id="a", target_artifact_id="b", run_id="r1", edge_type=EdgeType.REQUIRES, ) with pytest.raises(DuplicateDependencyError, match="already exists"): repository.create(edge2) def test_same_edge_different_runs_allowed(self, repository): """Test same source/target with different runs is allowed.""" edge1 = DependencyEdge.create( source_artifact_id="a", target_artifact_id="b", run_id="run-1", edge_type=EdgeType.REQUIRES, ) edge2 = DependencyEdge.create( source_artifact_id="a", target_artifact_id="b", run_id="run-2", edge_type=EdgeType.REQUIRES, ) repository.create(edge1) repository.create(edge2) # Should not raise def test_get_by_id(self, repository, sample_edge): """Test retrieving edge by ID.""" repository.create(sample_edge) retrieved = repository.get_by_id(sample_edge.id) assert retrieved is not None assert retrieved.id == sample_edge.id assert retrieved.source_artifact_id == sample_edge.source_artifact_id assert retrieved.edge_type == sample_edge.edge_type def test_get_by_id_not_found(self, repository): """Test retrieving non-existent edge returns None.""" assert repository.get_by_id("nonexistent") is None def test_get_by_source(self, repository): """Test querying edges by source artifact.""" edge1 = DependencyEdge.create( source_artifact_id="src-1", target_artifact_id="tgt-1", run_id="run-1", edge_type=EdgeType.REQUIRES, ) edge2 = DependencyEdge.create( source_artifact_id="src-1", target_artifact_id="tgt-2", run_id="run-1", edge_type=EdgeType.GENERATES, ) edge3 = DependencyEdge.create( source_artifact_id="src-2", target_artifact_id="tgt-3", run_id="run-1", edge_type=EdgeType.REQUIRES, ) repository.create(edge1) repository.create(edge2) repository.create(edge3) edges = repository.get_by_source("src-1") assert len(edges) == 2 assert all(e.source_artifact_id == "src-1" for e in edges) def test_get_by_target(self, repository): """Test querying edges by target artifact.""" edge1 = DependencyEdge.create( source_artifact_id="src-1", target_artifact_id="tgt-1", run_id="run-1", edge_type=EdgeType.REQUIRES, ) edge2 = DependencyEdge.create( source_artifact_id="src-2", target_artifact_id="tgt-1", run_id="run-1", edge_type=EdgeType.REQUIRES, ) repository.create(edge1) repository.create(edge2) edges = repository.get_by_target("tgt-1") assert len(edges) == 2 assert all(e.target_artifact_id == "tgt-1" for e in edges) def test_get_by_run(self, repository): """Test querying edges by run ID.""" edge1 = DependencyEdge.create( source_artifact_id="a", target_artifact_id="b", run_id="run-1", edge_type=EdgeType.REQUIRES, ) edge2 = DependencyEdge.create( source_artifact_id="c", target_artifact_id="d", run_id="run-1", edge_type=EdgeType.GENERATES, ) edge3 = DependencyEdge.create( source_artifact_id="e", target_artifact_id="f", run_id="run-2", edge_type=EdgeType.REQUIRES, ) repository.create(edge1) repository.create(edge2) repository.create(edge3) edges = repository.get_by_run("run-1") assert len(edges) == 2 assert all(e.run_id == "run-1" for e in edges) def test_get_by_type(self, repository): """Test querying edges by type.""" edge1 = DependencyEdge.create( source_artifact_id="a", target_artifact_id="b", run_id="run-1", edge_type=EdgeType.REQUIRES, ) edge2 = DependencyEdge.create( source_artifact_id="c", target_artifact_id="d", run_id="run-1", edge_type=EdgeType.GENERATES, ) edge3 = DependencyEdge.create( source_artifact_id="e", target_artifact_id="f", run_id="run-1", edge_type=EdgeType.REQUIRES, ) repository.create(edge1) repository.create(edge2) repository.create(edge3) requires_edges = repository.get_by_type(EdgeType.REQUIRES) assert len(requires_edges) == 2 assert all(e.edge_type == EdgeType.REQUIRES for e in requires_edges) generates_edges = repository.get_by_type(EdgeType.GENERATES) assert len(generates_edges) == 1 def test_get_all(self, repository): """Test getting all edges.""" for i in range(3): edge = DependencyEdge.create( source_artifact_id=f"src-{i}", target_artifact_id=f"tgt-{i}", run_id="run-1", edge_type=EdgeType.REQUIRES, ) repository.create(edge) all_edges = repository.get_all() assert len(all_edges) == 3 def test_get_all_empty(self, repository): """Test getting all edges from empty repository.""" assert repository.get_all() == [] def test_delete_edge(self, repository, sample_edge): """Test deleting an edge.""" repository.create(sample_edge) assert repository.delete(sample_edge.id) is True assert repository.get_by_id(sample_edge.id) is None def test_delete_nonexistent_edge(self, repository): """Test deleting non-existent edge returns False.""" assert repository.delete("nonexistent") is False def test_delete_by_run(self, repository): """Test deleting all edges for a run.""" for i in range(3): edge = DependencyEdge.create( source_artifact_id=f"src-{i}", target_artifact_id=f"tgt-{i}", run_id="run-1", edge_type=EdgeType.REQUIRES, ) repository.create(edge) other_edge = DependencyEdge.create( source_artifact_id="other-src", target_artifact_id="other-tgt", run_id="run-2", edge_type=EdgeType.REQUIRES, ) repository.create(other_edge) deleted = repository.delete_by_run("run-1") assert deleted == 3 assert len(repository.get_by_run("run-1")) == 0 assert len(repository.get_by_run("run-2")) == 1 def test_delete_by_run_no_matches(self, repository): """Test deleting by run with no matches returns 0.""" assert repository.delete_by_run("nonexistent") == 0 def test_edge_type_preserved(self, repository): """Test that edge type is correctly stored and retrieved.""" for edge_type in EdgeType: edge = DependencyEdge.create( source_artifact_id=f"src-{edge_type.value}", target_artifact_id=f"tgt-{edge_type.value}", run_id="run-1", edge_type=edge_type, ) repository.create(edge) retrieved = repository.get_by_id(edge.id) assert retrieved.edge_type == edge_type