@@ -1083,7 +1083,43 @@ async def test_mutate_rows_batcher_failure_grpc(
10831083
10841084 No headers expected
10851085 """
1086- pass
1086+ from google .cloud .bigtable .data .mutations import RowMutationEntry , SetCell
1087+ from google .cloud .bigtable .data .exceptions import MutationsExceptionGroup
1088+
1089+ row_key = b"row_key_1"
1090+ mutation = SetCell (TEST_FAMILY , b"q" , b"v" )
1091+ entry = RowMutationEntry (row_key , [mutation ])
1092+ assert entry .is_idempotent ()
1093+
1094+ exc = Aborted ("injected" )
1095+ num_retryable = 2
1096+ for i in range (num_retryable ):
1097+ error_injector .push (exc )
1098+ error_injector .push (PermissionDenied ("terminal" ))
1099+ with pytest .raises (MutationsExceptionGroup ) as e :
1100+ async with table .mutations_batcher (batch_retryable_errors = [Aborted ]) as batcher :
1101+ await batcher .append (entry )
1102+ # validate counts
1103+ assert len (handler .completed_operations ) == 1
1104+ assert len (handler .completed_attempts ) == num_retryable + 1
1105+ assert len (handler .cancelled_operations ) == 1 # from batcher auto-closing
1106+ # validate operation
1107+ operation = handler .completed_operations [0 ]
1108+ assert isinstance (operation , CompletedOperationMetric )
1109+ assert operation .final_status .name == "PERMISSION_DENIED"
1110+ assert operation .op_type .value == "MutateRows"
1111+ assert operation .is_streaming is False
1112+ assert len (operation .completed_attempts ) == num_retryable + 1
1113+ assert operation .cluster_id == "unspecified"
1114+ assert operation .zone == "global"
1115+ # validate attempts
1116+ for i in range (num_retryable ):
1117+ attempt = handler .completed_attempts [i ]
1118+ assert attempt .end_status .name == "ABORTED"
1119+ assert attempt .gfe_latency_ns is None
1120+ final_attempt = handler .completed_attempts [num_retryable ]
1121+ assert final_attempt .end_status .name == "PERMISSION_DENIED"
1122+ assert final_attempt .gfe_latency_ns is None
10871123
10881124 @CrossSync .pytest
10891125 async def test_mutate_rows_batcher_failure_timeout (
@@ -1094,7 +1130,33 @@ async def test_mutate_rows_batcher_failure_timeout(
10941130
10951131 No grpc headers expected
10961132 """
1097- pass
1133+ from google .cloud .bigtable .data .mutations import RowMutationEntry , SetCell
1134+ from google .cloud .bigtable .data .exceptions import MutationsExceptionGroup
1135+ from google .api_core .exceptions import DeadlineExceeded
1136+
1137+ row_key = b"row_key_1"
1138+ mutation = SetCell (TEST_FAMILY , b"q" , b"v" )
1139+ entry = RowMutationEntry (row_key , [mutation ])
1140+
1141+ with pytest .raises (MutationsExceptionGroup ) as e :
1142+ async with table .mutations_batcher (batch_operation_timeout = 0.001 ) as batcher :
1143+ await batcher .append (entry )
1144+ # validate counts
1145+ assert len (handler .completed_operations ) == 1
1146+ assert len (handler .completed_attempts ) == 1
1147+ assert len (handler .cancelled_operations ) == 1 # from batcher auto-closing
1148+ # validate operation
1149+ operation = handler .completed_operations [0 ]
1150+ assert operation .final_status .name == "DEADLINE_EXCEEDED"
1151+ assert operation .op_type .value == "MutateRows"
1152+ assert operation .is_streaming is False
1153+ assert len (operation .completed_attempts ) == 1
1154+ assert operation .cluster_id == "unspecified"
1155+ assert operation .zone == "global"
1156+ # validate attempt
1157+ attempt = handler .completed_attempts [0 ]
1158+ assert attempt .end_status .name == "DEADLINE_EXCEEDED"
1159+ assert attempt .gfe_latency_ns is None
10981160
10991161 @CrossSync .pytest
11001162 async def test_mutate_rows_batcher_failure_unauthorized (
@@ -1103,7 +1165,43 @@ async def test_mutate_rows_batcher_failure_unauthorized(
11031165 """
11041166 Test failure in backend by accessing an unauthorized family
11051167 """
1106- pass
1168+ from google .cloud .bigtable .data .mutations import RowMutationEntry , SetCell
1169+ from google .cloud .bigtable .data .exceptions import MutationsExceptionGroup
1170+
1171+ row_key = b"row_key_1"
1172+ mutation = SetCell ("unauthorized" , b"q" , b"v" )
1173+ entry = RowMutationEntry (row_key , [mutation ])
1174+
1175+ with pytest .raises (MutationsExceptionGroup ) as e :
1176+ async with authorized_view .mutations_batcher () as batcher :
1177+ await batcher .append (entry )
1178+ assert len (e .value .exceptions ) == 1
1179+ assert isinstance (e .value .exceptions [0 ].__cause__ , GoogleAPICallError )
1180+ assert (
1181+ e .value .exceptions [0 ].__cause__ .grpc_status_code .name == "PERMISSION_DENIED"
1182+ )
1183+ # validate counts
1184+ assert len (handler .completed_operations ) == 1
1185+ assert len (handler .completed_attempts ) == 1
1186+ assert len (handler .cancelled_operations ) == 1 # from batcher auto-closing
1187+ # validate operation
1188+ operation = handler .completed_operations [0 ]
1189+ assert operation .final_status .name == "PERMISSION_DENIED"
1190+ assert operation .op_type .value == "MutateRows"
1191+ assert operation .is_streaming is False
1192+ assert len (operation .completed_attempts ) == 1
1193+ assert operation .cluster_id == next (iter (cluster_config .keys ()))
1194+ assert (
1195+ operation .zone
1196+ == cluster_config [operation .cluster_id ].location .split ("/" )[- 1 ]
1197+ )
1198+ # validate attempt
1199+ attempt = handler .completed_attempts [0 ]
1200+ assert attempt .end_status .name == "PERMISSION_DENIED"
1201+ assert (
1202+ attempt .gfe_latency_ns >= 0
1203+ and attempt .gfe_latency_ns < operation .duration_ns
1204+ )
11071205
11081206 @CrossSync .pytest
11091207 async def test_mutate_row (self , table , temp_rows , handler , cluster_config ):
0 commit comments