-
Notifications
You must be signed in to change notification settings - Fork 336
Expand file tree
/
Copy pathtest_config.py
More file actions
252 lines (175 loc) · 7.85 KB
/
Copy pathtest_config.py
File metadata and controls
252 lines (175 loc) · 7.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
import logging
from openkb.config import (
DEFAULT_CONFIG,
get_extra_headers,
get_num_retries,
get_timeout,
load_config,
resolve_extra_headers,
resolve_litellm_settings,
resolve_num_retries,
resolve_timeout,
save_config,
set_extra_headers,
set_num_retries,
set_timeout,
)
def test_default_config_keys():
assert "model" in DEFAULT_CONFIG
assert "language" in DEFAULT_CONFIG
assert "pageindex_threshold" in DEFAULT_CONFIG
def test_default_config_values():
assert DEFAULT_CONFIG["model"] == "gpt-5.4"
assert DEFAULT_CONFIG["language"] == "en"
assert DEFAULT_CONFIG["pageindex_threshold"] == 20
def test_load_missing_file_returns_defaults(tmp_path):
missing = tmp_path / "nonexistent" / "config.yaml"
config = load_config(missing)
assert config == DEFAULT_CONFIG
def test_save_creates_parent_dirs(tmp_path):
config_path = tmp_path / "nested" / "dir" / "config.yaml"
save_config(config_path, DEFAULT_CONFIG)
assert config_path.exists()
def test_save_load_roundtrip(tmp_path):
config_path = tmp_path / "config.yaml"
custom = {"model": "gpt-3.5-turbo", "language": "fr"}
save_config(config_path, custom)
loaded = load_config(config_path)
# Custom values override defaults
assert loaded["model"] == "gpt-3.5-turbo"
assert loaded["language"] == "fr"
# Defaults fill in missing keys
assert loaded["pageindex_threshold"] == DEFAULT_CONFIG["pageindex_threshold"]
def test_load_overrides_defaults(tmp_path):
config_path = tmp_path / "config.yaml"
save_config(config_path, {"model": "claude-3", "pageindex_threshold": 100})
loaded = load_config(config_path)
assert loaded["model"] == "claude-3"
assert loaded["pageindex_threshold"] == 100
# Non-overridden defaults still present
assert loaded["language"] == "en"
# --- extra_headers -----------------------------------------------------------
def test_resolve_extra_headers_absent_returns_empty():
assert resolve_extra_headers({}) == {}
def test_resolve_extra_headers_valid_mapping():
config = {
"extra_headers": {
"Editor-Version": "vscode/1.95.0",
"Copilot-Integration-Id": "vscode-chat",
}
}
assert resolve_extra_headers(config) == {
"Editor-Version": "vscode/1.95.0",
"Copilot-Integration-Id": "vscode-chat",
}
def test_resolve_extra_headers_stringifies_scalar_values():
# YAML may parse version-ish values as numbers.
config = {"extra_headers": {"X-Api-Version": 2024, "X-Ratio": 1.5}}
assert resolve_extra_headers(config) == {"X-Api-Version": "2024", "X-Ratio": "1.5"}
def test_resolve_extra_headers_non_mapping_ignored():
assert resolve_extra_headers({"extra_headers": ["Editor-Version: x"]}) == {}
assert resolve_extra_headers({"extra_headers": "Editor-Version: x"}) == {}
def test_resolve_extra_headers_skips_bad_entries():
config = {
"extra_headers": {
"Good": "value",
"": "empty-key-skipped",
"NoneValue": None,
"ListValue": ["a"],
123: "non-string-key-skipped",
}
}
assert resolve_extra_headers(config) == {"Good": "value"}
def test_extra_headers_stash_roundtrip_and_isolation():
set_extra_headers({"A": "1"})
got = get_extra_headers()
assert got == {"A": "1"}
# Mutating the returned copy must not affect the stash.
got["B"] = "2"
assert get_extra_headers() == {"A": "1"}
set_extra_headers({})
assert get_extra_headers() == {}
# --- timeout -----------------------------------------------------------------
def test_resolve_timeout_absent_returns_none():
assert resolve_timeout({}) is None
def test_resolve_timeout_int_and_float():
assert resolve_timeout({"timeout": 1200}) == 1200.0
assert resolve_timeout({"timeout": 0.5}) == 0.5
def test_resolve_timeout_numeric_string_coerced():
assert resolve_timeout({"timeout": "1200"}) == 1200.0
def test_resolve_timeout_rejects_non_positive():
assert resolve_timeout({"timeout": 0}) is None
assert resolve_timeout({"timeout": -10}) is None
def test_resolve_timeout_rejects_bool():
# bool is a subclass of int; True/False are not durations.
assert resolve_timeout({"timeout": True}) is None
def test_resolve_timeout_rejects_non_numeric():
assert resolve_timeout({"timeout": "soon"}) is None
assert resolve_timeout({"timeout": [1200]}) is None
def test_resolve_timeout_rejects_nan_and_inf():
# nan/inf pass a naive `<= 0` check; YAML's .nan/.inf yield real floats.
assert resolve_timeout({"timeout": float("inf")}) is None
assert resolve_timeout({"timeout": float("nan")}) is None
assert resolve_timeout({"timeout": "inf"}) is None
assert resolve_timeout({"timeout": "nan"}) is None
def test_timeout_stash_roundtrip_and_reset():
set_timeout(1200.0)
assert get_timeout() == 1200.0
set_timeout(None)
assert get_timeout() is None
# --- num_retries -------------------------------------------------------------
def test_resolve_num_retries_absent_returns_none():
assert resolve_num_retries({}) is None
def test_resolve_num_retries_int():
assert resolve_num_retries({"num_retries": 3}) == 3
assert resolve_num_retries({"num_retries": 1}) == 1
def test_resolve_num_retries_zero_allowed():
# 0 is a valid retry count (no retries, but explicitly set).
assert resolve_num_retries({"num_retries": 0}) == 0
def test_resolve_num_retries_rejects_negative():
assert resolve_num_retries({"num_retries": -1}) is None
def test_resolve_num_retries_rejects_bool():
# bool is a subclass of int; True/False are not retry counts.
assert resolve_num_retries({"num_retries": True}) is None
assert resolve_num_retries({"num_retries": False}) is None
def test_resolve_num_retries_rejects_float():
# A retry count is discrete; floats are rejected (no coercion).
assert resolve_num_retries({"num_retries": 3.5}) is None
assert resolve_num_retries({"num_retries": 3.0}) is None
def test_resolve_num_retries_rejects_string():
# Unlike timeout, numeric strings are NOT coerced.
assert resolve_num_retries({"num_retries": "3"}) is None
def test_resolve_num_retries_rejects_non_int():
assert resolve_num_retries({"num_retries": [3]}) is None
assert resolve_num_retries({"num_retries": {"a": 1}}) is None
def test_num_retries_stash_roundtrip_and_reset():
set_num_retries(3)
assert get_num_retries() == 3
set_num_retries(None)
assert get_num_retries() is None
def test_resolve_litellm_settings_absent_returns_empty():
assert resolve_litellm_settings({}) == {}
def test_resolve_litellm_settings_passes_mapping_through_verbatim():
# Values are forwarded as-is — no validation or coercion.
config = {"litellm": {"drop_params": True, "num_retries": 3, "ssl_verify": False}}
assert resolve_litellm_settings(config) == {
"drop_params": True,
"num_retries": 3,
"ssl_verify": False,
}
def test_resolve_litellm_settings_non_mapping_ignored():
assert resolve_litellm_settings({"litellm": ["drop_params"]}) == {}
assert resolve_litellm_settings({"litellm": "drop_params=true"}) == {}
assert resolve_litellm_settings({"litellm": True}) == {}
def test_resolve_litellm_settings_drops_non_string_keys():
assert resolve_litellm_settings({"litellm": {5: "x", "drop_params": True}}) == {
"drop_params": True
}
def test_resolve_litellm_settings_warns_on_non_mapping(caplog):
with caplog.at_level(logging.WARNING, logger="openkb.config"):
assert resolve_litellm_settings({"litellm": ["drop_params"]}) == {}
assert "must be a mapping" in caplog.text
def test_resolve_litellm_settings_warns_on_non_string_key(caplog):
with caplog.at_level(logging.WARNING, logger="openkb.config"):
resolve_litellm_settings({"litellm": {5: "x", "drop_params": True}})
assert "non-string key" in caplog.text