2525from google .cloud .bigtable_v2 .types import ResponseParams
2626from google .cloud .bigtable .data ._metrics .handlers ._base import MetricsHandler
2727from google .cloud .bigtable .data ._metrics .data_model import CompletedOperationMetric , CompletedAttemptMetric , ActiveOperationMetric , OperationState
28+ from google .cloud .bigtable .data .read_rows_query import ReadRowsQuery
2829
2930from google .cloud .bigtable .data ._cross_sync import CrossSync
3031
@@ -193,7 +194,7 @@ async def test_read_rows(self, table, temp_rows, handler, cluster_config):
193194 await temp_rows .add_row (b"row_key_1" )
194195 await temp_rows .add_row (b"row_key_2" )
195196 handler .clear ()
196- row_list = await table .read_rows ({} )
197+ row_list = await table .read_rows (ReadRowsQuery () )
197198 assert len (row_list ) == 2
198199 # validate counts
199200 assert len (handler .completed_operations ) == 1
@@ -231,7 +232,38 @@ async def test_read_rows_failure_grpc(
231232
232233 No headers expected
233234 """
234- pass
235+ await temp_rows .add_row (b"row_key_1" )
236+ handler .clear ()
237+ exc = Aborted ("injected" )
238+ num_retryable = 2
239+ for i in range (num_retryable ):
240+ error_injector .push (exc )
241+ error_injector .push (PermissionDenied ("terminal" ))
242+ with pytest .raises (PermissionDenied ):
243+ await table .read_rows (ReadRowsQuery (), retryable_errors = [Aborted ])
244+ # validate counts
245+ assert len (handler .completed_operations ) == 1
246+ assert len (handler .completed_attempts ) == num_retryable + 1
247+ assert len (handler .cancelled_operations ) == 0
248+ # validate operation
249+ operation = handler .completed_operations [0 ]
250+ assert isinstance (operation , CompletedOperationMetric )
251+ assert operation .final_status .name == "PERMISSION_DENIED"
252+ assert operation .op_type .value == "ReadRows"
253+ assert operation .is_streaming is True
254+ assert len (operation .completed_attempts ) == num_retryable + 1
255+ assert operation .cluster_id == "unspecified"
256+ assert operation .zone == "global"
257+ # validate attempts
258+ for i in range (num_retryable ):
259+ attempt = handler .completed_attempts [i ]
260+ assert isinstance (attempt , CompletedAttemptMetric )
261+ assert attempt .end_status .name == "ABORTED"
262+ assert attempt .gfe_latency_ns is None
263+ final_attempt = handler .completed_attempts [num_retryable ]
264+ assert isinstance (final_attempt , CompletedAttemptMetric )
265+ assert final_attempt .end_status .name == "PERMISSION_DENIED"
266+ assert final_attempt .gfe_latency_ns is None
235267
236268 @CrossSync .pytest
237269 async def test_read_rows_failure_timeout (self , table , temp_rows , handler ):
@@ -240,7 +272,28 @@ async def test_read_rows_failure_timeout(self, table, temp_rows, handler):
240272
241273 No grpc headers expected
242274 """
243- pass
275+ await temp_rows .add_row (b"row_key_1" )
276+ handler .clear ()
277+ with pytest .raises (GoogleAPICallError ):
278+ await table .read_rows (ReadRowsQuery (), operation_timeout = 0.001 )
279+ # validate counts
280+ assert len (handler .completed_operations ) == 1
281+ assert len (handler .completed_attempts ) == 1
282+ assert len (handler .cancelled_operations ) == 0
283+ # validate operation
284+ operation = handler .completed_operations [0 ]
285+ assert isinstance (operation , CompletedOperationMetric )
286+ assert operation .final_status .name == "DEADLINE_EXCEEDED"
287+ assert operation .op_type .value == "ReadRows"
288+ assert operation .is_streaming is True
289+ assert len (operation .completed_attempts ) == 1
290+ assert operation .cluster_id == "unspecified"
291+ assert operation .zone == "global"
292+ # validate attempt
293+ attempt = handler .completed_attempts [0 ]
294+ assert isinstance (attempt , CompletedAttemptMetric )
295+ assert attempt .end_status .name == "DEADLINE_EXCEEDED"
296+ assert attempt .gfe_latency_ns is None
244297
245298 @CrossSync .pytest
246299 async def test_read_rows_failure_unauthorized (
@@ -249,15 +302,37 @@ async def test_read_rows_failure_unauthorized(
249302 """
250303 Test failure in backend by accessing an unauthorized family
251304 """
252- pass
305+ from google .cloud .bigtable .data .row_filters import FamilyNameRegexFilter
306+
307+ with pytest .raises (GoogleAPICallError ) as e :
308+ await authorized_view .read_rows (ReadRowsQuery (row_filter = FamilyNameRegexFilter ("unauthorized" )))
309+ assert e .value .grpc_status_code .name == "PERMISSION_DENIED"
310+ # validate counts
311+ assert len (handler .completed_operations ) == 1
312+ assert len (handler .completed_attempts ) == 1
313+ assert len (handler .cancelled_operations ) == 0
314+ # validate operation
315+ operation = handler .completed_operations [0 ]
316+ assert isinstance (operation , CompletedOperationMetric )
317+ assert operation .final_status .name == "PERMISSION_DENIED"
318+ assert operation .op_type .value == "ReadRows"
319+ assert operation .is_streaming is True
320+ assert len (operation .completed_attempts ) == 1
321+ assert operation .cluster_id == next (iter (cluster_config .keys ()))
322+ assert operation .zone == cluster_config [operation .cluster_id ].location .split ("/" )[- 1 ]
323+ # validate attempt
324+ attempt = handler .completed_attempts [0 ]
325+ assert isinstance (attempt , CompletedAttemptMetric )
326+ assert attempt .end_status .name == "PERMISSION_DENIED"
327+ assert attempt .gfe_latency_ns >= 0 and attempt .gfe_latency_ns < operation .duration_ns
253328
254329 @CrossSync .pytest
255330 async def test_read_rows_stream (self , table , temp_rows , handler , cluster_config ):
256331 await temp_rows .add_row (b"row_key_1" )
257332 await temp_rows .add_row (b"row_key_2" )
258333 handler .clear ()
259334 # full table scan
260- generator = await table .read_rows_stream ({} )
335+ generator = await table .read_rows_stream (ReadRowsQuery () )
261336 row_list = [r async for r in generator ]
262337 assert len (row_list ) == 2
263338 # validate counts
@@ -296,7 +371,39 @@ async def test_read_rows_stream_failure_grpc(
296371
297372 No headers expected
298373 """
299- pass
374+ await temp_rows .add_row (b"row_key_1" )
375+ handler .clear ()
376+ exc = Aborted ("injected" )
377+ num_retryable = 2
378+ for i in range (num_retryable ):
379+ error_injector .push (exc )
380+ error_injector .push (PermissionDenied ("terminal" ))
381+ generator = await table .read_rows_stream (ReadRowsQuery (), retryable_errors = [Aborted ])
382+ with pytest .raises (PermissionDenied ):
383+ [_ async for _ in generator ]
384+ # validate counts
385+ assert len (handler .completed_operations ) == 1
386+ assert len (handler .completed_attempts ) == num_retryable + 1
387+ assert len (handler .cancelled_operations ) == 0
388+ # validate operation
389+ operation = handler .completed_operations [0 ]
390+ assert isinstance (operation , CompletedOperationMetric )
391+ assert operation .final_status .name == "PERMISSION_DENIED"
392+ assert operation .op_type .value == "ReadRows"
393+ assert operation .is_streaming is True
394+ assert len (operation .completed_attempts ) == num_retryable + 1
395+ assert operation .cluster_id == "unspecified"
396+ assert operation .zone == "global"
397+ # validate attempts
398+ for i in range (num_retryable ):
399+ attempt = handler .completed_attempts [i ]
400+ assert isinstance (attempt , CompletedAttemptMetric )
401+ assert attempt .end_status .name == "ABORTED"
402+ assert attempt .gfe_latency_ns is None
403+ final_attempt = handler .completed_attempts [num_retryable ]
404+ assert isinstance (final_attempt , CompletedAttemptMetric )
405+ assert final_attempt .end_status .name == "PERMISSION_DENIED"
406+ assert final_attempt .gfe_latency_ns is None
300407
301408 @CrossSync .pytest
302409 async def test_read_rows_stream_failure_timeout (
@@ -307,7 +414,29 @@ async def test_read_rows_stream_failure_timeout(
307414
308415 No grpc headers expected
309416 """
310- pass
417+ await temp_rows .add_row (b"row_key_1" )
418+ handler .clear ()
419+ generator = await table .read_rows_stream (ReadRowsQuery (), operation_timeout = 0.001 )
420+ with pytest .raises (GoogleAPICallError ):
421+ [_ async for _ in generator ]
422+ # validate counts
423+ assert len (handler .completed_operations ) == 1
424+ assert len (handler .completed_attempts ) == 1
425+ assert len (handler .cancelled_operations ) == 0
426+ # validate operation
427+ operation = handler .completed_operations [0 ]
428+ assert isinstance (operation , CompletedOperationMetric )
429+ assert operation .final_status .name == "DEADLINE_EXCEEDED"
430+ assert operation .op_type .value == "ReadRows"
431+ assert operation .is_streaming is True
432+ assert len (operation .completed_attempts ) == 1
433+ assert operation .cluster_id == "unspecified"
434+ assert operation .zone == "global"
435+ # validate attempt
436+ attempt = handler .completed_attempts [0 ]
437+ assert isinstance (attempt , CompletedAttemptMetric )
438+ assert attempt .end_status .name == "DEADLINE_EXCEEDED"
439+ assert attempt .gfe_latency_ns is None
311440
312441 @CrossSync .pytest
313442 async def test_read_rows_stream_failure_unauthorized (
@@ -316,7 +445,30 @@ async def test_read_rows_stream_failure_unauthorized(
316445 """
317446 Test failure in backend by accessing an unauthorized family
318447 """
319- pass
448+ from google .cloud .bigtable .data .row_filters import FamilyNameRegexFilter
449+
450+ with pytest .raises (GoogleAPICallError ) as e :
451+ generator = await authorized_view .read_rows_stream (ReadRowsQuery (row_filter = FamilyNameRegexFilter ("unauthorized" )))
452+ [_ async for _ in generator ]
453+ assert e .value .grpc_status_code .name == "PERMISSION_DENIED"
454+ # validate counts
455+ assert len (handler .completed_operations ) == 1
456+ assert len (handler .completed_attempts ) == 1
457+ assert len (handler .cancelled_operations ) == 0
458+ # validate operation
459+ operation = handler .completed_operations [0 ]
460+ assert isinstance (operation , CompletedOperationMetric )
461+ assert operation .final_status .name == "PERMISSION_DENIED"
462+ assert operation .op_type .value == "ReadRows"
463+ assert operation .is_streaming is True
464+ assert len (operation .completed_attempts ) == 1
465+ assert operation .cluster_id == next (iter (cluster_config .keys ()))
466+ assert operation .zone == cluster_config [operation .cluster_id ].location .split ("/" )[- 1 ]
467+ # validate attempt
468+ attempt = handler .completed_attempts [0 ]
469+ assert isinstance (attempt , CompletedAttemptMetric )
470+ assert attempt .end_status .name == "PERMISSION_DENIED"
471+ assert attempt .gfe_latency_ns >= 0 and attempt .gfe_latency_ns < operation .duration_ns
320472
321473 @CrossSync .pytest
322474 async def test_read_rows_stream_failure_mid_stream (
@@ -325,7 +477,30 @@ async def test_read_rows_stream_failure_mid_stream(
325477 """
326478 Test failure in grpc stream
327479 """
328- pass
480+ await temp_rows .add_row (b"row_key_1" )
481+ handler .clear ()
482+ error_injector .fail_mid_stream = True
483+ error_injector .push (Aborted ("retryable" ))
484+ error_injector .push (PermissionDenied ("terminal" ))
485+ generator = await table .read_rows_stream (ReadRowsQuery (), retryable_errors = [Aborted ])
486+ with pytest .raises (PermissionDenied ):
487+ [_ async for _ in generator ]
488+ # validate counts
489+ assert len (handler .completed_operations ) == 1
490+ assert len (handler .completed_attempts ) == 2
491+ assert len (handler .cancelled_operations ) == 0
492+ # validate operation
493+ operation = handler .completed_operations [0 ]
494+ assert operation .final_status .name == "PERMISSION_DENIED"
495+ assert operation .op_type .value == "ReadRows"
496+ assert operation .is_streaming is True
497+ assert len (operation .completed_attempts ) == 2
498+ # validate retried attempt
499+ attempt = handler .completed_attempts [0 ]
500+ assert attempt .end_status .name == "ABORTED"
501+ # validate final attempt
502+ final_attempt = handler .completed_attempts [- 1 ]
503+ assert final_attempt .end_status .name == "PERMISSION_DENIED"
329504
330505 @CrossSync .pytest
331506 async def test_read_row (self , table , temp_rows , handler , cluster_config ):
0 commit comments