Skip to content

Commit 9c9e3d9

Browse files
author
Denis Megerle
committed
test: add ToolListCache caching coverage
Add a small unit test that validates ToolListCache caching semantics, deep-copy isolation, and retry/backoff behavior. Made-with: Cursor
1 parent 89f7c1d commit 9c9e3d9

File tree

1 file changed

+81
-0
lines changed

1 file changed

+81
-0
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/usr/bin/env python3
2+
# Copyright 2026 Bytedance Ltd. and/or its affiliates
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
from __future__ import annotations
17+
18+
from dataclasses import dataclass
19+
20+
import pytest
21+
22+
import verl.tools.utils.tool_registry as tool_registry
23+
from verl.tools.utils.tool_registry import ToolListCache
24+
25+
26+
@dataclass(frozen=True)
27+
class _DummyTool:
28+
name: str
29+
30+
31+
def test_tool_list_cache_returns_empty_for_none():
32+
ToolListCache._cache.clear()
33+
assert ToolListCache.get_tool_list(None) == []
34+
35+
36+
def test_tool_list_cache_caches_and_deepcopies(monkeypatch: pytest.MonkeyPatch):
37+
ToolListCache._cache.clear()
38+
39+
calls: list[tuple[str, float | None]] = []
40+
41+
def _fake_init(tools_config_file: str, mcp_init_timeout_s: float | None = None):
42+
calls.append((tools_config_file, mcp_init_timeout_s))
43+
return [_DummyTool(name="t1")]
44+
45+
monkeypatch.setattr(tool_registry, "initialize_tools_from_config", _fake_init)
46+
monkeypatch.setenv("VERL_TOOL_CACHE_INIT_TIMEOUT_S", "12")
47+
48+
out1 = ToolListCache.get_tool_list("dummy.yaml")
49+
out2 = ToolListCache.get_tool_list("dummy.yaml")
50+
51+
assert calls == [("dummy.yaml", 12.0)]
52+
assert out1 == out2
53+
assert out1 is not out2
54+
assert out1[0] is not out2[0]
55+
56+
57+
def test_tool_list_cache_retries_before_failing(monkeypatch: pytest.MonkeyPatch):
58+
ToolListCache._cache.clear()
59+
60+
attempts = {"n": 0}
61+
62+
def _fake_init(tools_config_file: str, mcp_init_timeout_s: float | None = None):
63+
attempts["n"] += 1
64+
if attempts["n"] == 1:
65+
raise RuntimeError("boom")
66+
return [_DummyTool(name="t1")]
67+
68+
sleeps: list[float] = []
69+
70+
monkeypatch.setattr(tool_registry, "initialize_tools_from_config", _fake_init)
71+
monkeypatch.setattr(tool_registry.time, "sleep", lambda s: sleeps.append(float(s)))
72+
monkeypatch.setenv("VERL_TOOL_CACHE_INIT_RETRIES", "2")
73+
monkeypatch.setenv("VERL_TOOL_CACHE_INIT_TIMEOUT_S", "0.5")
74+
monkeypatch.setenv("VERL_TOOL_CACHE_INIT_BACKOFF_BASE_S", "0.01")
75+
monkeypatch.setenv("VERL_TOOL_CACHE_INIT_BACKOFF_MAX_S", "0.01")
76+
77+
out = ToolListCache.get_tool_list("dummy.yaml")
78+
79+
assert out == [_DummyTool(name="t1")]
80+
assert attempts["n"] == 2
81+
assert sleeps == [0.01]

0 commit comments

Comments
 (0)