"""Unit tests for dependency graph module.""" import pytest from pathlib import Path from codesnap.core.dependency_graph import DependencyGraphBuilder, DependencyParser, Dependency class TestDependencyParser: """Tests for DependencyParser class.""" def setup_method(self) -> None: self.parser = DependencyParser() def test_parse_python_import(self) -> None: code = "import os" deps = self.parser.parse_file(Path("test.py"), code, "python") assert len(deps) >= 1 def test_parse_python_from_import(self) -> None: code = "from pathlib import Path" deps = self.parser.parse_file(Path("test.py"), code, "python") assert len(deps) >= 1 def test_parse_python_multiple_imports(self) -> None: code = """ import os import sys from pathlib import Path from collections import defaultdict """ deps = self.parser.parse_file(Path("test.py"), code, "python") assert len(deps) >= 3 def test_parse_javascript_require(self) -> None: code = "const express = require('express');" deps = self.parser.parse_file(Path("test.js"), code, "javascript") assert len(deps) >= 1 def test_parse_javascript_import(self) -> None: code = "import { useState } from 'react';" deps = self.parser.parse_file(Path("test.js"), code, "javascript") assert len(deps) >= 1 def test_parse_go_import(self) -> None: code = 'import "fmt"' deps = self.parser.parse_file(Path("test.go"), code, "go") assert len(deps) >= 1 def test_parse_rust_use(self) -> None: code = "use std::collections::HashMap;" deps = self.parser.parse_file(Path("test.rs"), code, "rust") assert len(deps) >= 1 def test_parse_java_import(self) -> None: code = "import java.util.ArrayList;" deps = self.parser.parse_file(Path("test.java"), code, "java") assert len(deps) >= 1 def test_parse_unsupported_language(self) -> None: code = "some random code" deps = self.parser.parse_file(Path("test.xyz"), code, "unsupported") assert len(deps) == 0 class TestDependencyGraphBuilder: """Tests for DependencyGraphBuilder class.""" def setup_method(self) -> None: self.graph = DependencyGraphBuilder() def test_add_file(self) -> None: self.graph.add_file(Path("main.py"), "python", 100, 10, 2, 1) assert self.graph.graph.number_of_nodes() == 1 assert Path("main.py") in self.graph.graph.nodes() def test_add_dependency(self) -> None: self.graph.add_file(Path("a.py"), "python", 50, 5, 1, 0) self.graph.add_file(Path("b.py"), "python", 60, 6, 1, 0) dep = Dependency( source_file=Path("a.py"), target_file=Path("b.py"), import_statement="import b", import_type="import" ) self.graph.add_dependency(dep) assert self.graph.graph.has_edge(Path("a.py"), Path("b.py")) def test_build_from_analysis(self) -> None: analysis_result = { "files": [ {"path": "main.py", "language": "python", "size": 100, "lines": 10, "functions": ["main"], "classes": []}, {"path": "utils.py", "language": "python", "size": 50, "lines": 5, "functions": ["helper"], "classes": []} ], "dependencies": [ {"source": "main.py", "target": "utils.py", "type": "import"} ] } self.graph.build_from_analysis(analysis_result) assert self.graph.graph.number_of_nodes() == 2 assert self.graph.graph.has_edge(Path("main.py"), Path("utils.py")) def test_find_cycles(self) -> None: self.graph.add_file(Path("a.py"), "python", 50, 5, 1, 0) self.graph.add_file(Path("b.py"), "python", 50, 5, 1, 0) self.graph.add_file(Path("c.py"), "python", 50, 5, 1, 0) dep1 = Dependency(Path("a.py"), Path("b.py"), "import b", "import") dep2 = Dependency(Path("b.py"), Path("c.py"), "import c", "import") dep3 = Dependency(Path("c.py"), Path("a.py"), "import a", "import") self.graph.add_dependency(dep1) self.graph.add_dependency(dep2) self.graph.add_dependency(dep3) cycles = self.graph.find_cycles() assert len(cycles) >= 1 def test_find_no_cycles(self) -> None: self.graph.add_file(Path("a.py"), "python", 50, 5, 1, 0) self.graph.add_file(Path("b.py"), "python", 50, 5, 1, 0) dep = Dependency(Path("a.py"), Path("b.py"), "import b", "import") self.graph.add_dependency(dep) cycles = self.graph.find_cycles() assert len(cycles) == 0 def test_find_orphaned_files(self) -> None: self.graph.add_file(Path("orphan.py"), "python", 50, 5, 1, 0) self.graph.add_file(Path("main.py"), "python", 100, 10, 2, 1) self.graph.add_file(Path("used.py"), "python", 50, 5, 1, 0) dep = Dependency(Path("main.py"), Path("used.py"), "import used", "import") self.graph.add_dependency(dep) orphaned = self.graph.find_orphaned_files() assert Path("orphan.py") in orphaned assert Path("main.py") not in orphaned assert Path("used.py") not in orphaned def test_calculate_metrics(self) -> None: self.graph.add_file(Path("main.py"), "python", 100, 10, 2, 1) self.graph.add_file(Path("utils.py"), "python", 50, 5, 1, 0) dep = Dependency(Path("main.py"), Path("utils.py"), "import utils", "import") self.graph.add_dependency(dep) metrics = self.graph.calculate_metrics() assert metrics.total_files == 2 assert metrics.total_edges == 1 assert metrics.density >= 0 def test_get_transitive_closure(self) -> None: self.graph.add_file(Path("a.py"), "python", 50, 5, 1, 0) self.graph.add_file(Path("b.py"), "python", 50, 5, 1, 0) self.graph.add_file(Path("c.py"), "python", 50, 5, 1, 0) self.graph.add_dependency(Dependency(Path("a.py"), Path("b.py"), "import b", "import")) self.graph.add_dependency(Dependency(Path("b.py"), Path("c.py"), "import c", "import")) dependents = self.graph.get_transitive_closure(Path("c.py")) assert len(dependents) >= 0 def test_get_dependencies(self) -> None: self.graph.add_file(Path("a.py"), "python", 50, 5, 1, 0) self.graph.add_file(Path("b.py"), "python", 50, 5, 1, 0) self.graph.add_file(Path("c.py"), "python", 50, 5, 1, 0) self.graph.add_dependency(Dependency(Path("a.py"), Path("b.py"), "import b", "import")) self.graph.add_dependency(Dependency(Path("a.py"), Path("c.py"), "import c", "import")) deps = self.graph.get_dependencies(Path("a.py")) assert isinstance(deps, set)