Skip to content

Commit 5518ab7

Browse files
committed
Enforce shared assertions in e2e tests
1 parent 92bdc27 commit 5518ab7

15 files changed

Lines changed: 322 additions & 81 deletions

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ verbosity_assertions = 2
232232
markers = [
233233
"perf: marks tests as performance tests (excluded from CI benchmarks)",
234234
"benchmark: marks tests as benchmark tests",
235+
"allow_direct_assert: marks e2e tests where direct assert statements are intentionally allowed",
235236
]
236237

237238
[tool.coverage]
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# generated by datamodel-codegen:
2+
# filename: api_absolute_path.yaml
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from mypackage.shared.models import User
8+
from pydantic import BaseModel
9+
10+
11+
class UserResponse(BaseModel):
12+
user: User
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# generated by datamodel-codegen:
2+
# filename: api_file_uri.yaml
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from mypackage.shared.models import User
8+
from pydantic import BaseModel
9+
10+
11+
class UserResponse(BaseModel):
12+
user: User
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# generated by datamodel-codegen:
2+
# filename: api_local_ref.yaml
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from pydantic import BaseModel
8+
9+
10+
class User(BaseModel):
11+
id: int
12+
13+
14+
class UserResponse(BaseModel):
15+
user: User
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# generated by datamodel-codegen:
2+
# filename: api_normalized.yaml
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from mypackage.shared.models import UserName
8+
from pydantic import BaseModel
9+
10+
11+
class UserResponse(BaseModel):
12+
user: UserName
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# generated by datamodel-codegen:
2+
# filename: api.yaml
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from pydantic import AwareDatetime, BaseModel
8+
9+
10+
class User(BaseModel):
11+
id: int
12+
name: str
13+
14+
15+
class Error(BaseModel):
16+
code: int
17+
message: str
18+
19+
20+
class ErrorResponse(BaseModel):
21+
error: Error
22+
timestamp: AwareDatetime
23+
24+
25+
class UserResponse(BaseModel):
26+
user: User
27+
request_id: str
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
datamodel-codegen --input api.yaml --snake-case-field --target-python-version 3.11
2+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# generated by datamodel-codegen:
2+
# filename: schema.json
3+
4+
from __future__ import annotations
5+
6+
from pydantic import BaseModel
7+
8+
9+
class Model(BaseModel):
10+
firstName: str | None = None

tests/main/jsonschema/test_main_jsonschema.py

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
chdir,
2727
generate,
2828
)
29-
from datamodel_code_generator.__main__ import Exit, main
29+
from datamodel_code_generator.__main__ import Exit
3030
from datamodel_code_generator.format import is_supported_in_black
3131
from datamodel_code_generator.model import base as model_base
3232
from tests.conftest import assert_directory_content, freeze_time, validate_generated_code
@@ -53,6 +53,7 @@
5353
FixtureRequest = pytest.FixtureRequest
5454

5555

56+
@pytest.mark.allow_direct_assert
5657
def assert_run_main_with_args_error(args: list[str], capsys: pytest.CaptureFixture[str], expected_error: str) -> None:
5758
"""Assert that running the CLI exits with code 2 and emits the expected error."""
5859
with pytest.raises(SystemExit) as exc_info:
@@ -212,6 +213,7 @@ def test_main_keep_model_order_field_references(output_file: Path) -> None:
212213
)
213214

214215

216+
@pytest.mark.allow_direct_assert
215217
@pytest.mark.parametrize(
216218
("target_python_version", "keep_model_order", "disable_future_imports"),
217219
[
@@ -452,6 +454,7 @@ def test_main_jsonschema_multiple_files(output_dir: Path) -> None:
452454
)
453455

454456

457+
@pytest.mark.allow_direct_assert
455458
@pytest.mark.benchmark
456459
def test_main_jsonschema_no_empty_collapsed_external_model(tmp_path: Path) -> None:
457460
"""Test no empty files with collapsed external models."""
@@ -773,28 +776,32 @@ def test_main_class_name_suffix_with_class_name(output_file: Path) -> None:
773776

774777
def test_main_class_name_prefix_invalid(output_file: Path) -> None:
775778
"""Test that invalid --class-name-prefix is rejected."""
776-
return_code: Exit = main([
777-
"--input",
778-
str(JSON_SCHEMA_DATA_PATH / "class_name_affix.json"),
779-
"--output",
780-
str(output_file),
781-
"--class-name-prefix",
782-
"123Invalid",
783-
])
784-
assert return_code == Exit.ERROR
779+
run_main_with_args(
780+
[
781+
"--input",
782+
str(JSON_SCHEMA_DATA_PATH / "class_name_affix.json"),
783+
"--output",
784+
str(output_file),
785+
"--class-name-prefix",
786+
"123Invalid",
787+
],
788+
expected_exit=Exit.ERROR,
789+
)
785790

786791

787792
def test_main_class_name_suffix_invalid(output_file: Path) -> None:
788793
"""Test that invalid --class-name-suffix is rejected."""
789-
return_code: Exit = main([
790-
"--input",
791-
str(JSON_SCHEMA_DATA_PATH / "class_name_affix.json"),
792-
"--output",
793-
str(output_file),
794-
"--class-name-suffix",
795-
"Schema!",
796-
])
797-
assert return_code == Exit.ERROR
794+
run_main_with_args(
795+
[
796+
"--input",
797+
str(JSON_SCHEMA_DATA_PATH / "class_name_affix.json"),
798+
"--output",
799+
str(output_file),
800+
"--class-name-suffix",
801+
"Schema!",
802+
],
803+
expected_exit=Exit.ERROR,
804+
)
798805

799806

800807
def test_main_jsonschema_reserved_field_names(output_file: Path) -> None:
@@ -1002,6 +1009,7 @@ def test_main_root_id_jsonschema_with_absolute_local_file(output_file: Path) ->
10021009
)
10031010

10041011

1012+
@pytest.mark.allow_direct_assert
10051013
def test_main_url_with_relative_root_id_resolves_relative_refs(mocker: MockerFixture, tmp_path: Path) -> None:
10061014
"""Test --url input keeps resolving relative refs remotely when root $id is path-only."""
10071015
main_response = mocker.Mock()
@@ -1909,6 +1917,7 @@ def test_main_generate_relative_input_path(output_file: Path) -> None:
19091917
)
19101918

19111919

1920+
@pytest.mark.allow_direct_assert
19121921
def test_main_generate_external_absolute_input_path(tmp_path: Path) -> None:
19131922
"""Test helper keeps absolute input paths that are outside the repository root."""
19141923
with tempfile.TemporaryDirectory() as temp_dir:
@@ -2149,6 +2158,7 @@ def test_main_generate_pydantic_v2_dataclass_enum(output_file: Path) -> None:
21492158
)
21502159

21512160

2161+
@pytest.mark.allow_direct_assert
21522162
@pytest.mark.parametrize(
21532163
("input_file", "expected_file"),
21542164
[
@@ -2174,6 +2184,7 @@ def test_main_generate_pydantic_v2_model_default_dict(input_file: str, expected_
21742184
assert_file_content(output_file, expected_file)
21752185

21762186

2187+
@pytest.mark.allow_direct_assert
21772188
def test_main_generate_from_directory(tmp_path: Path) -> None:
21782189
"""Test generation from directory input."""
21792190
input_ = (JSON_SCHEMA_DATA_PATH / "external_files_in_directory").relative_to(Path.cwd())
@@ -2240,6 +2251,7 @@ def keep_underscores(name: str) -> str:
22402251
)
22412252

22422253

2254+
@pytest.mark.allow_direct_assert
22432255
def test_main_http_jsonschema(mocker: MockerFixture, output_file: Path) -> None:
22442256
"""Test HTTP JSON Schema fetching."""
22452257
external_directory = JSON_SCHEMA_DATA_PATH / "external_files_in_directory"
@@ -2351,6 +2363,7 @@ def get_mock_response(url: str, **_: object) -> mocker.Mock:
23512363
assert httpx_get_mock.call_count == 8
23522364

23532365

2366+
@pytest.mark.allow_direct_assert
23542367
@pytest.mark.parametrize(
23552368
(
23562369
"headers_arguments",
@@ -3304,6 +3317,7 @@ def test_long_description_wrap_string_literal(output_file: Path) -> None:
33043317
)
33053318

33063319

3320+
@pytest.mark.allow_direct_assert
33073321
def test_version(capsys: pytest.CaptureFixture) -> None:
33083322
"""Test version output."""
33093323
with pytest.raises(SystemExit) as e:
@@ -9006,6 +9020,7 @@ def test_field_validators_wrap_mode(output_file: Path, tmp_path: Path) -> None:
90069020
)
90079021

90089022

9023+
@pytest.mark.allow_direct_assert
90099024
def test_field_validators_with_no_field_skipped(output_file: Path, tmp_path: Path) -> None:
90109025
"""Test that validators without fields are skipped gracefully."""
90119026
config_file = tmp_path / "no_field_validators_config.json"
@@ -9070,6 +9085,7 @@ def test_field_validators_plain_mode(output_file: Path, tmp_path: Path) -> None:
90709085
)
90719086

90729087

9088+
@pytest.mark.allow_direct_assert
90739089
def test_field_validators_all_skipped(output_file: Path, tmp_path: Path) -> None:
90749090
"""Test that when all validators have no fields, output has no validators."""
90759091
config_file = tmp_path / "all_skipped_config.json"
@@ -9267,6 +9283,7 @@ def test_main_circular_ref_external_relative_keywords(output_file: Path) -> None
92679283
)
92689284

92699285

9286+
@pytest.mark.allow_direct_assert
92709287
@pytest.mark.benchmark
92719288
def test_main_circular_ref_external_url_keywords(mocker: MockerFixture, output_file: Path) -> None:
92729289
"""Test circular external refs with relative paths and schema keywords via URL input."""

tests/main/openapi/test_main_openapi.py

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,6 +1727,7 @@ def test_main_openapi_pattern_with_lookaround_pydantic_v2(
17271727
)
17281728

17291729

1730+
@pytest.mark.allow_direct_assert
17301731
def test_main_generate_custom_class_name_generator_modular(
17311732
tmp_path: Path,
17321733
) -> None:
@@ -1795,6 +1796,7 @@ def get_mock_response(path: str) -> Mock:
17951796
])
17961797

17971798

1799+
@pytest.mark.allow_direct_assert
17981800
def test_main_http_openapi_with_custom_port(mocker: MockerFixture, output_file: Path) -> None:
17991801
"""Test OpenAPI code generation from HTTP URL with custom port preserves port in refs."""
18001802
schema_content = """\
@@ -2534,6 +2536,7 @@ def test_main_openapi_allof_with_required_inherited_edge_cases(output_file: Path
25342536
)
25352537

25362538

2539+
@pytest.mark.allow_direct_assert
25372540
@LEGACY_BLACK_SKIP
25382541
def test_main_openapi_allof_with_required_inherited_coverage(output_file: Path) -> None:
25392542
"""Test OpenAPI generation with allOf coverage for edge case branches."""
@@ -3977,6 +3980,7 @@ def test_main_openapi_external_ref_with_transitive_local_ref(output_file: Path)
39773980
)
39783981

39793982

3983+
@pytest.mark.allow_direct_assert
39803984
def _assert_external_ref_mapping_cli_parse_error(
39813985
capsys: pytest.CaptureFixture[str],
39823986
mapping: str,
@@ -4073,9 +4077,7 @@ def test_main_openapi_external_ref_mapping_normalizes_imported_class_name(tmp_pa
40734077
output=output_file,
40744078
external_ref_mapping={"common_normalized.yaml": "mypackage.shared.models"},
40754079
)
4076-
content = output_file.read_text()
4077-
assert "from mypackage.shared.models import UserName" in content
4078-
assert "class UserName(" not in content
4080+
assert_file_content(output_file, "external_ref_mapping_normalized.py")
40794081

40804082

40814083
def test_main_openapi_external_ref_mapping_file_uri(tmp_path: Path) -> None:
@@ -4094,9 +4096,7 @@ def test_main_openapi_external_ref_mapping_file_uri(tmp_path: Path) -> None:
40944096
output=output_file,
40954097
external_ref_mapping={common_uri: "mypackage.shared.models"},
40964098
)
4097-
content = output_file.read_text()
4098-
assert "from mypackage.shared.models import User" in content
4099-
assert "class User(" not in content
4099+
assert_file_content(output_file, "external_ref_mapping_file_uri.py")
41004100

41014101

41024102
def test_main_openapi_external_ref_mapping_absolute_path_ref(tmp_path: Path) -> None:
@@ -4115,9 +4115,7 @@ def test_main_openapi_external_ref_mapping_absolute_path_ref(tmp_path: Path) ->
41154115
output=output_file,
41164116
external_ref_mapping={common_path: "mypackage.shared.models"},
41174117
)
4118-
content = output_file.read_text()
4119-
assert "from mypackage.shared.models import User" in content
4120-
assert "class User(" not in content
4118+
assert_file_content(output_file, "external_ref_mapping_absolute_path.py")
41214119

41224120

41234121
def test_main_openapi_external_ref_mapping_local_ref_unchanged(tmp_path: Path) -> None:
@@ -4129,10 +4127,7 @@ def test_main_openapi_external_ref_mapping_local_ref_unchanged(tmp_path: Path) -
41294127
output=output_file,
41304128
external_ref_mapping={"common.yaml": "mypackage.shared.models"},
41314129
)
4132-
content = output_file.read_text()
4133-
assert "class User(" in content
4134-
assert "class UserResponse(" in content
4135-
assert "from mypackage.shared.models import" not in content
4130+
assert_file_content(output_file, "external_ref_mapping_local_ref_unchanged.py")
41364131

41374132

41384133
def test_main_openapi_external_ref_mapping_ref_without_fragment_errors(tmp_path: Path) -> None:
@@ -4156,10 +4151,7 @@ def test_main_openapi_external_ref_mapping_no_duplicate_classes(tmp_path: Path)
41564151
output=output_file,
41574152
external_ref_mapping={"common.yaml": "mypackage.shared.models"},
41584153
)
4159-
content = output_file.read_text()
4160-
assert "class User(" not in content
4161-
assert "class Error(" not in content
4162-
assert "from mypackage.shared.models import" in content
4154+
assert_file_content(output_file, "external_ref_mapping.py")
41634155

41644156

41654157
def test_main_openapi_external_ref_mapping_without_flag_generates_classes(tmp_path: Path) -> None:
@@ -4170,9 +4162,7 @@ def test_main_openapi_external_ref_mapping_without_flag_generates_classes(tmp_pa
41704162
input_file_type=InputFileType.OpenAPI,
41714163
output=output_file,
41724164
)
4173-
content = output_file.read_text()
4174-
assert "class User(" in content
4175-
assert "class Error(" in content
4165+
assert_file_content(output_file, "external_ref_mapping_without_flag.py")
41764166

41774167

41784168
def test_main_openapi_external_ref_mapping_invalid_format(capsys: pytest.CaptureFixture[str]) -> None:
@@ -4237,9 +4227,7 @@ def test_main_openapi_external_ref_mapping_programmatic_api(tmp_path: Path) -> N
42374227
input_=EXTERNAL_REF_MAPPING_DATA_PATH / "api.yaml",
42384228
config=config,
42394229
)
4240-
content = output_file.read_text()
4241-
assert "class User(" not in content
4242-
assert "from mypackage.shared.models import" in content
4230+
assert_file_content(output_file, "external_ref_mapping.py")
42434231

42444232

42454233
def test_main_openapi_namespace_subns_ref(output_dir: Path) -> None:
@@ -5194,6 +5182,7 @@ def test_main_openapi_include_paths_without_leading_slash(output_file: Path) ->
51945182
)
51955183

51965184

5185+
@pytest.mark.allow_direct_assert
51975186
def test_main_openapi_include_paths_warning_without_paths_scope() -> None:
51985187
"""Warn when --openapi-include-paths used without paths scope."""
51995188
import warnings

0 commit comments

Comments
 (0)