Initial upload with CI/CD workflow

This commit is contained in:
2026-01-30 22:12:55 +00:00
parent 4be4704784
commit 230c05e545

View File

@@ -0,0 +1,177 @@
"""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)