forked from repowise-dev/repowise
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_helpers.py
More file actions
260 lines (190 loc) · 8.84 KB
/
test_helpers.py
File metadata and controls
260 lines (190 loc) · 8.84 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
253
254
255
256
257
258
259
260
"""Unit tests for repowise.cli.helpers."""
from __future__ import annotations
from pathlib import Path
import pytest
from repowise.cli.helpers import (
ensure_repowise_dir,
get_db_url_for_repo,
get_head_commit,
get_repowise_dir,
load_state,
resolve_repo_path,
run_async,
save_state,
validate_provider_config,
)
# ---------------------------------------------------------------------------
# run_async
# ---------------------------------------------------------------------------
class TestRunAsync:
def test_returns_coroutine_result(self):
async def _add(a, b):
return a + b
assert run_async(_add(3, 4)) == 7
def test_raises_exception_from_coroutine(self):
async def _fail():
raise ValueError("boom")
with pytest.raises(ValueError, match="boom"):
run_async(_fail())
# ---------------------------------------------------------------------------
# Path resolution
# ---------------------------------------------------------------------------
class TestResolveRepoPath:
def test_none_defaults_to_cwd(self):
result = resolve_repo_path(None)
assert result == Path.cwd().resolve()
def test_resolves_relative_path(self, tmp_path):
import os
old = os.getcwd()
try:
os.chdir(tmp_path)
result = resolve_repo_path(".")
assert result == tmp_path.resolve()
finally:
os.chdir(old)
def test_resolves_absolute_path(self, tmp_path):
result = resolve_repo_path(str(tmp_path))
assert result == tmp_path.resolve()
# ---------------------------------------------------------------------------
# .repowise/ directory
# ---------------------------------------------------------------------------
class TestrepowiseDir:
def test_get_repowise_dir(self, tmp_path):
assert get_repowise_dir(tmp_path) == tmp_path / ".repowise"
def test_ensure_repowise_dir_creates(self, tmp_path):
d = ensure_repowise_dir(tmp_path)
assert d.exists()
assert d == tmp_path / ".repowise"
def test_ensure_repowise_dir_idempotent(self, tmp_path):
ensure_repowise_dir(tmp_path)
d = ensure_repowise_dir(tmp_path)
assert d.exists()
# ---------------------------------------------------------------------------
# DB URL
# ---------------------------------------------------------------------------
class TestDbUrl:
def test_defaults_to_repo_local_database(self, tmp_path):
url = get_db_url_for_repo(tmp_path)
expected_path = (tmp_path / ".repowise" / "wiki.db").as_posix()
assert url == f"sqlite+aiosqlite:///{expected_path}"
assert (tmp_path / ".repowise").exists()
# ---------------------------------------------------------------------------
# State file
# ---------------------------------------------------------------------------
class TestStateFile:
def test_load_missing_returns_empty(self, tmp_path):
ensure_repowise_dir(tmp_path)
assert load_state(tmp_path) == {}
def test_save_and_load_roundtrip(self, tmp_path):
ensure_repowise_dir(tmp_path)
state = {"last_sync_commit": "abc123", "total_pages": 42}
save_state(tmp_path, state)
loaded = load_state(tmp_path)
assert loaded == state
def test_save_creates_repowise_dir(self, tmp_path):
save_state(tmp_path, {"key": "value"})
assert (tmp_path / ".repowise" / "state.json").exists()
# ---------------------------------------------------------------------------
# Git HEAD
# ---------------------------------------------------------------------------
class TestGetHeadCommit:
def test_non_git_returns_none(self, tmp_path):
assert get_head_commit(tmp_path) is None
# ---------------------------------------------------------------------------
# Provider validation
# ---------------------------------------------------------------------------
class TestValidateProviderConfig:
def test_no_provider_returns_empty_warnings(self, monkeypatch):
# Clear all provider env vars
monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
monkeypatch.delenv("OLLAMA_BASE_URL", raising=False)
monkeypatch.delenv("GEMINI_API_KEY", raising=False)
monkeypatch.delenv("GOOGLE_API_KEY", raising=False)
monkeypatch.delenv("REPOWISE_PROVIDER", raising=False)
assert validate_provider_config() == []
def test_anthropic_missing_key(self, monkeypatch):
monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)
monkeypatch.setenv("REPOWISE_PROVIDER", "anthropic")
warnings = validate_provider_config()
assert len(warnings) == 1
assert "anthropic" in warnings[0]
assert "ANTHROPIC_API_KEY" in warnings[0]
def test_anthropic_valid_key(self, monkeypatch):
monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-ant-test")
monkeypatch.setenv("REPOWISE_PROVIDER", "anthropic")
assert validate_provider_config() == []
def test_anthropic_empty_key(self, monkeypatch):
monkeypatch.setenv("ANTHROPIC_API_KEY", "")
monkeypatch.setenv("REPOWISE_PROVIDER", "anthropic")
warnings = validate_provider_config()
assert len(warnings) == 1
assert "ANTHROPIC_API_KEY" in warnings[0]
def test_openai_missing_key(self, monkeypatch):
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
monkeypatch.setenv("REPOWISE_PROVIDER", "openai")
warnings = validate_provider_config()
assert len(warnings) == 1
assert "openai" in warnings[0]
assert "OPENAI_API_KEY" in warnings[0]
def test_gemini_with_gemini_key(self, monkeypatch):
monkeypatch.setenv("GEMINI_API_KEY", "test-key")
monkeypatch.setenv("REPOWISE_PROVIDER", "gemini")
assert validate_provider_config() == []
def test_gemini_with_google_key(self, monkeypatch):
monkeypatch.setenv("GOOGLE_API_KEY", "test-key")
monkeypatch.setenv("REPOWISE_PROVIDER", "gemini")
assert validate_provider_config() == []
def test_gemini_missing_keys(self, monkeypatch):
monkeypatch.delenv("GEMINI_API_KEY", raising=False)
monkeypatch.delenv("GOOGLE_API_KEY", raising=False)
monkeypatch.setenv("REPOWISE_PROVIDER", "gemini")
warnings = validate_provider_config()
assert len(warnings) == 1
assert "gemini" in warnings[0]
def test_ollama_missing_url(self, monkeypatch):
monkeypatch.delenv("OLLAMA_BASE_URL", raising=False)
monkeypatch.setenv("REPOWISE_PROVIDER", "ollama")
warnings = validate_provider_config()
assert len(warnings) == 1
assert "ollama" in warnings[0]
assert "OLLAMA_BASE_URL" in warnings[0]
def test_unknown_provider(self, monkeypatch):
warnings = validate_provider_config("unknown")
assert len(warnings) == 1
assert "unknown provider" in warnings[0].lower()
def test_auto_detect_anthropic(self, monkeypatch):
monkeypatch.setenv("ANTHROPIC_API_KEY", "test")
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
monkeypatch.delenv("OLLAMA_BASE_URL", raising=False)
# Should not warn when env var is properly set
assert validate_provider_config() == []
def test_anthropic_empty_key_auto_detect(self, monkeypatch):
monkeypatch.setenv("ANTHROPIC_API_KEY", "")
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
monkeypatch.delenv("OLLAMA_BASE_URL", raising=False)
# Should warn when env var exists but is empty
warnings = validate_provider_config()
assert len(warnings) == 1
assert "anthropic" in warnings[0]
assert "ANTHROPIC_API_KEY" in warnings[0]
# --- litellm tests ---
def test_litellm_with_api_key(self, monkeypatch):
monkeypatch.setenv("LITELLM_API_KEY", "test-key")
monkeypatch.setenv("REPOWISE_PROVIDER", "litellm")
assert validate_provider_config() == []
def test_litellm_with_base_url(self, monkeypatch):
"""Local proxy without API key should be valid."""
monkeypatch.delenv("LITELLM_API_KEY", raising=False)
monkeypatch.setenv("LITELLM_BASE_URL", "http://localhost:4000/v1")
monkeypatch.setenv("REPOWISE_PROVIDER", "litellm")
assert validate_provider_config() == []
def test_litellm_missing_both(self, monkeypatch):
"""Should warn when neither API key nor base URL is set."""
monkeypatch.delenv("LITELLM_API_KEY", raising=False)
monkeypatch.delenv("LITELLM_BASE_URL", raising=False)
monkeypatch.setenv("REPOWISE_PROVIDER", "litellm")
warnings = validate_provider_config()
assert len(warnings) == 1
assert "litellm" in warnings[0]
assert "LITELLM_API_KEY" in warnings[0] or "LITELLM_BASE_URL" in warnings[0]