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
115 changes: 115 additions & 0 deletions .roost/runreport.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
{
"run_id": "a19da768",
"started_at": "2026-04-29T04:46:52.523840Z",
"ended_at": "2026-04-29T04:53:01.404346Z",
"status": "failed",
"config": {
"repo": "/private/var/tmp/Roost/RoostGPT/unit-adk-python/1777437937/source/adk-python",
"out": "/private/var/tmp/Roost/RoostGPT/unit-adk-python/1777437937/source/adk-python",
"workers": 5,
"scenarios_from": "planner",
"provider": "env:AI_TYPE",
"model": "azure/gpt-4.1",
"jedi": true
},
"summary": {
"targets_total": 1,
"targets_passed": 0,
"targets_failed": 0,
"targets_partial": 1,
"targets_skipped": 0,
"scenarios_total": 5,
"scenarios_passed": 3,
"scenarios_failed": 2,
"coverage_weighted": null,
"llm_cost_usd": 1.3646,
"llm_input_tokens": 2165294,
"llm_output_tokens": 17975,
"llm_cache_read_input_tokens": 2073216,
"llm_cache_creation_input_tokens": 0,
"llm_api_calls": 71,
"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/1777437937/source/adk-python/adk/a2a/converters/event_converter/test_convert_a2a_task_to_event.py",
"test_file_hash": "sha256:50bdffbc2dd5f1d294f674928c299b522b69b8a72f3df57e9ab8734e00b94e40",
"test_status": "partial",
"iterations": 71,
"pivots": 0,
"scenarios": [
{
"scenario_id": "src.google.adk.a2a.converters.event_converter.convert_a2a_task_to_event#0",
"scenario_name": "happy_path_task_with_artifacts",
"passed": false,
"duration_ms": null,
"failure_reason": "failed",
"stderr_excerpt": null
},
{
"scenario_id": "src.google.adk.a2a.converters.event_converter.convert_a2a_task_to_event#1",
"scenario_name": "minimal_event_no_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": "error_on_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#3",
"scenario_name": "runtime_error_on_message_conversion_failure",
"passed": false,
"duration_ms": null,
"failure_reason": "failed",
"stderr_excerpt": null
},
{
"scenario_id": "src.google.adk.a2a.converters.event_converter.convert_a2a_task_to_event#4",
"scenario_name": "invocation_context_branch_and_id_propagation",
"passed": true,
"duration_ms": null,
"failure_reason": null,
"stderr_excerpt": null
}
],
"coverage_pct": null,
"writer_cost_usd": 1.3645639999999999,
"writer_input_tokens": 2165294,
"writer_output_tokens": 17975,
"writer_cache_read_input_tokens": 2073216,
"writer_cache_creation_input_tokens": 0,
"diagnostics": []
}
],
"files_written": [
{
"path": "adk/a2a/converters/event_converter/test_convert_a2a_task_to_event.py",
"hash": "sha256:50bdffbc2dd5f1d294f674928c299b522b69b8a72f3df57e9ab8734e00b94e40",
"status": "created"
}
],
"pr_ready": {
"branch_suggestion": "roost/python-tests-20260429-0453",
"commit_message": "roost-pytest: add 1 test file(s)",
"pr_title": "Auto-generated Python unit tests (1 file(s))",
"pr_body_markdown": "## Summary\n- 0 targets with fully green tests\n- 1 targets with partial coverage\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.
145 changes: 145 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,145 @@
# 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_task_with_artifacts: Verifies conversion when the Task has artifacts with parts, producing a valid Event.
# - [edge_case] minimal_event_no_message: Checks that a minimal Event is created when Task has no artifacts, status, or history.
# - [error_path] error_on_none_task: Ensures ValueError is raised if a2a_task is None.
# - [error_path] runtime_error_on_message_conversion_failure: Ensures RuntimeError is raised if message conversion fails internally.
# - [property] invocation_context_branch_and_id_propagation: Checks that branch and invocation_id from InvocationContext are propagated to the Event.

import pytest
from unittest import mock

from google.adk.a2a.converters.event_converter import convert_a2a_task_to_event

# Minimal fakes for required types (since real ones are unavailable in the workspace)
def make_part(text=None):
class FakeTextPart:
def __init__(self, text):
self.text = text
return FakeTextPart(text)

class Artifact:
def __init__(self, parts=None):
self.parts = parts or []

class Task:
def __init__(self, artifacts=None, status=None, history=None):
self.artifacts = artifacts or []
self.status = status
self.history = history or []

class Message:
def __init__(self, message_id="", role=None, parts=None):
self.message_id = message_id
self.role = role
self.parts = parts or []

class Event:
def __init__(self, invocation_id=None, author=None, branch=None, content=None):
self.invocation_id = invocation_id
self.author = author
self.branch = branch
self.content = content

class InvocationContext:
def __init__(self, invocation_id=None, branch=None):
self.invocation_id = invocation_id
self.branch = branch

def fake_convert_a2a_message_to_event(message, author, invocation_context, part_converter=None):
# Simulate conversion: just wrap the message's parts in Event.content
class Content:
def __init__(self, parts):
self.parts = parts
return Event(
invocation_id=invocation_context.invocation_id if invocation_context else "iid",
author=author or "a2a agent",
branch=invocation_context.branch if invocation_context else None,
content=Content(message.parts)
)

@pytest.fixture(autouse=True)
def patch_types(monkeypatch):
monkeypatch.setattr("a2a.types.Task", Task)
monkeypatch.setattr("a2a.types.Artifact", Artifact)
monkeypatch.setattr("a2a.types.Part", make_part)
monkeypatch.setattr("a2a.types.TextPart", make_part)
monkeypatch.setattr("a2a.types.Message", Message)
monkeypatch.setattr("google.adk.a2a.converters.event_converter.Event", Event)
monkeypatch.setattr("google.adk.a2a.converters.event_converter.InvocationContext", InvocationContext)

@pytest.mark.generated
@pytest.mark.happy_path
def test_happy_path_task_with_artifacts(monkeypatch):
"""Verifies conversion when the Task has artifacts with parts, producing a valid Event."""
# Patch convert_a2a_message_to_event to our fake
monkeypatch.setattr(
"google.adk.a2a.converters.event_converter.convert_a2a_message_to_event",
fake_convert_a2a_message_to_event
)
a2a_task = Task(artifacts=[Artifact(parts=[make_part(text='foo')])], status=None, history=None)
result = convert_a2a_task_to_event(a2a_task)
assert isinstance(result, Event)
assert hasattr(result, "content")
assert getattr(result.content.parts[0], 'text', None) == 'foo'

@pytest.mark.generated
@pytest.mark.edge_case
def test_minimal_event_no_message(monkeypatch):
"""Checks that a minimal Event is created when Task has no artifacts, status, or history."""
# Patch convert_a2a_message_to_event to ensure it's not called
monkeypatch.setattr(
"google.adk.a2a.converters.event_converter.convert_a2a_message_to_event",
lambda *a, **kw: pytest.fail("Should not be called")
)
a2a_task = Task(artifacts=[], status=None, history=[])
result = convert_a2a_task_to_event(a2a_task)
assert isinstance(result, Event)
assert getattr(result, "content", None) is None

@pytest.mark.generated
@pytest.mark.error_path
def test_error_on_none_task():
"""Ensures ValueError is raised if a2a_task is None."""
with pytest.raises(ValueError):
convert_a2a_task_to_event(None)

@pytest.mark.generated
@pytest.mark.error_path
def test_runtime_error_on_message_conversion_failure(monkeypatch):
"""Ensures RuntimeError is raised if message conversion fails internally."""
def part_converter(_):
raise Exception('fail')
# Patch convert_a2a_message_to_event to raise Exception
def fail_convert(*a, **kw):
raise Exception('fail')
monkeypatch.setattr(
"google.adk.a2a.converters.event_converter.convert_a2a_message_to_event",
fail_convert
)
a2a_task = Task(artifacts=[Artifact(parts=[make_part(text='foo')])], status=None, history=None)
with pytest.raises(RuntimeError):
convert_a2a_task_to_event(a2a_task, part_converter=part_converter)

@pytest.mark.generated
@pytest.mark.property
def test_invocation_context_branch_and_id_propagation(monkeypatch):
"""Checks that branch and invocation_id from InvocationContext are propagated to the Event."""
# Patch convert_a2a_message_to_event to our fake
monkeypatch.setattr(
"google.adk.a2a.converters.event_converter.convert_a2a_message_to_event",
fake_convert_a2a_message_to_event
)
ctx = InvocationContext(invocation_id='123', branch='main')
a2a_task = Task(artifacts=[], status=None, history=[])
result = convert_a2a_task_to_event(a2a_task, invocation_context=ctx)
assert result.invocation_id == '123'
assert result.branch == 'main'
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}")
10 changes: 6 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,12 @@ eval = [
]

test = [
# go/keep-sorted start
"a2a-sdk>=0.3.0,<0.4.0",
"anthropic>=0.43.0", # For anthropic model tests
"crewai[tools];python_version>='3.11' and python_version<'3.12'", # For CrewaiTool tests; chromadb/pypika fail on 3.12+
"# go/keep-sorted start\n "a2a-sdk>=0.3.0",
"<0.4.0",
"anthropic>=0.43.0",
"# For anthropic model tests\n "crewai[tools",
"google",
];python_version>='3.11' and python_version<'3.12'", # For CrewaiTool tests; chromadb/pypika fail on 3.12+
"kubernetes>=29.0.0", # For GkeCodeExecutor
"langchain-community>=0.3.17",
"langgraph>=0.2.60, <0.4.8", # For LangGraphAgent
Expand Down