Files
devplacepy/tests/unit/responses.py
T
retoor 56becfb3f7 Answer workspace refusals instead of crashing on them
json_error takes (status_code, message). Every refusal in the two
workspace routers passed them the other way round, which builds a
JSONResponse with a string status and raises TypeError inside
Response.init_headers before a byte is written. The branch meant to
explain a limit to the user returned 500 instead.

A member already holding the default two workspaces therefore got a 500
from the workspace page's Open workspace button rather than the quota
message. Twelve call sites corrected across both routers.

workspace_open also let ContainerError escape as a second 500 on the
same button when the ppy image is not built; it now returns 400 through
the shared fail helper.

tests/unit/responses.py AST-scans the whole package for both argument
orders, so the swap cannot reappear anywhere.
2026-08-08 13:51:50 +02:00

51 lines
1.8 KiB
Python

# retoor <retoor@molodetz.nl>
import ast
from pathlib import Path
PACKAGE = Path(__file__).resolve().parents[2] / "devplacepy"
def _json_error_calls():
for path in sorted(PACKAGE.rglob("*.py")):
tree = ast.parse(path.read_text(), filename=str(path))
for node in ast.walk(tree):
if not isinstance(node, ast.Call):
continue
name = node.func.attr if isinstance(node.func, ast.Attribute) else getattr(node.func, "id", "")
if name == "json_error" and node.args:
yield path.relative_to(PACKAGE.parent), node
def test_json_error_never_receives_a_message_as_its_status():
offenders = [
f"{path}:{node.lineno}"
for path, node in _json_error_calls()
if isinstance(node.args[0], ast.Constant) and isinstance(node.args[0].value, str)
]
assert not offenders, (
"json_error(status_code, message) - a string in the status position builds a "
f"JSONResponse with a non-int status and raises TypeError at request time: {offenders}"
)
def test_json_error_never_receives_a_status_as_its_message():
offenders = [
f"{path}:{node.lineno}"
for path, node in _json_error_calls()
if len(node.args) > 1
and isinstance(node.args[1], ast.Constant)
and isinstance(node.args[1].value, int)
]
assert not offenders, (
"json_error(status_code, message) - an int in the message position means the "
f"arguments were swapped: {offenders}"
)
def test_every_json_error_status_is_a_real_http_error_code():
for path, node in _json_error_calls():
first = node.args[0]
if isinstance(first, ast.Constant) and isinstance(first.value, int):
assert 400 <= first.value <= 599, f"{path}:{node.lineno} status {first.value}"