mirror of
https://github.com/q3alique/codeflow
synced 2026-06-21 14:06:02 +00:00
acc1b4f3e7
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.
106 lines
4.2 KiB
Python
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
|