File size: 4,636 Bytes
eda351c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
"""tests/test_codegraph.py — Unit tests for CodeGraph V2."""
import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

import pytest
from codegraph.graph import CodeGraph, _naming_style
from codegraph.extractor import extract_metadata


class TestNamingStyle:
    def test_snake_case(self):
        assert _naming_style("get_user") == "snake_case"
        assert _naming_style("handle_path") == "snake_case"

    def test_camel_case(self):
        assert _naming_style("getUser") == "camelCase"
        assert _naming_style("handlePath") == "camelCase"

    def test_pascal_case(self):
        assert _naming_style("GetUser") == "PascalCase"
        assert _naming_style("UserManager") == "PascalCase"

    def test_all_lowercase(self):
        assert _naming_style("foo") == "snake_case"


class TestCodeGraph:
    def test_empty_graph(self):
        g = CodeGraph(episode_seed=1)
        assert g.components == {}
        assert g.conventions == {}

    def test_update_adds_component(self):
        g = CodeGraph(episode_seed=1)
        meta = extract_metadata(
            "def get_user(uid: int) -> dict:\n    \"\"\"Get user.\"\"\"\n    return {}",
            "users.py", 0
        )
        g.update("users.py", meta)
        assert "users" in g.components

    def test_syntax_error_not_added(self):
        g = CodeGraph(episode_seed=1)
        bad_meta = {"status": "syntax_error", "functions": [], "imports": []}
        g.update("bad.py", bad_meta)
        assert len(g.components) == 0

    def test_conventions_inferred_after_update(self):
        g = CodeGraph(episode_seed=1)
        meta = extract_metadata(
            "def snake_one(x: int) -> str:\n    \"\"\"Doc.\"\"\"\n    return str(x)\n"
            "def snake_two(y: int) -> str:\n    \"\"\"Doc.\"\"\"\n    return str(y)",
            "module.py", 0
        )
        g.update("module.py", meta)
        assert g.conventions.get("naming") in ("snake_case", "camelCase", "PascalCase", "mixed", "unknown")

    def test_mixed_style_detected(self):
        g = CodeGraph(episode_seed=1)
        # Create artificial metadata with exactly 50/50 split
        meta = {
            "status": "ok",
            "functions": [
                {"name": "get_user"},    # snake_case
                {"name": "getUser"},     # camelCase
                {"name": "set_value"},   # snake_case
                {"name": "getValue"},    # camelCase
            ],
            "imports": [],
            "conventions": {},
            "language": "py",
            "created_at_step": 0,
        }
        g.update("mixed.py", meta)
        # 50/50 split — below 60% threshold → should be "mixed"
        assert g.conventions.get("naming") == "mixed"

    def test_slim_dict_under_limit(self):
        g = CodeGraph(episode_seed=1)
        for i in range(10):
            meta = extract_metadata(
                f"def func_{i}(x: int) -> str:\n    return str(x)",
                f"module_{i}.py", i
            )
            g.update(f"module_{i}.py", meta)
        slim = g.to_slim_dict(limit=6000)
        assert len(slim) <= 6000


class TestExtractor:
    def test_extracts_functions(self):
        code = "def hello(x: int) -> str:\n    return str(x)"
        meta = extract_metadata(code, "test.py", 0)
        assert meta["status"] == "ok"
        assert any(f["name"] == "hello" for f in meta["functions"])

    def test_extracts_imports(self):
        code = "import os\nfrom pathlib import Path\ndef foo(): pass"
        meta = extract_metadata(code, "test.py", 0)
        assert meta["status"] == "ok"
        assert len(meta["imports"]) >= 1

    def test_syntax_error_returns_structured(self):
        code = "def broken(:\n    pass"
        meta = extract_metadata(code, "bad.py", 0)
        assert meta["status"] == "syntax_error"
        assert "line" in meta
        assert "feedback" in meta

    def test_conventions_detected(self):
        code = "try:\n    pass\nexcept ValueError:\n    pass\ndef f(x: int) -> str:\n    return str(x)"
        meta = extract_metadata(code, "test.py", 0)
        assert meta["conventions"]["uses_try_catch"] is True
        assert meta["conventions"]["uses_type_hints"] is True

    def test_no_print_detected(self):
        code = "def f():\n    print('hello')"
        meta = extract_metadata(code, "test.py", 0)
        assert meta["conventions"]["no_print_stmts"] is False

    def test_language_set_correctly(self):
        meta_py = extract_metadata("def f(): pass", "module.py", 0)
        assert meta_py["language"] == "py"


if __name__ == "__main__":
    pytest.main([__file__, "-v"])