Skip to content

Commit 637a61e

Browse files
committed
- Added comprehensive unit tests for all codegen modules
1 parent ee535ab commit 637a61e

12 files changed

Lines changed: 1708 additions & 0 deletions

tests/test_gen_conftest.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""
2+
Pytest configuration for codegen tests.
3+
"""
4+
5+
import pytest
6+
import logging
7+
8+
9+
@pytest.fixture(scope="session", autouse=True)
10+
def configure_logging():
11+
"""Configure logging for tests."""
12+
logging.basicConfig(
13+
level=logging.WARNING, # Reduce noise during tests
14+
format="%(name)s - %(levelname)s - %(message)s",
15+
)
16+
17+
18+
@pytest.fixture
19+
def sample_data():
20+
"""Sample JSON data for testing."""
21+
return {
22+
"user_id": 123,
23+
"name": "John Doe",
24+
"email": "[email protected]",
25+
"active": True,
26+
"score": 98.5,
27+
}
28+
29+
30+
@pytest.fixture
31+
def nested_data():
32+
"""Sample nested JSON data."""
33+
return {
34+
"user": {
35+
"id": 1,
36+
"profile": {
37+
"name": "John",
38+
"age": 30,
39+
},
40+
}
41+
}
42+
43+
44+
@pytest.fixture
45+
def array_data():
46+
"""Sample array JSON data."""
47+
return {
48+
"users": [
49+
{"id": 1, "name": "John"},
50+
{"id": 2, "name": "Jane"},
51+
],
52+
"tags": ["python", "go", "rust"],
53+
"scores": [95, 87, 92],
54+
}

tests/test_gen_core_config.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
"""
2+
Tests for core configuration.
3+
"""
4+
5+
import pytest
6+
import tempfile
7+
import json
8+
from pathlib import Path
9+
from json_explorer.codegen.core.config import (
10+
GeneratorConfig,
11+
ConfigError,
12+
load_config,
13+
save_config,
14+
)
15+
16+
17+
class TestGeneratorConfig:
18+
"""Test GeneratorConfig dataclass."""
19+
20+
def test_default_config(self):
21+
config = GeneratorConfig()
22+
assert config.package_name == "main"
23+
assert config.add_comments is True
24+
assert config.indent_size == 4
25+
26+
def test_custom_config(self):
27+
config = GeneratorConfig(
28+
package_name="models",
29+
add_comments=False,
30+
indent_size=2,
31+
)
32+
assert config.package_name == "models"
33+
assert config.add_comments is False
34+
assert config.indent_size == 2
35+
36+
def test_config_language_specific(self):
37+
config = GeneratorConfig(language_config={"custom_option": "value"})
38+
assert config.language_config["custom_option"] == "value"
39+
40+
def test_validation_fails(self):
41+
with pytest.raises(ConfigError):
42+
GeneratorConfig(indent_size=0)
43+
44+
def test_merge_config(self):
45+
config = GeneratorConfig(package_name="main")
46+
merged = config.merge({"package_name": "models", "add_comments": False})
47+
48+
assert merged.package_name == "models"
49+
assert merged.add_comments is False
50+
51+
def test_to_dict(self):
52+
config = GeneratorConfig(package_name="test")
53+
result = config.to_dict()
54+
55+
assert isinstance(result, dict)
56+
assert result["package_name"] == "test"
57+
58+
59+
class TestConfigLoading:
60+
"""Test configuration loading."""
61+
62+
def test_load_from_dict(self):
63+
config = load_config(custom_config={"package_name": "test"})
64+
assert config.package_name == "test"
65+
66+
def test_load_from_file(self):
67+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
68+
json.dump({"package_name": "file_test"}, f)
69+
temp_path = f.name
70+
71+
try:
72+
config = load_config(config_file=temp_path)
73+
assert config.package_name == "file_test"
74+
finally:
75+
Path(temp_path).unlink()
76+
77+
def test_load_invalid_file(self):
78+
with pytest.raises(ConfigError):
79+
load_config(config_file="nonexistent.json")
80+
81+
def test_save_config(self):
82+
config = GeneratorConfig(package_name="save_test")
83+
84+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
85+
temp_path = f.name
86+
87+
try:
88+
save_config(config, temp_path)
89+
loaded = load_config(config_file=temp_path)
90+
assert loaded.package_name == "save_test"
91+
finally:
92+
Path(temp_path).unlink()

tests/test_gen_core_naming.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
"""
2+
Tests for core naming utilities.
3+
"""
4+
5+
from json_explorer.codegen.core.naming import (
6+
to_snake_case,
7+
to_camel_case,
8+
to_pascal_case,
9+
sanitize_name,
10+
NameTracker,
11+
)
12+
13+
14+
class TestCaseConversion:
15+
"""Test case conversion functions."""
16+
17+
def test_to_snake_case(self):
18+
assert to_snake_case("UserName") == "user_name"
19+
assert to_snake_case("user-name") == "user_name"
20+
assert to_snake_case("userName") == "user_name"
21+
22+
def test_to_camel_case(self):
23+
assert to_camel_case("user_name") == "userName"
24+
assert to_camel_case("UserName") == "userName"
25+
assert to_camel_case("user-name") == "userName"
26+
27+
def test_to_pascal_case(self):
28+
assert to_pascal_case("user_name") == "UserName"
29+
assert to_pascal_case("userName") == "UserName"
30+
assert to_pascal_case("user-name") == "UserName"
31+
32+
33+
class TestSanitizeName:
34+
"""Test name sanitization."""
35+
36+
def test_basic_sanitization(self):
37+
result = sanitize_name("user-name", "snake")
38+
assert result == "user_name"
39+
40+
def test_reserved_word_conflict(self):
41+
reserved = {"class", "def"}
42+
result = sanitize_name("class", "snake", reserved_words=reserved)
43+
assert result == "class_"
44+
45+
def test_duplicate_name_conflict(self):
46+
used = {"user"}
47+
result = sanitize_name("user", "snake", used_names=used)
48+
assert result == "user_1"
49+
50+
def test_invalid_characters(self):
51+
result = sanitize_name("user@name!", "snake")
52+
assert result == "user_name"
53+
54+
def test_starts_with_digit(self):
55+
result = sanitize_name("123user", "snake")
56+
assert result == "_123user"
57+
58+
59+
class TestNameTracker:
60+
"""Test NameTracker stateful sanitization."""
61+
62+
def test_tracks_used_names(self):
63+
tracker = NameTracker()
64+
name1 = tracker.sanitize("user", "snake")
65+
name2 = tracker.sanitize("user", "snake")
66+
67+
assert name1 == "user"
68+
assert name2 == "user_1"
69+
70+
def test_reserved_words(self):
71+
tracker = NameTracker(reserved_words={"class"})
72+
result = tracker.sanitize("class", "pascal")
73+
assert result == "Class_"
74+
75+
def test_reset(self):
76+
tracker = NameTracker()
77+
tracker.sanitize("user", "snake")
78+
tracker.reset()
79+
80+
result = tracker.sanitize("user", "snake")
81+
assert result == "user"

tests/test_gen_core_schema.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
"""
2+
Tests for core schema representation.
3+
"""
4+
5+
from json_explorer.codegen.core.schema import (
6+
Field,
7+
FieldType,
8+
Schema,
9+
convert_analyzer_output,
10+
extract_all_schemas,
11+
)
12+
13+
14+
class TestFieldType:
15+
"""Test FieldType enum."""
16+
17+
def test_field_types_exist(self):
18+
assert FieldType.STRING.value == "string"
19+
assert FieldType.INTEGER.value == "integer"
20+
assert FieldType.ARRAY.value == "array"
21+
assert FieldType.OBJECT.value == "object"
22+
23+
24+
class TestField:
25+
"""Test Field dataclass."""
26+
27+
def test_basic_field(self):
28+
field = Field(
29+
name="user_id",
30+
original_name="userId",
31+
type=FieldType.INTEGER,
32+
)
33+
assert field.name == "user_id"
34+
assert field.type == FieldType.INTEGER
35+
assert field.optional is False
36+
37+
def test_optional_field(self):
38+
field = Field(
39+
name="email",
40+
original_name="email",
41+
type=FieldType.STRING,
42+
optional=True,
43+
)
44+
assert field.optional is True
45+
46+
def test_array_field(self):
47+
field = Field(
48+
name="tags",
49+
original_name="tags",
50+
type=FieldType.ARRAY,
51+
array_element_type=FieldType.STRING,
52+
)
53+
assert field.type == FieldType.ARRAY
54+
assert field.array_element_type == FieldType.STRING
55+
56+
57+
class TestSchema:
58+
"""Test Schema dataclass."""
59+
60+
def test_basic_schema(self):
61+
schema = Schema(name="User", original_name="User")
62+
assert schema.name == "User"
63+
assert len(schema.fields) == 0
64+
65+
def test_add_field(self):
66+
schema = Schema(name="User", original_name="User")
67+
field = Field("id", "id", FieldType.INTEGER)
68+
69+
schema.add_field(field)
70+
assert len(schema.fields) == 1
71+
assert schema.fields[0].name == "id"
72+
73+
def test_get_field(self):
74+
schema = Schema(name="User", original_name="User")
75+
field = Field("id", "id", FieldType.INTEGER)
76+
schema.add_field(field)
77+
78+
found = schema.get_field("id")
79+
assert found is not None
80+
assert found.name == "id"
81+
82+
83+
class TestConvertAnalyzerOutput:
84+
"""Test analyzer output conversion."""
85+
86+
def test_simple_object(self):
87+
analyzer_result = {
88+
"type": "object",
89+
"children": {
90+
"name": {"type": "str", "optional": False},
91+
"age": {"type": "int", "optional": False},
92+
},
93+
"conflicts": {},
94+
}
95+
96+
schema = convert_analyzer_output(analyzer_result, "User")
97+
98+
assert schema.name == "User"
99+
assert len(schema.fields) == 2
100+
assert schema.fields[0].type == FieldType.STRING
101+
assert schema.fields[1].type == FieldType.INTEGER
102+
103+
def test_nested_object(self):
104+
analyzer_result = {
105+
"type": "object",
106+
"children": {
107+
"profile": {
108+
"type": "object",
109+
"children": {
110+
"age": {"type": "int", "optional": False},
111+
},
112+
"conflicts": {},
113+
},
114+
},
115+
"conflicts": {},
116+
}
117+
118+
schema = convert_analyzer_output(analyzer_result, "User")
119+
120+
assert len(schema.fields) == 1
121+
assert schema.fields[0].type == FieldType.OBJECT
122+
assert schema.fields[0].nested_schema is not None
123+
124+
def test_array_of_primitives(self):
125+
analyzer_result = {
126+
"type": "object",
127+
"children": {
128+
"tags": {
129+
"type": "list",
130+
"child_type": "str",
131+
"optional": False,
132+
},
133+
},
134+
"conflicts": {},
135+
}
136+
137+
schema = convert_analyzer_output(analyzer_result, "Data")
138+
139+
assert schema.fields[0].type == FieldType.ARRAY
140+
assert schema.fields[0].array_element_type == FieldType.STRING
141+
142+
def test_extract_all_schemas(self):
143+
analyzer_result = {
144+
"type": "object",
145+
"children": {
146+
"profile": {
147+
"type": "object",
148+
"children": {
149+
"age": {"type": "int", "optional": False},
150+
},
151+
"conflicts": {},
152+
},
153+
},
154+
"conflicts": {},
155+
}
156+
157+
root_schema = convert_analyzer_output(analyzer_result, "User")
158+
all_schemas = extract_all_schemas(root_schema)
159+
160+
assert len(all_schemas) == 2 # User + UserProfile
161+
assert "User" in all_schemas

0 commit comments

Comments
 (0)