From 723b0ae330a19abc5f23a3e8a23787773441d2b5 Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Thu, 2 Jul 2026 16:11:45 +0900 Subject: [PATCH 1/3] Cover input error messages --- tests/data/malformed/bad.graphql | 3 + tests/data/malformed/bad_openapi.yaml | 4 + tests/data/malformed/dangling_local_ref.json | 1 + tests/data/malformed/empty_jsonschema.json | 0 tests/data/malformed/enum_as_dict.json | 4 + .../data/malformed/missing_external_ref.json | 1 + tests/data/malformed/non_dict_root.yaml | 3 + .../malformed/pointer_through_scalar.json | 6 + .../pointer_through_scalar_openapi.yaml | 10 ++ tests/data/malformed/required_as_string.json | 7 ++ .../data/malformed/truncated_jsonschema.json | 1 + .../data/malformed/wrong_type_properties.json | 4 + tests/main/test_error_messages.py | 118 ++++++++++++++++++ 13 files changed, 162 insertions(+) create mode 100644 tests/data/malformed/bad.graphql create mode 100644 tests/data/malformed/bad_openapi.yaml create mode 100644 tests/data/malformed/dangling_local_ref.json create mode 100644 tests/data/malformed/empty_jsonschema.json create mode 100644 tests/data/malformed/enum_as_dict.json create mode 100644 tests/data/malformed/missing_external_ref.json create mode 100644 tests/data/malformed/non_dict_root.yaml create mode 100644 tests/data/malformed/pointer_through_scalar.json create mode 100644 tests/data/malformed/pointer_through_scalar_openapi.yaml create mode 100644 tests/data/malformed/required_as_string.json create mode 100644 tests/data/malformed/truncated_jsonschema.json create mode 100644 tests/data/malformed/wrong_type_properties.json create mode 100644 tests/main/test_error_messages.py diff --git a/tests/data/malformed/bad.graphql b/tests/data/malformed/bad.graphql new file mode 100644 index 000000000..eb7c2a316 --- /dev/null +++ b/tests/data/malformed/bad.graphql @@ -0,0 +1,3 @@ +type Query { + user: +} diff --git a/tests/data/malformed/bad_openapi.yaml b/tests/data/malformed/bad_openapi.yaml new file mode 100644 index 000000000..2815edcd1 --- /dev/null +++ b/tests/data/malformed/bad_openapi.yaml @@ -0,0 +1,4 @@ +openapi: 3.0.0 +info: + title: Bad +paths: [ diff --git a/tests/data/malformed/dangling_local_ref.json b/tests/data/malformed/dangling_local_ref.json new file mode 100644 index 000000000..af00a507f --- /dev/null +++ b/tests/data/malformed/dangling_local_ref.json @@ -0,0 +1 @@ +{"$ref": "#/definitions/Missing"} diff --git a/tests/data/malformed/empty_jsonschema.json b/tests/data/malformed/empty_jsonschema.json new file mode 100644 index 000000000..e69de29bb diff --git a/tests/data/malformed/enum_as_dict.json b/tests/data/malformed/enum_as_dict.json new file mode 100644 index 000000000..a54f9fc9a --- /dev/null +++ b/tests/data/malformed/enum_as_dict.json @@ -0,0 +1,4 @@ +{ + "type": "string", + "enum": {"one": 1} +} diff --git a/tests/data/malformed/missing_external_ref.json b/tests/data/malformed/missing_external_ref.json new file mode 100644 index 000000000..ac5849522 --- /dev/null +++ b/tests/data/malformed/missing_external_ref.json @@ -0,0 +1 @@ +{"$ref": "missing.json"} diff --git a/tests/data/malformed/non_dict_root.yaml b/tests/data/malformed/non_dict_root.yaml new file mode 100644 index 000000000..452daddcf --- /dev/null +++ b/tests/data/malformed/non_dict_root.yaml @@ -0,0 +1,3 @@ +- not +- a +- mapping diff --git a/tests/data/malformed/pointer_through_scalar.json b/tests/data/malformed/pointer_through_scalar.json new file mode 100644 index 000000000..f9f3c090f --- /dev/null +++ b/tests/data/malformed/pointer_through_scalar.json @@ -0,0 +1,6 @@ +{ + "definitions": { + "Name": "scalar" + }, + "$ref": "#/definitions/Name/properties/value" +} diff --git a/tests/data/malformed/pointer_through_scalar_openapi.yaml b/tests/data/malformed/pointer_through_scalar_openapi.yaml new file mode 100644 index 000000000..4d8dd17bb --- /dev/null +++ b/tests/data/malformed/pointer_through_scalar_openapi.yaml @@ -0,0 +1,10 @@ +openapi: 3.0.0 +info: + title: Bad + version: '1.0' +paths: {} +components: + schemas: + Name: scalar + UseName: + $ref: '#/components/schemas/Name/properties/value' diff --git a/tests/data/malformed/required_as_string.json b/tests/data/malformed/required_as_string.json new file mode 100644 index 000000000..80f939b64 --- /dev/null +++ b/tests/data/malformed/required_as_string.json @@ -0,0 +1,7 @@ +{ + "type": "object", + "required": "name", + "properties": { + "name": {"type": "string"} + } +} diff --git a/tests/data/malformed/truncated_jsonschema.json b/tests/data/malformed/truncated_jsonschema.json new file mode 100644 index 000000000..b383686ba --- /dev/null +++ b/tests/data/malformed/truncated_jsonschema.json @@ -0,0 +1 @@ +{"type": "object", "properties": { diff --git a/tests/data/malformed/wrong_type_properties.json b/tests/data/malformed/wrong_type_properties.json new file mode 100644 index 000000000..9cabff28f --- /dev/null +++ b/tests/data/malformed/wrong_type_properties.json @@ -0,0 +1,4 @@ +{ + "type": "object", + "properties": [] +} diff --git a/tests/main/test_error_messages.py b/tests/main/test_error_messages.py new file mode 100644 index 000000000..19b948fdd --- /dev/null +++ b/tests/main/test_error_messages.py @@ -0,0 +1,118 @@ +"""Regression tests for malformed CLI input diagnostics.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from datamodel_code_generator.__main__ import Exit +from tests.main.conftest import DATA_PATH, InputFileTypeLiteral, run_main_and_assert + +if TYPE_CHECKING: + from pathlib import Path + + +MALFORMED_DATA_PATH = DATA_PATH / "malformed" +TRACEBACK_HEADER = "Traceback (most recent call last)" + +ERROR_CASES: tuple[tuple[str, InputFileTypeLiteral, str], ...] = ( + ("truncated_jsonschema.json", "jsonschema", TRACEBACK_HEADER), + ("bad_openapi.yaml", "openapi", TRACEBACK_HEADER), + ("bad.graphql", "graphql", TRACEBACK_HEADER), + ("non_dict_root.yaml", "openapi", TRACEBACK_HEADER), + ("pointer_through_scalar_openapi.yaml", "openapi", TRACEBACK_HEADER), + ( + "pointer_through_scalar.json", + "jsonschema", + "Error at schema path 'pointer_through_scalar.json/#/definitions/Name': ValidationError", + ), + ("wrong_type_properties.json", "jsonschema", "Error at schema path 'wrong_type_properties.json'"), + ("required_as_string.json", "jsonschema", "Error at schema path 'required_as_string.json'"), + ("enum_as_dict.json", "jsonschema", "Error at schema path 'enum_as_dict.json'"), + ("missing_external_ref.json", "jsonschema", "$ref file not found:"), + ("empty_jsonschema.json", "jsonschema", "Models not found in the input data"), +) + +MISSING_INPUT_CASES: tuple[tuple[InputFileTypeLiteral | None, str], ...] = ( + (None, "File not found"), + ("jsonschema", TRACEBACK_HEADER), +) + +TOLERATED_CASES: tuple[tuple[str, InputFileTypeLiteral], ...] = ( + ("dangling_local_ref.json", "jsonschema"), +) + + +def _input_file_type_id(input_file_type: InputFileTypeLiteral | None) -> str: + match input_file_type: + case None: + return "auto" + case input_type: + return input_type + + +@pytest.mark.parametrize( + ("fixture_name", "input_file_type", "expected_stderr_contains"), + ERROR_CASES, + ids=[fixture_name for fixture_name, _, _ in ERROR_CASES], +) +def test_malformed_input_error_messages( + fixture_name: str, + input_file_type: InputFileTypeLiteral, + expected_stderr_contains: str, + output_file: Path, + capsys: pytest.CaptureFixture[str], +) -> None: + """Malformed inputs keep their current stderr signal and non-zero exit code.""" + run_main_and_assert( + input_path=MALFORMED_DATA_PATH / fixture_name, + output_path=output_file, + input_file_type=input_file_type, + expected_exit=Exit.ERROR, + capsys=capsys, + expected_stderr_contains=expected_stderr_contains, + output_should_not_exist=True, + ) + + +@pytest.mark.parametrize( + ("input_file_type", "expected_stderr_contains"), + MISSING_INPUT_CASES, + ids=[_input_file_type_id(input_file_type) for input_file_type, _ in MISSING_INPUT_CASES], +) +def test_missing_input_error_messages( + input_file_type: InputFileTypeLiteral | None, + expected_stderr_contains: str, + output_file: Path, + capsys: pytest.CaptureFixture[str], +) -> None: + """Missing local input paths keep their current CLI diagnostics.""" + run_main_and_assert( + input_path=MALFORMED_DATA_PATH / "missing.json", + output_path=output_file, + input_file_type=input_file_type, + expected_exit=Exit.ERROR, + capsys=capsys, + expected_stderr_contains=expected_stderr_contains, + output_should_not_exist=True, + ) + + +@pytest.mark.parametrize( + ("fixture_name", "input_file_type"), + TOLERATED_CASES, + ids=[fixture_name for fixture_name, _ in TOLERATED_CASES], +) +def test_malformed_input_tolerated_behavior( + fixture_name: str, + input_file_type: InputFileTypeLiteral, + output_file: Path, +) -> None: + """Document malformed inputs that are intentionally tolerated until stricter diagnostics exist.""" + run_main_and_assert( + input_path=MALFORMED_DATA_PATH / fixture_name, + output_path=output_file, + input_file_type=input_file_type, + expected_exit=Exit.OK, + ) From c848a7aa9513986b2893c45cf2fbb303134a6a89 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Jul 2026 07:12:14 +0000 Subject: [PATCH 2/3] style: auto-fix by pre-commit.ci --- tests/main/test_error_messages.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/main/test_error_messages.py b/tests/main/test_error_messages.py index 19b948fdd..6c88e949a 100644 --- a/tests/main/test_error_messages.py +++ b/tests/main/test_error_messages.py @@ -39,9 +39,7 @@ ("jsonschema", TRACEBACK_HEADER), ) -TOLERATED_CASES: tuple[tuple[str, InputFileTypeLiteral], ...] = ( - ("dangling_local_ref.json", "jsonschema"), -) +TOLERATED_CASES: tuple[tuple[str, InputFileTypeLiteral], ...] = (("dangling_local_ref.json", "jsonschema"),) def _input_file_type_id(input_file_type: InputFileTypeLiteral | None) -> str: From fd2ec042f8c80b65dc0355119ec0eef095ed38b5 Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Thu, 2 Jul 2026 16:20:46 +0900 Subject: [PATCH 3/3] Address input error review --- tests/main/test_error_messages.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tests/main/test_error_messages.py b/tests/main/test_error_messages.py index 6c88e949a..494f74929 100644 --- a/tests/main/test_error_messages.py +++ b/tests/main/test_error_messages.py @@ -36,20 +36,14 @@ MISSING_INPUT_CASES: tuple[tuple[InputFileTypeLiteral | None, str], ...] = ( (None, "File not found"), + # Explicit input types currently bypass auto-detection's missing-file check. + # Keep this pinned until the diagnostic behavior is intentionally changed. ("jsonschema", TRACEBACK_HEADER), ) TOLERATED_CASES: tuple[tuple[str, InputFileTypeLiteral], ...] = (("dangling_local_ref.json", "jsonschema"),) -def _input_file_type_id(input_file_type: InputFileTypeLiteral | None) -> str: - match input_file_type: - case None: - return "auto" - case input_type: - return input_type - - @pytest.mark.parametrize( ("fixture_name", "input_file_type", "expected_stderr_contains"), ERROR_CASES, @@ -77,7 +71,7 @@ def test_malformed_input_error_messages( @pytest.mark.parametrize( ("input_file_type", "expected_stderr_contains"), MISSING_INPUT_CASES, - ids=[_input_file_type_id(input_file_type) for input_file_type, _ in MISSING_INPUT_CASES], + ids=[input_file_type or "auto" for input_file_type, _ in MISSING_INPUT_CASES], ) def test_missing_input_error_messages( input_file_type: InputFileTypeLiteral | None,