pytest-openapi 0.2.3.dev202603201619__tar.gz → 0.3.1.dev202604100227__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pytest_openapi-0.2.3.dev202603201619/src/pytest_openapi.egg-info → pytest_openapi-0.3.1.dev202604100227}/PKG-INFO +4 -3
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/README.md +3 -2
- pytest_openapi-0.3.1.dev202604100227/src/pytest_openapi/__init__.py +1 -0
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi/case_generator.py +49 -28
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi/contract.py +153 -73
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi/openapi.py +31 -12
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi/plugin.py +34 -13
- pytest_openapi-0.3.1.dev202604100227/src/pytest_openapi/schema.py +130 -0
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227/src/pytest_openapi.egg-info}/PKG-INFO +4 -3
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi.egg-info/SOURCES.txt +1 -0
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/tests/test_integration.py +238 -0
- pytest_openapi-0.2.3.dev202603201619/src/pytest_openapi/__init__.py +0 -1
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/LICENSE +0 -0
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/pyproject.toml +0 -0
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/setup.cfg +0 -0
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi.egg-info/dependency_links.txt +0 -0
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi.egg-info/entry_points.txt +0 -0
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi.egg-info/requires.txt +0 -0
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi.egg-info/top_level.txt +0 -0
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/tests/test_output_formats.py +0 -0
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/tests/test_plugin_behavior.py +0 -0
- {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/tests/test_unit.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytest-openapi
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1.dev202604100227
|
|
4
4
|
Summary: `pytest --openapi` - an opinionated, lightweight black-box contract tester against a live API using its OpenAPI specification as the source of truth
|
|
5
5
|
Author-email: Sinan Ozel <coding@sinan.slmail.me>
|
|
6
6
|
License: MIT License
|
|
@@ -91,7 +91,7 @@ pytest --openapi=http://localhost:8000 -v
|
|
|
91
91
|
platform linux -- Python 3.11.14, pytest-9.0.2, pluggy-1.6.0 -- /usr/local/bin/python3.11
|
|
92
92
|
cachedir: .pytest_cache
|
|
93
93
|
rootdir: /workspace
|
|
94
|
-
plugins: openapi-0.
|
|
94
|
+
plugins: openapi-0.3.0, depends-1.0.1, mock-3.15.1
|
|
95
95
|
collected 3 items
|
|
96
96
|
created 2 items from openapi examples
|
|
97
97
|
created 20 items generated from schema
|
|
@@ -186,10 +186,11 @@ Each OpenAPI test appears as an individual pytest test item.
|
|
|
186
186
|
✔️ Validates OpenAPI request/response definitions
|
|
187
187
|
✔️ Enforces schema field descriptions
|
|
188
188
|
✔️ Generates test cases from schemas, checks response codes and types in the response
|
|
189
|
-
✔️ Tests the
|
|
189
|
+
✔️ Tests the examples
|
|
190
190
|
✔️ Tests **GET / POST / PUT / DELETE** endpoints
|
|
191
191
|
✔️ Compares live responses against examples
|
|
192
192
|
✔️ Produces a readable test report
|
|
193
|
+
✔️ Supports OpenAPI 3.0 and **3.1.x** (nullable types, `const`, `$ref` siblings, `allOf`)
|
|
193
194
|
|
|
194
195
|
|
|
195
196
|
# ▶️ Detailed Example
|
|
@@ -36,7 +36,7 @@ pytest --openapi=http://localhost:8000 -v
|
|
|
36
36
|
platform linux -- Python 3.11.14, pytest-9.0.2, pluggy-1.6.0 -- /usr/local/bin/python3.11
|
|
37
37
|
cachedir: .pytest_cache
|
|
38
38
|
rootdir: /workspace
|
|
39
|
-
plugins: openapi-0.
|
|
39
|
+
plugins: openapi-0.3.0, depends-1.0.1, mock-3.15.1
|
|
40
40
|
collected 3 items
|
|
41
41
|
created 2 items from openapi examples
|
|
42
42
|
created 20 items generated from schema
|
|
@@ -131,10 +131,11 @@ Each OpenAPI test appears as an individual pytest test item.
|
|
|
131
131
|
✔️ Validates OpenAPI request/response definitions
|
|
132
132
|
✔️ Enforces schema field descriptions
|
|
133
133
|
✔️ Generates test cases from schemas, checks response codes and types in the response
|
|
134
|
-
✔️ Tests the
|
|
134
|
+
✔️ Tests the examples
|
|
135
135
|
✔️ Tests **GET / POST / PUT / DELETE** endpoints
|
|
136
136
|
✔️ Compares live responses against examples
|
|
137
137
|
✔️ Produces a readable test report
|
|
138
|
+
✔️ Supports OpenAPI 3.0 and **3.1.x** (nullable types, `const`, `$ref` siblings, `allOf`)
|
|
138
139
|
|
|
139
140
|
|
|
140
141
|
# ▶️ Detailed Example
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.1.dev202604100227"
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
"""Generate test cases from OpenAPI schemas."""
|
|
2
2
|
|
|
3
|
+
import re
|
|
3
4
|
from itertools import product
|
|
4
5
|
|
|
6
|
+
from .schema import primary_type, resolve_schema
|
|
7
|
+
|
|
5
8
|
|
|
6
9
|
def generate_invalid_enum_value(schema):
|
|
7
10
|
"""Generate an invalid value for an enum field.
|
|
@@ -49,11 +52,12 @@ def generate_invalid_enum_value(schema):
|
|
|
49
52
|
return "invalid_value"
|
|
50
53
|
|
|
51
54
|
|
|
52
|
-
def generate_string_test_cases(schema):
|
|
55
|
+
def generate_string_test_cases(schema, valid_only=False):
|
|
53
56
|
"""Generate string test cases from schema.
|
|
54
57
|
|
|
55
58
|
Args:
|
|
56
59
|
schema: OpenAPI schema for a string field
|
|
60
|
+
valid_only: If True, omit invalid/negative test values
|
|
57
61
|
|
|
58
62
|
Returns:
|
|
59
63
|
list: List of test values
|
|
@@ -74,7 +78,7 @@ def generate_string_test_cases(schema):
|
|
|
74
78
|
)
|
|
75
79
|
print(" Install with: pip install exrex")
|
|
76
80
|
test_cases.append("test-string")
|
|
77
|
-
except
|
|
81
|
+
except (re.error, ValueError, OverflowError) as e:
|
|
78
82
|
print(
|
|
79
83
|
f"⚠️ Warning: Could not generate from pattern "
|
|
80
84
|
f"'{schema['pattern']}': {e}"
|
|
@@ -85,10 +89,11 @@ def generate_string_test_cases(schema):
|
|
|
85
89
|
elif "enum" in schema:
|
|
86
90
|
# Add all valid enum values
|
|
87
91
|
test_cases.extend(schema["enum"])
|
|
88
|
-
# Add one invalid enum value as a negative test case
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
# Add one invalid enum value as a negative test case (unless valid_only)
|
|
93
|
+
if not valid_only:
|
|
94
|
+
invalid_value = generate_invalid_enum_value(schema)
|
|
95
|
+
if invalid_value:
|
|
96
|
+
test_cases.append(invalid_value)
|
|
92
97
|
|
|
93
98
|
# Check for format
|
|
94
99
|
elif "format" in schema:
|
|
@@ -176,12 +181,13 @@ def generate_string_test_cases(schema):
|
|
|
176
181
|
return filtered if filtered else ["test-string"]
|
|
177
182
|
|
|
178
183
|
|
|
179
|
-
def generate_integer_test_cases(schema, field_name="field"):
|
|
184
|
+
def generate_integer_test_cases(schema, field_name="field", valid_only=False):
|
|
180
185
|
"""Generate integer test cases from schema.
|
|
181
186
|
|
|
182
187
|
Args:
|
|
183
188
|
schema: OpenAPI schema for an integer field
|
|
184
189
|
field_name: Name of the field (for error messages)
|
|
190
|
+
valid_only: If True, omit invalid/negative test values
|
|
185
191
|
|
|
186
192
|
Returns:
|
|
187
193
|
tuple: (list of test values, optional warning message)
|
|
@@ -193,10 +199,11 @@ def generate_integer_test_cases(schema, field_name="field"):
|
|
|
193
199
|
if "enum" in schema:
|
|
194
200
|
# Add all valid enum values
|
|
195
201
|
valid_cases = list(schema["enum"])
|
|
196
|
-
# Add one invalid enum value as a negative test case
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
202
|
+
# Add one invalid enum value as a negative test case (unless valid_only)
|
|
203
|
+
if not valid_only:
|
|
204
|
+
invalid_value = generate_invalid_enum_value(schema)
|
|
205
|
+
if invalid_value:
|
|
206
|
+
valid_cases.append(invalid_value)
|
|
200
207
|
return valid_cases, None
|
|
201
208
|
|
|
202
209
|
# Get constraints
|
|
@@ -281,12 +288,13 @@ def generate_integer_test_cases(schema, field_name="field"):
|
|
|
281
288
|
return sorted(set(test_cases)), warning
|
|
282
289
|
|
|
283
290
|
|
|
284
|
-
def generate_number_test_cases(schema, field_name="field"):
|
|
291
|
+
def generate_number_test_cases(schema, field_name="field", valid_only=False):
|
|
285
292
|
"""Generate number (float) test cases from schema.
|
|
286
293
|
|
|
287
294
|
Args:
|
|
288
295
|
schema: OpenAPI schema for a number field
|
|
289
296
|
field_name: Name of the field (for error messages)
|
|
297
|
+
valid_only: If True, omit invalid/negative test values
|
|
290
298
|
|
|
291
299
|
Returns:
|
|
292
300
|
tuple: (list of test values, optional warning message)
|
|
@@ -298,10 +306,11 @@ def generate_number_test_cases(schema, field_name="field"):
|
|
|
298
306
|
if "enum" in schema:
|
|
299
307
|
# Add all valid enum values
|
|
300
308
|
valid_cases = list(schema["enum"])
|
|
301
|
-
# Add one invalid enum value as a negative test case
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
309
|
+
# Add one invalid enum value as a negative test case (unless valid_only)
|
|
310
|
+
if not valid_only:
|
|
311
|
+
invalid_value = generate_invalid_enum_value(schema)
|
|
312
|
+
if invalid_value:
|
|
313
|
+
valid_cases.append(invalid_value)
|
|
305
314
|
return valid_cases, None
|
|
306
315
|
|
|
307
316
|
# Get constraints
|
|
@@ -398,12 +407,13 @@ def generate_boolean_test_cases(schema):
|
|
|
398
407
|
return [True, False]
|
|
399
408
|
|
|
400
409
|
|
|
401
|
-
def generate_array_test_cases(schema, field_name="field"):
|
|
410
|
+
def generate_array_test_cases(schema, field_name="field", spec=None):
|
|
402
411
|
"""Generate array test cases from schema.
|
|
403
412
|
|
|
404
413
|
Args:
|
|
405
414
|
schema: OpenAPI schema for an array field
|
|
406
415
|
field_name: Name of the field (for error messages)
|
|
416
|
+
spec: Full OpenAPI spec dict for $ref resolution
|
|
407
417
|
|
|
408
418
|
Returns:
|
|
409
419
|
tuple: (list of test arrays, optional warning message)
|
|
@@ -414,7 +424,7 @@ def generate_array_test_cases(schema, field_name="field"):
|
|
|
414
424
|
|
|
415
425
|
# Generate test cases for the item type
|
|
416
426
|
item_test_cases, warning = generate_test_cases_for_schema(
|
|
417
|
-
items_schema, f"{field_name}[]"
|
|
427
|
+
items_schema, f"{field_name}[]", spec
|
|
418
428
|
)
|
|
419
429
|
|
|
420
430
|
arrays = []
|
|
@@ -438,12 +448,13 @@ def generate_array_test_cases(schema, field_name="field"):
|
|
|
438
448
|
return arrays, warning
|
|
439
449
|
|
|
440
450
|
|
|
441
|
-
def generate_object_test_cases(schema, field_name="field"):
|
|
451
|
+
def generate_object_test_cases(schema, field_name="field", spec=None):
|
|
442
452
|
"""Generate object test cases from schema.
|
|
443
453
|
|
|
444
454
|
Args:
|
|
445
455
|
schema: OpenAPI schema for an object field
|
|
446
456
|
field_name: Name of the field (for error messages)
|
|
457
|
+
spec: Full OpenAPI spec dict for $ref resolution
|
|
447
458
|
|
|
448
459
|
Returns:
|
|
449
460
|
tuple: (list of test objects, list of warnings)
|
|
@@ -452,11 +463,12 @@ def generate_object_test_cases(schema, field_name="field"):
|
|
|
452
463
|
|
|
453
464
|
warnings = []
|
|
454
465
|
|
|
455
|
-
# Collect test cases for each property
|
|
466
|
+
# Collect test cases for each property (valid values only, to avoid
|
|
467
|
+
# generating request bodies that the server will reject with 400/422)
|
|
456
468
|
prop_values = {}
|
|
457
469
|
for prop_name, prop_schema in properties.items():
|
|
458
470
|
prop_test_cases, warning = generate_test_cases_for_schema(
|
|
459
|
-
prop_schema, f"{field_name}.{prop_name}"
|
|
471
|
+
prop_schema, f"{field_name}.{prop_name}", spec, valid_only=True
|
|
460
472
|
)
|
|
461
473
|
if warning:
|
|
462
474
|
warnings.append(warning)
|
|
@@ -486,30 +498,39 @@ def generate_object_test_cases(schema, field_name="field"):
|
|
|
486
498
|
return combos, warnings
|
|
487
499
|
|
|
488
500
|
|
|
489
|
-
def generate_test_cases_for_schema(
|
|
501
|
+
def generate_test_cases_for_schema(
|
|
502
|
+
schema, field_name="field", spec=None, valid_only=False
|
|
503
|
+
):
|
|
490
504
|
"""Generate test cases for any schema type.
|
|
491
505
|
|
|
492
506
|
Args:
|
|
493
507
|
schema: OpenAPI schema
|
|
494
508
|
field_name: Name of the field (for error messages)
|
|
509
|
+
spec: Full OpenAPI spec dict for $ref resolution
|
|
510
|
+
valid_only: If True, omit invalid/negative test values
|
|
495
511
|
|
|
496
512
|
Returns:
|
|
497
513
|
tuple: (list of test values, optional warning message or
|
|
498
514
|
list of warnings)
|
|
499
515
|
"""
|
|
500
|
-
|
|
516
|
+
schema = resolve_schema(spec, schema)
|
|
517
|
+
schema_type = primary_type(schema.get("type", "string")) or "string"
|
|
501
518
|
|
|
502
519
|
if schema_type == "string":
|
|
503
|
-
return generate_string_test_cases(schema), None
|
|
520
|
+
return generate_string_test_cases(schema, valid_only=valid_only), None
|
|
504
521
|
elif schema_type == "integer":
|
|
505
|
-
return generate_integer_test_cases(
|
|
522
|
+
return generate_integer_test_cases(
|
|
523
|
+
schema, field_name, valid_only=valid_only
|
|
524
|
+
)
|
|
506
525
|
elif schema_type == "number":
|
|
507
|
-
return generate_number_test_cases(
|
|
526
|
+
return generate_number_test_cases(
|
|
527
|
+
schema, field_name, valid_only=valid_only
|
|
528
|
+
)
|
|
508
529
|
elif schema_type == "boolean":
|
|
509
530
|
return generate_boolean_test_cases(schema), None
|
|
510
531
|
elif schema_type == "array":
|
|
511
|
-
return generate_array_test_cases(schema, field_name)
|
|
532
|
+
return generate_array_test_cases(schema, field_name, spec)
|
|
512
533
|
elif schema_type == "object":
|
|
513
|
-
return generate_object_test_cases(schema, field_name)
|
|
534
|
+
return generate_object_test_cases(schema, field_name, spec)
|
|
514
535
|
else:
|
|
515
536
|
return ["test-value"], None
|