"""Regression: finance saved-view owner identity. ``FinanceSavedView.owner_person_id`` is a plain ``String(36)`` while ``user.id`` is a ``uuid.UUID``. Before the fix the read filter bound a raw ``uuid.UUID`` against the CHAR column (asyncmy → silently 0 rows, so a private view never listed) and the edit/delete guard compared ``str != uuid.UUID`` (always True → the genuine owner got 403'd). Both paths must work for the creator of a *private* (non-shared) view. """ from __future__ import annotations import pytest @pytest.mark.asyncio async def test_owner_can_list_edit_delete_private_view(client, finance_user): _user, token = finance_user h = {"Authorization": f"Bearer {token}"} # Create a PRIVATE (not shared) saved view. r = await client.post( "/api/v1/finance/views", headers=h, json={"name": "My private view", "config": {"group_by": ["cost_type"]}, "is_shared": False}, ) assert r.status_code == 200, r.text view_id = r.json()["data"]["id"] # It must appear in the owner's list (regression: 0 rows before the fix). r = await client.get("/api/v1/finance/views", headers=h) assert r.status_code == 200, r.text assert view_id in [v["id"] for v in r.json()["data"]] # Owner can edit it (regression: str != uuid always 403'd the owner). r = await client.put( f"/api/v1/finance/views/{view_id}", headers=h, json={"name": "Renamed", "config": {"group_by": ["vendor"]}, "is_shared": False}, ) assert r.status_code == 200, r.text assert r.json()["data"]["name"] == "Renamed" # Owner can delete it. r = await client.delete(f"/api/v1/finance/views/{view_id}", headers=h) assert r.status_code == 200, r.text