fix(accounts): register /accounts/hierarchy before /accounts/{account_id}

FastAPI matches in registration order, and hierarchy was registered
~3,300 lines after the {account_id} route — every request resolved as
account_id="hierarchy" and 404'd, so the endpoint has been unreachable
since it was added. Moved above the param routes in the accounts
module, with a functional reachability test and an updated route
snapshot (literal-before-param is now asserted for both overlap
families).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-07-12 16:12:37 +02:00
commit a0166d4026
2 changed files with 105 additions and 88 deletions

View file

@ -10,14 +10,11 @@ Regenerate after a DELIBERATE routing change with:
from libra.views_api import libra_api_router
for r in libra_api_router.routes:
print((",".join(sorted(r.methods)), r.path, r.endpoint.__name__))
Note the one known wart this table records: /api/v1/accounts/hierarchy
is registered AFTER /api/v1/accounts/{account_id}, so it is shadowed
(requests resolve as account_id="hierarchy"). Fixing that is a
deliberate behavior change with its own commit + snapshot update.
"""
import importlib
import pytest
def _module(name: str):
for prefix in ("lnbits.extensions.libra", "libra"):
@ -34,10 +31,10 @@ EXPECTED_ROUTES = [
("GET", "/api/v1/currencies", "api_get_currencies"),
("GET", "/api/v1/accounts", "api_get_accounts"),
("POST", "/api/v1/accounts", "api_create_account"),
("GET", "/api/v1/accounts/hierarchy", "api_get_account_hierarchy"),
("GET", "/api/v1/accounts/{account_id}", "api_get_account"),
("GET", "/api/v1/accounts/{account_id}/balance", "api_get_account_balance"),
("GET", "/api/v1/accounts/{account_id}/transactions", "api_get_account_transactions"),
("GET", "/api/v1/accounts/hierarchy", "api_get_account_hierarchy"),
("GET", "/api/v1/entries", "api_get_journal_entries"),
("GET", "/api/v1/entries/user", "api_get_user_entries"),
("GET", "/api/v1/entries/pending", "api_get_pending_entries"),
@ -125,11 +122,26 @@ def test_overlapping_route_order_is_preserved():
these are the two overlap families in the table. The package split
keeps each family inside one module so include order can't reorder
them."""
# Known wart carried over from before the split: hierarchy is
# shadowed by the {account_id} route (fixed in a separate commit).
assert _index("/api/v1/accounts/{account_id}") < _index(
"/api/v1/accounts/hierarchy"
# The literal must precede the {param} sibling or it is unreachable.
assert _index("/api/v1/accounts/hierarchy") < _index(
"/api/v1/accounts/{account_id}"
)
assert _index("/api/v1/admin/accounts/sync") < _index(
"/api/v1/admin/accounts/sync/{account_name:path}"
)
@pytest.mark.anyio
async def test_accounts_hierarchy_is_reachable(
client, configured_user, standard_accounts,
):
"""GET /accounts/hierarchy must reach the hierarchy endpoint. It was
registered after /accounts/{account_id} since it was added, so every
request resolved as account_id="hierarchy" and 404'd."""
_, wallet = configured_user
r = await client.get(
"/libra/api/v1/accounts/hierarchy",
headers={"X-Api-Key": wallet.inkey},
)
assert r.status_code == 200, f"hierarchy shadowed again? {r.status_code} {r.text}"
assert isinstance(r.json(), list)