Tidy formatting in cucumber tests

Style cleanup across testing/cucumber features: compacted multi-line f-strings, normalized kwargs formatting (headers/timeout), removed stray blank lines and minor whitespace adjustments. Affected files: testing/cucumber/features/environment.py, job_step_definitions.py, job_support.py, parallel_support.py. No functional behavior changes intended.
This commit is contained in:
Ludy87
2026-08-16 16:03:35 +02:00
parent 444f6ea01f
commit e4bb1b4b9f
4 changed files with 38 additions and 63 deletions
+1 -4
View File
@@ -276,10 +276,7 @@ def _cleanup_async_job_files():
)
return
if response.status_code != 200:
print(
f"\n[CLEANUP] Async job cleanup returned {response.status_code}: "
f"{response.text[:200]}"
)
print(f"\n[CLEANUP] Async job cleanup returned {response.status_code}: {response.text[:200]}")
return
try:
summary = response.json()
@@ -1,4 +1,5 @@
"""Steps for the async job API. DELETE is a cancel, so it 400s once the job finishes."""
import time
import requests
@@ -21,27 +22,25 @@ def step_wait_for_job(context):
while time.time() < deadline:
response = requests.get(
f"{BASE_URL}/api/v1/general/job/{context.job_id}",
headers=API_HEADERS, timeout=30,
)
assert response.status_code == 200, (
f"Job status returned {response.status_code}: {response.text}"
headers=API_HEADERS,
timeout=30,
)
assert response.status_code == 200, f"Job status returned {response.status_code}: {response.text}"
context.response = response
payload = response.json()
if payload.get("complete"):
context.job_status = payload
return
time.sleep(0.2)
raise AssertionError(
f"Job {context.job_id} did not complete within {POLL_TIMEOUT_SECONDS}s"
)
raise AssertionError(f"Job {context.job_id} did not complete within {POLL_TIMEOUT_SECONDS}s")
@when("I request the job result")
def step_request_job_result(context):
context.response = requests.get(
f"{BASE_URL}/api/v1/general/job/{context.job_id}/result",
headers=API_HEADERS, timeout=60,
headers=API_HEADERS,
timeout=60,
)
@@ -49,7 +48,8 @@ def step_request_job_result(context):
def step_request_job_result_files(context):
context.response = requests.get(
f"{BASE_URL}/api/v1/general/job/{context.job_id}/result/files",
headers=API_HEADERS, timeout=60,
headers=API_HEADERS,
timeout=60,
)
files = context.response.json().get("files") or []
context.job_files = files
@@ -62,7 +62,8 @@ def step_download_job_file(context):
assert getattr(context, "job_file_id", None), "No fileId captured from the result file list"
context.response = requests.get(
f"{BASE_URL}/api/v1/general/files/{context.job_file_id}",
headers=API_HEADERS, timeout=60,
headers=API_HEADERS,
timeout=60,
)
@@ -71,7 +72,8 @@ def step_job_file_metadata(context):
assert getattr(context, "job_file_id", None), "No fileId captured from the result file list"
context.response = requests.get(
f"{BASE_URL}/api/v1/general/files/{context.job_file_id}/metadata",
headers=API_HEADERS, timeout=60,
headers=API_HEADERS,
timeout=60,
)
@@ -79,7 +81,8 @@ def step_job_file_metadata(context):
def step_cancel_job(context):
context.response = requests.delete(
f"{BASE_URL}/api/v1/general/job/{context.job_id}",
headers=API_HEADERS, timeout=30,
headers=API_HEADERS,
timeout=30,
)
@@ -127,8 +130,7 @@ def step_check_cleanup_idempotent(context):
removed = context.cleanup_summary.get("jobsRemoved")
deleted = context.cleanup_summary.get("filesDeleted")
assert removed == 0 and deleted == 0, (
"A repeat cleanup still found work to do, so the first pass did not fully clean up: "
f"{context.cleanup_summary}"
f"A repeat cleanup still found work to do, so the first pass did not fully clean up: {context.cleanup_summary}"
)
@@ -136,11 +138,11 @@ def step_check_cleanup_idempotent(context):
def step_check_job_gone(context):
response = requests.get(
f"{BASE_URL}/api/v1/general/job/{context.job_id}",
headers=API_HEADERS, timeout=30,
headers=API_HEADERS,
timeout=30,
)
assert response.status_code == 404, (
f"Job {context.job_id} still exists after cleanup: "
f"{response.status_code} {response.text[:200]}"
f"Job {context.job_id} still exists after cleanup: {response.status_code} {response.text[:200]}"
)
@@ -149,11 +151,11 @@ def step_check_job_file_gone(context):
assert getattr(context, "job_file_id", None), "No fileId captured from the result file list"
response = requests.get(
f"{BASE_URL}/api/v1/general/files/{context.job_file_id}",
headers=API_HEADERS, timeout=30,
headers=API_HEADERS,
timeout=30,
)
assert response.status_code == 404, (
f"File {context.job_file_id} still downloadable after cleanup: "
f"{response.status_code} {response.text[:200]}"
f"File {context.job_file_id} still downloadable after cleanup: {response.status_code} {response.text[:200]}"
)
@@ -162,10 +164,10 @@ def step_check_job_file_still_there(context):
assert getattr(context, "job_file_id", None), "No fileId captured from the result file list"
response = requests.get(
f"{BASE_URL}/api/v1/general/files/{context.job_file_id}",
headers=API_HEADERS, timeout=60,
headers=API_HEADERS,
timeout=60,
)
assert response.status_code == 200, (
f"File {context.job_file_id} was not retrievable a second time: "
f"{response.status_code} {response.text[:200]}"
f"File {context.job_file_id} was not retrievable a second time: {response.status_code} {response.text[:200]}"
)
assert len(response.content) > 0, "Second download returned an empty body"
@@ -4,6 +4,7 @@ Support module, not a step module: behave execs everything under features/steps
step definitions, so anything environment.py needs to import has to live apart from
the @when/@then decorators or they would register twice.
"""
import requests
BASE_URL = "http://localhost:8080"
@@ -58,11 +58,7 @@ def _normalize_name(name):
def _strip_volatile(value):
"""Drop keys whose values legitimately differ between two identical requests."""
if isinstance(value, dict):
return {
k: _strip_volatile(v)
for k, v in sorted(value.items())
if not _VOLATILE_KEY_RE.match(k)
}
return {k: _strip_volatile(v) for k, v in sorted(value.items()) if not _VOLATILE_KEY_RE.match(k)}
if isinstance(value, list):
return [_strip_volatile(v) for v in value]
if isinstance(value, str):
@@ -130,7 +126,6 @@ def fingerprint(response):
content_type = (response.headers.get("Content-Type") or "").split(";")[0].strip()
parts = {"status": response.status_code, "content_type": content_type, "size": len(body)}
if "json" in content_type:
try:
parts["json"] = _strip_volatile(json_module.loads(body.decode("utf-8")))
@@ -151,11 +146,7 @@ def fingerprint(response):
def differing_keys(baseline, other):
return {
key
for key in set(baseline) | set(other)
if key != "size" and baseline.get(key) != other.get(key)
}
return {key for key in set(baseline) | set(other) if key != "size" and baseline.get(key) != other.get(key)}
def size_differs(baseline, other):
@@ -167,9 +158,7 @@ def compare(baseline, other, ignore=frozenset(), ignore_size=False):
"""Return a list of human-readable differences between two fingerprints."""
diffs = []
for key in sorted(differing_keys(baseline, other) - set(ignore)):
diffs.append(
f"{key}: baseline={_short(baseline.get(key))} parallel={_short(other.get(key))}"
)
diffs.append(f"{key}: baseline={_short(baseline.get(key))} parallel={_short(other.get(key))}")
if not ignore_size and size_differs(baseline, other):
diffs.append(
f"size: baseline={baseline.get('size', 0)} parallel={other.get('size', 0)} "
@@ -211,9 +200,7 @@ def build_decoy_spec(spec):
width, height = float(box.width), float(box.height)
overlay_buffer = io.BytesIO()
overlay_canvas = canvas.Canvas(overlay_buffer, pagesize=(width, height))
overlay_canvas.drawString(
20, max(20.0, height - 20), f"DECOY-MARKER-{index}-do-not-mix"
)
overlay_canvas.drawString(20, max(20.0, height - 20), f"DECOY-MARKER-{index}-do-not-mix")
overlay_canvas.showPage()
overlay_canvas.save()
overlay_buffer.seek(0)
@@ -260,16 +247,12 @@ def validate(context, url, spec, headers, baseline, label, timeout=300):
baseline_fp = fingerprint(baseline)
noise, noisy_size = frozenset(), False
failures = _collect_failures(
main_results, decoy_results, baseline_fp, repeat, noise, noisy_size
)
failures = _collect_failures(main_results, decoy_results, baseline_fp, repeat, noise, noisy_size)
if failures:
# Some endpoints are inherently nondeterministic (embedded ids, timestamps,
# deliberate randomness). Re-run sequentially to tell that apart from a real bug.
noise, noisy_size = _probe_noise(url, spec, headers, baseline_fp, timeout)
failures = _collect_failures(
main_results, decoy_results, baseline_fp, repeat, noise, noisy_size
)
failures = _collect_failures(main_results, decoy_results, baseline_fp, repeat, noise, noisy_size)
VALIDATIONS.append(
{
@@ -302,12 +285,9 @@ def _collect_failures(main_results, decoy_results, baseline_fp, repeat, noise, n
diffs = compare(baseline_fp, actual_fp, noise, noisy_size)
if not diffs:
continue
if decoy_fp is not None and not compare(
decoy_fp, actual_fp, noise, noisy_size
):
if decoy_fp is not None and not compare(decoy_fp, actual_fp, noise, noisy_size):
failures.append(
f"copy {index + 1}/{repeat} returned the CONCURRENT DECOY REQUEST'S response "
f"(cross-request bleed)"
f"copy {index + 1}/{repeat} returned the CONCURRENT DECOY REQUEST'S response (cross-request bleed)"
)
else:
failures.append(f"copy {index + 1}/{repeat} diverged: " + "; ".join(diffs))
@@ -373,7 +353,7 @@ def validate_get(context, url, params, headers, baseline, label, timeout=60):
diffs = compare(
baseline_fp,
fingerprint(response),
noise,
noise,
noisy_size,
)
if diffs:
@@ -386,11 +366,7 @@ def validate_get(context, url, params, headers, baseline, label, timeout=60):
probes = []
for _ in range(NOISE_PROBE_SAMPLES):
try:
probes.append(
fingerprint(
requests.get(url, params=params, headers=headers, timeout=timeout)
)
)
probes.append(fingerprint(requests.get(url, params=params, headers=headers, timeout=timeout)))
except Exception:
break
noise, noisy_size = _noise_from_samples(baseline_fp, probes)
@@ -407,8 +383,7 @@ def validate_get(context, url, params, headers, baseline, label, timeout=60):
)
if failures:
raise AssertionError(
f"Parallel consistency failed for GET {label} at concurrency {repeat}.\n - "
+ "\n - ".join(failures)
f"Parallel consistency failed for GET {label} at concurrency {repeat}.\n - " + "\n - ".join(failures)
)