Files
q3alique acc1b4f3e7 Initial release of codeflow
Static taint-analysis and visualization tool for source-code security review.
Supports deep analysis for Python, JavaScript/TypeScript, Java, Go, and C#,
with structural support for all other languages via the generic extractor.

Outputs: interactive HTML report, LLM-ready Markdown review document,
and optional Burp Suite JSON export. Self-bootstrapping launcher (run.py)
requires no virtual environment.
2026-06-08 00:55:14 +02:00

106 lines
3.6 KiB
Python

"""Task 013 tests: Graph builder."""
import pytest
from pathlib import Path
from codeflow.discovery.file_scanner import FileInfo, scan
from codeflow.analysis.graph_builder import build, _select_extractor, _extract_file
from codeflow.models.node import NodeType
from codeflow.models.edge import EdgeType
from tests.fixtures import FLASK_FIXTURE_DIR
class TestGraphBuilder:
def test_select_extractor_python(self):
ext = _select_extractor("python")
from codeflow.analysis.python_extractor import PythonExtractor
assert isinstance(ext, PythonExtractor)
def test_select_extractor_javascript(self):
ext = _select_extractor("javascript")
from codeflow.analysis.js_extractor import JSExtractor
assert isinstance(ext, JSExtractor)
def test_select_extractor_generic(self):
ext = _select_extractor("java")
from codeflow.analysis.generic_extractor import GenericExtractor
assert isinstance(ext, GenericExtractor)
def test_build_graph_from_flask_fixture(self):
file_infos = scan(FLASK_FIXTURE_DIR)
graph = build(file_infos)
assert graph.node_count() > 0
assert graph.edge_count() > 0
def test_graph_has_module_nodes(self):
file_infos = scan(FLASK_FIXTURE_DIR)
graph = build(file_infos)
mods = graph.nodes_by_type(NodeType.MODULE)
assert len(mods) >= 1
def test_graph_has_function_nodes(self):
file_infos = scan(FLASK_FIXTURE_DIR)
graph = build(file_infos)
funcs = graph.nodes_by_type(NodeType.FUNCTION)
assert len(funcs) >= 1
def test_graph_has_entry_points(self):
file_infos = scan(FLASK_FIXTURE_DIR)
graph = build(file_infos)
eps = graph.nodes_by_type(NodeType.ENTRY_POINT)
assert len(eps) >= 1
def test_graph_has_source_nodes(self):
file_infos = scan(FLASK_FIXTURE_DIR)
graph = build(file_infos)
sources = graph.nodes_by_type(NodeType.SOURCE)
assert len(sources) >= 1
def test_graph_has_sink_nodes(self):
file_infos = scan(FLASK_FIXTURE_DIR)
graph = build(file_infos)
sinks = graph.nodes_by_type(NodeType.SINK)
assert len(sinks) >= 1
def test_graph_has_calls_edges(self):
file_infos = scan(FLASK_FIXTURE_DIR)
graph = build(file_infos)
has_calls = False
for _, _, edge in graph.edges():
if edge.edge_type == EdgeType.CALLS:
has_calls = True
break
assert has_calls
def test_graph_has_contains_edges(self):
file_infos = scan(FLASK_FIXTURE_DIR)
graph = build(file_infos)
has_contains = False
for _, _, edge in graph.edges():
if edge.edge_type == EdgeType.CONTAINS:
has_contains = True
break
assert has_contains
def test_boundary_nodes_created(self):
file_infos = scan(FLASK_FIXTURE_DIR)
graph = build(file_infos)
boundaries = graph.nodes_by_type(NodeType.BOUNDARY)
assert len(boundaries) >= 1
def test_graph_build_empty(self):
graph = build([])
assert graph.node_count() == 0
def test_cross_file_calls_resolved(self):
file_infos = scan(FLASK_FIXTURE_DIR)
graph = build(file_infos)
authenticate_found = any(
"authenticate" in nid for nid in graph.nodes()
)
assert authenticate_found
def test_unique_node_ids(self):
file_infos = scan(FLASK_FIXTURE_DIR)
graph = build(file_infos)
ids = list(graph.nodes())
assert len(ids) == len(set(ids))