From e3728bd88621f0902085e16f4dbde25c91e433e8 Mon Sep 17 00:00:00 2001 From: David Traina <44659830+DavidTraina@users.noreply.github.com> Date: Thu, 5 Nov 2026 00:39:43 -0600 Subject: [PATCH] test: follow observation cursors past empty pages The e2e helper get_observations stopped on an empty page, which the observations v2 API can now return while meta.cursor is set. Two e2e tests also asserted no cursor for unbounded no-match queries, which depends on how fast the server scans back to 2020-01-01; bound them to the last day so the server covers the range in one scan window. --- tests/e2e/test_core_sdk.py | 12 ++++++++++-- tests/support/utils.py | 3 ++- tests/unit/test_e2e_support.py | 9 +++++---- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/tests/e2e/test_core_sdk.py b/tests/e2e/test_core_sdk.py index e02d139f1..0d92ed3f7 100644 --- a/tests/e2e/test_core_sdk.py +++ b/tests/e2e/test_core_sdk.py @@ -1843,7 +1843,12 @@ def test_get_observations(): def test_get_observations_for_unknown_trace_is_empty(): - response = get_api(retry=False).observations.get_many(trace_id=create_uuid()) + # A lower bound within the server's first 7-day scan window makes one + # request cover the whole range, so the cursor is reliably absent. + response = get_api(retry=False).observations.get_many( + trace_id=create_uuid(), + from_start_time=datetime.now(timezone.utc) - timedelta(days=1), + ) assert response.data == [] assert response.meta.cursor is None @@ -1851,7 +1856,10 @@ def test_get_observations_for_unknown_trace_is_empty(): def test_get_observations_empty(): # Fetch observations with a filter that should return no results - response = get_api(retry=False).observations.get_many(name=create_uuid()) + response = get_api(retry=False).observations.get_many( + name=create_uuid(), + from_start_time=datetime.now(timezone.utc) - timedelta(days=1), + ) assert response.data == [] assert response.meta.cursor is None diff --git a/tests/support/utils.py b/tests/support/utils.py index fc827eacb..8baa32f38 100644 --- a/tests/support/utils.py +++ b/tests/support/utils.py @@ -186,7 +186,8 @@ def get_observations( ) observations.extend(response.data) cursor = response.meta.cursor - if not cursor or not response.data: + # Pages can be short or empty before the end. + if not cursor: break # The events table can briefly return several rows for one span until diff --git a/tests/unit/test_e2e_support.py b/tests/unit/test_e2e_support.py index b27e5aa0d..5e0a3f1c7 100644 --- a/tests/unit/test_e2e_support.py +++ b/tests/unit/test_e2e_support.py @@ -124,11 +124,12 @@ def test_user_metadata_drops_server_added_keys(): assert user_metadata(observation) == {"key": "value"} -def test_get_observations_follows_cursor_and_sorts_by_start_time(monkeypatch): +def test_get_observations_follows_cursor_past_empty_pages_and_sorts(monkeypatch): calls = [] pages = { - None: _page([_observation(2), _observation(0)], cursor="next"), - "next": _page([_observation(1)]), + None: _page([_observation(2), _observation(0)], cursor="empty"), + "empty": _page([], cursor="last"), + "last": _page([_observation(1)]), } def get_many(**kwargs): @@ -140,7 +141,7 @@ def get_many(**kwargs): observations = get_observations(trace_id="trace-123") assert [o.id for o in observations] == ["obs-0", "obs-1", "obs-2"] - assert [call["cursor"] for call in calls] == [None, "next"] + assert [call["cursor"] for call in calls] == [None, "empty", "last"] assert all(call["trace_id"] == "trace-123" for call in calls)