|
6 | 6 |
|
7 | 7 | import pytest |
8 | 8 |
|
9 | | -from datamodel_code_generator.reference import ModelResolver, get_relative_path |
| 9 | +from datamodel_code_generator.reference import ModelResolver, get_relative_path, is_url |
10 | 10 |
|
11 | 11 |
|
12 | 12 | @pytest.mark.parametrize( |
@@ -70,3 +70,140 @@ def test_model_resolver_add_ref_unevaluated() -> None: |
70 | 70 | model_resolver = ModelResolver() |
71 | 71 | reference = model_resolver.add_ref("meta/unevaluated") |
72 | 72 | assert reference.original_name == "unevaluated" |
| 73 | + |
| 74 | + |
| 75 | +def test_base_url_context_sets_url_when_base_url_already_set() -> None: |
| 76 | + """When _base_url is already set, base_url_context should switch to new URL.""" |
| 77 | + resolver = ModelResolver(base_url="https://example.com/original.json") |
| 78 | + assert resolver.base_url == "https://example.com/original.json" |
| 79 | + |
| 80 | + with resolver.base_url_context("https://example.com/new.json"): |
| 81 | + assert resolver.base_url == "https://example.com/new.json" |
| 82 | + |
| 83 | + # Should restore original |
| 84 | + assert resolver.base_url == "https://example.com/original.json" |
| 85 | + |
| 86 | + |
| 87 | +def test_base_url_context_sets_url_when_new_value_is_url() -> None: |
| 88 | + """When _base_url is None but new value is a URL, should set base_url.""" |
| 89 | + resolver = ModelResolver() |
| 90 | + assert resolver.base_url is None |
| 91 | + |
| 92 | + with resolver.base_url_context("https://example.com/schema.json"): |
| 93 | + assert resolver.base_url == "https://example.com/schema.json" |
| 94 | + |
| 95 | + # Should restore to None |
| 96 | + assert resolver.base_url is None |
| 97 | + |
| 98 | + |
| 99 | +def test_base_url_context_noop_when_new_value_is_not_url() -> None: |
| 100 | + """When _base_url is None and new value is not a URL, should do nothing.""" |
| 101 | + resolver = ModelResolver() |
| 102 | + assert resolver.base_url is None |
| 103 | + |
| 104 | + with resolver.base_url_context("../relative/path.json"): |
| 105 | + # Should remain None because the value is not a URL |
| 106 | + assert resolver.base_url is None |
| 107 | + |
| 108 | + assert resolver.base_url is None |
| 109 | + |
| 110 | + |
| 111 | +def test_base_url_context_nested() -> None: |
| 112 | + """Nested base_url_context should properly restore values.""" |
| 113 | + resolver = ModelResolver(base_url="https://example.com/level0.json") |
| 114 | + |
| 115 | + with resolver.base_url_context("https://example.com/level1.json"): |
| 116 | + assert resolver.base_url == "https://example.com/level1.json" |
| 117 | + |
| 118 | + with resolver.base_url_context("https://example.com/level2.json"): |
| 119 | + assert resolver.base_url == "https://example.com/level2.json" |
| 120 | + |
| 121 | + assert resolver.base_url == "https://example.com/level1.json" |
| 122 | + |
| 123 | + assert resolver.base_url == "https://example.com/level0.json" |
| 124 | + |
| 125 | + |
| 126 | +def test_resolve_ref_with_base_url_does_not_prepend_root_id_base_path() -> None: |
| 127 | + """When base_url is set, root_id_base_path should not be prepended to refs.""" |
| 128 | + resolver = ModelResolver(base_url="https://example.com/schemas/main.json") |
| 129 | + resolver.set_root_id("https://example.com/schemas/main.json") |
| 130 | + |
| 131 | + # Resolve a relative ref |
| 132 | + result = resolver.resolve_ref("../other/schema.json") |
| 133 | + |
| 134 | + # Should resolve via join_url, not prepend root_id_base_path |
| 135 | + assert result == "https://example.com/other/schema.json#" |
| 136 | + # Should NOT be like "https://example.com/schemas/../other/schema.json#" |
| 137 | + |
| 138 | + |
| 139 | +def test_resolve_ref_with_base_url_nested_relative_refs() -> None: |
| 140 | + """Nested relative refs should resolve correctly when base_url is set.""" |
| 141 | + resolver = ModelResolver(base_url="https://example.com/a/b/c/main.json") |
| 142 | + |
| 143 | + # Resolve a deeply nested relative ref |
| 144 | + result = resolver.resolve_ref("../../other/schema.json") |
| 145 | + |
| 146 | + assert result == "https://example.com/a/other/schema.json#" |
| 147 | + |
| 148 | + |
| 149 | +def test_resolve_ref_with_base_url_context_switch() -> None: |
| 150 | + """Relative refs should resolve correctly after base_url context switch.""" |
| 151 | + resolver = ModelResolver(base_url="https://example.com/schemas/person.json") |
| 152 | + |
| 153 | + # Switch context to a different file |
| 154 | + with resolver.base_url_context("https://example.com/schemas/definitions/pet.json"): |
| 155 | + # Resolve a relative ref from the new context |
| 156 | + result = resolver.resolve_ref("../common/types.json") |
| 157 | + |
| 158 | + assert result == "https://example.com/schemas/common/types.json#" |
| 159 | + |
| 160 | + |
| 161 | +def test_resolve_ref_local_fragment_with_base_url() -> None: |
| 162 | + """Local fragment refs should resolve to full URL when base_url is set.""" |
| 163 | + resolver = ModelResolver(base_url="https://example.com/schemas/main.json") |
| 164 | + |
| 165 | + result = resolver.resolve_ref("#/definitions/Foo") |
| 166 | + |
| 167 | + # When base_url is set, local fragments are resolved to full URL |
| 168 | + assert result == "https://example.com/schemas/main.json#/definitions/Foo" |
| 169 | + |
| 170 | + |
| 171 | +@pytest.mark.parametrize( |
| 172 | + ("ref", "expected"), |
| 173 | + [ |
| 174 | + # HTTP/HTTPS URLs (only supported schemes) |
| 175 | + ("https://example.com/schema.json", True), |
| 176 | + ("http://example.com/schema.json", True), |
| 177 | + ("https://example.com/path/to/schema.json", True), |
| 178 | + # file:// URLs - NOT recognized (fetcher only supports HTTP) |
| 179 | + ("file:///home/user/schema.json", False), |
| 180 | + ("file:/home/user/schema.json", False), |
| 181 | + # Other URL schemes - NOT recognized |
| 182 | + ("ftp://example.com/schema.json", False), |
| 183 | + # Relative paths (not URLs) |
| 184 | + ("../relative/path.json", False), |
| 185 | + ("relative/path.json", False), |
| 186 | + # Local fragments (not URLs) |
| 187 | + ("#/definitions/Foo", False), |
| 188 | + ("#", False), |
| 189 | + # Absolute paths (not URLs) |
| 190 | + ("/absolute/path.json", False), |
| 191 | + # Windows paths (not URLs) |
| 192 | + ("c:/windows/path.json", False), |
| 193 | + ("d:/path/to/file.json", False), |
| 194 | + ], |
| 195 | +) |
| 196 | +def test_is_url(ref: str, expected: bool) -> None: |
| 197 | + """Test is_url correctly identifies HTTP(S) URLs only.""" |
| 198 | + assert is_url(ref) == expected |
| 199 | + |
| 200 | + |
| 201 | +def test_resolve_ref_with_root_id_differs_from_base_url() -> None: |
| 202 | + """When $id differs from fetch URL, refs should resolve against $id.""" |
| 203 | + # Scenario: Schema fetched from CDN but has canonical $id |
| 204 | + resolver = ModelResolver(base_url="https://cdn.example.com/latest/schema.json") |
| 205 | + resolver.set_root_id("https://example.com/v1/schema.json") |
| 206 | + |
| 207 | + result = resolver.resolve_ref("../common/types.json") |
| 208 | + |
| 209 | + assert result == "https://example.com/common/types.json#" |
0 commit comments