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.
143 lines
4.1 KiB
Python
143 lines
4.1 KiB
Python
"""Task 003 tests: CLI entry point."""
|
|
import pytest
|
|
from click.testing import CliRunner
|
|
from codeflow.cli import cli
|
|
from pathlib import Path
|
|
from tests.fixtures import FLASK_FIXTURE_DIR
|
|
|
|
|
|
def test_valid_invocation():
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
"analyze",
|
|
str(FLASK_FIXTURE_DIR),
|
|
"--output", str(FLASK_FIXTURE_DIR / "test_report.html"),
|
|
])
|
|
assert result.exit_code in (0, 2)
|
|
|
|
|
|
def test_nonexistent_output_dir():
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
"analyze",
|
|
str(FLASK_FIXTURE_DIR),
|
|
"--output", "/nonexistent_dir/report.html",
|
|
])
|
|
assert result.exit_code == 1
|
|
assert "output directory does not exist" in result.output.lower()
|
|
|
|
|
|
def test_no_html_extension():
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
"analyze",
|
|
str(FLASK_FIXTURE_DIR),
|
|
"--output", str(FLASK_FIXTURE_DIR / "report"),
|
|
])
|
|
assert result.exit_code == 1
|
|
assert "output must be a .html file" in result.output.lower()
|
|
|
|
|
|
def test_cluster_threshold_zero(mocker):
|
|
mock_run = mocker.patch("codeflow.orchestrator.run_analysis")
|
|
mock_run.return_value = {
|
|
"html_path": "", "burp_path": None,
|
|
"file_count": 0, "node_count": 0, "flow_count": 0,
|
|
"elapsed_seconds": 0.0, "errors": [],
|
|
}
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
"analyze",
|
|
str(FLASK_FIXTURE_DIR),
|
|
"--output", str(FLASK_FIXTURE_DIR / "r.html"),
|
|
"--cluster-threshold", "0",
|
|
])
|
|
assert result.exit_code == 0
|
|
_, kwargs = mock_run.call_args
|
|
assert kwargs["cluster_threshold"] == 0
|
|
|
|
|
|
def test_no_burp(mocker):
|
|
mock_run = mocker.patch("codeflow.orchestrator.run_analysis")
|
|
mock_run.return_value = {
|
|
"html_path": "", "burp_path": None,
|
|
"file_count": 0, "node_count": 0, "flow_count": 0,
|
|
"elapsed_seconds": 0.0, "errors": [],
|
|
}
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
"analyze",
|
|
str(FLASK_FIXTURE_DIR),
|
|
"--output", str(FLASK_FIXTURE_DIR / "r.html"),
|
|
"--no-burp",
|
|
])
|
|
assert result.exit_code == 0
|
|
_, kwargs = mock_run.call_args
|
|
assert kwargs["generate_burp"] is False
|
|
|
|
|
|
def test_verbose_flag(mocker):
|
|
mock_run = mocker.patch("codeflow.orchestrator.run_analysis")
|
|
mock_run.return_value = {
|
|
"html_path": "", "burp_path": None,
|
|
"file_count": 0, "node_count": 0, "flow_count": 0,
|
|
"elapsed_seconds": 0.0, "errors": [],
|
|
}
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
"analyze",
|
|
str(FLASK_FIXTURE_DIR),
|
|
"--output", str(FLASK_FIXTURE_DIR / "r.html"),
|
|
"-v",
|
|
])
|
|
assert result.exit_code == 0
|
|
_, kwargs = mock_run.call_args
|
|
assert kwargs["verbose"] is True
|
|
|
|
|
|
def test_help_shown():
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, ["analyze", "--help"])
|
|
assert result.exit_code == 0
|
|
assert "REPO_PATH" in result.output
|
|
|
|
|
|
def test_parse_error_warning(mocker):
|
|
mock_run = mocker.patch("codeflow.orchestrator.run_analysis")
|
|
mock_run.return_value = {
|
|
"html_path": "/tmp/r.html", "burp_path": None,
|
|
"file_count": 3, "node_count": 10, "flow_count": 2,
|
|
"elapsed_seconds": 0.5, "errors": ["bad.py: parse error"],
|
|
}
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
"analyze",
|
|
str(FLASK_FIXTURE_DIR),
|
|
"--output", str(FLASK_FIXTURE_DIR / "r.html"),
|
|
])
|
|
assert result.exit_code == 0
|
|
assert "WARNING" in result.output
|
|
|
|
|
|
def test_max_file_size_passed(mocker):
|
|
mock_run = mocker.patch("codeflow.orchestrator.run_analysis")
|
|
mock_run.return_value = {
|
|
"html_path": "", "burp_path": None,
|
|
"file_count": 0, "node_count": 0, "flow_count": 0,
|
|
"elapsed_seconds": 0.0, "errors": [],
|
|
}
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, [
|
|
"analyze",
|
|
str(FLASK_FIXTURE_DIR),
|
|
"--output", str(FLASK_FIXTURE_DIR / "r.html"),
|
|
"--max-file-size", "10",
|
|
])
|
|
assert result.exit_code == 0
|
|
|
|
|
|
def test_missing_repo_path():
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, ["analyze"])
|
|
assert result.exit_code != 0
|