Skip to content

Commit f077210

Browse files
feat: add query_entities parameter validation and tests
1 parent fd2f61b commit f077210

File tree

2 files changed

+89
-6
lines changed

2 files changed

+89
-6
lines changed

src/arkiv/module.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -379,20 +379,21 @@ def query_entities(
379379
Use the returned cursor to fetch subsequent pages.
380380
381381
Args:
382-
query: SQL-like query string. Required for first page, ignored when using cursor.
382+
query: SQL-like query string for the first page. Mutually exclusive with cursor.
383383
Example: "SELECT * WHERE owner = '0x...' ORDER BY created_at DESC"
384384
limit: Maximum number of entities to return per page.
385-
Server may enforce a maximum limit. Ignored when using cursor.
385+
Server may enforce a maximum limit. Can be modified between pagination requests.
386386
at_block: Block number or "latest" at which to execute query.
387-
Ensures consistency across paginated results. Ignored when using cursor.
387+
Ensures consistency across paginated results. Ignored when using cursor
388+
(cursor already contains the block number).
388389
cursor: Cursor from previous QueryResult to fetch next page.
389-
When provided, query, limit, and at_block are ignored (cursor contains them).
390+
Mutually exclusive with query. The cursor encodes the query and block number.
390391
391392
Returns:
392393
QueryResult with entities, block number, and optional next cursor.
393394
394395
Raises:
395-
ValueError: If neither query nor cursor is provided.
396+
ValueError: If both query and cursor are provided, or if neither is provided.
396397
NotImplementedError: This method is not yet implemented.
397398
398399
Examples:
@@ -411,8 +412,11 @@ def query_entities(
411412
Note:
412413
For automatic pagination across all pages, use query_all_entities() instead.
413414
"""
415+
# Validate mutual exclusivity of query and cursor
416+
if cursor is not None and query is not None:
417+
raise ValueError("Cannot provide both query and cursor")
414418
if cursor is None and query is None:
415-
raise ValueError("Either query or cursor must be provided")
419+
raise ValueError("Must provide either query or cursor")
416420

417421
raise NotImplementedError("query_entities is not implemented yet")
418422

tests/test_entity_query.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""Tests for entity query functionality."""
2+
3+
import pytest
4+
5+
from arkiv import Arkiv
6+
from arkiv.types import Cursor
7+
8+
9+
class TestQueryEntitiesParameterValidation:
10+
"""Test parameter validation for query_entities method."""
11+
12+
def test_query_entities_requires_query_or_cursor(
13+
self, arkiv_client_http: Arkiv
14+
) -> None:
15+
"""Test that query_entities raises ValueError when neither query nor cursor is provided."""
16+
with pytest.raises(ValueError, match="Must provide either query or cursor"):
17+
arkiv_client_http.arkiv.query_entities()
18+
19+
def test_query_entities_validates_none_for_both(
20+
self, arkiv_client_http: Arkiv
21+
) -> None:
22+
"""Test that explicitly passing None for both query and cursor raises ValueError."""
23+
with pytest.raises(ValueError, match="Must provide either query or cursor"):
24+
arkiv_client_http.arkiv.query_entities(query=None, cursor=None)
25+
26+
def test_query_entities_accepts_query_only(self, arkiv_client_http: Arkiv) -> None:
27+
"""Test that query_entities accepts query without cursor."""
28+
# Should not raise ValueError for missing cursor
29+
# Will raise NotImplementedError since method is not implemented yet
30+
with pytest.raises(NotImplementedError, match="not implemented yet"):
31+
arkiv_client_http.arkiv.query_entities(query="SELECT *")
32+
33+
def test_query_entities_accepts_cursor_only(self, arkiv_client_http: Arkiv) -> None:
34+
"""Test that query_entities accepts cursor without query."""
35+
# Create a dummy cursor (opaque string)
36+
cursor = Cursor("dummy_cursor_value")
37+
38+
# Should not raise ValueError for missing query
39+
# Will raise NotImplementedError since method is not implemented yet
40+
with pytest.raises(NotImplementedError, match="not implemented yet"):
41+
arkiv_client_http.arkiv.query_entities(cursor=cursor)
42+
43+
def test_query_entities_both_query_and_cursor_not_allowed(
44+
self, arkiv_client_http: Arkiv
45+
) -> None:
46+
"""Test that query_entities rejects both query and cursor (mutually exclusive)."""
47+
cursor = Cursor("dummy_cursor_value")
48+
49+
# Should raise ValueError when both are provided
50+
with pytest.raises(ValueError, match="Cannot provide both query and cursor"):
51+
arkiv_client_http.arkiv.query_entities(query="SELECT *", cursor=cursor)
52+
53+
def test_query_entities_with_all_parameters(
54+
self, arkiv_client_http: Arkiv
55+
) -> None:
56+
"""Test that query_entities accepts all parameters."""
57+
# Should not raise ValueError
58+
# Will raise NotImplementedError since method is not implemented yet
59+
with pytest.raises(NotImplementedError, match="not implemented yet"):
60+
arkiv_client_http.arkiv.query_entities(
61+
query="SELECT * WHERE owner = '0x1234'",
62+
limit=50,
63+
at_block=1000,
64+
)
65+
66+
def test_query_entities_with_cursor_and_parameters(
67+
self, arkiv_client_http: Arkiv
68+
) -> None:
69+
"""Test that query_entities accepts cursor with other parameters (which are ignored)."""
70+
cursor = Cursor("dummy_cursor_value")
71+
72+
# Should not raise ValueError
73+
# Will raise NotImplementedError since method is not implemented yet
74+
with pytest.raises(NotImplementedError, match="not implemented yet"):
75+
arkiv_client_http.arkiv.query_entities(
76+
cursor=cursor,
77+
limit=100, # Ignored when cursor is provided
78+
at_block=1000, # Ignored when cursor is provided
79+
)

0 commit comments

Comments
 (0)