Skip to content

Commit 732579b

Browse files
test(tools): add coverage for ForwardingArtifactService
1 parent a238884 commit 732579b

1 file changed

Lines changed: 185 additions & 0 deletions

File tree

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from unittest.mock import AsyncMock
16+
17+
from google.adk.tools._forwarding_artifact_service import ForwardingArtifactService
18+
from google.genai import types
19+
from pytest import mark
20+
from pytest import raises
21+
22+
23+
class _StubSession:
24+
25+
def __init__(self, session_id: str):
26+
self.id = session_id
27+
28+
29+
class _StubInvocationContext:
30+
"""Minimal InvocationContext stub for ForwardingArtifactService tests."""
31+
32+
def __init__(self, artifact_service=None):
33+
self.app_name = 'test_app'
34+
self.user_id = 'test_user'
35+
self.session = _StubSession('test_session')
36+
self.artifact_service = artifact_service
37+
38+
39+
class _StubToolContext:
40+
"""Minimal ToolContext stub for ForwardingArtifactService tests."""
41+
42+
def __init__(self, invocation_context):
43+
self._invocation_context = invocation_context
44+
self.save_artifact = AsyncMock(return_value=1)
45+
self.load_artifact = AsyncMock(return_value=None)
46+
self.list_artifacts = AsyncMock(return_value=[])
47+
48+
49+
def _create_service(artifact_service=None):
50+
invocation_context = _StubInvocationContext(artifact_service)
51+
tool_context = _StubToolContext(invocation_context)
52+
return (
53+
ForwardingArtifactService(tool_context),
54+
tool_context,
55+
invocation_context,
56+
)
57+
58+
59+
@mark.asyncio
60+
async def test_save_artifact_delegates_to_tool_context():
61+
service, tool_context, _ = _create_service()
62+
artifact = types.Part(text='hello')
63+
64+
result = await service.save_artifact(
65+
app_name='ignored_app',
66+
user_id='ignored_user',
67+
filename='file.txt',
68+
artifact=artifact,
69+
custom_metadata={'k': 'v'},
70+
)
71+
72+
assert result == 1
73+
tool_context.save_artifact.assert_awaited_once_with(
74+
filename='file.txt',
75+
artifact=artifact,
76+
custom_metadata={'k': 'v'},
77+
)
78+
79+
80+
@mark.asyncio
81+
async def test_load_artifact_delegates_to_tool_context():
82+
service, tool_context, _ = _create_service()
83+
artifact = types.Part(text='hello')
84+
tool_context.load_artifact.return_value = artifact
85+
86+
result = await service.load_artifact(
87+
app_name='ignored_app',
88+
user_id='ignored_user',
89+
filename='file.txt',
90+
version=2,
91+
)
92+
93+
assert result is artifact
94+
tool_context.load_artifact.assert_awaited_once_with(
95+
filename='file.txt', version=2
96+
)
97+
98+
99+
@mark.asyncio
100+
async def test_list_artifact_keys_delegates_to_tool_context():
101+
service, tool_context, _ = _create_service()
102+
tool_context.list_artifacts.return_value = ['a.txt', 'b.txt']
103+
104+
result = await service.list_artifact_keys(
105+
app_name='ignored_app', user_id='ignored_user'
106+
)
107+
108+
assert result == ['a.txt', 'b.txt']
109+
tool_context.list_artifacts.assert_awaited_once_with()
110+
111+
112+
@mark.asyncio
113+
async def test_delete_artifact_delegates_to_invocation_context_service():
114+
root_artifact_service = AsyncMock()
115+
service, _, invocation_context = _create_service(root_artifact_service)
116+
117+
await service.delete_artifact(
118+
app_name='ignored_app', user_id='ignored_user', filename='file.txt'
119+
)
120+
121+
root_artifact_service.delete_artifact.assert_awaited_once_with(
122+
app_name=invocation_context.app_name,
123+
user_id=invocation_context.user_id,
124+
session_id=invocation_context.session.id,
125+
filename='file.txt',
126+
)
127+
128+
129+
@mark.asyncio
130+
async def test_delete_artifact_raises_when_no_root_artifact_service():
131+
service, _, _ = _create_service(artifact_service=None)
132+
133+
with raises(ValueError, match='Artifact service is not initialized.'):
134+
await service.delete_artifact(
135+
app_name='ignored_app', user_id='ignored_user', filename='file.txt'
136+
)
137+
138+
139+
@mark.asyncio
140+
async def test_list_versions_delegates_to_invocation_context_service():
141+
root_artifact_service = AsyncMock()
142+
root_artifact_service.list_versions.return_value = [1, 2, 3]
143+
service, _, invocation_context = _create_service(root_artifact_service)
144+
145+
result = await service.list_versions(
146+
app_name='ignored_app', user_id='ignored_user', filename='file.txt'
147+
)
148+
149+
assert result == [1, 2, 3]
150+
root_artifact_service.list_versions.assert_awaited_once_with(
151+
app_name=invocation_context.app_name,
152+
user_id=invocation_context.user_id,
153+
session_id=invocation_context.session.id,
154+
filename='file.txt',
155+
)
156+
157+
158+
@mark.asyncio
159+
async def test_list_versions_raises_when_no_root_artifact_service():
160+
service, _, _ = _create_service(artifact_service=None)
161+
162+
with raises(ValueError, match='Artifact service is not initialized.'):
163+
await service.list_versions(
164+
app_name='ignored_app', user_id='ignored_user', filename='file.txt'
165+
)
166+
167+
168+
@mark.asyncio
169+
async def test_list_artifact_versions_raises_not_implemented():
170+
service, _, _ = _create_service()
171+
172+
with raises(NotImplementedError):
173+
await service.list_artifact_versions(
174+
app_name='ignored_app', user_id='ignored_user', filename='file.txt'
175+
)
176+
177+
178+
@mark.asyncio
179+
async def test_get_artifact_version_raises_not_implemented():
180+
service, _, _ = _create_service()
181+
182+
with raises(NotImplementedError):
183+
await service.get_artifact_version(
184+
app_name='ignored_app', user_id='ignored_user', filename='file.txt'
185+
)

0 commit comments

Comments
 (0)