Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions src/pytest_openapi/openapi.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""OpenAPI specification validation and parsing."""

import sys
import time

import requests

Expand Down Expand Up @@ -178,7 +179,7 @@ def check_endpoint_schema_descriptions(method, path, operation):
return errors


def validate_openapi_spec(base_url, timeout=10):
def validate_openapi_spec(base_url, timeout=10, retries=3, retry_wait=1.0):
"""Validate that the OpenAPI spec is available and meets
requirements.

Expand All @@ -190,14 +191,17 @@ def validate_openapi_spec(base_url, timeout=10):

Args:
base_url: Base URL of the API server
timeout: HTTP request timeout in seconds
retries: Number of retry attempts after the first failure (default: 3)
retry_wait: Seconds to wait between retries (default: 1.0)

Raises:
SystemExit: If validation fails
"""
openapi_url = f"{base_url}/openapi.json"
max_attempts = retries + 1

# Check 1: Fetch OpenAPI spec (with up to 3 retries)
max_attempts = 4
# Check 1: Fetch OpenAPI spec
last_error = None
spec = None
for attempt in range(1, max_attempts + 1):
Expand All @@ -211,8 +215,10 @@ def validate_openapi_spec(base_url, timeout=10):
if attempt < max_attempts:
print(
f"\n⚠️ Attempt {attempt}/{max_attempts} failed"
f" fetching {openapi_url}: {e}. Retrying..."
f" fetching {openapi_url}: {e}."
f" Retrying in {retry_wait}s..."
)
time.sleep(retry_wait)
except ValueError as e:
last_error = e
break
Expand All @@ -228,7 +234,6 @@ def validate_openapi_spec(base_url, timeout=10):
)
print(f" Reason: {last_error}")
sys.exit(1)
# TODO: Add JsonDecodeError handling

# Validate the spec structure
if "paths" not in spec:
Expand Down
47 changes: 39 additions & 8 deletions src/pytest_openapi/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import json
import re
import time

import pytest
import requests
Expand Down Expand Up @@ -64,6 +65,26 @@ def pytest_addoption(parser):
" this will be ignored"
),
)
group.addoption(
"--openapi-retries",
action="store",
metavar="N",
default=3,
help=(
"Number of times to retry fetching /openapi.json on failure"
" (default: 3)"
),
)
group.addoption(
"--openapi-retry-wait",
action="store",
metavar="SECONDS",
default=1.0,
help=(
"Seconds to wait between /openapi.json fetch retries"
" (default: 1.0)"
),
)


def pytest_configure(config):
Expand All @@ -84,14 +105,22 @@ def pytest_configure(config):
no_stdout = config.getoption("--openapi-no-stdout")
ignore_pattern = config.getoption("--openapi-ignore")
openapi_timeout = float(config.getoption("--openapi-timeout"))
openapi_retries = int(config.getoption("--openapi-retries"))
openapi_retry_wait = float(config.getoption("--openapi-retry-wait"))
max_attempts = openapi_retries + 1

# Run validation checks
validate_openapi_spec(base_url, timeout=openapi_timeout)
validate_openapi_spec(
base_url,
timeout=openapi_timeout,
retries=openapi_retries,
retry_wait=openapi_retry_wait,
)

# Fetch the OpenAPI spec (with up to 3 retries)
# Fetch the OpenAPI spec
spec = None
last_exc = None
for attempt in range(1, 5):
for attempt in range(1, max_attempts + 1):
try:
response = requests.get(
f"{base_url}/openapi.json", timeout=openapi_timeout
Expand All @@ -101,15 +130,17 @@ def pytest_configure(config):
break
except (requests.exceptions.RequestException, ValueError) as e:
last_exc = e
if attempt < 4:
if attempt < max_attempts:
print(
f"\n⚠️ Attempt {attempt}/4 failed fetching"
f" OpenAPI spec: {e}. Retrying..."
f"\n⚠️ Attempt {attempt}/{max_attempts} failed fetching"
f" OpenAPI spec: {e}. Retrying in"
f" {openapi_retry_wait}s..."
)
time.sleep(openapi_retry_wait)
if spec is None:
pytest.exit(
f"\n❌ Failed to fetch OpenAPI spec after 4 attempts:"
f" {last_exc}",
f"\n❌ Failed to fetch OpenAPI spec after {max_attempts}"
f" attempts: {last_exc}",
returncode=1,
)

Expand Down
Loading