Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/datamodel_code_generator/parser/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1070,7 +1070,7 @@ def _get_text_from_url(self, url: str) -> str:
def get_url_path_parts(cls, url: ParseResult) -> list[str]:
"""Split URL into scheme/host and path components."""
return [
f"{url.scheme}://{url.hostname}",
f"{url.scheme}://{url.netloc}",
*url.path.split("/")[1:],
]

Expand Down
47 changes: 47 additions & 0 deletions tests/main/openapi/test_main_openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -1813,6 +1813,53 @@ def get_mock_response(path: str) -> Mock:
])


def test_main_http_openapi_with_custom_port(mocker: MockerFixture, output_file: Path) -> None:
"""Test OpenAPI code generation from HTTP URL with custom port preserves port in refs."""
schema_content = """\
openapi: "3.0.0"
info:
title: Minimal API
version: "1.0.0"
paths: {}
components:
schemas:
Item:
type: object
properties:
id:
type: string
owner:
$ref: "#/components/schemas/User"
User:
type: object
properties:
name:
type: string
"""
mock_response = mocker.Mock()
mock_response.text = schema_content

httpx_get_mock = mocker.patch("httpx.get", return_value=mock_response)

from datamodel_code_generator.__main__ import main

return_code = main(["--url", "http://127.0.0.1:8123/openapi.json", "--output", str(output_file)])
assert return_code == Exit.OK

result = output_file.read_text(encoding="utf-8")
assert "class Item" in result
assert "class User" in result

httpx_get_mock.assert_called_once_with(
"http://127.0.0.1:8123/openapi.json",
headers=None,
verify=True,
follow_redirects=True,
params=None,
timeout=30.0,
)

Comment on lines +1816 to +1861
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Test doesn't verify the fix for external $refs with custom ports.

The test uses an internal $ref (#/components/schemas/User at line 1832), which doesn't trigger additional HTTP requests. According to issue #2931, the bug occurs when "subsequent requests for $ref resources attempt to connect to port 80 instead of the original custom port."

To properly verify the fix, the test should include an external $ref (e.g., $ref: "http://127.0.0.1:8123/definitions.yaml" or a relative $ref: "./definitions.yaml"), mock multiple httpx.get calls, and assert that all requests preserve the custom port.

🔎 Proposed enhancement to test external refs

Consider following the pattern from test_main_http_openapi (lines 1773-1813):

 def test_main_http_openapi_with_custom_port(mocker: MockerFixture, output_file: Path) -> None:
     """Test OpenAPI code generation from HTTP URL with custom port preserves port in refs."""
-    schema_content = """\
+    main_schema = """\
 openapi: "3.0.0"
 info:
   title: Minimal API
   version: "1.0.0"
 paths: {}
 components:
   schemas:
     Item:
       type: object
       properties:
         id:
           type: string
         owner:
-          $ref: "#/components/schemas/User"
-    User:
-      type: object
-      properties:
-        name:
-          type: string
+          $ref: "http://127.0.0.1:8123/definitions.yaml#/components/schemas/User"
 """
-    mock_response = mocker.Mock()
-    mock_response.text = schema_content
+    
+    definitions_schema = """\
+components:
+  schemas:
+    User:
+      type: object
+      properties:
+        name:
+          type: string
+"""
 
-    httpx_get_mock = mocker.patch("httpx.get", return_value=mock_response)
+    def get_mock_response(url: str) -> mocker.Mock:
+        mock = mocker.Mock()
+        if "definitions.yaml" in url:
+            mock.text = definitions_schema
+        else:
+            mock.text = main_schema
+        return mock
+
+    httpx_get_mock = mocker.patch("httpx.get", side_effect=get_mock_response)
 
     from datamodel_code_generator.__main__ import main
 
     return_code = main(["--url", "http://127.0.0.1:8123/openapi.json", "--output", str(output_file)])
     assert return_code == Exit.OK
 
     result = output_file.read_text(encoding="utf-8")
     assert "class Item" in result
     assert "class User" in result
 
-    httpx_get_mock.assert_called_once_with(
-        "http://127.0.0.1:8123/openapi.json",
-        headers=None,
-        verify=True,
-        follow_redirects=True,
-        params=None,
-        timeout=30.0,
-    )
+    httpx_get_mock.assert_has_calls([
+        call(
+            "http://127.0.0.1:8123/openapi.json",
+            headers=None,
+            verify=True,
+            follow_redirects=True,
+            params=None,
+            timeout=30.0,
+        ),
+        call(
+            "http://127.0.0.1:8123/definitions.yaml",
+            headers=None,
+            verify=True,
+            follow_redirects=True,
+            params=None,
+            timeout=30.0,
+        ),
+    ])

This would verify that both the initial request and the subsequent request for the external $ref preserve the custom port 8123.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @tests/main/openapi/test_main_openapi.py around lines 1816-1861, The test
test_main_http_openapi_with_custom_port currently uses an internal $ref and thus
never exercises external fetching; update the OpenAPI schema in that test to
include an external $ref (for example $ref:
"http://127.0.0.1:8123/definitions.yaml" or a relative "./definitions.yaml") so
the generator will perform a second HTTP fetch, mock httpx.get to return two
distinct mock responses (one for the main openapi.json and one for
definitions.yaml) and then assert httpx_get_mock was called for both URLs
preserving port 8123 (e.g., assert_called_with for
"http://127.0.0.1:8123/openapi.json" and for
"http://127.0.0.1:8123/definitions.yaml"), ensuring the main function (main)
preserves the custom port on subsequent $ref requests.


@pytest.mark.cli_doc(
options=["--disable-appending-item-suffix"],
option_description="""Disable appending 'Item' suffix to array item types.
Expand Down
Loading