1
1
import shutil
2
2
from filecmp import cmpfiles , dircmp
3
3
from pathlib import Path
4
- from typing import Dict , List , Optional , Tuple
4
+ from typing import Dict , List , Optional , Set
5
5
6
6
import pytest
7
+ from click .testing import Result
7
8
from typer .testing import CliRunner
8
9
9
10
from openapi_python_client .cli import app
@@ -13,6 +14,7 @@ def _compare_directories(
13
14
record : Path ,
14
15
test_subject : Path ,
15
16
expected_differences : Dict [Path , str ],
17
+ expected_missing : Optional [Set [str ]] = None ,
16
18
ignore : List [str ] = None ,
17
19
depth = 0 ,
18
20
):
@@ -28,7 +30,7 @@ def _compare_directories(
28
30
first_printable = record .relative_to (Path .cwd ())
29
31
second_printable = test_subject .relative_to (Path .cwd ())
30
32
dc = dircmp (record , test_subject , ignore = [".ruff_cache" , "__pycache__" ] + (ignore or []))
31
- missing_files = dc .left_only + dc .right_only
33
+ missing_files = set ( dc .left_only + dc .right_only ) - ( expected_missing or set ())
32
34
if missing_files :
33
35
pytest .fail (
34
36
f"{ first_printable } or { second_printable } was missing: { missing_files } " ,
@@ -77,21 +79,23 @@ def _compare_directories(
77
79
def run_e2e_test (
78
80
openapi_document : str ,
79
81
extra_args : List [str ],
80
- expected_differences : Dict [Path , str ],
82
+ expected_differences : Optional [ Dict [Path , str ]] = None ,
81
83
golden_record_path : str = "golden-record" ,
82
84
output_path : str = "my-test-api-client" ,
83
- ):
85
+ expected_missing : Optional [Set [str ]] = None ,
86
+ ) -> Result :
84
87
output_path = Path .cwd () / output_path
85
88
shutil .rmtree (output_path , ignore_errors = True )
86
- generate (extra_args , openapi_document )
89
+ result = generate (extra_args , openapi_document )
87
90
gr_path = Path (__file__ ).parent / golden_record_path
88
91
92
+ expected_differences = expected_differences or {}
89
93
# Use absolute paths for expected differences for easier comparisons
90
94
expected_differences = {
91
95
output_path .joinpath (key ): value for key , value in expected_differences .items ()
92
96
}
93
97
_compare_directories (
94
- gr_path , output_path , expected_differences = expected_differences
98
+ gr_path , output_path , expected_differences = expected_differences , expected_missing = expected_missing
95
99
)
96
100
97
101
import mypy .api
@@ -100,19 +104,31 @@ def run_e2e_test(
100
104
assert status == 0 , f"Type checking client failed: { out } "
101
105
102
106
shutil .rmtree (output_path )
107
+ return result
103
108
104
109
105
- def generate (extra_args : Optional [List [str ]], openapi_document : str ):
110
+ def generate (extra_args : Optional [List [str ]], openapi_document : str ) -> Result :
111
+ """Generate a client from an OpenAPI document and return the path to the generated code"""
112
+ _run_command ("generate" , extra_args , openapi_document )
113
+
114
+
115
+ def _run_command (command : str , extra_args : Optional [List [str ]] = None , openapi_document : Optional [str ] = None , url : Optional [str ] = None , config_path : Optional [Path ] = None ) -> Result :
106
116
"""Generate a client from an OpenAPI document and return the path to the generated code"""
107
117
runner = CliRunner ()
108
- openapi_path = Path (__file__ ).parent / openapi_document
109
- config_path = Path (__file__ ).parent / "config.yml"
110
- args = ["generate" , f"--config={ config_path } " , f"--path={ openapi_path } " ]
118
+ if openapi_document is not None :
119
+ openapi_path = Path (__file__ ).parent / openapi_document
120
+ source_arg = f"--path={ openapi_path } "
121
+ else :
122
+ source_arg = f"--url={ url } "
123
+ config_path = config_path or (Path (__file__ ).parent / "config.yml" )
124
+ args = [command , f"--config={ config_path } " , source_arg ]
111
125
if extra_args :
112
126
args .extend (extra_args )
113
127
result = runner .invoke (app , args )
114
128
if result .exit_code != 0 :
115
- raise result .exception
129
+ raise Exception (result .stdout )
130
+ return result
131
+
116
132
117
133
def test_baseline_end_to_end_3_0 ():
118
134
run_e2e_test ("baseline_openapi_3.0.json" , [], {})
@@ -147,19 +163,22 @@ def test_meta(meta: str, generated_file: Optional[str], expected_file: Optional[
147
163
148
164
if generated_file and expected_file :
149
165
assert (output_path / generated_file ).exists ()
150
- assert (output_path / generated_file ).read_text () == (Path (__file__ ).parent / "metadata_snapshots" / expected_file ).read_text ()
166
+ assert (
167
+ (output_path / generated_file ).read_text () ==
168
+ (Path (__file__ ).parent / "metadata_snapshots" / expected_file ).read_text ()
169
+ )
151
170
152
171
shutil .rmtree (output_path )
153
172
154
173
155
- def test_no_meta ():
156
- output_path = Path . cwd () / "test_3_1_features_client"
157
- shutil . rmtree ( output_path , ignore_errors = True )
158
- generate ([ f "--meta=none" ], "3.1_specific.openapi.yaml" )
159
-
160
- assert output_path . exists () # Has a different name than with-meta generation
161
- assert ( output_path / "__init__.py" ). exists ()
162
- shutil . rmtree ( output_path )
174
+ def test_none_meta ():
175
+ run_e2e_test (
176
+ "3.1_specific.openapi.yaml" ,
177
+ [ "--meta=none" ],
178
+ golden_record_path = "test-3-1-golden-record/test_3_1_features_client" ,
179
+ output_path = "test_3_1_features_client" ,
180
+ expected_missing = { "py.typed" },
181
+ )
163
182
164
183
165
184
def test_custom_templates ():
@@ -194,3 +213,76 @@ def test_custom_templates():
194
213
extra_args = ["--custom-template-path=end_to_end_tests/test_custom_templates/" ],
195
214
expected_differences = expected_differences ,
196
215
)
216
+
217
+
218
+ @pytest .mark .parametrize (
219
+ "command" , ("generate" , "update" )
220
+ )
221
+ def test_bad_url (command : str ):
222
+ runner = CliRunner ()
223
+ result = runner .invoke (app , [command , "--url=not_a_url" ])
224
+ assert result .exit_code == 1
225
+ assert "Could not get OpenAPI document from provided URL" in result .stdout
226
+
227
+
228
+ def test_custom_post_hooks ():
229
+ shutil .rmtree (Path .cwd () / "my-test-api-client" , ignore_errors = True )
230
+ runner = CliRunner ()
231
+ openapi_document = Path (__file__ ).parent / "baseline_openapi_3.0.json"
232
+ config_path = Path (__file__ ).parent / "custom_post_hooks.config.yml"
233
+ result = runner .invoke (app , ["generate" , f"--path={ openapi_document } " , f"--config={ config_path } " ])
234
+ assert result .exit_code == 1
235
+ assert "this should fail" in result .stdout
236
+ shutil .rmtree (Path .cwd () / "my-test-api-client" , ignore_errors = True )
237
+
238
+
239
+ def test_generate_dir_already_exists ():
240
+ project_dir = Path .cwd () / "my-test-api-client"
241
+ if not project_dir .exists ():
242
+ project_dir .mkdir ()
243
+ runner = CliRunner ()
244
+ openapi_document = Path (__file__ ).parent / "baseline_openapi_3.0.json"
245
+ result = runner .invoke (app , ["generate" , f"--path={ openapi_document } " ])
246
+ assert result .exit_code == 1
247
+ assert "Directory already exists" in result .stdout
248
+ shutil .rmtree (Path .cwd () / "my-test-api-client" , ignore_errors = True )
249
+
250
+
251
+ def test_update_dir_not_found ():
252
+ project_dir = Path .cwd () / "my-test-api-client"
253
+ shutil .rmtree (project_dir , ignore_errors = True )
254
+ runner = CliRunner ()
255
+ openapi_document = Path (__file__ ).parent / "baseline_openapi_3.0.json"
256
+ result = runner .invoke (app , ["update" , f"--path={ openapi_document } " ])
257
+ assert result .exit_code == 1
258
+ assert str (project_dir ) in result .stdout
259
+
260
+
261
+ @pytest .mark .parametrize (
262
+ ("file_name" , "content" , "expected_error" ),
263
+ (
264
+ ("invalid_openapi.yaml" , "not a valid openapi document" , "Failed to parse OpenAPI document" ),
265
+ ("invalid_json.json" , "Invalid JSON" , "Invalid JSON" ),
266
+ ("invalid_yaml.yaml" , "{" , "Invalid YAML" ),
267
+ ),
268
+ ids = ("invalid_openapi" , "invalid_json" , "invalid_yaml" )
269
+ )
270
+ def test_invalid_openapi_document (file_name , content , expected_error ):
271
+ runner = CliRunner ()
272
+ openapi_document = Path .cwd () / file_name
273
+ openapi_document .write_text (content )
274
+ result = runner .invoke (app , ["generate" , f"--path={ openapi_document } " ])
275
+ assert result .exit_code == 1
276
+ assert expected_error in result .stdout
277
+ openapi_document .unlink ()
278
+
279
+
280
+ def test_update_integration_tests ():
281
+ url = "https://raw.githubusercontent.com/openapi-generators/openapi-test-server/main/openapi.json"
282
+ source_path = Path (__file__ ).parent .parent / "integration-tests"
283
+ project_path = Path .cwd () / "integration-tests"
284
+ if source_path != project_path : # Just in case someone runs this from root dir
285
+ shutil .copytree (source_path , project_path )
286
+ config_path = project_path / "config.yaml"
287
+ _run_command ("update" , url = url , config_path = config_path )
288
+ _compare_directories (source_path , project_path , expected_differences = {})
0 commit comments