diff --git a/src/willow/client.py b/src/willow/client.py index 19ca9c2..85785a9 100644 --- a/src/willow/client.py +++ b/src/willow/client.py @@ -636,6 +636,7 @@ async def graphql_query( subgrove_id: str, query: str, variables: Optional[Dict[str, Any]] = None, + include_proof: bool = False, source: QuerySource = QuerySource.AUTO, ) -> RoutedQueryResult[GraphQLResponse]: """Execute a GraphQL query against a subgrove with source routing. @@ -654,6 +655,7 @@ async def graphql_query( subgrove_id: Subgrove identifier query: GraphQL query string variables: Optional query variables + include_proof: Whether to request a Merkle proof (default: ``False``) source: Routing preference (default: ``AUTO``) Returns: @@ -663,6 +665,7 @@ async def graphql_query( request_data: Dict[str, Any] = {"query": query} if variables: request_data["variables"] = variables + request_data["include_proof"] = include_proof raw = await self.client._route_query( "graphql", subgrove_id, request_data, source @@ -678,7 +681,7 @@ async def sql_query( self, subgrove_id: str, query: str, - include_proof: bool = True, + include_proof: bool = False, source: QuerySource = QuerySource.AUTO, ) -> RoutedQueryResult[SqlResponse]: """Execute a SQL query against a subgrove with source routing. diff --git a/src/willow/types.py b/src/willow/types.py index 5c6927a..e5aa3dc 100644 --- a/src/willow/types.py +++ b/src/willow/types.py @@ -512,6 +512,7 @@ class GraphQLRequest(BaseModel): """GraphQL query request.""" query: str variables: Optional[Dict[str, Any]] = None + include_proof: Optional[bool] = None class GraphQLError(BaseModel): diff --git a/tests/test_new_operations.py b/tests/test_new_operations.py index 2a1106c..f5712ad 100644 --- a/tests/test_new_operations.py +++ b/tests/test_new_operations.py @@ -382,6 +382,64 @@ async def test_get_verification_stats(self, client, mock_http_client): assert result.verification_rate == 0.95 +class TestIncludeProofWire: + """Assert display/analytics read paths serialize ``include_proof`` correctly. + + These are Bucket-A *Unverified paths: they must default to + ``include_proof=False`` on the wire (proof off) and only send ``True`` + when the caller explicitly opts in. + """ + + @staticmethod + def _routed(result): + from willow.indexers import RoutedQueryResult, ServedBy + return RoutedQueryResult(result=result, source=ServedBy.INDEXER) + + @pytest.mark.asyncio + async def test_graphql_default_off(self, client): + """graphql_query omits proof by default -> include_proof=False on wire.""" + route = AsyncMock(return_value=self._routed({"data": {}, "errors": None})) + client._route_query = route + + await client.indexing.graphql_query("sg", "query { x }") + + body = route.call_args.args[2] + assert body["include_proof"] is False + + @pytest.mark.asyncio + async def test_graphql_opt_in(self, client): + """graphql_query honors explicit include_proof=True.""" + route = AsyncMock(return_value=self._routed({"data": {}, "errors": None})) + client._route_query = route + + await client.indexing.graphql_query("sg", "query { x }", include_proof=True) + + body = route.call_args.args[2] + assert body["include_proof"] is True + + @pytest.mark.asyncio + async def test_sql_default_off(self, client): + """sql_query defaults to include_proof=False on the wire.""" + route = AsyncMock(return_value=self._routed({"columns": [], "rows": []})) + client._route_query = route + + await client.indexing.sql_query("sg", "SELECT 1") + + body = route.call_args.args[2] + assert body["include_proof"] is False + + @pytest.mark.asyncio + async def test_sql_opt_in(self, client): + """sql_query honors explicit include_proof=True.""" + route = AsyncMock(return_value=self._routed({"columns": [], "rows": []})) + client._route_query = route + + await client.indexing.sql_query("sg", "SELECT 1", include_proof=True) + + body = route.call_args.args[2] + assert body["include_proof"] is True + + class TestHealthAndRootHash: """Test health and root hash operations."""