Skip to content

Commit 3f1c821

Browse files
authored
Merge pull request #796 from MetaCell/feature/enable-schemathesis-v3
Enable experimental OPEN_API_3_1 support for schematesis; Update openapi.json look up logic
2 parents d955a7b + 9ae98d0 commit 3f1c821

3 files changed

Lines changed: 44 additions & 24 deletions

File tree

applications/jupyterhub/deploy/resources/hub/jupyterhub_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
configuration_directory = os.path.dirname(os.path.realpath(__file__))
2525
sys.path.insert(0, configuration_directory)
2626

27-
from z2jh import ( # noqa
27+
from z2jh import ( # noqa
2828
get_config,
2929
get_name,
3030
get_name_env,

applications/jupyterhub/deploy/resources/hub/z2jh.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,4 @@ def set_config_if_not_none(cparent, name, key):
142142
"""
143143
data = get_config(key)
144144
if data is not None:
145-
setattr(cparent, name, data)
145+
setattr(cparent, name, data)
Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,45 @@
11
import os
22
import logging
3-
import requests
4-
53
import schemathesis as st
4+
from schemathesis.hooks import HookContext
65

76
from cloudharness.auth import get_token
87

8+
st.experimental.OPEN_API_3_1.enable()
9+
910
if "APP_URL" or "APP_SCHEMA_FILE" in os.environ:
1011
app_schema = os.environ.get("APP_SCHEMA_FILE", None)
1112
app_url = os.environ.get("APP_URL", "http://samples.ch.local/api")
1213
logging.info("Start schemathesis tests on %s", app_url)
14+
1315
schema = None
16+
17+
# First, attempt to load the local file if provided
1418
if app_schema:
15-
# Test locally with harness-test -- use local schema for convenience during test development
16-
openapi_uri = app_schema
1719
try:
18-
schema = st.from_file(openapi_uri)
20+
schema = st.from_file(app_schema)
21+
logging.info("Successfully loaded schema from local file: %s", app_schema)
1922
except st.exceptions.SchemaError:
20-
logging.exception("The local schema file %s cannot be loaded. Attempting loading from URL", openapi_uri)
23+
logging.exception("The local schema file %s cannot be loaded. Attempting loading from URL", app_schema)
2124

25+
# If no schema from file, then loop over URL candidates
2226
if not schema:
23-
# remote testing: might be /api/openapi.json or /openapi.json
24-
try:
25-
openapi_uri = openapi_uri = app_url + "/openapi.json"
26-
logging.info("Using openapi spec at %s", openapi_uri)
27-
schema = st.from_uri(openapi_uri)
28-
except st.exceptions.SchemaError:
29-
# Use alternative configuration
27+
candidates = [
28+
app_url.rstrip("/") + "/openapi.json",
29+
app_url.rstrip("/") + "/api/openapi.json",
30+
]
31+
for candidate in candidates:
3032
try:
31-
openapi_uri = app_url.replace("/api", "") + "/openapi.json"
32-
print(requests.get(openapi_uri))
33-
schema = st.from_uri(openapi_uri)
33+
logging.info("Attempting to load schema from URI: %s", candidate)
34+
schema = st.from_uri(candidate)
35+
logging.info("Successfully loaded schema from %s", candidate)
36+
break # Exit loop on successful load
3437
except st.exceptions.SchemaError as e:
35-
raise Exception(
36-
f"Cannot setup api tests: {openapi_uri} not valid. Check your deployment is up and configuration") from e
37-
38-
except Exception as e:
39-
raise Exception(
40-
f"Cannot setup api tests: {openapi_uri}: {e}") from e
38+
logging.warning("Failed to load schema from %s: %s", candidate, e)
39+
except Exception as e:
40+
logging.error("Unexpected error when loading schema from %s: %s", candidate, e)
41+
if not schema:
42+
raise Exception("Cannot setup API tests: No valid schema found. Check your deployment and configuration.")
4143

4244
if "USERNAME" in os.environ and "PASSWORD" in os.environ:
4345
logging.info("Setting token from username and password")
@@ -66,3 +68,21 @@ def set(self, case, data, context):
6668
case.headers = case.headers or {}
6769
case.headers["Authorization"] = f"Bearer {data}"
6870
case.headers["Cookie"] = f"kc-access={data}"
71+
72+
UNSAFE_VALUES = ("%", )
73+
74+
@st.hook
75+
def filter_path_parameters(context: HookContext, x):
76+
# Extract the candidate value.
77+
param = x["key"] if isinstance(x, dict) and "key" in x else x
78+
79+
if param is None or param == "":
80+
return True
81+
82+
param_str = str(param)
83+
84+
# Reject if any unsafe substring is present.
85+
if any(unsafe in param_str for unsafe in UNSAFE_VALUES):
86+
return False
87+
88+
return True

0 commit comments

Comments
 (0)