chore: Make UT of a2a consistent about how tests should be skipped when python verison < 3.10

PiperOrigin-RevId: 801040421
This commit is contained in:
Xiang (Sean) Zhou
2025-08-29 14:59:27 -07:00
committed by Copybara-Service
parent 2eddc5e4d3
commit 98b0426cd2
11 changed files with 197 additions and 323 deletions
+76 -9
View File
@@ -14,17 +14,84 @@
from unittest.mock import MagicMock
from google.adk.agents.invocation_context import InvocationContext
from google.adk.agents.langgraph_agent import LangGraphAgent
from google.adk.events.event import Event
from google.adk.plugins.plugin_manager import PluginManager
from google.genai import types
from langchain_core.messages import AIMessage
from langchain_core.messages import HumanMessage
from langchain_core.messages import SystemMessage
from langgraph.graph.graph import CompiledGraph
import pytest
# Skip all tests in this module if LangGraph dependencies are not available
LANGGRAPH_AVAILABLE = True
try:
from google.adk.agents.invocation_context import InvocationContext
from google.adk.agents.langgraph_agent import LangGraphAgent
from google.adk.events.event import Event
from google.adk.plugins.plugin_manager import PluginManager
from google.genai import types
from langchain_core.messages import AIMessage
from langchain_core.messages import HumanMessage
from langchain_core.messages import SystemMessage
from langgraph.graph.graph import CompiledGraph
except ImportError:
LANGGRAPH_AVAILABLE = False
# IMPORTANT: Dummy classes are REQUIRED in this file but NOT in A2A test files.
# Here's why this file is different from A2A test files:
#
# 1. MODULE-LEVEL USAGE IN DECORATORS:
# This file uses @pytest.mark.parametrize decorator with complex nested structures
# that directly reference imported types like Event(), types.Content(), types.Part.from_text().
# These decorator expressions are evaluated during MODULE COMPILATION TIME,
# not during test execution time.
#
# 2. A2A TEST FILES PATTERN:
# Most A2A test files only use imported types within test method bodies:
# - Inside test functions: def test_something(): Message(...)
# - These are evaluated during TEST EXECUTION TIME when tests are skipped
# - No NameError occurs because skipped tests don't execute their bodies
#
# 3. WHAT HAPPENS WITHOUT DUMMIES:
# If we remove dummy classes from this file:
# - Python tries to compile the @pytest.mark.parametrize decorator
# - It encounters Event(...), types.Content(...), etc.
# - These names are undefined → NameError during module compilation
# - Test collection fails before pytest.mark.skipif can even run
#
# 4. WHY DUMMIES WORK:
# - DummyTypes() can be called like Event() → returns DummyTypes instance
# - DummyTypes.__getattr__ handles types.Content → returns DummyTypes instance
# - DummyTypes.__call__ handles types.Part.from_text() → returns DummyTypes instance
# - The parametrize decorator gets dummy objects instead of real ones
# - Tests still get skipped due to pytestmark, so dummies never actually run
#
# 5. EXCEPTION CASES IN A2A FILES:
# A few A2A files DID need dummies initially because they had:
# - Type annotations: def create_helper(x: str) -> Message
# - But we removed those type annotations to eliminate the need for dummies
#
# This file cannot avoid dummies because the parametrize decorator usage
# is fundamental to the test structure and cannot be easily refactored.
class DummyTypes:
def __getattr__(self, name):
return DummyTypes()
def __call__(self, *args, **kwargs):
return DummyTypes()
InvocationContext = DummyTypes()
LangGraphAgent = DummyTypes()
Event = DummyTypes()
PluginManager = DummyTypes()
types = (
DummyTypes()
) # Must support chained calls like types.Content(), types.Part.from_text()
AIMessage = DummyTypes()
HumanMessage = DummyTypes()
SystemMessage = DummyTypes()
CompiledGraph = DummyTypes()
pytestmark = pytest.mark.skipif(
not LANGGRAPH_AVAILABLE, reason="LangGraph dependencies not available"
)
@pytest.mark.parametrize(
"checkpointer_value, events_list, expected_messages",
+25 -24
View File
@@ -22,8 +22,12 @@ from unittest.mock import patch
import pytest
# Check if A2A dependencies are available
A2A_AVAILABLE = True
# Skip all tests in this module if Python version is less than 3.10
pytestmark = pytest.mark.skipif(
sys.version_info < (3, 10), reason="A2A requires Python 3.10+"
)
# Import dependencies with version checking
try:
from a2a.types import AgentCapabilities
from a2a.types import AgentCard
@@ -35,29 +39,26 @@ try:
from google.adk.agents.remote_a2a_agent import A2A_METADATA_PREFIX
from google.adk.agents.remote_a2a_agent import AgentCardResolutionError
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
except ImportError:
A2A_AVAILABLE = False
except ImportError as e:
if sys.version_info < (3, 10):
# Create dummy classes to prevent NameError during module compilation.
# These are needed because the module has type annotations and module-level
# helper functions that reference imported types.
class DummyTypes:
pass
# Create dummy classes to prevent NameError during test collection
class DummyTypes:
pass
AgentCapabilities = DummyTypes()
AgentCard = DummyTypes()
AgentSkill = DummyTypes()
A2AMessage = DummyTypes()
SendMessageSuccessResponse = DummyTypes()
A2ATask = DummyTypes()
InvocationContext = DummyTypes()
RemoteA2aAgent = DummyTypes()
AgentCardResolutionError = Exception
A2A_METADATA_PREFIX = ""
# Skip all tests in this module if Python < 3.10 or A2A dependencies are not available
pytestmark = pytest.mark.skipif(
sys.version_info < (3, 10) or not A2A_AVAILABLE,
reason="A2A requires Python 3.10+ and A2A dependencies must be available",
)
AgentCapabilities = DummyTypes()
AgentCard = DummyTypes()
AgentSkill = DummyTypes()
A2AMessage = DummyTypes()
SendMessageSuccessResponse = DummyTypes()
A2ATask = DummyTypes()
InvocationContext = DummyTypes()
RemoteA2aAgent = DummyTypes()
AgentCardResolutionError = Exception
A2A_METADATA_PREFIX = ""
else:
raise e
from google.adk.events.event import Event