|
1 | 1 | import os |
2 | 2 | import logging |
3 | | -import requests |
4 | | - |
| 3 | +from urllib.parse import urlparse |
5 | 4 | import schemathesis as st |
| 5 | +from schemathesis.hooks import HookContext |
6 | 6 |
|
7 | 7 | from cloudharness.auth import get_token |
8 | 8 |
|
| 9 | +st.experimental.OPEN_API_3_1.enable() |
| 10 | + |
9 | 11 | if "APP_URL" or "APP_SCHEMA_FILE" in os.environ: |
10 | 12 | app_schema = os.environ.get("APP_SCHEMA_FILE", None) |
11 | 13 | app_url = os.environ.get("APP_URL", "http://samples.ch.local/api") |
12 | | - logging.info("Start schemathesis tests on %s", app_url) |
| 14 | + |
| 15 | + parsed_url = urlparse(app_url) |
| 16 | + base_url = f"{parsed_url.scheme}://{parsed_url.netloc}".rstrip("/") |
| 17 | + |
| 18 | + logging.info("Start schemathesis tests on %s", base_url) |
| 19 | + |
13 | 20 | schema = None |
| 21 | + |
| 22 | + # First, attempt to load the local file if provided |
14 | 23 | if app_schema: |
15 | | - # Test locally with harness-test -- use local schema for convenience during test development |
16 | | - openapi_uri = app_schema |
17 | 24 | try: |
18 | | - schema = st.from_file(openapi_uri) |
| 25 | + schema = st.from_file(app_schema) |
| 26 | + logging.info("Successfully loaded schema from local file: %s", app_schema) |
19 | 27 | except st.exceptions.SchemaError: |
20 | | - logging.exception("The local schema file %s cannot be loaded. Attempting loading from URL", openapi_uri) |
| 28 | + logging.exception("The local schema file %s cannot be loaded. Attempting loading from URL", app_schema) |
21 | 29 |
|
| 30 | + # If no schema from file, then loop over URL candidates |
22 | 31 | 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 |
| 32 | + candidates = [ |
| 33 | + base_url + "/openapi.json", |
| 34 | + base_url + "/api/openapi.json", |
| 35 | + ] |
| 36 | + for candidate in candidates: |
30 | 37 | try: |
31 | | - openapi_uri = app_url.replace("/api", "") + "/openapi.json" |
32 | | - print(requests.get(openapi_uri)) |
33 | | - schema = st.from_uri(openapi_uri) |
| 38 | + logging.info("Attempting to load schema from URI: %s", candidate) |
| 39 | + schema = st.from_uri(candidate) |
| 40 | + logging.info("Successfully loaded schema from %s", candidate) |
| 41 | + break # Exit loop on successful load |
34 | 42 | 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 |
| 43 | + logging.warning("Failed to load schema from %s: %s", candidate, e) |
| 44 | + except Exception as e: |
| 45 | + logging.error("Unexpected error when loading schema from %s: %s", candidate, e) |
| 46 | + if not schema: |
| 47 | + raise Exception("Cannot setup API tests: No valid schema found. Check your deployment and configuration.") |
41 | 48 |
|
42 | 49 | if "USERNAME" in os.environ and "PASSWORD" in os.environ: |
43 | 50 | logging.info("Setting token from username and password") |
@@ -66,3 +73,21 @@ def set(self, case, data, context): |
66 | 73 | case.headers = case.headers or {} |
67 | 74 | case.headers["Authorization"] = f"Bearer {data}" |
68 | 75 | case.headers["Cookie"] = f"kc-access={data}" |
| 76 | + |
| 77 | + UNSAFE_VALUES = ("%", ) |
| 78 | + |
| 79 | + @st.hook |
| 80 | + def filter_path_parameters(context: HookContext, x): |
| 81 | + # Extract the candidate value. |
| 82 | + param = x["key"] if isinstance(x, dict) and "key" in x else x |
| 83 | + |
| 84 | + if param is None or param == "": |
| 85 | + return True |
| 86 | + |
| 87 | + param_str = str(param) |
| 88 | + |
| 89 | + # Reject if any unsafe substring is present. |
| 90 | + if any(unsafe in param_str for unsafe in UNSAFE_VALUES): |
| 91 | + return False |
| 92 | + |
| 93 | + return True |
0 commit comments