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.
130 lines
3.5 KiB
Python
130 lines
3.5 KiB
Python
"""Task 005 tests: Tree-sitter AST parser wrapper."""
|
|
import pytest
|
|
from pathlib import Path
|
|
from codeflow.analysis.ast_parser import (
|
|
parse_file, query_tree, get_node_text, SUPPORTED_LANGUAGES,
|
|
_PARSER_CACHE, _get_cached_parser,
|
|
)
|
|
|
|
|
|
def test_parse_python(tmp_path):
|
|
f = tmp_path / "test.py"
|
|
f.write_text("def hello():\n pass")
|
|
tree = parse_file(str(f), "python")
|
|
assert tree is not None
|
|
assert tree.root_node.type == "module"
|
|
|
|
|
|
def test_parse_js(tmp_path):
|
|
f = tmp_path / "test.js"
|
|
f.write_text("function hello() {}")
|
|
tree = parse_file(str(f), "javascript")
|
|
assert tree is not None
|
|
assert tree.root_node.type == "program"
|
|
|
|
|
|
def test_parse_unknown_language(tmp_path):
|
|
f = tmp_path / "test.py"
|
|
f.write_text("x = 1")
|
|
tree = parse_file(str(f), "unknown")
|
|
assert tree is None
|
|
|
|
|
|
def test_parse_unsupported_language(tmp_path):
|
|
f = tmp_path / "test.py"
|
|
f.write_text("x = 1")
|
|
tree = parse_file(str(f), "cobol")
|
|
assert tree is None
|
|
|
|
|
|
def test_parse_nonexistent_file():
|
|
tree = parse_file("/nonexistent/file.py", "python")
|
|
assert tree is None
|
|
|
|
|
|
def test_parse_empty_file(tmp_path):
|
|
f = tmp_path / "empty.py"
|
|
f.write_text("")
|
|
tree = parse_file(str(f), "python")
|
|
assert tree is not None
|
|
|
|
|
|
def test_parse_syntax_error(tmp_path):
|
|
f = tmp_path / "broken.py"
|
|
f.write_text("def broken(")
|
|
tree = parse_file(str(f), "python")
|
|
assert tree is not None
|
|
|
|
|
|
def test_get_node_text(tmp_path):
|
|
f = tmp_path / "test.py"
|
|
source = "def foo():\n pass"
|
|
f.write_text(source)
|
|
source_bytes = source.encode("utf-8")
|
|
tree = parse_file(str(f), "python")
|
|
assert tree is not None
|
|
root = tree.root_node
|
|
text = get_node_text(root, source_bytes)
|
|
assert "def foo" in text
|
|
|
|
|
|
def test_query_tree(tmp_path):
|
|
f = tmp_path / "test.py"
|
|
f.write_text("def hello():\n pass\ndef world():\n pass")
|
|
tree = parse_file(str(f), "python")
|
|
assert tree is not None
|
|
source_bytes = f.read_bytes()
|
|
captures = query_tree(tree, "python", """
|
|
(function_definition
|
|
name: (identifier) @func_name) @func_def
|
|
""")
|
|
assert len(captures) >= 4
|
|
|
|
|
|
def test_query_tree_invalid(tmp_path):
|
|
f = tmp_path / "test.py"
|
|
f.write_text("x = 1")
|
|
tree = parse_file(str(f), "python")
|
|
assert tree is not None
|
|
captures = query_tree(tree, "python", "(invalid query!!!")
|
|
assert captures == []
|
|
|
|
|
|
def test_parser_cache(mocker):
|
|
_PARSER_CACHE.clear()
|
|
spy = mocker.patch("codeflow.analysis.ast_parser.get_parser", wraps=mocker.patch("tree_sitter_languages.get_parser"))
|
|
p1 = _get_cached_parser("python")
|
|
p2 = _get_cached_parser("python")
|
|
assert p1 is p2
|
|
_PARSER_CACHE.clear()
|
|
|
|
|
|
def test_supported_languages():
|
|
assert "python" in SUPPORTED_LANGUAGES
|
|
assert "javascript" in SUPPORTED_LANGUAGES
|
|
assert "typescript" in SUPPORTED_LANGUAGES
|
|
assert "java" in SUPPORTED_LANGUAGES
|
|
assert "go" in SUPPORTED_LANGUAGES
|
|
|
|
|
|
def test_parse_typescript(tmp_path):
|
|
f = tmp_path / "test.ts"
|
|
f.write_text("function hello(): void {}")
|
|
tree = parse_file(str(f), "typescript")
|
|
assert tree is not None
|
|
assert tree.root_node.type in ("program", "statement_block")
|
|
|
|
|
|
def test_parse_go(tmp_path):
|
|
f = tmp_path / "main.go"
|
|
f.write_text("package main\nfunc main() {}")
|
|
tree = parse_file(str(f), "go")
|
|
assert tree is not None
|
|
|
|
|
|
def test_parse_rust(tmp_path):
|
|
f = tmp_path / "main.rs"
|
|
f.write_text("fn main() {}")
|
|
tree = parse_file(str(f), "rust")
|
|
assert tree is not None
|