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.
Files changed (22) hide show
  1. {pytest_openapi-0.2.3.dev202603201619/src/pytest_openapi.egg-info → pytest_openapi-0.3.1.dev202604100227}/PKG-INFO +4 -3
  2. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/README.md +3 -2
  3. pytest_openapi-0.3.1.dev202604100227/src/pytest_openapi/__init__.py +1 -0
  4. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi/case_generator.py +49 -28
  5. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi/contract.py +153 -73
  6. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi/openapi.py +31 -12
  7. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi/plugin.py +34 -13
  8. pytest_openapi-0.3.1.dev202604100227/src/pytest_openapi/schema.py +130 -0
  9. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227/src/pytest_openapi.egg-info}/PKG-INFO +4 -3
  10. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi.egg-info/SOURCES.txt +1 -0
  11. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/tests/test_integration.py +238 -0
  12. pytest_openapi-0.2.3.dev202603201619/src/pytest_openapi/__init__.py +0 -1
  13. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/LICENSE +0 -0
  14. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/pyproject.toml +0 -0
  15. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/setup.cfg +0 -0
  16. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi.egg-info/dependency_links.txt +0 -0
  17. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi.egg-info/entry_points.txt +0 -0
  18. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi.egg-info/requires.txt +0 -0
  19. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/src/pytest_openapi.egg-info/top_level.txt +0 -0
  20. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/tests/test_output_formats.py +0 -0
  21. {pytest_openapi-0.2.3.dev202603201619 → pytest_openapi-0.3.1.dev202604100227}/tests/test_plugin_behavior.py +0 -0
  22. {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.2.3.dev202603201619
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.2.1, depends-1.0.1, mock-3.15.1
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 exanples
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.2.1, depends-1.0.1, mock-3.15.1
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 exanples
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 Exception as e:
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
- invalid_value = generate_invalid_enum_value(schema)
90
- if invalid_value:
91
- test_cases.append(invalid_value)
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
- invalid_value = generate_invalid_enum_value(schema)
198
- if invalid_value:
199
- valid_cases.append(invalid_value)
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
- invalid_value = generate_invalid_enum_value(schema)
303
- if invalid_value:
304
- valid_cases.append(invalid_value)
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(schema, field_name="field"):
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
- schema_type = schema.get("type", "string")
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(schema, field_name)
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(schema, field_name)
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