Skip to content

Commit ce478ce

Browse files
authored
Merge branch 'main' into feat/app-name-override
2 parents 3c7e09b + 8addc44 commit ce478ce

1 file changed

Lines changed: 57 additions & 53 deletions

File tree

tests/unittests/plugins/test_bigquery_agent_analytics_plugin.py

Lines changed: 57 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2226,7 +2226,7 @@ async def test_on_agent_error_callback_logs_correctly(
22262226
callback_context=callback_context,
22272227
error=error,
22282228
)
2229-
await asyncio.sleep(0.05)
2229+
await bq_plugin_inst.flush()
22302230
rows = await _get_captured_rows_async(mock_write_client, dummy_arrow_schema)
22312231
log_entry = next(r for r in rows if r["event_type"] == "AGENT_ERROR")
22322232
assert log_entry["error_message"] == "Agent crashed"
@@ -2267,7 +2267,7 @@ async def test_on_agent_error_does_not_pop_foreign_invocation_span(
22672267
callback_context=callback_context,
22682268
error=error,
22692269
)
2270-
await asyncio.sleep(0.05)
2270+
await bq_plugin_inst.flush()
22712271

22722272
# The invocation root was NOT consumed by the agent-error pop.
22732273
assert trace_manager.get_current_span_id() == inv_span_id
@@ -2297,7 +2297,7 @@ async def test_on_run_error_callback_logs_correctly(
22972297
invocation_context=invocation_context,
22982298
error=error,
22992299
)
2300-
await asyncio.sleep(0.05)
2300+
await bq_plugin_inst.flush()
23012301
rows = await _get_captured_rows_async(mock_write_client, dummy_arrow_schema)
23022302
log_entry = next(r for r in rows if r["event_type"] == "INVOCATION_ERROR")
23032303
assert log_entry["error_message"] == "Invocation failed"
@@ -2376,7 +2376,7 @@ async def test_traceback_not_truncated_with_negative_max_len(
23762376
),
23772377
error=error,
23782378
)
2379-
await asyncio.sleep(0.05)
2379+
await plugin.flush()
23802380
rows = await _get_captured_rows_async(
23812381
mock_write_client, dummy_arrow_schema
23822382
)
@@ -5698,7 +5698,7 @@ async def test_hitl_confirmation_emits_additional_event(
56985698
await bq_plugin_inst.on_event_callback(
56995699
invocation_context=invocation_context, event=event
57005700
)
5701-
await asyncio.sleep(0.05)
5701+
await bq_plugin_inst.flush()
57025702
rows = await _get_captured_rows_async(mock_write_client, dummy_arrow_schema)
57035703
event_types = [r["event_type"] for r in rows]
57045704
assert "HITL_CONFIRMATION_REQUEST" in event_types
@@ -5715,7 +5715,7 @@ async def test_hitl_credential_emits_additional_event(
57155715
await bq_plugin_inst.on_event_callback(
57165716
invocation_context=invocation_context, event=event
57175717
)
5718-
await asyncio.sleep(0.05)
5718+
await bq_plugin_inst.flush()
57195719
rows = await _get_captured_rows_async(mock_write_client, dummy_arrow_schema)
57205720
event_types = [r["event_type"] for r in rows]
57215721
assert "HITL_CREDENTIAL_REQUEST" in event_types
@@ -5732,7 +5732,7 @@ async def test_hitl_completion_emits_additional_event(
57325732
await bq_plugin_inst.on_event_callback(
57335733
invocation_context=invocation_context, event=event
57345734
)
5735-
await asyncio.sleep(0.05)
5735+
await bq_plugin_inst.flush()
57365736
rows = await _get_captured_rows_async(mock_write_client, dummy_arrow_schema)
57375737
event_types = [r["event_type"] for r in rows]
57385738
assert "HITL_CONFIRMATION_REQUEST_COMPLETED" in event_types
@@ -5749,7 +5749,7 @@ async def test_regular_tool_no_hitl_event(
57495749
await bq_plugin_inst.on_event_callback(
57505750
invocation_context=invocation_context, event=event
57515751
)
5752-
await asyncio.sleep(0.05)
5752+
await bq_plugin_inst.flush()
57535753
# No HITL events should be emitted for non-HITL function calls.
57545754
# on_event_callback only logs STATE_DELTA and HITL events; a regular
57555755
# function call produces neither.
@@ -7903,7 +7903,7 @@ async def test_cache_metadata_logged_when_present(
79037903
callback_context=callback_context,
79047904
llm_response=llm_response,
79057905
)
7906-
await asyncio.sleep(0.05)
7906+
await bq_plugin_inst.flush()
79077907
rows = await _get_captured_rows_async(mock_write_client, dummy_arrow_schema)
79087908
log_entry = next(r for r in rows if r["event_type"] == "LLM_RESPONSE")
79097909

@@ -7938,7 +7938,7 @@ def __init__(self):
79387938
callback_context=callback_context,
79397939
llm_response=mock_response,
79407940
)
7941-
await asyncio.sleep(0.05)
7941+
await bq_plugin_inst.flush()
79427942
rows = await _get_captured_rows_async(mock_write_client, dummy_arrow_schema)
79437943
log_entry = next(r for r in rows if r["event_type"] == "LLM_RESPONSE")
79447944

@@ -7986,7 +7986,7 @@ async def test_a2a_interaction_logged_for_response_metadata(
79867986
)
79877987
assert result is None
79887988

7989-
await asyncio.sleep(0.05)
7989+
await bq_plugin_inst.flush()
79907990
rows = await _get_captured_rows_async(mock_write_client, dummy_arrow_schema)
79917991
event_types = [r["event_type"] for r in rows]
79927992
assert "A2A_INTERACTION" in event_types
@@ -8025,7 +8025,7 @@ async def test_a2a_interaction_logged_for_request_metadata(
80258025
)
80268026
assert result is None
80278027

8028-
await asyncio.sleep(0.05)
8028+
await bq_plugin_inst.flush()
80298029
rows = await _get_captured_rows_async(mock_write_client, dummy_arrow_schema)
80308030
event_types = [r["event_type"] for r in rows]
80318031
assert "A2A_INTERACTION" in event_types
@@ -8058,7 +8058,7 @@ async def test_no_a2a_interaction_for_irrelevant_metadata(
80588058
)
80598059
assert result is None
80608060

8061-
await asyncio.sleep(0.05)
8061+
await bq_plugin_inst.flush()
80628062
# No events logged — a2a:task_id alone is not a meaningful
80638063
# interaction payload.
80648064
assert mock_write_client.append_rows.call_count == 0
@@ -8078,7 +8078,7 @@ async def test_no_a2a_interaction_for_no_metadata(
80788078
)
80798079
assert result is None
80808080

8081-
await asyncio.sleep(0.05)
8081+
await bq_plugin_inst.flush()
80828082
assert mock_write_client.append_rows.call_count == 0
80838083

80848084

@@ -8392,7 +8392,7 @@ async def test_logs_final_text_response(
83928392
await bq_plugin_inst.on_event_callback(
83938393
invocation_context=invocation_context, event=event
83948394
)
8395-
await asyncio.sleep(0.05)
8395+
await bq_plugin_inst.flush()
83968396
rows = await _get_captured_rows_async(mock_write_client, dummy_arrow_schema)
83978397
agent_resp_rows = [r for r in rows if r["event_type"] == "AGENT_RESPONSE"]
83988398
assert len(agent_resp_rows) == 1
@@ -8420,7 +8420,7 @@ async def test_skips_function_call_events(
84208420
await bq_plugin_inst.on_event_callback(
84218421
invocation_context=invocation_context, event=event
84228422
)
8423-
await asyncio.sleep(0.05)
8423+
await bq_plugin_inst.flush()
84248424
assert mock_write_client.append_rows.call_count == 0
84258425

84268426
@pytest.mark.asyncio
@@ -8441,7 +8441,7 @@ async def test_skips_function_response_events(
84418441
await bq_plugin_inst.on_event_callback(
84428442
invocation_context=invocation_context, event=event
84438443
)
8444-
await asyncio.sleep(0.05)
8444+
await bq_plugin_inst.flush()
84458445
assert mock_write_client.append_rows.call_count == 0
84468446

84478447
@pytest.mark.asyncio
@@ -8461,7 +8461,7 @@ async def test_skips_partial_events(
84618461
await bq_plugin_inst.on_event_callback(
84628462
invocation_context=invocation_context, event=event
84638463
)
8464-
await asyncio.sleep(0.05)
8464+
await bq_plugin_inst.flush()
84658465
assert mock_write_client.append_rows.call_count == 0
84668466

84678467
@pytest.mark.asyncio
@@ -8488,7 +8488,7 @@ async def test_skips_long_running_tool_events(
84888488
await bq_plugin_inst.on_event_callback(
84898489
invocation_context=invocation_context, event=event
84908490
)
8491-
await asyncio.sleep(0.05)
8491+
await bq_plugin_inst.flush()
84928492
rows = await _get_captured_rows_async(mock_write_client, dummy_arrow_schema)
84938493
types_emitted = [r["event_type"] for r in rows]
84948494
assert "AGENT_RESPONSE" not in types_emitted
@@ -8513,7 +8513,7 @@ async def test_skips_thought_only_events(
85138513
await bq_plugin_inst.on_event_callback(
85148514
invocation_context=invocation_context, event=event
85158515
)
8516-
await asyncio.sleep(0.05)
8516+
await bq_plugin_inst.flush()
85178517
assert mock_write_client.append_rows.call_count == 0
85188518

85198519
@pytest.mark.asyncio
@@ -8539,7 +8539,7 @@ async def test_mixed_thought_and_visible_logs_only_visible(
85398539
await bq_plugin_inst.on_event_callback(
85408540
invocation_context=invocation_context, event=event
85418541
)
8542-
await asyncio.sleep(0.05)
8542+
await bq_plugin_inst.flush()
85438543
rows = await _get_captured_rows_async(mock_write_client, dummy_arrow_schema)
85448544
agent_resp_rows = [r for r in rows if r["event_type"] == "AGENT_RESPONSE"]
85458545
assert len(agent_resp_rows) == 1
@@ -8563,7 +8563,7 @@ async def test_skips_empty_part_events(
85638563
await bq_plugin_inst.on_event_callback(
85648564
invocation_context=invocation_context, event=event
85658565
)
8566-
await asyncio.sleep(0.05)
8566+
await bq_plugin_inst.flush()
85678567
assert mock_write_client.append_rows.call_count == 0
85688568

85698569
@pytest.mark.asyncio
@@ -8582,7 +8582,7 @@ async def test_skips_empty_text_events(
85828582
await bq_plugin_inst.on_event_callback(
85838583
invocation_context=invocation_context, event=event
85848584
)
8585-
await asyncio.sleep(0.05)
8585+
await bq_plugin_inst.flush()
85868586
assert mock_write_client.append_rows.call_count == 0
85878587

85888588
@pytest.mark.asyncio
@@ -8609,7 +8609,7 @@ async def test_skips_executable_code_only_events(
86098609
await bq_plugin_inst.on_event_callback(
86108610
invocation_context=invocation_context, event=event
86118611
)
8612-
await asyncio.sleep(0.05)
8612+
await bq_plugin_inst.flush()
86138613
assert mock_write_client.append_rows.call_count == 0
86148614

86158615

@@ -9913,7 +9913,7 @@ async def test_final_attributes_pass_redacts_direct_producers(
99139913
},
99149914
),
99159915
)
9916-
await asyncio.sleep(0.01)
9916+
await plugin.flush()
99179917
log_entry = await _get_captured_event_dict_async(
99189918
mock_write_client, dummy_arrow_schema
99199919
)
@@ -10460,20 +10460,21 @@ async def fake_lazy_setup(**kwargs):
1046010460

1046110461
def run_in_fresh_loop():
1046210462
try:
10463-
with mock.patch.object(
10464-
plugin, "_lazy_setup", side_effect=fake_lazy_setup
10465-
):
10466-
asyncio.run(plugin._ensure_started())
10463+
asyncio.run(plugin._ensure_started())
1046710464
except BaseException as e: # noqa: BLE001 - collecting for assertion
1046810465
errors.append(e)
1046910466

10470-
threads = [
10471-
platform_thread.create_thread(run_in_fresh_loop) for _ in range(2)
10472-
]
10473-
for t in threads:
10474-
t.start()
10475-
for t in threads:
10476-
t.join(timeout=10)
10467+
# Patch from this thread only. patch.object swaps a single shared
10468+
# attribute and is not itself thread safe, so entering it from both
10469+
# threads raced on _lazy_setup instead of on the code under test.
10470+
with mock.patch.object(plugin, "_lazy_setup", side_effect=fake_lazy_setup):
10471+
threads = [
10472+
platform_thread.create_thread(run_in_fresh_loop) for _ in range(2)
10473+
]
10474+
for t in threads:
10475+
t.start()
10476+
for t in threads:
10477+
t.join(timeout=10)
1047710478
assert not errors, f"cross-loop startup raised: {errors}"
1047810479

1047910480
def test_concurrent_stale_cleanup_folds_once(
@@ -10593,24 +10594,27 @@ async def slow_setup(**kwargs):
1059310594

1059410595
def run_in_fresh_loop():
1059510596
try:
10596-
with mock.patch.object(plugin, "_lazy_setup", side_effect=slow_setup):
10597-
barrier.wait(timeout=5)
10598-
asyncio.run(plugin._ensure_started())
10597+
barrier.wait(timeout=5)
10598+
asyncio.run(plugin._ensure_started())
1059910599
except BaseException as e: # noqa: BLE001
1060010600
errors.append(e)
1060110601

10602-
threads = [
10603-
platform_thread.create_thread(run_in_fresh_loop) for _ in range(2)
10604-
]
10605-
for t in threads:
10606-
t.start()
10607-
# Deterministic rendezvous: hold the owner inside setup until BOTH
10608-
# threads have entered _ensure_started.
10609-
entered.wait(timeout=5)
10610-
release.set()
10611-
for t in threads:
10612-
t.join(timeout=10)
10613-
assert not t.is_alive(), "thread failed to terminate"
10602+
# Patch from this thread only. patch.object swaps a single shared
10603+
# attribute and is not itself thread safe, so entering it from both
10604+
# threads raced on _lazy_setup instead of on the code under test.
10605+
with mock.patch.object(plugin, "_lazy_setup", side_effect=slow_setup):
10606+
threads = [
10607+
platform_thread.create_thread(run_in_fresh_loop) for _ in range(2)
10608+
]
10609+
for t in threads:
10610+
t.start()
10611+
# Deterministic rendezvous: hold the owner inside setup until BOTH
10612+
# threads have entered _ensure_started.
10613+
entered.wait(timeout=5)
10614+
release.set()
10615+
for t in threads:
10616+
t.join(timeout=10)
10617+
assert not t.is_alive(), "thread failed to terminate"
1061410618

1061510619
assert not errors, f"cross-loop startup raised: {errors}"
1061610620
assert len(setup_calls) == 1, f"shared setup ran {len(setup_calls)} times"
@@ -10697,7 +10701,7 @@ async def test_namedtuple_attribute_does_not_drop_row(
1069710701
extra_attributes={"point": Point(1, 2)},
1069810702
),
1069910703
)
10700-
await asyncio.sleep(0.01)
10704+
await plugin.flush()
1070110705
log_entry = await _get_captured_event_dict_async(
1070210706
mock_write_client, dummy_arrow_schema
1070310707
)
@@ -10786,7 +10790,7 @@ async def test_depth_capped_payload_flags_row_truncated(
1078610790
extra_attributes={"deep": deep},
1078710791
),
1078810792
)
10789-
await asyncio.sleep(0.01)
10793+
await plugin.flush()
1079010794
log_entry = await _get_captured_event_dict_async(
1079110795
mock_write_client, dummy_arrow_schema
1079210796
)

0 commit comments

Comments
 (0)