From e8ad0b7b2eb1bc816843d748c0e4dbe01688c6f9 Mon Sep 17 00:00:00 2001 From: roost-io <8110509+mgdevstack@users.noreply.github.com> Date: Wed, 29 Apr 2026 11:01:56 +0530 Subject: [PATCH] Unit test generated by RoostGPT Using AI Model gemini-3-flash-preview --- .roost/runreport.json | 123 ++++++++++++++++++ __init__.py | 0 adk/__init__.py | 0 adk/a2a/__init__.py | 0 adk/a2a/converters/__init__.py | 0 .../converters/event_converter/__init__.py | 0 .../test_convert_a2a_task_to_event.py | 123 ++++++++++++++++++ conftest.py | 27 ++++ pyproject.toml | 2 + 9 files changed, 275 insertions(+) create mode 100644 .roost/runreport.json create mode 100644 __init__.py create mode 100644 adk/__init__.py create mode 100644 adk/a2a/__init__.py create mode 100644 adk/a2a/converters/__init__.py create mode 100644 adk/a2a/converters/event_converter/__init__.py create mode 100644 adk/a2a/converters/event_converter/test_convert_a2a_task_to_event.py create mode 100644 conftest.py diff --git a/.roost/runreport.json b/.roost/runreport.json new file mode 100644 index 0000000000..d7246e0234 --- /dev/null +++ b/.roost/runreport.json @@ -0,0 +1,123 @@ +{ + "run_id": "23475726", + "started_at": "2026-04-29T05:28:56.971093Z", + "ended_at": "2026-04-29T05:31:53.965484Z", + "status": "ok", + "config": { + "repo": "/private/var/tmp/Roost/RoostGPT/unit-adk-python/1777440475/source/adk-python", + "out": "/private/var/tmp/Roost/RoostGPT/unit-adk-python/1777440475/source/adk-python", + "workers": 5, + "scenarios_from": "planner", + "provider": "env:AI_TYPE", + "model": "gemini/gemini-3-flash-preview", + "jedi": true + }, + "summary": { + "targets_total": 1, + "targets_passed": 1, + "targets_failed": 0, + "targets_partial": 0, + "targets_skipped": 0, + "scenarios_total": 6, + "scenarios_passed": 6, + "scenarios_failed": 0, + "coverage_weighted": null, + "llm_cost_usd": 0.1788, + "llm_input_tokens": 740513, + "llm_output_tokens": 17984, + "llm_cache_read_input_tokens": 545423, + "llm_cache_creation_input_tokens": 0, + "llm_api_calls": 26, + "elapsed_ms": 0 + }, + "targets": [ + { + "target_symbol_id": "src.google.adk.a2a.converters.event_converter.convert_a2a_task_to_event", + "symbol_kind": "function", + "source_file": "src/google/adk/a2a/converters/event_converter.py", + "source_range": [ + 201, + 265 + ], + "test_file": "/private/var/tmp/Roost/RoostGPT/unit-adk-python/1777440475/source/adk-python/adk/a2a/converters/event_converter/test_convert_a2a_task_to_event.py", + "test_file_hash": "sha256:fccdd611f3cc1af8ad4d5246a61324549a852d102931ad92ab5b903c383a0a12", + "test_status": "passed", + "iterations": 26, + "pivots": 0, + "scenarios": [ + { + "scenario_id": "src.google.adk.a2a.converters.event_converter.convert_a2a_task_to_event#0", + "scenario_name": "happy_path_extract_from_artifacts", + "passed": true, + "duration_ms": null, + "failure_reason": null, + "stderr_excerpt": null + }, + { + "scenario_id": "src.google.adk.a2a.converters.event_converter.convert_a2a_task_to_event#1", + "scenario_name": "happy_path_extract_from_history", + "passed": true, + "duration_ms": null, + "failure_reason": null, + "stderr_excerpt": null + }, + { + "scenario_id": "src.google.adk.a2a.converters.event_converter.convert_a2a_task_to_event#2", + "scenario_name": "happy_path_no_message_minimal_event", + "passed": true, + "duration_ms": null, + "failure_reason": null, + "stderr_excerpt": null + }, + { + "scenario_id": "src.google.adk.a2a.converters.event_converter.convert_a2a_task_to_event#3", + "scenario_name": "edge_case_with_context", + "passed": true, + "duration_ms": null, + "failure_reason": null, + "stderr_excerpt": null + }, + { + "scenario_id": "src.google.adk.a2a.converters.event_converter.convert_a2a_task_to_event#4", + "scenario_name": "error_path_none_input", + "passed": true, + "duration_ms": null, + "failure_reason": null, + "stderr_excerpt": null + }, + { + "scenario_id": "src.google.adk.a2a.converters.event_converter.convert_a2a_task_to_event#5", + "scenario_name": "error_path_conversion_exception", + "passed": true, + "duration_ms": null, + "failure_reason": null, + "stderr_excerpt": null + } + ], + "coverage_pct": null, + "writer_cost_usd": 0.17876814999999996, + "writer_input_tokens": 740513, + "writer_output_tokens": 17984, + "writer_cache_read_input_tokens": 545423, + "writer_cache_creation_input_tokens": 0, + "diagnostics": [] + } + ], + "files_written": [ + { + "path": "adk/a2a/converters/event_converter/test_convert_a2a_task_to_event.py", + "hash": "sha256:fccdd611f3cc1af8ad4d5246a61324549a852d102931ad92ab5b903c383a0a12", + "status": "created" + } + ], + "pr_ready": { + "branch_suggestion": "roost/python-tests-20260429-0531", + "commit_message": "roost-pytest: add 1 test file(s)", + "pr_title": "Auto-generated Python unit tests (1 file(s))", + "pr_body_markdown": "## Summary\n- 1 targets with fully green tests\n\n## Generated test files\n- `adk/a2a/converters/event_converter/test_convert_a2a_task_to_event.py`", + "changed_files": [ + "adk/a2a/converters/event_converter/test_convert_a2a_task_to_event.py" + ] + }, + "diagnostics": [] +} \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/adk/__init__.py b/adk/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/adk/a2a/__init__.py b/adk/a2a/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/adk/a2a/converters/__init__.py b/adk/a2a/converters/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/adk/a2a/converters/event_converter/__init__.py b/adk/a2a/converters/event_converter/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/adk/a2a/converters/event_converter/test_convert_a2a_task_to_event.py b/adk/a2a/converters/event_converter/test_convert_a2a_task_to_event.py new file mode 100644 index 0000000000..50e24de433 --- /dev/null +++ b/adk/a2a/converters/event_converter/test_convert_a2a_task_to_event.py @@ -0,0 +1,123 @@ +# ROOST_METHOD_HASH=convert_a2a_task_to_event_40cb3ce258 +# ROOST_METHOD_SIG_HASH=convert_a2a_task_to_event_124ed678ea + +"""Tests for `src.google.adk.a2a.converters.event_converter.convert_a2a_task_to_event`. + +Auto-generated by roost-pytest. Re-generate when the source +changes; see the run report for generation details. +""" +# Source: src/google/adk/a2a/converters/event_converter.py:201-265 +# Scenarios: +# - [happy_path] happy_path_extract_from_artifacts: Verify that the function extracts the message from the last artifact when artifacts are present. +# - [happy_path] happy_path_extract_from_history: Verify that the function falls back to history when artifacts and status message are missing. +# - [happy_path] happy_path_no_message_minimal_event: Verify that a minimal Event is returned when no message content can be found in the task. +# - [edge_case] edge_case_with_context: Verify that invocation_id and branch are correctly propagated from the InvocationContext. +# - [error_path] error_path_none_input: Verify that a ValueError is raised if the input task is None. +# - [error_path] error_path_conversion_exception: Verify that a RuntimeError is raised if the underlying message conversion fails. + +import pytest +from unittest.mock import MagicMock, patch +from a2a.types import Task, Message, Role, TextPart, Part as A2APart +from google.adk.agents.invocation_context import InvocationContext +from google.adk.events.event import Event +from google.adk.a2a.converters.event_converter import convert_a2a_task_to_event +from google.adk.a2a.converters.part_converter import convert_a2a_part_to_genai_part + +@pytest.mark.generated +@pytest.mark.happy_path +def test_happy_path_extract_from_artifacts(): + """Verify that the function extracts the message from the last artifact when artifacts are present.""" + mock_task = MagicMock(spec=Task) + mock_artifact = MagicMock() + # Use TextPart here; Message will wrap it in A2APart + mock_artifact.parts = [TextPart(text='part1')] + mock_task.artifacts = [mock_artifact] + mock_task.status = None + mock_task.history = [] + + with patch('google.adk.a2a.converters.event_converter.convert_a2a_message_to_event') as mock_conv: + mock_conv.return_value = Event(invocation_id='123', author='a2a agent') + result = convert_a2a_task_to_event(mock_task) + + mock_conv.assert_called_once() + call_args = mock_conv.call_args[0] + assert isinstance(call_args[0], Message) + # Message.parts will contain A2APart objects wrapping the TextPart + assert call_args[0].parts == [A2APart(root=TextPart(text='part1'))] + assert call_args[0].role == Role.agent + assert result == mock_conv.return_value + +@pytest.mark.generated +@pytest.mark.happy_path +def test_happy_path_extract_from_history(): + """Verify that the function falls back to history when artifacts and status message are missing.""" + mock_task = MagicMock(spec=Task) + mock_task.artifacts = [] + mock_task.status = None + hist_message = Message(message_id='m1', role=Role.agent, parts=[TextPart(text='hist_part')]) + mock_task.history = [hist_message] + + with patch('google.adk.a2a.converters.event_converter.convert_a2a_message_to_event') as mock_conv: + mock_conv.return_value = Event(invocation_id='123', author='a2a agent') + result = convert_a2a_task_to_event(mock_task) + + mock_conv.assert_called_once_with( + hist_message, None, None, part_converter=convert_a2a_part_to_genai_part + ) + assert result == mock_conv.return_value + +@pytest.mark.generated +@pytest.mark.happy_path +def test_happy_path_no_message_minimal_event(): + """Verify that a minimal Event is returned when no message content can be found in the task.""" + mock_task = MagicMock(spec=Task) + mock_task.artifacts = [] + mock_task.status = None + mock_task.history = [] + + result = convert_a2a_task_to_event(mock_task, author='custom-author') + + assert isinstance(result, Event) + assert result.author == 'custom-author' + assert result.content is None + assert len(result.invocation_id) > 0 + +@pytest.mark.generated +@pytest.mark.edge_case +def test_edge_case_with_context(): + """Verify that invocation_id and branch are correctly propagated from the InvocationContext.""" + mock_task = MagicMock(spec=Task) + mock_task.artifacts = [] + mock_task.status = None + mock_task.history = [] + ctx = MagicMock(spec=InvocationContext) + ctx.invocation_id = 'inv-456' + ctx.branch = 'main.sub' + + result = convert_a2a_task_to_event(mock_task, invocation_context=ctx) + + assert result.invocation_id == 'inv-456' + assert result.branch == 'main.sub' + +@pytest.mark.generated +@pytest.mark.error_path +def test_error_path_none_input(): + """Verify that a ValueError is raised if the input task is None.""" + with pytest.raises(ValueError, match="A2A task cannot be None"): + convert_a2a_task_to_event(None) + +@pytest.mark.generated +@pytest.mark.error_path +def test_error_path_conversion_exception(): + """Verify that a RuntimeError is raised if the underlying message conversion fails.""" + mock_task = MagicMock(spec=Task) + mock_artifact = MagicMock() + mock_artifact.parts = [TextPart(text='p')] + mock_task.artifacts = [mock_artifact] + mock_task.status = None + mock_task.history = [] + + with patch('google.adk.a2a.converters.event_converter.convert_a2a_message_to_event', + side_effect=Exception('inner error')): + with pytest.raises(RuntimeError, match="Failed to convert task message: inner error"): + convert_a2a_task_to_event(mock_task) diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000000..5964a6943d --- /dev/null +++ b/conftest.py @@ -0,0 +1,27 @@ +"""roost-pytest test-suite configuration. + +This file is auto-generated. It registers the custom markers used by +generated tests so pytest doesn't warn ``PytestUnknownMarkWarning``. +Add your own fixtures/hooks below the marker block as you would in +any pytest project. +""" + +from __future__ import annotations + +import pytest + + +_GENERATED_MARKERS = { + 'generated': 'test was auto-generated by roost-pytest', + 'happy_path': 'exercises the primary success path', + 'edge_case': 'exercises an edge / boundary condition', + 'error_path': 'exercises an exception or failure path', + 'security': 'exercises a security-sensitive behaviour', + 'property': 'property / invariant test (often parametrised)', + 'regression': 'guards against a previously fixed bug', +} + + +def pytest_configure(config: pytest.Config) -> None: + for name, description in _GENERATED_MARKERS.items(): + config.addinivalue_line("markers", f"{name}: {description}") diff --git a/pyproject.toml b/pyproject.toml index 69ba9984e6..3ac9692460 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -136,6 +136,8 @@ test = [ "rouge-score>=0.1.2", "tabulate>=0.9.0", # go/keep-sorted end + "a2a", + "google" ] docs = [