"""Envelope + camelCase renderer (API-STANDARD.md §3, §4).""" import json from infrasynth.api.renderers import EnvelopeJSONRenderer class _FakeRequest: def __init__(self, request_id="req_1", tenant=None): self.request_id = request_id self.tenant = tenant class _FakeResponse: def __init__(self, status_code=200): self.status_code = status_code def _render(data, *, status=200, request=None, response=None): renderer = EnvelopeJSONRenderer() raw = renderer.render( data, renderer_context={"request": request or _FakeRequest(), "response": response or _FakeResponse(status)}, ) return json.loads(raw) def test_success_envelope_camelcases_data(): body = _render({"first_name": "Ada", "nested": {"last_name": "L"}}) assert body["success"] is True assert body["error"] is None assert body["data"] == {"firstName": "Ada", "nested": {"lastName": "L"}} assert body["meta"]["requestId"] == "req_1" assert body["meta"]["timestamp"].endswith("Z") def test_error_envelope_uses_error_member(): body = _render( {"code": "ENTITLEMENT_EXPIRED", "message": "expired", "details": []}, status=402, ) assert body["success"] is False assert body["data"] is None assert body["error"]["code"] == "ENTITLEMENT_EXPIRED" def test_paginated_response_moves_results_to_data(): payload = { "next": "cursor_next", "previous": None, "page_size": 25, "results": [{"id": 1}, {"id": 2}], } body = _render(payload) assert body["data"] == [{"id": 1}, {"id": 2}] assert body["meta"]["pagination"] == {"nextCursor": "cursor_next", "prevCursor": None, "pageSize": 25} def test_meta_includes_tenant_id_when_resolved(): class _Tenant: pk = "11111111-1111-1111-1111-111111111111" body = _render({"ok": True}, request=_FakeRequest(tenant=_Tenant())) assert body["meta"]["tenantId"] == "11111111-1111-1111-1111-111111111111"