chore: import upstream snapshot with attribution
Continuous Integration / Pre-commit Linter (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.10) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.11) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.12) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.12) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.14) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Has been cancelled
Copybara PR Handler / close-imported-pr (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Has been cancelled
Continuous Integration / Pre-commit Linter (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.10) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.11) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.12) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.12) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.14) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Has been cancelled
Copybara PR Handler / close-imported-pr (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Tests for agent transfer utilities."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from google.adk.agents.llm_agent import LlmAgent
|
||||
from google.adk.workflow.utils._transfer_utils import resolve_and_derive_transfer_context
|
||||
import pytest
|
||||
|
||||
|
||||
def test_resolve_and_derive_transfer_context_raises_value_error_on_self_transfer():
|
||||
"""resolve_and_derive_transfer_context raises ValueError when target is the current agent."""
|
||||
# Arrange
|
||||
current = LlmAgent(name='current')
|
||||
root = LlmAgent(name='root', sub_agents=[current])
|
||||
|
||||
# Act & Assert
|
||||
with pytest.raises(ValueError, match='cannot transfer to itself'):
|
||||
resolve_and_derive_transfer_context(
|
||||
'current', current, root, MagicMock(), None
|
||||
)
|
||||
|
||||
|
||||
def test_resolve_and_derive_transfer_context_returns_child_context():
|
||||
"""resolve_and_derive_transfer_context returns current context as parent context for CHILD transfers."""
|
||||
# Arrange
|
||||
target = LlmAgent(name='target')
|
||||
current = LlmAgent(name='current', sub_agents=[target])
|
||||
root = LlmAgent(name='root', sub_agents=[current])
|
||||
|
||||
curr_ctx = MagicMock()
|
||||
|
||||
# Act
|
||||
resolved_agent, parent_ctx = resolve_and_derive_transfer_context(
|
||||
'target', current, root, curr_ctx, None
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert resolved_agent is target
|
||||
assert parent_ctx is curr_ctx
|
||||
|
||||
|
||||
def test_resolve_and_derive_transfer_context_returns_sibling_context():
|
||||
"""resolve_and_derive_transfer_context returns parent context for SIBLING transfers."""
|
||||
# Arrange
|
||||
current = LlmAgent(name='current')
|
||||
target = LlmAgent(name='target')
|
||||
root = LlmAgent(name='root', sub_agents=[current, target])
|
||||
|
||||
parent_ctx = MagicMock()
|
||||
|
||||
# Act
|
||||
resolved_agent, derived_ctx = resolve_and_derive_transfer_context(
|
||||
'target', current, root, MagicMock(), parent_ctx
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert resolved_agent is target
|
||||
assert derived_ctx is parent_ctx
|
||||
|
||||
|
||||
def test_resolve_and_derive_transfer_context_climbs_parent_context():
|
||||
"""resolve_and_derive_transfer_context climbs context chain to find the target parent's parent context."""
|
||||
# Arrange
|
||||
root_ctx = MagicMock()
|
||||
root_ctx.node = MagicMock()
|
||||
root_ctx.node.name = 'root'
|
||||
root_ctx.parent_ctx = MagicMock()
|
||||
root_ctx.parent_ctx.node = None
|
||||
root_ctx.parent_ctx.parent_ctx = None
|
||||
|
||||
child_ctx = MagicMock()
|
||||
child_ctx.node = MagicMock()
|
||||
child_ctx.node.name = 'child'
|
||||
child_ctx.parent_ctx = root_ctx
|
||||
|
||||
# Target is 'root', current is 'child'
|
||||
child = LlmAgent(name='child')
|
||||
root = LlmAgent(name='root', sub_agents=[child])
|
||||
child.parent_agent = root
|
||||
|
||||
# Act
|
||||
resolved_agent, derived_ctx = resolve_and_derive_transfer_context(
|
||||
'root', child, root, child_ctx, None
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert resolved_agent is root
|
||||
assert derived_ctx is root_ctx.parent_ctx
|
||||
|
||||
|
||||
def test_resolve_and_derive_transfer_context_returns_root_context_when_parent_bypassed():
|
||||
"""resolve_and_derive_transfer_context returns root context for PARENT transfers when parent was bypassed."""
|
||||
# Arrange
|
||||
root_ctx = MagicMock()
|
||||
root_ctx.node = None
|
||||
root_ctx.parent_ctx = None
|
||||
|
||||
child_ctx = MagicMock()
|
||||
child_ctx.node = MagicMock()
|
||||
child_ctx.node.name = 'child'
|
||||
child_ctx.parent_ctx = root_ctx
|
||||
|
||||
# Target is 'root', current is 'child'
|
||||
child = LlmAgent(name='child')
|
||||
root = LlmAgent(name='root', sub_agents=[child])
|
||||
child.parent_agent = root
|
||||
|
||||
# Act
|
||||
resolved_agent, derived_ctx = resolve_and_derive_transfer_context(
|
||||
'root', child, root, child_ctx, None
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert resolved_agent is root
|
||||
assert derived_ctx is root_ctx
|
||||
|
||||
|
||||
def test_resolve_and_derive_transfer_context_returns_none_when_agent_not_found():
|
||||
"""resolve_and_derive_transfer_context returns (None, None) when target agent is not found."""
|
||||
# Arrange
|
||||
current = LlmAgent(name='current')
|
||||
root = LlmAgent(name='root', sub_agents=[current])
|
||||
|
||||
# Act
|
||||
resolved_agent, derived_ctx = resolve_and_derive_transfer_context(
|
||||
'target', current, root, MagicMock(), None
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert resolved_agent is None
|
||||
assert derived_ctx is None
|
||||
|
||||
|
||||
def test_resolve_and_derive_transfer_context_returns_target_and_none_when_no_relationship():
|
||||
"""resolve_and_derive_transfer_context returns (target_agent, None) for unrelated transfers."""
|
||||
# Arrange
|
||||
current = LlmAgent(name='current')
|
||||
target = LlmAgent(name='target')
|
||||
root1 = LlmAgent(name='root1', sub_agents=[current])
|
||||
root2 = LlmAgent(name='root2', sub_agents=[target])
|
||||
|
||||
# Act
|
||||
resolved_agent, derived_ctx = resolve_and_derive_transfer_context(
|
||||
'target', current, root2, MagicMock(), None
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert resolved_agent is target
|
||||
assert derived_ctx is None
|
||||
|
||||
|
||||
def test_resolve_and_derive_transfer_context_works_with_cloned_agents():
|
||||
"""resolve_and_derive_transfer_context works correctly when the current agent is cloned (name-based matching)."""
|
||||
# Arrange
|
||||
target = LlmAgent(name='target')
|
||||
current = LlmAgent(name='current', sub_agents=[target])
|
||||
root = LlmAgent(name='root', sub_agents=[current])
|
||||
|
||||
cloned_current = current.clone()
|
||||
assert cloned_current is not current
|
||||
assert cloned_current.name == current.name
|
||||
|
||||
curr_ctx = MagicMock()
|
||||
|
||||
# Act
|
||||
resolved_agent, derived_ctx = resolve_and_derive_transfer_context(
|
||||
'target', cloned_current, root, curr_ctx, None
|
||||
)
|
||||
|
||||
# Assert
|
||||
assert resolved_agent is target
|
||||
assert derived_ctx is curr_ctx
|
||||
Reference in New Issue
Block a user