Skip to content

Commit 74ebf45

Browse files
aditik0303claude
andcommitted
test(openai-agents): cover swallow/delegate/extraction branches (Sonar coverage)
New-code coverage was ~89% (just under Sonar's 90% gate). Added tests for the uncovered defensive branches: non-block-error swallow on every hook, the pass-through boundary hooks (on_start/on_end/on_handoff) + _delegate error path, and the text-extraction edges (_stringify circular fallback, output-only item, object .text content, no-output response, single input). governance/hooks.py: 83% -> 94.5%. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 99fcc15 commit 74ebf45

1 file changed

Lines changed: 86 additions & 0 deletions

File tree

packages/uipath-openai-agents/tests/governance/test_hooks.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
from uipath_openai_agents.governance.hooks import (
2626
_BEFORE_MODEL_TEXT_CAP,
2727
GovernanceAgentHooks,
28+
_content_text,
29+
_item_text,
30+
_latest_input_text,
31+
_model_response_text,
32+
_stringify,
2833
install_governance,
2934
)
3035

@@ -395,6 +400,87 @@ async def test_hooks_return_none():
395400
assert await cb.on_tool_end(None, FakeAgent(), FakeTool("t"), {}) is None # type: ignore[func-returns-value]
396401

397402

403+
# --------------------------------------------------------------------------
404+
# coverage: swallow paths on every hook, boundary delegation, extraction edges
405+
# --------------------------------------------------------------------------
406+
407+
408+
class _Boom:
409+
"""Evaluator whose every evaluate_* raises a non-block error."""
410+
411+
def __getattr__(self, _name: str) -> Any:
412+
def _raise(*_a: Any, **_k: Any) -> None:
413+
raise RuntimeError("evaluator bug")
414+
415+
return _raise
416+
417+
418+
@pytest.mark.parametrize(
419+
"invoke",
420+
[
421+
lambda cb: cb.on_llm_end(None, FakeAgent(), SimpleNamespace(output=[])),
422+
lambda cb: cb.on_tool_start(None, FakeAgent(), FakeTool("t")),
423+
lambda cb: cb.on_tool_end(None, FakeAgent(), FakeTool("t"), {"r": 1}),
424+
],
425+
)
426+
async def test_model_and_tool_hooks_swallow_non_block_errors(invoke, caplog):
427+
cb = GovernanceAgentHooks(evaluator=_Boom(), agent_name="a", session_id="s") # type: ignore[arg-type]
428+
with _capture_hooks_logs(caplog):
429+
await invoke(cb) # must NOT raise — a governance bug can't break the run
430+
assert any("governance check failed" in r.message for r in caplog.records)
431+
432+
433+
class _InnerBoundary:
434+
def __init__(self) -> None:
435+
self.seen: List[str] = []
436+
437+
async def on_start(self, *_a: Any) -> None:
438+
self.seen.append("on_start")
439+
440+
async def on_end(self, *_a: Any) -> None:
441+
self.seen.append("on_end")
442+
443+
async def on_handoff(self, *_a: Any) -> None:
444+
self.seen.append("on_handoff")
445+
446+
447+
async def test_boundary_hooks_delegate_to_inner():
448+
inner = _InnerBoundary()
449+
cb = _make_hooks(FakeEvaluator(), inner=inner)
450+
await cb.on_start(None, FakeAgent())
451+
await cb.on_end(None, FakeAgent(), "out")
452+
await cb.on_handoff(None, FakeAgent(), FakeAgent())
453+
assert inner.seen == ["on_start", "on_end", "on_handoff"]
454+
455+
456+
async def test_delegate_swallows_inner_hook_error(caplog):
457+
class _BadInner:
458+
async def on_llm_start(self, *_a: Any) -> None:
459+
raise RuntimeError("inner boom")
460+
461+
cb = _make_hooks(FakeEvaluator(), inner=_BadInner())
462+
with _capture_hooks_logs(caplog):
463+
await cb.on_llm_start(None, FakeAgent(), None, [_msg("x")]) # must not raise
464+
assert any("chained user hook" in r.message for r in caplog.records)
465+
466+
467+
def test_extraction_edges():
468+
# _stringify: str passthrough; circular ref → str() fallback (not a crash)
469+
assert _stringify("hi") == "hi"
470+
circular: dict[str, Any] = {}
471+
circular["self"] = circular
472+
assert isinstance(_stringify(circular), str)
473+
# _item_text: tool-result output-only item
474+
assert "42" in _item_text({"output": {"balance": 42}})
475+
# _content_text: object exposing .text, and a bare-string part in a list
476+
assert _content_text(SimpleNamespace(text="hello")) == "hello"
477+
assert "raw" in _content_text(["raw", {"text": "block"}])
478+
# _model_response_text: response with no .output → falls back to item text
479+
assert _model_response_text(SimpleNamespace(content="direct")) == "direct"
480+
# _latest_input_text: single (non-list) item
481+
assert _latest_input_text(_msg("solo")) == "solo"
482+
483+
398484
# --------------------------------------------------------------------------
399485
# Factory wiring — the evaluator kwarg drives install_governance
400486
# --------------------------------------------------------------------------

0 commit comments

Comments
 (0)