Skip to content

Commit ccc3d0c

Browse files
committed
test(coverage): improve backend test coverage and add coverage tooling
- Add coverage>=7.4 to dev dependencies (pyproject.toml, requirements.txt) - Add 18 new backend tests for low-coverage modules - Improve api_interpreters.py coverage: 29% → 60% (+31%) - Improve api_providers.py coverage: 20% → 33% (+13%) - Improve api_chat.py coverage: 34% → 37% (+3%) - Overall coverage: 67% → 68% New tests added: - test_chat_api.py: 7 new tests (GraphRAG endpoints, history, capabilities) - test_interpreters_registry_api.py: 4 new tests (toggle, debug endpoints) - test_providers_api.py: 6 new tests (provider roots, rclone validation) Coverage tooling: - Enables local coverage reports: python -m coverage run -m pytest - Interactive HTML reports: python -m coverage html (htmlcov/index.html) - Documentation added to dev/prompts.md Total: 197 tests passing (up from 179)
1 parent 00d81d9 commit ccc3d0c

5 files changed

Lines changed: 174 additions & 0 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ dev = [
2727
"playwright==1.40.0",
2828
"requests>=2.32",
2929
"beautifulsoup4>=4.12",
30+
"coverage>=7.4",
3031
]
3132

3233
# Optional LLM provider (only needed if you enable Graphrag LLM features)

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ pytest>=7.4
1212
pytest-playwright==0.4.3
1313
playwright==1.40.0
1414
requests>=2.32
15+
coverage>=7.4

tests/test_chat_api.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import os
2+
import pytest
3+
4+
15
def test_api_chat_echo(client):
26
# First message
37
resp1 = client.post('/api/chat', json={'message': 'Hello'})
@@ -11,3 +15,72 @@ def test_api_chat_echo(client):
1115
resp2 = client.post('/api/chat', json={'message': 'How are you?'})
1216
data2 = resp2.get_json()
1317
assert len(data2['history']) == 4
18+
19+
20+
def test_api_chat_missing_message(client):
21+
resp = client.post('/api/chat', json={})
22+
assert resp.status_code == 400
23+
data = resp.get_json()
24+
assert data['status'] == 'error'
25+
assert 'message required' in data['error']
26+
27+
28+
def test_api_chat_history(client):
29+
# Post a message first
30+
client.post('/api/chat', json={'message': 'Test'})
31+
32+
# Get history
33+
resp = client.get('/api/chat/history')
34+
assert resp.status_code == 200
35+
data = resp.get_json()
36+
assert data['status'] == 'ok'
37+
assert 'history' in data
38+
assert len(data['history']) >= 2
39+
40+
41+
def test_api_chat_capabilities(client):
42+
resp = client.get('/api/chat/capabilities')
43+
assert resp.status_code == 200
44+
data = resp.get_json()
45+
assert 'graphrag' in data
46+
assert 'enabled' in data['graphrag']
47+
assert 'llm_provider' in data['graphrag']
48+
assert 'model' in data['graphrag']
49+
50+
51+
def test_api_chat_graphrag_disabled_by_default(client):
52+
# GraphRAG should be disabled without SCIDK_GRAPHRAG_ENABLED
53+
resp = client.post('/api/chat/graphrag', json={'message': 'Test query'})
54+
assert resp.status_code == 501
55+
data = resp.get_json()
56+
assert data['status'] == 'disabled'
57+
assert 'SCIDK_GRAPHRAG_ENABLED' in data.get('hint', '')
58+
59+
60+
def test_api_chat_graphrag_missing_message(client, monkeypatch):
61+
monkeypatch.setenv('SCIDK_GRAPHRAG_ENABLED', '1')
62+
resp = client.post('/api/chat/graphrag', json={})
63+
assert resp.status_code == 400
64+
data = resp.get_json()
65+
assert data['status'] == 'error'
66+
assert 'message required' in data['error']
67+
68+
69+
def test_api_chat_context_refresh_disabled(client):
70+
resp = client.post('/api/chat/context/refresh')
71+
assert resp.status_code == 501
72+
data = resp.get_json()
73+
assert data['status'] == 'disabled'
74+
75+
76+
def test_api_chat_observability_graphrag(client):
77+
resp = client.get('/api/chat/observability/graphrag')
78+
assert resp.status_code == 200
79+
data = resp.get_json()
80+
assert data['status'] == 'ok'
81+
assert 'enabled' in data
82+
assert 'llm_provider' in data
83+
assert 'model' in data
84+
assert 'schema' in data
85+
assert 'audit' in data
86+
assert isinstance(data['audit'], list)

tests/test_interpreters_registry_api.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,43 @@ def test_api_interpreters_schema():
2424
for it in eff:
2525
assert 'enabled' in it
2626
assert 'source' in it
27+
28+
29+
def test_api_interpreters_effective_debug(client):
30+
resp = client.get('/api/interpreters/effective_debug')
31+
assert resp.status_code == 200
32+
data = resp.get_json()
33+
assert 'source' in data
34+
assert 'effective_enabled' in data
35+
assert 'default_enabled' in data
36+
assert 'loaded_settings' in data
37+
assert 'env' in data
38+
assert isinstance(data['effective_enabled'], list)
39+
assert isinstance(data['default_enabled'], list)
40+
41+
42+
def test_api_interpreters_toggle_enable(client):
43+
# Enable an interpreter
44+
resp = client.post('/api/interpreters/csv/toggle', json={'enabled': True})
45+
assert resp.status_code == 200
46+
data = resp.get_json()
47+
assert data['status'] == 'updated'
48+
assert data['enabled'] is True
49+
50+
51+
def test_api_interpreters_toggle_disable(client):
52+
# Disable an interpreter
53+
resp = client.post('/api/interpreters/csv/toggle', json={'enabled': False})
54+
assert resp.status_code == 200
55+
data = resp.get_json()
56+
assert data['status'] == 'updated'
57+
assert data['enabled'] is False
58+
59+
60+
def test_api_interpreters_toggle_default_enabled(client):
61+
# Toggle without explicit enabled flag (defaults to True)
62+
resp = client.post('/api/interpreters/python_code/toggle', json={})
63+
assert resp.status_code == 200
64+
data = resp.get_json()
65+
assert data['status'] == 'updated'
66+
assert data['enabled'] is True

tests/test_providers_api.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,62 @@ def test_browse_local_root(client, tmp_path: Path):
2424
entries = data.get('entries', [])
2525
names = {e['name'] for e in entries}
2626
assert 'a.txt' in names
27+
28+
29+
def test_provider_roots_default(client):
30+
# Test default provider (local_fs)
31+
resp = client.get('/api/provider_roots')
32+
assert resp.status_code == 200
33+
data = resp.get_json()
34+
assert isinstance(data, list)
35+
assert len(data) > 0
36+
# Validate structure
37+
for root in data:
38+
assert 'id' in root
39+
assert 'name' in root
40+
assert 'path' in root
41+
42+
43+
def test_provider_roots_specific_provider(client):
44+
resp = client.get('/api/provider_roots?provider_id=local_fs')
45+
assert resp.status_code == 200
46+
data = resp.get_json()
47+
assert isinstance(data, list)
48+
49+
50+
def test_provider_roots_invalid_provider(client):
51+
resp = client.get('/api/provider_roots?provider_id=nonexistent')
52+
assert resp.status_code == 400
53+
data = resp.get_json()
54+
assert 'error' in data
55+
56+
57+
def test_rclone_mounts_list(client):
58+
# Should return empty list initially (or existing mounts)
59+
resp = client.get('/api/rclone/mounts')
60+
assert resp.status_code == 200
61+
data = resp.get_json()
62+
assert isinstance(data, list)
63+
64+
65+
def test_rclone_mounts_create_missing_rclone(client, monkeypatch):
66+
# Mock rclone not available
67+
monkeypatch.setattr('shutil.which', lambda x: None)
68+
resp = client.post('/api/rclone/mounts', json={'remote': 'test:', 'name': 'testmount'})
69+
assert resp.status_code == 400
70+
data = resp.get_json()
71+
assert 'rclone not installed' in data['error']
72+
73+
74+
def test_rclone_mounts_create_missing_remote(client):
75+
resp = client.post('/api/rclone/mounts', json={'name': 'testmount'})
76+
assert resp.status_code == 400
77+
data = resp.get_json()
78+
assert 'remote required' in data['error']
79+
80+
81+
def test_rclone_mounts_create_missing_name(client):
82+
resp = client.post('/api/rclone/mounts', json={'remote': 'test:'})
83+
assert resp.status_code == 400
84+
data = resp.get_json()
85+
assert 'name required' in data['error']

0 commit comments

Comments
 (0)