cucumber for days (#5766)

This commit is contained in:
Anthony Stirling
2026-02-21 23:17:28 +00:00
committed by GitHub
parent 7631b222bd
commit 30c258ce0b
40 changed files with 4079 additions and 49 deletions
@@ -0,0 +1,263 @@
"""
Step definitions for enterprise and proprietary API endpoints.
Covers:
- User management (password change, API keys, admin CRUD)
- Admin settings
- Audit log
- Invite links
- Mobile scanner sessions
- Signatures
- Teams
"""
import requests
from behave import given, then, when
BASE_URL = "http://localhost:8080"
def _jwt_headers(context):
"""Return Authorization: Bearer headers using the stored JWT token."""
token = getattr(context, "jwt_token", None)
assert token, "No JWT token stored in context did you use 'Given I am logged in as admin'?"
return {"Authorization": f"Bearer {token}"}
def _parse_params(params_str):
"""Parse 'key1=val1&key2=val2' into a dict, supporting values that contain '='."""
result = {}
for part in params_str.split("&"):
if "=" in part:
k, v = part.split("=", 1)
result[k.strip()] = v.strip()
return result
def _expand_stored(template, context):
"""Replace '{stored}' in an endpoint template with context.stored_value."""
return template.replace("{stored}", getattr(context, "stored_value", ""))
# ---------------------------------------------------------------------------
# WHEN POST with query params
# ---------------------------------------------------------------------------
@when('I send a POST request to "{endpoint}" with JWT authentication and params "{params}"')
def step_post_with_jwt_and_params(context, endpoint, params):
"""Send POST request with query parameters using the stored JWT token."""
context.response = requests.post(
f"{BASE_URL}{endpoint}",
headers=_jwt_headers(context),
params=_parse_params(params),
timeout=60,
)
@when('I send a POST request to "{endpoint}" with no authentication and params "{params}"')
def step_post_no_auth_and_params(context, endpoint, params):
"""Send POST request with query parameters and no authentication."""
context.response = requests.post(
f"{BASE_URL}{endpoint}",
params=_parse_params(params),
timeout=60,
)
# ---------------------------------------------------------------------------
# WHEN GET with query params
# ---------------------------------------------------------------------------
@when('I send a GET request to "{endpoint}" with JWT authentication and params "{params}"')
def step_get_with_jwt_and_params(context, endpoint, params):
"""Send GET request with query parameters using the stored JWT token."""
context.response = requests.get(
f"{BASE_URL}{endpoint}",
headers=_jwt_headers(context),
params=_parse_params(params),
timeout=60,
)
# ---------------------------------------------------------------------------
# WHEN DELETE
# ---------------------------------------------------------------------------
@when('I send a DELETE request to "{endpoint}" with JWT authentication')
def step_delete_with_jwt(context, endpoint):
"""Send DELETE request using the stored JWT token."""
context.response = requests.delete(
f"{BASE_URL}{endpoint}",
headers=_jwt_headers(context),
timeout=60,
)
@when('I send a DELETE request to "{endpoint}" with no authentication')
def step_delete_no_auth(context, endpoint):
"""Send DELETE request with no authentication headers."""
context.response = requests.delete(
f"{BASE_URL}{endpoint}",
timeout=60,
)
@when('I send a DELETE request to "{endpoint}" with JWT authentication and params "{params}"')
def step_delete_with_jwt_and_params(context, endpoint, params):
"""Send DELETE request with query parameters using the stored JWT token."""
context.response = requests.delete(
f"{BASE_URL}{endpoint}",
headers=_jwt_headers(context),
params=_parse_params(params),
timeout=60,
)
@when('I send a DELETE request to "{endpoint}" with no authentication and params "{params}"')
def step_delete_no_auth_and_params(context, endpoint, params):
"""Send DELETE request with query parameters and no authentication."""
context.response = requests.delete(
f"{BASE_URL}{endpoint}",
params=_parse_params(params),
timeout=60,
)
# ---------------------------------------------------------------------------
# WHEN steps that use a previously stored value in the URL path
# ---------------------------------------------------------------------------
@when(
'I use the stored value to send a GET request to "{endpoint_template}" with JWT authentication'
)
def step_get_stored_jwt(context, endpoint_template):
"""Send GET request substituting {stored} in the path with context.stored_value."""
endpoint = _expand_stored(endpoint_template, context)
context.response = requests.get(
f"{BASE_URL}{endpoint}",
headers=_jwt_headers(context),
timeout=60,
)
@when(
'I use the stored value to send a GET request to "{endpoint_template}" with no authentication'
)
def step_get_stored_no_auth(context, endpoint_template):
"""Send GET request substituting {stored} in the path with no authentication."""
endpoint = _expand_stored(endpoint_template, context)
context.response = requests.get(
f"{BASE_URL}{endpoint}",
timeout=60,
)
@when(
'I use the stored value to send a DELETE request to "{endpoint_template}" with JWT authentication'
)
def step_delete_stored_jwt(context, endpoint_template):
"""Send DELETE request substituting {stored} in the path with context.stored_value."""
endpoint = _expand_stored(endpoint_template, context)
context.response = requests.delete(
f"{BASE_URL}{endpoint}",
headers=_jwt_headers(context),
timeout=60,
)
@when(
'I use the stored value to send a POST request to "{endpoint_template}" with JWT authentication'
)
def step_post_stored_jwt(context, endpoint_template):
"""Send POST request substituting {stored} in the path with context.stored_value."""
endpoint = _expand_stored(endpoint_template, context)
context.response = requests.post(
f"{BASE_URL}{endpoint}",
headers=_jwt_headers(context),
timeout=60,
)
# ---------------------------------------------------------------------------
# THEN store response value for later steps
# ---------------------------------------------------------------------------
@then('I store the response JSON field "{field}"')
def step_store_response_field(context, field):
"""Store a top-level JSON field from the current response into context.stored_value."""
data = context.response.json()
value = data.get(field)
assert value is not None, f"Field '{field}' not found in response: {data}"
context.stored_value = str(value)
# ---------------------------------------------------------------------------
# THEN response body / field assertions
# ---------------------------------------------------------------------------
@then('the response JSON field "{field}" should not be empty')
def step_json_top_field_not_empty(context, field):
"""Assert a top-level JSON field is present and non-empty."""
data = context.response.json()
value = data.get(field)
assert value is not None and str(value) != "", (
f"Expected field '{field}' to be non-empty, got: {value!r}. "
f"Full response: {data}"
)
@then("the response body should not be empty")
def step_response_body_not_empty(context):
"""Assert that the raw response body contains at least one byte."""
assert len(context.response.content) > 0, "Response body is empty"
@then("the response JSON should be a list")
def step_response_is_list(context):
"""Assert that the top-level response JSON value is a list."""
data = context.response.json()
assert isinstance(data, list), (
f"Expected response to be a JSON list but got: {type(data).__name__}. "
f"Content: {str(data)[:200]}"
)
@then('the response JSON field "{field}" should be a list')
def step_json_field_is_list(context, field):
"""Assert a top-level JSON field is a list."""
data = context.response.json()
value = data.get(field)
assert isinstance(value, list), (
f"Expected field '{field}' to be a list but got: {type(value).__name__}. "
f"Full response: {data}"
)
@then('the response JSON field "{field}" should be true')
def step_json_field_is_true(context, field):
"""Assert a top-level JSON boolean field is true."""
data = context.response.json()
value = data.get(field)
actual = str(value).lower() if isinstance(value, bool) else str(value).lower()
assert actual == "true", (
f"Expected field '{field}' to be true but got: {value!r}. "
f"Full response: {data}"
)
@then('the response JSON field "{field}" should be false')
def step_json_field_is_false(context, field):
"""Assert a top-level JSON boolean field is false."""
data = context.response.json()
value = data.get(field)
actual = str(value).lower() if isinstance(value, bool) else str(value).lower()
assert actual == "false", (
f"Expected field '{field}' to be false but got: {value!r}. "
f"Full response: {data}"
)