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
4.2 KiB
Python

"""Task 018 tests: Layout engine."""
import pytest
from codeflow.renderer.layout_engine import (
layout_flow, layout_overview, compute_svg_dimensions, Position,
)
from codeflow.models.graph import CodeGraph
from codeflow.models.node import Node, NodeType
from codeflow.models.edge import Edge, EdgeType
from codeflow.models.flow import Flow
class TestLayoutEngine:
def test_layout_flow_returns_positions(self):
g = CodeGraph()
g.add_node(Node(id="ep", node_type=NodeType.ENTRY_POINT, label="GET /"))
g.add_node(Node(id="f", node_type=NodeType.FUNCTION, label="f"))
g.add_edge(Edge("ep", "f", EdgeType.CALLS))
flow = Flow(id="ep", name="GET /", entry_node_id="ep", node_ids=["ep", "f"])
positions = layout_flow(g, flow)
assert len(positions) == 2
assert "ep" in positions
assert "f" in positions
def test_positions_have_attributes(self):
g = CodeGraph()
g.add_node(Node(id="ep", node_type=NodeType.ENTRY_POINT, label="GET /"))
g.add_node(Node(id="f", node_type=NodeType.FUNCTION, label="f"))
g.add_edge(Edge("ep", "f", EdgeType.CALLS))
flow = Flow(id="ep", name="GET /", entry_node_id="ep", node_ids=["ep", "f"])
positions = layout_flow(g, flow)
pos = positions["ep"]
assert isinstance(pos, Position)
assert pos.x >= 0
assert pos.y >= 0
assert pos.width > 0
assert pos.height > 0
def test_empty_flow(self):
g = CodeGraph()
flow = Flow(id="empty", name="empty", entry_node_id="nonexistent", node_ids=[])
positions = layout_flow(g, flow)
assert positions == {}
def test_layout_overview_returns_positions(self):
g = CodeGraph()
g.add_node(Node(id="mod1", node_type=NodeType.MODULE, label="mod1"))
g.add_node(Node(id="mod2", node_type=NodeType.MODULE, label="mod2"))
g.add_node(Node(id="ep1", node_type=NodeType.ENTRY_POINT, label="GET /a"))
flows = [Flow(id="ep1", name="GET /a", entry_node_id="ep1")]
positions = layout_overview(g, flows)
assert len(positions) >= 2
def test_empty_overview(self):
g = CodeGraph()
positions = layout_overview(g, [])
assert positions == {}
def test_compute_svg_dimensions(self):
positions = {
"a": Position(x=0, y=0, width=160, height=50),
"b": Position(x=200, y=100, width=160, height=50),
}
w, h = compute_svg_dimensions(positions)
assert w >= 400
assert h >= 190
def test_empty_svg_dimensions(self):
w, h = compute_svg_dimensions({})
assert w == 800
assert h == 600
def test_position_properties(self):
p = Position(x=10, y=20, width=160, height=50)
assert p.left == 10
assert p.right == 170
assert p.top == 20
assert p.bottom == 70
def test_layer_assignment(self):
g = CodeGraph()
g.add_node(Node(id="ep", node_type=NodeType.ENTRY_POINT, label="ep"))
g.add_node(Node(id="f1", node_type=NodeType.FUNCTION, label="f1"))
g.add_node(Node(id="f2", node_type=NodeType.FUNCTION, label="f2"))
g.add_edge(Edge("ep", "f1", EdgeType.CALLS))
g.add_edge(Edge("f1", "f2", EdgeType.CALLS))
flow = Flow(id="ep", name="ep", entry_node_id="ep", node_ids=["ep", "f1", "f2"])
positions = layout_flow(g, flow)
assert positions["ep"].y <= positions["f1"].y
assert positions["f1"].y <= positions["f2"].y
def test_flow_with_disconnected_nodes(self):
g = CodeGraph()
g.add_node(Node(id="ep", node_type=NodeType.ENTRY_POINT, label="ep"))
g.add_node(Node(id="orphan", node_type=NodeType.FUNCTION, label="orphan"))
flow = Flow(id="ep", name="ep", entry_node_id="ep", node_ids=["ep", "orphan"])
positions = layout_flow(g, flow)
assert "ep" in positions
assert "orphan" in positions
def test_nonexistent_node_ids_in_flow(self):
g = CodeGraph()
g.add_node(Node(id="ep", node_type=NodeType.ENTRY_POINT, label="ep"))
flow = Flow(id="ep", name="ep", entry_node_id="ep", node_ids=["ep", "ghost"])
positions = layout_flow(g, flow)
assert "ep" in positions