Skip to content

Commit 975e865

Browse files
committed
Add tests for initiation of apps with missing deps
The added tests check that ImportError with relevant messages are raised when creating instances of A2AStarletteApplication, A2AFastAPIApplication, and JSONRPCApplication with missing optional dependencies.
1 parent 7e159cd commit 975e865

3 files changed

Lines changed: 246 additions & 0 deletions

File tree

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
from typing import Any
2+
from unittest.mock import MagicMock
3+
4+
import pytest
5+
6+
7+
from a2a.server.apps.jsonrpc import fastapi_app
8+
from a2a.server.apps.jsonrpc.fastapi_app import A2AFastAPIApplication
9+
from a2a.server.request_handlers.request_handler import (
10+
RequestHandler, # For mock spec
11+
)
12+
from a2a.types import AgentCard # For mock spec
13+
14+
15+
# --- A2AFastAPIApplication Tests ---
16+
17+
18+
class TestA2AFastAPIApplicationOptionalDeps:
19+
# Running tests in this class requires the optional dependency fastapi to be
20+
# present in the test environment.
21+
22+
@pytest.fixture(scope='class', autouse=True)
23+
def ensure_pkg_fastapi_is_present(self):
24+
try:
25+
import fastapi as _fastapi
26+
except ImportError:
27+
pytest.fail(
28+
f'Running tests in {self.__class__.__name__} requires'
29+
' the optional dependency fastapi to be present in the test'
30+
' environment. Run `uv sync --dev ...` before running the test'
31+
' suite.'
32+
)
33+
34+
@pytest.fixture(scope='class')
35+
def mock_app_params(self) -> dict:
36+
# Mock http_handler
37+
mock_handler = MagicMock(spec=RequestHandler)
38+
# Mock agent_card with essential attributes accessed in __init__
39+
mock_agent_card = MagicMock(spec=AgentCard)
40+
# Ensure 'url' attribute exists on the mock_agent_card, as it's accessed
41+
# in __init__
42+
mock_agent_card.url = 'http://example.com'
43+
# Ensure 'supportsAuthenticatedExtendedCard' attribute exists
44+
mock_agent_card.supportsAuthenticatedExtendedCard = False
45+
return dict(agent_card=mock_agent_card, http_handler=mock_handler)
46+
47+
@pytest.fixture(scope='class')
48+
def mark_pkg_fastapi_not_installed(self):
49+
pkg_fastapi_installed_flag = fastapi_app._package_fastapi_installed
50+
fastapi_app._package_fastapi_installed = False
51+
yield
52+
fastapi_app._package_fastapi_installed = pkg_fastapi_installed_flag
53+
54+
def test_create_a2a_fastapi_app_with_present_deps_succeeds(
55+
self, mock_app_params: dict
56+
):
57+
try:
58+
_app = A2AFastAPIApplication(**mock_app_params)
59+
except ImportError:
60+
pytest.fail(
61+
'With the fastapi package present, creating a'
62+
' A2AFastAPIApplication instance should not raise ImportError'
63+
)
64+
65+
def test_create_a2a_fastapi_app_with_missing_deps_raises_importerror(
66+
self,
67+
mock_app_params: dict,
68+
mark_pkg_fastapi_not_installed: Any,
69+
):
70+
with pytest.raises(
71+
ImportError,
72+
match=(
73+
'The `fastapi` package is required to use the'
74+
' `A2AFastAPIApplication`'
75+
),
76+
):
77+
_app = A2AFastAPIApplication(**mock_app_params)
78+
79+
80+
if __name__ == '__main__':
81+
pytest.main([__file__])

tests/server/apps/jsonrpc/test_jsonrpc_app.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from typing import Any
12
from unittest.mock import MagicMock
23

34
import pytest
@@ -9,6 +10,7 @@
910
except ImportError:
1011
StarletteBaseUser = MagicMock() # type: ignore
1112

13+
from a2a.server.apps.jsonrpc import jsonrpc_app
1214
from a2a.server.apps.jsonrpc.jsonrpc_app import (
1315
JSONRPCApplication, # Still needed for JSONRPCApplication default constructor arg
1416
StarletteUserProxy,
@@ -86,5 +88,85 @@ def some_other_method(self):
8688
)
8789

8890

91+
class TestJSONRPCApplicationOptionalDeps:
92+
# Running tests in this class requires optional dependencies starlette and
93+
# sse-starlette to be present in the test environment.
94+
95+
@pytest.fixture(scope='class', autouse=True)
96+
def ensure_pkg_starlette_is_present(self):
97+
try:
98+
import starlette as _starlette
99+
import sse_starlette as _sse_starlette
100+
except ImportError:
101+
pytest.fail(
102+
f'Running tests in {self.__class__.__name__} requires'
103+
' optional dependencies starlette and sse-starlette to be'
104+
' present in the test environment. Run `uv sync --dev ...`'
105+
' before running the test suite.'
106+
)
107+
108+
@pytest.fixture(scope='class')
109+
def mock_app_params(self) -> dict:
110+
# Mock http_handler
111+
mock_handler = MagicMock(spec=RequestHandler)
112+
# Mock agent_card with essential attributes accessed in __init__
113+
mock_agent_card = MagicMock(spec=AgentCard)
114+
# Ensure 'url' attribute exists on the mock_agent_card, as it's accessed
115+
# in __init__
116+
mock_agent_card.url = 'http://example.com'
117+
# Ensure 'supportsAuthenticatedExtendedCard' attribute exists
118+
mock_agent_card.supportsAuthenticatedExtendedCard = False
119+
return dict(agent_card=mock_agent_card, http_handler=mock_handler)
120+
121+
@pytest.fixture(scope='class')
122+
def mark_pkg_starlette_not_installed(self):
123+
pkg_starlette_installed_flag = jsonrpc_app._package_starlette_installed
124+
jsonrpc_app._package_starlette_installed = False
125+
yield
126+
jsonrpc_app._package_starlette_installed = pkg_starlette_installed_flag
127+
128+
def test_create_jsonrpc_based_app_with_present_deps_succeeds(
129+
self, mock_app_params: dict
130+
):
131+
class DummyJSONRPCApp(JSONRPCApplication):
132+
def build(
133+
self,
134+
agent_card_url='/.well-known/agent.json',
135+
rpc_url='/',
136+
**kwargs,
137+
):
138+
return object()
139+
140+
try:
141+
_app = DummyJSONRPCApp(**mock_app_params)
142+
except ImportError:
143+
pytest.fail(
144+
'With packages starlette and see-starlette present, creating a'
145+
' JSONRPCApplication-based instance should not raise'
146+
' ImportError'
147+
)
148+
149+
def test_create_jsonrpc_based_app_with_missing_deps_raises_importerror(
150+
self, mock_app_params: dict, mark_pkg_starlette_not_installed: Any
151+
):
152+
class DummyJSONRPCApp(JSONRPCApplication):
153+
def build(
154+
self,
155+
agent_card_url='/.well-known/agent.json',
156+
rpc_url='/',
157+
**kwargs,
158+
):
159+
return object()
160+
161+
with pytest.raises(
162+
ImportError,
163+
match=(
164+
'Packages `starlette` and `sse-starlette` are required to use'
165+
' the `JSONRPCApplication`'
166+
),
167+
):
168+
_app = DummyJSONRPCApp(**mock_app_params)
169+
170+
89171
if __name__ == '__main__':
90172
pytest.main([__file__])
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
from typing import Any
2+
from unittest.mock import MagicMock
3+
4+
import pytest
5+
6+
7+
from a2a.server.apps.jsonrpc import starlette_app
8+
from a2a.server.apps.jsonrpc.starlette_app import A2AStarletteApplication
9+
from a2a.server.request_handlers.request_handler import (
10+
RequestHandler, # For mock spec
11+
)
12+
from a2a.types import AgentCard # For mock spec
13+
14+
15+
# --- A2AStarletteApplication Tests ---
16+
17+
18+
class TestA2AStarletteApplicationOptionalDeps:
19+
# Running tests in this class requires optional dependencies starlette and
20+
# sse-starlette to be present in the test environment.
21+
22+
@pytest.fixture(scope='class', autouse=True)
23+
def ensure_pkg_starlette_is_present(self):
24+
try:
25+
import starlette as _starlette
26+
import sse_starlette as _sse_starlette
27+
except ImportError:
28+
pytest.fail(
29+
f'Running tests in {self.__class__.__name__} requires'
30+
' optional dependencies starlette and sse-starlette to be'
31+
' present in the test environment. Run `uv sync --dev ...`'
32+
' before running the test suite.'
33+
)
34+
35+
@pytest.fixture(scope='class')
36+
def mock_app_params(self) -> dict:
37+
# Mock http_handler
38+
mock_handler = MagicMock(spec=RequestHandler)
39+
# Mock agent_card with essential attributes accessed in __init__
40+
mock_agent_card = MagicMock(spec=AgentCard)
41+
# Ensure 'url' attribute exists on the mock_agent_card, as it's accessed
42+
# in __init__
43+
mock_agent_card.url = 'http://example.com'
44+
# Ensure 'supportsAuthenticatedExtendedCard' attribute exists
45+
mock_agent_card.supportsAuthenticatedExtendedCard = False
46+
return dict(agent_card=mock_agent_card, http_handler=mock_handler)
47+
48+
@pytest.fixture(scope='class')
49+
def mark_pkg_starlette_not_installed(self):
50+
pkg_starlette_installed_flag = (
51+
starlette_app._package_starlette_installed
52+
)
53+
starlette_app._package_starlette_installed = False
54+
yield
55+
starlette_app._package_starlette_installed = (
56+
pkg_starlette_installed_flag
57+
)
58+
59+
def test_create_a2a_starlette_app_with_present_deps_succeeds(
60+
self, mock_app_params: dict
61+
):
62+
try:
63+
_app = A2AStarletteApplication(**mock_app_params)
64+
except ImportError:
65+
pytest.fail(
66+
'With packages starlette and see-starlette present, creating an'
67+
' A2AStarletteApplication instance should not raise ImportError'
68+
)
69+
70+
def test_create_a2a_starlette_app_with_missing_deps_raises_importerror(
71+
self,
72+
mock_app_params: dict,
73+
mark_pkg_starlette_not_installed: Any,
74+
):
75+
with pytest.raises(
76+
ImportError,
77+
match='Packages `starlette` and `sse-starlette` are required',
78+
):
79+
_app = A2AStarletteApplication(**mock_app_params)
80+
81+
82+
if __name__ == '__main__':
83+
pytest.main([__file__])

0 commit comments

Comments
 (0)