Fix #015 (nested-JSON 500) and #016 (ragged CSV data loss)

#015 — deeply nested JSON raised RecursionError (a RuntimeError, not
ValueError) and became an unhandled 500 on two paths:
- json-kind content validation now catches RecursionError -> 400.
- Added a custom DRF EXCEPTION_HANDLER
  (artifacts/exception_handlers.py, wired via REST_FRAMEWORK) mapping
  otherwise-unhandled RecursionError -> 400, covering the parser-level
  crash on deeply nested request bodies (metadata etc.) too.

#016 — wrap_csv_viewer handed Grid.js rows without reconciling width to the
header, so over-long rows silently dropped cells. Now normalizes every row
to the max width: long rows widen the columns (auto-named "Column N"),
short rows pad with "". No cell is dropped.

Both verified live; 4 regression tests added. 106 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
peter 2026-09-03 10:57:07 +01:00
parent 5c53643349
commit 5f872b4b32
6 changed files with 110 additions and 7 deletions

View file

@ -594,3 +594,42 @@ class ArtifactAPITestCase(APITestCase):
response = self.client.get(reverse("artifact", args=[aid]))
self.assertIsNone(response.headers.get("Content-Security-Policy"))
# -- deeply nested JSON -> 400 not 500 (issue #015) ----------------------
def test_deeply_nested_json_content_returns_400(self):
deep = "[" * 5000 + "]" * 5000 # valid JSON, just too deep to parse
response = self.create_artifact(kind="json", content=deep)
self.assertEqual(response.status_code, 400)
def test_deeply_nested_request_body_returns_400(self):
# Nesting in the request body itself crashes DRF's parser before the
# view runs; the custom exception handler must map it to 400.
nested = "null"
for _ in range(5000):
nested = '{"n":' + nested + "}"
body = (
'{"source":"t","kind":"markdown","title":"x","content":"hi",'
'"metadata":' + nested + "}"
)
response = self.client.post(
reverse("artifact-api"),
data=body,
content_type="application/json",
**self.auth_headers(self.token),
)
self.assertEqual(response.status_code, 400)
def test_normal_depth_json_still_accepted(self):
response = self.create_artifact(kind="json", content='{"a": {"b": {"c": [1, 2, 3]}}}')
self.assertEqual(response.status_code, 201)
# -- ragged CSV rows normalized, no data lost (issue #016) ---------------
def test_ragged_csv_rows_preserve_all_cells(self):
aid = self.create_artifact(kind="csv", content="a,b,c\n1,2,3,4,5\n6,7").data["id"]
html = self._stored_html(aid)
# Long row's extra cells are kept via auto-named columns; short row padded.
self.assertIn('"columns": ["a", "b", "c", "Column 4", "Column 5"]', html)
self.assertIn('["1", "2", "3", "4", "5"]', html)
self.assertIn('["6", "7", "", "", ""]', html)