Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions .roost/runreport.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
{
"run_id": "66bf2165",
"started_at": "2026-04-29T08:53:45.621073Z",
"ended_at": "2026-04-29T08:56:25.723592Z",
"status": "ok",
"config": {
"repo": "/private/var/tmp/Roost/RoostGPT/unit-adk-python/1777452750/source/adk-python",
"out": "/private/var/tmp/Roost/RoostGPT/unit-adk-python/1777452750/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": 7,
"scenarios_passed": 7,
"scenarios_failed": 0,
"coverage_weighted": null,
"llm_cost_usd": 0.1565,
"llm_input_tokens": 549347,
"llm_output_tokens": 17765,
"llm_cache_read_input_tokens": 380971,
"llm_cache_creation_input_tokens": 0,
"llm_api_calls": 18,
"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/1777452750/source/adk-python/adk/a2a/converters/event_converter/test_convert_a2a_task_to_event.py",
"test_file_hash": "sha256:158d67202970f17d0a2764c6b77c2cc4fed47a1041ea91d5808d8d25ef8ec0b1",
"test_status": "passed",
"iterations": 18,
"pivots": 0,
"scenarios": [
{
"scenario_id": "src.google.adk.a2a.converters.event_converter.convert_a2a_task_to_event#0",
"scenario_name": "happy_path_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_status_message",
"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_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#3",
"scenario_name": "edge_case_no_content",
"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": "with_invocation_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#5",
"scenario_name": "error_none_task",
"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#6",
"scenario_name": "error_conversion_failure",
"passed": true,
"duration_ms": null,
"failure_reason": null,
"stderr_excerpt": null
}
],
"coverage_pct": null,
"writer_cost_usd": 0.15653155,
"writer_input_tokens": 549347,
"writer_output_tokens": 17765,
"writer_cache_read_input_tokens": 380971,
"writer_cache_creation_input_tokens": 0,
"diagnostics": []
}
],
"files_written": [
{
"path": "adk/a2a/converters/event_converter/test_convert_a2a_task_to_event.py",
"hash": "sha256:158d67202970f17d0a2764c6b77c2cc4fed47a1041ea91d5808d8d25ef8ec0b1",
"status": "created"
}
],
"pr_ready": {
"branch_suggestion": "roost/python-tests-20260429-0856",
"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": []
}
Empty file added __init__.py
Empty file.
Empty file added adk/__init__.py
Empty file.
Empty file added adk/a2a/__init__.py
Empty file.
Empty file added adk/a2a/converters/__init__.py
Empty file.
Empty file.
166 changes: 166 additions & 0 deletions adk/a2a/converters/event_converter/test_convert_a2a_task_to_event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# 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_artifacts: Verifies that when a task has artifacts, the last artifact's parts are used to create a message for conversion.
# - [happy_path] happy_path_status_message: Verifies that when a task has no artifacts but has a status message, that message is used for conversion.
# - [happy_path] happy_path_history: Verifies that when a task has no artifacts or status message but has history, the last message in history is used.
# - [edge_case] edge_case_no_content: Verifies that a minimal Event is returned when no message content is found in the task.
# - [happy_path] with_invocation_context: Verifies that invocation_id and branch are correctly propagated from the InvocationContext.
# - [error_path] error_none_task: Verifies that passing None as a2a_task raises a ValueError.
# - [error_path] error_conversion_failure: Verifies that a RuntimeError is raised if the underlying message conversion fails.

import pytest
from unittest.mock import MagicMock, patch
from google.adk.a2a.converters.event_converter import convert_a2a_task_to_event

@pytest.mark.generated
@pytest.mark.happy_path
def test_happy_path_artifacts():
"""Verifies that when a task has artifacts, the last artifact's parts are used to create a message for conversion."""
mock_task = MagicMock()
mock_task.artifacts = [MagicMock(parts=['part1', 'part2'])]
mock_task.status = None
mock_task.history = []

mock_event = MagicMock()

with patch('google.adk.a2a.converters.event_converter.Message') as mock_message_cls, \
patch('google.adk.a2a.converters.event_converter.Role') as mock_role_cls, \
patch('google.adk.a2a.converters.event_converter.convert_a2a_message_to_event') as mock_convert:

mock_convert.return_value = mock_event
mock_message_instance = MagicMock()
mock_message_cls.return_value = mock_message_instance

result = convert_a2a_task_to_event(mock_task)

mock_message_cls.assert_called_once_with(
message_id="", role=mock_role_cls.agent, parts=['part1', 'part2']
)
mock_convert.assert_called_once()
args, kwargs = mock_convert.call_args
assert args[0] == mock_message_instance
assert result == mock_event

@pytest.mark.generated
@pytest.mark.happy_path
def test_happy_path_status_message():
"""Verifies that when a task has no artifacts but has a status message, that message is used for conversion."""
mock_task = MagicMock()
mock_task.artifacts = []
mock_task.status.message.parts = ['status_part']
mock_task.status.message.role = 'agent'
mock_task.history = []

mock_event = MagicMock()

with patch('google.adk.a2a.converters.event_converter.convert_a2a_message_to_event') as mock_convert:
mock_convert.return_value = mock_event

result = convert_a2a_task_to_event(mock_task)

mock_convert.assert_called_once()
args, kwargs = mock_convert.call_args
assert args[0] == mock_task.status.message
assert result == mock_event

@pytest.mark.generated
@pytest.mark.happy_path
def test_happy_path_history():
"""Verifies that when a task has no artifacts or status message but has history, the last message in history is used."""
mock_task = MagicMock()
mock_task.artifacts = []
mock_task.status = None
mock_task.history = [MagicMock(parts=['hist1']), MagicMock(parts=['hist2'])]

mock_event = MagicMock()

with patch('google.adk.a2a.converters.event_converter.convert_a2a_message_to_event') as mock_convert:
mock_convert.return_value = mock_event

result = convert_a2a_task_to_event(mock_task)

mock_convert.assert_called_once()
args, kwargs = mock_convert.call_args
assert args[0] == mock_task.history[-1]
assert result == mock_event

@pytest.mark.generated
@pytest.mark.edge_case
def test_edge_case_no_content():
"""Verifies that a minimal Event is returned when no message content is found in the task."""
mock_task = MagicMock()
mock_task.artifacts = []
mock_task.status = None
mock_task.history = []

with patch('google.adk.a2a.converters.event_converter.Event') as mock_event_cls, \
patch('google.adk.a2a.converters.event_converter.uuid.uuid4') as mock_uuid:

mock_uuid.return_value = 'generated-id'
mock_event_instance = MagicMock()
mock_event_cls.return_value = mock_event_instance

result = convert_a2a_task_to_event(mock_task)

mock_event_cls.assert_called_once_with(
invocation_id='generated-id',
author='a2a agent',
branch=None
)
assert result == mock_event_instance

@pytest.mark.generated
@pytest.mark.happy_path
def test_with_invocation_context():
"""Verifies that invocation_id and branch are correctly propagated from the InvocationContext."""
mock_task = MagicMock()
mock_task.artifacts = []
mock_task.status = None
mock_task.history = []

mock_context = MagicMock()
mock_context.invocation_id = 'ctx-id'
mock_context.branch = 'ctx-branch'

with patch('google.adk.a2a.converters.event_converter.Event') as mock_event_cls:
mock_event_instance = MagicMock()
mock_event_cls.return_value = mock_event_instance

result = convert_a2a_task_to_event(mock_task, invocation_context=mock_context)

mock_event_cls.assert_called_once_with(
invocation_id='ctx-id',
author='a2a agent',
branch='ctx-branch'
)
assert result == mock_event_instance

@pytest.mark.generated
@pytest.mark.error_path
def test_error_none_task():
"""Verifies that passing None as a2a_task raises a ValueError."""
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_conversion_failure():
"""Verifies that a RuntimeError is raised if the underlying message conversion fails."""
mock_task = MagicMock()
mock_task.artifacts = [MagicMock(parts=['p1'])]
mock_task.status = None
mock_task.history = []

with patch('google.adk.a2a.converters.event_converter.Message'), \
patch('google.adk.a2a.converters.event_converter.Role'), \
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)
27 changes: 27 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -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}")
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ test = [
"rouge-score>=0.1.2",
"tabulate>=0.9.0",
# go/keep-sorted end
"google"
]

docs = [
Expand Down