@@ -203,14 +203,10 @@ async def test_resource(get_all_metrics, instance_id, table_id, project_id):
203203 assert resource .labels ["instance" ] == instance_id
204204 assert resource .labels ["table" ] == table_id
205205 assert resource .labels ["project_id" ] == project_id
206- if 'success' in m .metric .labels ["app_profile" ]:
207- # for attempts that succeeded, zone and cluster should be populated
208- assert resource .labels ["zone" ] == TEST_ZONE
209- assert resource .labels ["cluster" ] == TEST_CLUSTER
210- else :
211- # others should fall back to defaults
212- assert resource .labels ["zone" ] == "global"
213- assert resource .labels ["cluster" ] == "unspecified"
206+ # zone and cluster should use default values for failed attempts
207+ assert resource .labels ["zone" ] in [TEST_ZONE , 'global' ]
208+ assert resource .labels ["cluster" ] in [TEST_CLUSTER , 'unspecified' ]
209+
214210
215211@pytest .mark .asyncio
216212async def test_client_name (get_all_metrics ):
@@ -378,7 +374,7 @@ async def test_status_exception(get_all_metrics):
378374 """
379375 check the subset of rpcs with a single terminal exception
380376
381- They should have no retries, 1 connectivity errors, a status of NOT_FOUND
377+ They should have no retries, 1+ connectivity errors, a status of NOT_FOUND
382378 Should have default values for cluster and zone
383379 """
384380 fail_metrics = [m for m in get_all_metrics if m .metric .labels ["app_profile" ] == "terminal_exception" ]
@@ -399,12 +395,16 @@ async def test_status_exception(get_all_metrics):
399395 # check for cluster and zone
400396 assert m .resource .labels ["zone" ] == 'global'
401397 assert m .resource .labels ["cluster" ] == 'unspecified'
402- # ensure connectivity error count is 1
398+ # each rpc should have at least one connectivity error
399+ # ReadRows will have more, since we test point reads and streams
403400 connectivity_error_counts = [m for m in fail_metrics if "connectivity_error_count" in m .metric .type ]
404- assert len (connectivity_error_counts ) > 0
405- for m in connectivity_error_counts :
406- for pt in m .points :
407- assert pt .value .int64_value == 1
401+ for error_metric in connectivity_error_counts :
402+ total_points = sum ([int (pt .value .int64_value ) for pt in error_metric .points ])
403+ assert total_points >= 1
404+ # ensure each rpc reported connectivity errors
405+ for prc in OperationType :
406+ assert any (m .metric .labels ["method" ] == prc .value for m in connectivity_error_counts )
407+
408408
409409@pytest .mark .asyncio
410410@pytest .mark .parametrize ("app_profile,final_status" , [
@@ -435,22 +435,33 @@ async def test_status_retry(get_all_metrics, app_profile, final_status):
435435 server_latencies = [m for m in retry_metrics if "server_latencies" in m .metric .type ] # may not be present. only if reached server
436436 first_response_latencies = [m for m in retry_metrics if "first_response_latencies" in m .metric .type ] # may not be present
437437
438- # each retry_count and connectivity_error_count should be 2
439- for m in retry_counts + connectivity_error_counts :
440- for pt in m .points :
441- assert pt .value .int64_value == 2
438+ # should have at least 2 retry attempts
439+ # ReadRows will have more, because it is called multiple times in the test data
440+ for m in retry_counts :
441+ total_errors = sum ([int (pt .value .int64_value ) for pt in m .points ])
442+ assert total_errors >= 2 , f"{ m } has { total_errors } errors"
443+ # each rpc should have at least one connectivity error
444+ # most will have 2, but will have 1 if status == NOT_FOUND
445+ for m in connectivity_error_counts :
446+ total_errors = sum ([int (pt .value .int64_value ) for pt in m .points ])
447+ assert total_errors >= 1 , f"{ m } has { total_errors } errors"
442448
443449 # all operation-level status should be final_status
444450 for m in operation_latencies + retry_counts :
445451 assert m .metric .labels ["status" ] == final_status
446- # all attempt-level status should have a 2:1 mix of final_status and UNAVAILABLE
447- status_map = {}
448- for m in attempt_latencies + server_latencies + first_response_latencies + connectivity_error_counts :
449- status_map [m .metric .labels ["status" ]] = status_map .get (m .metric .labels ["status" ], 0 ) + 1
450- assert len (status_map ) == 2
451- assert final_status in status_map
452- assert "UNAVAILABLE" in status_map
453- assert len (status_map [final_status ]) * 2 == len (status_map ["UNAVAILABLE" ])
452+
453+ # check attempt statuses
454+ attempt_statuses = set ([m .metric .labels ["status" ] for m in attempt_latencies + server_latencies + first_response_latencies + connectivity_error_counts ])
455+ if final_status == "DEADLINE_EXCEEDED" :
456+ # operation DEADLINE_EXCEEDED never shows up in attempts
457+ assert len (attempt_statuses ) == 1
458+ assert "UNAVAILABLE" in attempt_statuses
459+ else :
460+ # all other attempt-level status should have a mix of final_status and UNAVAILABLE
461+ assert len (attempt_statuses ) == 2
462+ assert "UNAVAILABLE" in attempt_statuses
463+ assert final_status in attempt_statuses
464+
454465
455466@pytest .mark .asyncio
456467async def test_latency_metric_histogram_buckets (get_all_metrics ):
0 commit comments