pytest-openapi 0.2.3.dev202603041917__tar.gz → 0.2.3.dev202603201619__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.dev202603041917/src/pytest_openapi.egg-info → pytest_openapi-0.2.3.dev202603201619}/PKG-INFO +1 -1
- pytest_openapi-0.2.3.dev202603201619/src/pytest_openapi/__init__.py +1 -0
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/src/pytest_openapi/contract.py +58 -8
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619/src/pytest_openapi.egg-info}/PKG-INFO +1 -1
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/tests/test_integration.py +78 -0
- pytest_openapi-0.2.3.dev202603041917/src/pytest_openapi/__init__.py +0 -1
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/LICENSE +0 -0
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/README.md +0 -0
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/pyproject.toml +0 -0
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/setup.cfg +0 -0
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/src/pytest_openapi/case_generator.py +0 -0
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/src/pytest_openapi/openapi.py +0 -0
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/src/pytest_openapi/plugin.py +0 -0
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/src/pytest_openapi.egg-info/SOURCES.txt +0 -0
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/src/pytest_openapi.egg-info/dependency_links.txt +0 -0
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/src/pytest_openapi.egg-info/entry_points.txt +0 -0
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/src/pytest_openapi.egg-info/requires.txt +0 -0
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/src/pytest_openapi.egg-info/top_level.txt +0 -0
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/tests/test_output_formats.py +0 -0
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/tests/test_plugin_behavior.py +0 -0
- {pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/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.
|
|
3
|
+
Version: 0.2.3.dev202603201619
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.3.dev202603201619"
|
|
@@ -11,6 +11,56 @@ from .case_generator import generate_test_cases_for_schema
|
|
|
11
11
|
test_reports = []
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
def collect_streaming_response(response):
|
|
15
|
+
"""Collect and parse streaming response content into a structured
|
|
16
|
+
form.
|
|
17
|
+
|
|
18
|
+
Reads the buffered response text and parses SSE or NDJSON chunks.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
response: requests.Response object with a streaming content-type
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
list or str: List of parsed chunks for SSE/NDJSON, raw text otherwise
|
|
25
|
+
"""
|
|
26
|
+
content_type = response.headers.get("Content-Type", "").lower()
|
|
27
|
+
text = response.text
|
|
28
|
+
|
|
29
|
+
if "text/event-stream" in content_type:
|
|
30
|
+
chunks = []
|
|
31
|
+
for line in text.splitlines():
|
|
32
|
+
line = line.strip()
|
|
33
|
+
if line.startswith("data:"):
|
|
34
|
+
data = line[5:].strip()
|
|
35
|
+
if not data:
|
|
36
|
+
continue
|
|
37
|
+
if data == "[DONE]":
|
|
38
|
+
chunks.append("[DONE]")
|
|
39
|
+
else:
|
|
40
|
+
try:
|
|
41
|
+
chunks.append(json.loads(data))
|
|
42
|
+
except (json.JSONDecodeError, ValueError):
|
|
43
|
+
chunks.append(data)
|
|
44
|
+
return chunks if chunks else text
|
|
45
|
+
|
|
46
|
+
if (
|
|
47
|
+
"application/x-ndjson" in content_type
|
|
48
|
+
or "application/stream+json" in content_type
|
|
49
|
+
):
|
|
50
|
+
chunks = []
|
|
51
|
+
for line in text.splitlines():
|
|
52
|
+
line = line.strip()
|
|
53
|
+
if not line:
|
|
54
|
+
continue
|
|
55
|
+
try:
|
|
56
|
+
chunks.append(json.loads(line))
|
|
57
|
+
except (json.JSONDecodeError, ValueError):
|
|
58
|
+
chunks.append(line)
|
|
59
|
+
return chunks if chunks else text
|
|
60
|
+
|
|
61
|
+
return text
|
|
62
|
+
|
|
63
|
+
|
|
14
64
|
def make_request(method, url, json=None, timeout=10):
|
|
15
65
|
"""Wrapper for HTTP requests that logs all requests and responses
|
|
16
66
|
for reporting.
|
|
@@ -1066,7 +1116,7 @@ def test_post_endpoint(
|
|
|
1066
1116
|
expected_status,
|
|
1067
1117
|
expected_response,
|
|
1068
1118
|
response.status_code,
|
|
1069
|
-
|
|
1119
|
+
collect_streaming_response(response),
|
|
1070
1120
|
True,
|
|
1071
1121
|
None,
|
|
1072
1122
|
test_origin,
|
|
@@ -1085,7 +1135,7 @@ def test_post_endpoint(
|
|
|
1085
1135
|
expected_status,
|
|
1086
1136
|
expected_response,
|
|
1087
1137
|
response.status_code,
|
|
1088
|
-
|
|
1138
|
+
collect_streaming_response(response),
|
|
1089
1139
|
False,
|
|
1090
1140
|
error_msg,
|
|
1091
1141
|
test_origin,
|
|
@@ -1116,7 +1166,7 @@ def test_post_endpoint(
|
|
|
1116
1166
|
expected_status,
|
|
1117
1167
|
expected_response,
|
|
1118
1168
|
response.status_code,
|
|
1119
|
-
|
|
1169
|
+
collect_streaming_response(response),
|
|
1120
1170
|
True,
|
|
1121
1171
|
None,
|
|
1122
1172
|
test_origin,
|
|
@@ -1135,7 +1185,7 @@ def test_post_endpoint(
|
|
|
1135
1185
|
expected_status,
|
|
1136
1186
|
expected_response,
|
|
1137
1187
|
response.status_code,
|
|
1138
|
-
|
|
1188
|
+
collect_streaming_response(response),
|
|
1139
1189
|
False,
|
|
1140
1190
|
error_msg,
|
|
1141
1191
|
test_origin,
|
|
@@ -2200,7 +2250,7 @@ def test_post_endpoint_single(
|
|
|
2200
2250
|
expected_status,
|
|
2201
2251
|
expected_response,
|
|
2202
2252
|
response.status_code,
|
|
2203
|
-
|
|
2253
|
+
collect_streaming_response(response),
|
|
2204
2254
|
True,
|
|
2205
2255
|
None,
|
|
2206
2256
|
test_origin,
|
|
@@ -2219,7 +2269,7 @@ def test_post_endpoint_single(
|
|
|
2219
2269
|
expected_status,
|
|
2220
2270
|
expected_response,
|
|
2221
2271
|
response.status_code,
|
|
2222
|
-
|
|
2272
|
+
collect_streaming_response(response),
|
|
2223
2273
|
False,
|
|
2224
2274
|
error_msg,
|
|
2225
2275
|
test_origin,
|
|
@@ -2249,7 +2299,7 @@ def test_post_endpoint_single(
|
|
|
2249
2299
|
expected_status,
|
|
2250
2300
|
expected_response,
|
|
2251
2301
|
response.status_code,
|
|
2252
|
-
|
|
2302
|
+
collect_streaming_response(response),
|
|
2253
2303
|
True,
|
|
2254
2304
|
None,
|
|
2255
2305
|
test_origin,
|
|
@@ -2268,7 +2318,7 @@ def test_post_endpoint_single(
|
|
|
2268
2318
|
expected_status,
|
|
2269
2319
|
expected_response,
|
|
2270
2320
|
response.status_code,
|
|
2271
|
-
|
|
2321
|
+
collect_streaming_response(response),
|
|
2272
2322
|
False,
|
|
2273
2323
|
error_msg,
|
|
2274
2324
|
test_origin,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytest-openapi
|
|
3
|
-
Version: 0.2.3.
|
|
3
|
+
Version: 0.2.3.dev202603201619
|
|
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
|
|
@@ -1253,6 +1253,84 @@ def test_streaming_api_all_endpoints_tested():
|
|
|
1253
1253
|
), f"Expected all tests to pass with no failures, got: {output}"
|
|
1254
1254
|
|
|
1255
1255
|
|
|
1256
|
+
@pytest.mark.depends(on=["test_openapi_flag_is_recognized"])
|
|
1257
|
+
def test_streaming_vvv_shows_sse_content_not_placeholder():
|
|
1258
|
+
"""Test that -vvv mode shows collected SSE content, not a placeholder."""
|
|
1259
|
+
print(
|
|
1260
|
+
"\n🔍 Testing -vvv shows SSE content instead of placeholder...",
|
|
1261
|
+
flush=True,
|
|
1262
|
+
)
|
|
1263
|
+
time.sleep(0.5)
|
|
1264
|
+
|
|
1265
|
+
result = subprocess.run(
|
|
1266
|
+
[
|
|
1267
|
+
"pytest",
|
|
1268
|
+
"--openapi=http://mock-server-streaming-api:8000",
|
|
1269
|
+
"-k",
|
|
1270
|
+
"stream/sse",
|
|
1271
|
+
"-vvv",
|
|
1272
|
+
],
|
|
1273
|
+
capture_output=True,
|
|
1274
|
+
text=True,
|
|
1275
|
+
cwd="/app",
|
|
1276
|
+
)
|
|
1277
|
+
|
|
1278
|
+
output = result.stdout + result.stderr
|
|
1279
|
+
|
|
1280
|
+
assert (
|
|
1281
|
+
result.returncode == 0
|
|
1282
|
+
), f"Expected SSE streaming tests to pass, got: {output}"
|
|
1283
|
+
|
|
1284
|
+
# Should NOT show the opaque placeholder string
|
|
1285
|
+
assert (
|
|
1286
|
+
"[Streaming response:" not in output
|
|
1287
|
+
), f"Expected no placeholder, but got: {output}"
|
|
1288
|
+
|
|
1289
|
+
# Should show actual collected chunk content
|
|
1290
|
+
assert (
|
|
1291
|
+
"Chunk" in output
|
|
1292
|
+
), f"Expected actual streaming content ('Chunk') in -vvv output, got: {output}"
|
|
1293
|
+
|
|
1294
|
+
|
|
1295
|
+
@pytest.mark.depends(on=["test_openapi_flag_is_recognized"])
|
|
1296
|
+
def test_streaming_vvv_shows_ndjson_content_not_placeholder():
|
|
1297
|
+
"""Test that -vvv mode shows collected NDJSON content, not a placeholder."""
|
|
1298
|
+
print(
|
|
1299
|
+
"\n🔍 Testing -vvv shows NDJSON content instead of placeholder...",
|
|
1300
|
+
flush=True,
|
|
1301
|
+
)
|
|
1302
|
+
time.sleep(0.5)
|
|
1303
|
+
|
|
1304
|
+
result = subprocess.run(
|
|
1305
|
+
[
|
|
1306
|
+
"pytest",
|
|
1307
|
+
"--openapi=http://mock-server-streaming-api:8000",
|
|
1308
|
+
"-k",
|
|
1309
|
+
"stream/ndjson",
|
|
1310
|
+
"-vvv",
|
|
1311
|
+
],
|
|
1312
|
+
capture_output=True,
|
|
1313
|
+
text=True,
|
|
1314
|
+
cwd="/app",
|
|
1315
|
+
)
|
|
1316
|
+
|
|
1317
|
+
output = result.stdout + result.stderr
|
|
1318
|
+
|
|
1319
|
+
assert (
|
|
1320
|
+
result.returncode == 0
|
|
1321
|
+
), f"Expected NDJSON streaming tests to pass, got: {output}"
|
|
1322
|
+
|
|
1323
|
+
# Should NOT show the opaque placeholder string
|
|
1324
|
+
assert (
|
|
1325
|
+
"[Streaming response:" not in output
|
|
1326
|
+
), f"Expected no placeholder, but got: {output}"
|
|
1327
|
+
|
|
1328
|
+
# Should show actual collected chunk content
|
|
1329
|
+
assert (
|
|
1330
|
+
"Chunk" in output
|
|
1331
|
+
), f"Expected actual streaming content ('Chunk') in -vvv output, got: {output}"
|
|
1332
|
+
|
|
1333
|
+
|
|
1256
1334
|
@pytest.mark.depends(on=["test_openapi_flag_is_recognized"])
|
|
1257
1335
|
def test_post_no_request_body_passes():
|
|
1258
1336
|
"""Test that POST endpoints with no request body are tested and pass."""
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.3.dev202603041917"
|
|
File without changes
|
|
File without changes
|
{pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/pyproject.toml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytest_openapi-0.2.3.dev202603041917 → pytest_openapi-0.2.3.dev202603201619}/tests/test_unit.py
RENAMED
|
File without changes
|