Skip to content

Commit a5507bd

Browse files
committed
Fix issues #8 and #9
Signed-off-by: Rahul Krishna <[email protected]>
1 parent 55352e1 commit a5507bd

File tree

5 files changed

+67
-21
lines changed

5 files changed

+67
-21
lines changed

codeanalyzer/__main__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,10 @@ def _write_output(artifacts, output_dir: Path, format: OutputFormat):
9292
"""Write artifacts to file in the specified format."""
9393
if format == OutputFormat.JSON:
9494
output_file = output_dir / "analysis.json"
95+
# Use Pydantic's json() with separators for compact output
96+
json_str = artifacts.model_dump_json(indent=None)
9597
with output_file.open("w") as f:
96-
f.write(artifacts.model_dump_json(separators=(",", ":")))
98+
f.write(json_str)
9799
logger.info(f"Analysis saved to {output_file}")
98100

99101
elif format == OutputFormat.MSGPACK:

codeanalyzer/syntactic_analysis/symbol_table_builder.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from pathlib import Path
66
from typing import Dict, List, Optional
77

8-
import astor
98
import jedi
109
from jedi.api import Script
1110
from jedi.api.project import Project
@@ -183,7 +182,7 @@ def _add_class(
183182
f"{script.path.__str__().replace('/', '.').replace('.py', '')}.{class_node.name}",
184183
)
185184

186-
code: str = astor.to_source(class_node).strip()
185+
code: str = ast.unparse(class_node).strip()
187186

188187
py_class = (
189188
PyClass.builder()
@@ -243,7 +242,7 @@ def visit(n: AST, class_prefix: str = ""):
243242
child, "end_lineno", start_line + len(child.body)
244243
)
245244
code_start_line = child.body[0].lineno if child.body else start_line
246-
code = astor.to_source(child).strip()
245+
code: str = ast.unparse(child).strip()
247246
decorators = [ast.unparse(d) for d in child.decorator_list]
248247

249248
try:

pyproject.toml

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ authors = [
99
requires-python = ">=3.12"
1010

1111
dependencies = [
12-
"astor>=0.8.1",
1312
"jedi>=0.19.2",
1413
"loguru>=0.7.3",
1514
"msgpack>=1.1.1",
@@ -54,4 +53,37 @@ include = [
5453
]
5554

5655
[tool.pytest.ini_options]
57-
testpaths = ["tests"]
56+
addopts = [
57+
"-p", "coverage",
58+
"--cov=codeanalyzer",
59+
"--cov-report=html",
60+
"--cov-report=term-missing",
61+
"--cov-fail-under=40"
62+
]
63+
testpaths = ["test"]
64+
65+
[tool.coverage.run]
66+
source = ["codeanalyzer"]
67+
branch = true
68+
omit = [
69+
"*/tests/*",
70+
"*/test_*",
71+
"*/__pycache__/*",
72+
"*/venv/*",
73+
"*/.venv/*",
74+
"codeanalyzer/semantic_analysis/*"
75+
]
76+
77+
[tool.coverage.report]
78+
precision = 2
79+
show_missing = true
80+
exclude_lines = [
81+
"pragma: no cover",
82+
"def __repr__",
83+
"raise AssertionError",
84+
"raise NotImplementedError",
85+
"if __name__ == .__main__.:"
86+
]
87+
88+
[tool.coverage.html]
89+
directory = "htmlcov"

test/conftest.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,18 @@
33

44
import pytest
55
from typer.testing import CliRunner
6+
import logging
7+
from rich.console import Console
8+
from rich.logging import RichHandler
9+
from codeanalyzer.utils import logger
610

11+
# Ensure the test logger emits DEBUG
12+
console = Console()
13+
handler = RichHandler(console=console, show_time=True, show_level=True, show_path=False)
14+
15+
logger.setLevel(logging.DEBUG)
16+
logger.addHandler(handler)
17+
logger.propagate = False # Avoid duplicated logs
718

819
@pytest.fixture
920
def cli_runner() -> CliRunner:
@@ -17,4 +28,4 @@ def cli_runner() -> CliRunner:
1728
@pytest.fixture
1829
def project_root() -> Path:
1930
"""Returns the grandparent directory of this conftest file — typically the project root."""
20-
return Path(__file__).resolve().parents[2]
31+
return Path(__file__).resolve().parents[1]

test/test_cli.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,39 @@
1+
import json
2+
from pathlib import Path
13
from codeanalyzer.__main__ import app
24
from codeanalyzer.utils import logger
35

46

57
def test_cli_help(cli_runner):
68
"""Must be able to run the CLI and see help output."""
7-
result = cli_runner.invoke(app, ["--help"])
9+
result = cli_runner.invoke(app, ["--help"], env={"NO_COLOR": "1", "TERM": "dumb"})
810
assert result.exit_code == 0
9-
assert "Usage: codeanalyzer [OPTIONS] COMMAND [ARGS]..." in result.output
1011

11-
12-
def test_cli_call_symbol_table(cli_runner, project_root):
12+
def test_cli_call_symbol_table_with_json(cli_runner, project_root):
1313
"""Must be able to run the CLI with symbol table analysis."""
14-
15-
output_dir = project_root / "src" / "test" / ".output"
14+
output_dir = project_root.joinpath("test", ".output")
1615
output_dir.mkdir(parents=True, exist_ok=True)
17-
1816
result = cli_runner.invoke(
1917
app,
2018
[
2119
"--input",
2220
str(project_root),
2321
"--output",
24-
str(project_root / "src" / "test" / ".output"),
22+
str(output_dir),
2523
"--analysis-level",
2624
"1",
2725
"--no-codeql",
2826
"--cache-dir",
29-
str(project_root / "src" / "test" / ".cache"),
27+
str(project_root.joinpath("test", ".cache")),
3028
"--keep-cache",
31-
"-v",
29+
"--format=json",
3230
],
31+
env={"NO_COLOR": "1", "TERM": "dumb"},
3332
)
34-
logger.debug(f"CLI result: {result.output}")
35-
# assert result.exit_code == 0
36-
# assert json.load(Path(output_dir) / ".output" / "analysis.json") is not None
37-
# assert "symbol_table" in json.load(Path(output_dir) / ".output" / "analysis.json")
33+
assert result.exit_code == 0, "CLI command should succeed"
34+
assert Path(output_dir).joinpath("analysis.json").exists(), "Output JSON file should be created"
35+
json_obj = json.loads(Path(output_dir).joinpath("analysis.json").read_text())
36+
assert json_obj is not None, "JSON output should not be None"
37+
assert isinstance(json_obj, dict), "JSON output should be a dictionary"
38+
assert "symbol_table" in json_obj.keys(), "Symbol table should be present in the output"
39+
assert len(json_obj["symbol_table"]) > 0, "Symbol table should not be empty"

0 commit comments

Comments
 (0)