test(pricing): say once what a rate-validation test pins

The failure history these tests were written against now lives in one place,
`payment/rates.py`. Cut the longest test docstrings down to what the test
itself pins, rather than restating that history at every boundary.

No test changes behaviour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014X8RZzzbAuQCbavhFjTvJ4
This commit is contained in:
Jeroen Ubbink
2026-08-26 15:56:10 +02:00
co-authored by Claude Opus 5
parent 59bf8f4c90
commit 76d7b8fceb
3 changed files with 19 additions and 42 deletions
@@ -189,12 +189,9 @@ async def test_a_rate_that_is_not_there_is_rejected(
) -> None:
"""A rate given as ``null``, or a required rate left out, is not a price.
The validator read both through ``dict.get``, which cannot tell an absent
key from an explicit ``null``, and skipped both. ``Pricing`` declares
``prompt`` and ``completion`` without a default and every rate as a float,
so such a row is written with a 200 and then fails to parse on read — and
a row that will not parse is withheld from the catalog, leaving the operator
a model that was accepted and is nowhere to be seen.
``dict.get`` cannot tell the two apart and skipped both, so a row
``Pricing`` cannot parse was committed and the response that reads it back
raised.
"""
provider_id = await _make_provider(integration_session)
@@ -351,12 +348,9 @@ async def test_admin_model_listing_shows_a_non_finite_stored_rate(
) -> None:
"""The operator must be able to see the rate that needs fixing.
The admin listing deliberately includes disabled models, so it is the one
view that still carries a row the served-catalog backstop holds back.
FastAPI's encoder rendered a stored ``Infinity`` rate as ``null``, which is
indistinguishable from a rate the row never carried — the operator could see
the row but not the reason it was withheld. Render the offending value as
text instead, as the 422 handler already does.
The admin listing is the one view that still carries a row the served
catalog holds back, and its encoder rendered a stored ``Infinity`` as
``null`` — indistinguishable from a rate the row never carried.
"""
provider_id = await _make_provider(integration_session)
integration_session.add(
+3 -6
View File
@@ -1025,12 +1025,9 @@ def test_create_model_mappings_survives_an_unreadable_override_row(
) -> None:
"""One row that cannot be read must not empty the whole routing map.
Stored pricing is JSON from whatever wrote the row, so converting it can
raise. Converting an override while walking a provider's catalog let that
exception unwind the entire map build: at boot the node came up routing
nothing, and on a later refresh the map it already had went permanently
stale. The sibling loop over override-only rows already skips and logs such
a row.
Converting an override while walking a provider's catalog let the exception
unwind the entire map build: the node came up routing nothing. The sibling
loop over override-only rows already skips and logs such a row.
"""
broken = create_test_model("broken-model")
healthy = create_test_model("healthy-model")
+10 -24
View File
@@ -77,12 +77,9 @@ def _usage_response() -> dict[str, Any]:
async def test_unusable_token_rate_falls_back_to_max_cost(bad_rate: float) -> None:
"""An unusable configured rate must not be billed on.
The "no token pricing configured" gate is a truthiness test, and ``NaN`` and
negative floats are both truthy, so an unusable rate passes the guard that
exists to catch it. It then reaches the integer conversion in the token math,
which raises ``ValueError`` for ``NaN`` and ``OverflowError`` for ``inf`` —
after the upstream response has already been served, where the streaming
handlers swallow it and the request goes unbilled.
It reached the token math, which raises after the response was already
served — where the streaming handlers swallow it and the request goes
unbilled.
"""
model = _model(Pricing(prompt=bad_rate, completion=1.0))
@@ -124,11 +121,8 @@ async def test_junk_cost_component_still_bills_the_reported_total(junk: Any) ->
"""A malformed component must not discard the upstream's real total cost.
``cost_details`` only splits the total across input and output; the total is
the authoritative billed amount. A non-finite component poisons the
proportional allocation (``inf / inf`` is ``NaN``), which raised out of the
USD path and was swallowed by the broad handler around it — so the request
silently fell through to token-estimated pricing and was billed at a small
fraction of what the upstream actually charged.
the authoritative billed amount. A non-finite component poisoned the split,
and the request fell through to token estimation for a fraction of it.
"""
model = _model(Pricing(prompt=1e-06, completion=2e-06))
response = {
@@ -289,14 +283,9 @@ async def test_unusable_exchange_quote_does_not_set_the_node_price(
) -> None:
"""One exchange returning junk must not set the price the node bills at.
The feed takes the ``min()`` of the quotes it collects, so an unusable quote
does not merely join the sample — it *wins*, and poisons the rate every model
and every request is priced at until the next refresh. Zero then divides by
zero on the USD path, ``NaN`` raises out of the integer conversion, and a
negative rate produces a negative charge that settlement credits back to the
caller.
The two healthy quotes must still price the node.
The feed takes the ``min()`` of what it collects, so an unusable quote does
not merely join the sample — it *wins*. The two healthy quotes must still
price the node.
"""
from routstr.payment.price import btc_usd_price
@@ -351,11 +340,8 @@ async def test_an_unreadable_exchange_response_drops_only_that_quote(
) -> None:
"""An exchange whose response never yields a quote costs one quote.
The price is aggregated across three exchanges precisely so that one of them
having a bad day is survivable. A body that is not JSON, or whose shape moved
under the reader, raises before any number is seen; unhandled, it aborted the
whole aggregation and left the node on a stale rate even though two healthy
quotes were already in hand.
The price is aggregated across three exchanges so that one of them having a
bad day is survivable; unhandled, the raise aborted the whole aggregation.
"""
from routstr.payment.price import btc_usd_price