Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions src/smallestai/atoms/helpers/_envelope.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ class Page:
has_more: Optional[bool] = None


def _first_present(*values: Any) -> Any:
"""First value the server actually sent, treating 0 and False as sent.

`a or b` would skip a real 0, so a page the server honestly reports as empty
came back with total_count None instead of 0.
"""
return next((value for value in values if value is not None), None)


def _get(obj: Any, name: str) -> Any:
"""Attribute- or key-based access (works for pydantic models and dicts)."""
if obj is None:
Expand Down Expand Up @@ -83,18 +92,16 @@ def as_page(response: Any) -> Page:
if items is not None:
return Page(
items=list(items),
total_count=(
_get(data, "total_count")
or _get(data, "total")
or _get(data, "count")
or _get(data, "total_campaign_count")
or _get(pagination, "total")
or _get(pagination, "total_count")
),
total_pages=_get(data, "total_pages") or _get(pagination, "total_pages"),
has_more=(
_get(data, "has_more") if _get(data, "has_more") is not None else _get(pagination, "has_more")
total_count=_first_present(
_get(data, "total_count"),
_get(data, "total"),
_get(data, "count"),
_get(data, "total_campaign_count"),
_get(pagination, "total"),
_get(pagination, "total_count"),
),
total_pages=_first_present(_get(data, "total_pages"), _get(pagination, "total_pages")),
has_more=_first_present(_get(data, "has_more"), _get(pagination, "has_more")),
)

# Unknown shape — return the payload as a single-item page rather than guessing.
Expand Down
23 changes: 23 additions & 0 deletions tests/custom/test_as_page_envelope.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,26 @@ def test_campaigns_total_campaign_count() -> None:
def test_nested_pagination() -> None:
pg = as_page(_Resp({"logs": [1, 2], "pagination": {"total": 9, "hasMore": True}}))
assert pg.items == [1, 2] and pg.total_count == 9


def test_an_honestly_empty_page_keeps_its_zeros() -> None:
# `a or b` chaining skipped a real 0, so a page the server reported as empty came
# back as total_count None, which reads as "the server did not say".
pg = as_page(_Resp({"agents": [], "total_count": 0, "total_pages": 0, "has_more": False}))
assert pg.items == []
assert pg.total_count == 0
assert pg.total_pages == 0
assert pg.has_more is False


def test_zeros_from_nested_pagination_are_kept_too() -> None:
pg = as_page(_Resp({"agents": [], "pagination": {"total": 0, "total_pages": 0, "has_more": False}}))
assert pg.total_count == 0
assert pg.total_pages == 0
assert pg.has_more is False


def test_a_flat_zero_still_wins_over_a_nested_value() -> None:
# Precedence is unchanged: the flat field is read first whether or not it is truthy.
pg = as_page(_Resp({"agents": [], "total_count": 0, "pagination": {"total": 7}}))
assert pg.total_count == 0