Skip to content

Commit 178e7e1

Browse files
aditik0303claude
andcommitted
test(agent-framework): cover swallow/extraction/_coerce_args (Sonar coverage)
New-code coverage ~89% -> over the 90% gate. Added non-block swallow on every callback, _message_text/_response_text/_latest_message_text edges, and _coerce_args variants incl. the model_dump-failure warn branch. governance/middleware.py: 85% -> 97%. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 5e3c5c5 commit 178e7e1

1 file changed

Lines changed: 72 additions & 0 deletions

File tree

packages/uipath-agent-framework/tests/governance/test_middleware.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,3 +411,75 @@ def evaluate_before_model(self, **_: Any) -> None:
411411
_noop_next,
412412
)
413413
assert any("governance check failed" in r.message for r in caplog.records)
414+
415+
416+
# --------------------------------------------------------------------------
417+
# coverage: swallow on every callback + extraction / _coerce_args edges
418+
# --------------------------------------------------------------------------
419+
420+
421+
class _Boom:
422+
"""Evaluator whose every evaluate_* raises a non-block error."""
423+
424+
def __getattr__(self, _name: str) -> Any:
425+
def _raise(*_a: Any, **_k: Any) -> None:
426+
raise RuntimeError("evaluator bug")
427+
428+
return _raise
429+
430+
431+
@pytest.mark.parametrize(
432+
"invoke",
433+
[
434+
lambda cb: cb.before_model([_msg("x")]),
435+
lambda cb: cb.after_model(SimpleNamespace(text="y")),
436+
lambda cb: cb.before_tool(FakeTool("t"), {}),
437+
lambda cb: cb.after_tool(FakeTool("t"), {"r": 1}),
438+
],
439+
)
440+
def test_callbacks_swallow_non_block_errors(invoke, caplog):
441+
cb = GovernanceCallbacks(evaluator=_Boom(), agent_name="a", session_id="s")
442+
with caplog.at_level(logging.WARNING):
443+
invoke(cb) # must NOT raise — a governance bug can't break the run
444+
assert any("governance check failed" in r.message for r in caplog.records)
445+
446+
447+
def test_text_extraction_and_coerce_edges():
448+
from uipath_agent_framework.governance.middleware import _coerce_args, _stringify
449+
450+
M = GovernanceCallbacks
451+
# _latest_message_text: empty + single (non-list)
452+
assert M._latest_message_text([]) == ""
453+
assert M._latest_message_text(SimpleNamespace(text="solo")) == "solo"
454+
# _message_text: None / str / object-without-.text -> _stringify fallback
455+
assert M._message_text(None) == ""
456+
assert M._message_text("plain") == "plain"
457+
assert isinstance(M._message_text(SimpleNamespace()), str)
458+
# _response_text: None / .text / .messages[-1] / _stringify fallback
459+
assert M._response_text(None) == ""
460+
assert M._response_text(SimpleNamespace(text="via")) == "via"
461+
assert "m" in M._response_text(
462+
SimpleNamespace(text=None, messages=[SimpleNamespace(text="m")])
463+
)
464+
assert isinstance(M._response_text(SimpleNamespace(text=None, messages=None)), str)
465+
# _coerce_args: None / Mapping / model_dump / non-coercible
466+
assert _coerce_args(None) == {}
467+
assert _coerce_args({"a": 1}) == {"a": 1}
468+
assert _coerce_args(SimpleNamespace(model_dump=lambda: {"b": 2})) == {"b": 2}
469+
assert _coerce_args(object()) == {}
470+
# _stringify: str passthrough + circular-ref fallback (no crash)
471+
assert _stringify("hi") == "hi"
472+
circular: dict[str, Any] = {}
473+
circular["self"] = circular
474+
assert isinstance(_stringify(circular), str)
475+
476+
477+
def test_coerce_args_warns_on_model_dump_failure(caplog):
478+
from uipath_agent_framework.governance.middleware import _coerce_args
479+
480+
def _bad() -> dict[str, Any]:
481+
raise ValueError("boom")
482+
483+
with caplog.at_level(logging.WARNING):
484+
assert _coerce_args(SimpleNamespace(model_dump=_bad)) == {}
485+
assert any("could not coerce" in r.message for r in caplog.records)

0 commit comments

Comments
 (0)