Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ playwright-report/
test-results/
pytest-of-patch/
.output.txt

# Coverage reports
.coverage
.coverage.*
htmlcov/
coverage.xml
*.cover
# Accidental directories from env expansion
(pwd)/
sqlite:/
Expand Down
2 changes: 1 addition & 1 deletion dev
Submodule dev updated from 1d7cf4 to 362dd7
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ dev = [
"playwright==1.40.0",
"requests>=2.32",
"beautifulsoup4>=4.12",
"coverage>=7.4",
]

# Optional LLM provider (only needed if you enable Graphrag LLM features)
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ pytest>=7.4
pytest-playwright==0.4.3
playwright==1.40.0
requests>=2.32
coverage>=7.4
73 changes: 73 additions & 0 deletions tests/test_chat_api.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import os
import pytest


def test_api_chat_echo(client):
# First message
resp1 = client.post('/api/chat', json={'message': 'Hello'})
Expand All @@ -11,3 +15,72 @@ def test_api_chat_echo(client):
resp2 = client.post('/api/chat', json={'message': 'How are you?'})
data2 = resp2.get_json()
assert len(data2['history']) == 4


def test_api_chat_missing_message(client):
resp = client.post('/api/chat', json={})
assert resp.status_code == 400
data = resp.get_json()
assert data['status'] == 'error'
assert 'message required' in data['error']


def test_api_chat_history(client):
# Post a message first
client.post('/api/chat', json={'message': 'Test'})

# Get history
resp = client.get('/api/chat/history')
assert resp.status_code == 200
data = resp.get_json()
assert data['status'] == 'ok'
assert 'history' in data
assert len(data['history']) >= 2


def test_api_chat_capabilities(client):
resp = client.get('/api/chat/capabilities')
assert resp.status_code == 200
data = resp.get_json()
assert 'graphrag' in data
assert 'enabled' in data['graphrag']
assert 'llm_provider' in data['graphrag']
assert 'model' in data['graphrag']


def test_api_chat_graphrag_disabled_by_default(client):
# GraphRAG should be disabled without SCIDK_GRAPHRAG_ENABLED
resp = client.post('/api/chat/graphrag', json={'message': 'Test query'})
assert resp.status_code == 501
data = resp.get_json()
assert data['status'] == 'disabled'
assert 'SCIDK_GRAPHRAG_ENABLED' in data.get('hint', '')


def test_api_chat_graphrag_missing_message(client, monkeypatch):
monkeypatch.setenv('SCIDK_GRAPHRAG_ENABLED', '1')
resp = client.post('/api/chat/graphrag', json={})
assert resp.status_code == 400
data = resp.get_json()
assert data['status'] == 'error'
assert 'message required' in data['error']


def test_api_chat_context_refresh_disabled(client):
resp = client.post('/api/chat/context/refresh')
assert resp.status_code == 501
data = resp.get_json()
assert data['status'] == 'disabled'


def test_api_chat_observability_graphrag(client):
resp = client.get('/api/chat/observability/graphrag')
assert resp.status_code == 200
data = resp.get_json()
assert data['status'] == 'ok'
assert 'enabled' in data
assert 'llm_provider' in data
assert 'model' in data
assert 'schema' in data
assert 'audit' in data
assert isinstance(data['audit'], list)
40 changes: 40 additions & 0 deletions tests/test_interpreters_registry_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,43 @@ def test_api_interpreters_schema():
for it in eff:
assert 'enabled' in it
assert 'source' in it


def test_api_interpreters_effective_debug(client):
resp = client.get('/api/interpreters/effective_debug')
assert resp.status_code == 200
data = resp.get_json()
assert 'source' in data
assert 'effective_enabled' in data
assert 'default_enabled' in data
assert 'loaded_settings' in data
assert 'env' in data
assert isinstance(data['effective_enabled'], list)
assert isinstance(data['default_enabled'], list)


def test_api_interpreters_toggle_enable(client):
# Enable an interpreter
resp = client.post('/api/interpreters/csv/toggle', json={'enabled': True})
assert resp.status_code == 200
data = resp.get_json()
assert data['status'] == 'updated'
assert data['enabled'] is True


def test_api_interpreters_toggle_disable(client):
# Disable an interpreter
resp = client.post('/api/interpreters/csv/toggle', json={'enabled': False})
assert resp.status_code == 200
data = resp.get_json()
assert data['status'] == 'updated'
assert data['enabled'] is False


def test_api_interpreters_toggle_default_enabled(client):
# Toggle without explicit enabled flag (defaults to True)
resp = client.post('/api/interpreters/python_code/toggle', json={})
assert resp.status_code == 200
data = resp.get_json()
assert data['status'] == 'updated'
assert data['enabled'] is True
63 changes: 63 additions & 0 deletions tests/test_providers_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,66 @@ def test_browse_local_root(client, tmp_path: Path):
entries = data.get('entries', [])
names = {e['name'] for e in entries}
assert 'a.txt' in names


def test_provider_roots_default(client):
# Test default provider (local_fs)
resp = client.get('/api/provider_roots')
assert resp.status_code == 200
data = resp.get_json()
assert isinstance(data, list)
assert len(data) > 0
# Validate structure
for root in data:
assert 'id' in root
assert 'name' in root
assert 'path' in root


def test_provider_roots_specific_provider(client):
resp = client.get('/api/provider_roots?provider_id=local_fs')
assert resp.status_code == 200
data = resp.get_json()
assert isinstance(data, list)


def test_provider_roots_invalid_provider(client):
resp = client.get('/api/provider_roots?provider_id=nonexistent')
assert resp.status_code == 400
data = resp.get_json()
assert 'error' in data


def test_rclone_mounts_list(client):
# Should return empty list initially (or existing mounts)
resp = client.get('/api/rclone/mounts')
assert resp.status_code == 200
data = resp.get_json()
assert isinstance(data, list)


def test_rclone_mounts_create_missing_rclone(client, monkeypatch):
# Mock rclone not available
monkeypatch.setattr('shutil.which', lambda x: None)
resp = client.post('/api/rclone/mounts', json={'remote': 'test:', 'name': 'testmount'})
assert resp.status_code == 400
data = resp.get_json()
assert 'rclone not installed' in data['error']


def test_rclone_mounts_create_missing_remote(client, monkeypatch):
# Mock rclone available so we can test validation logic
monkeypatch.setattr('scidk.web.routes.api_providers.shutil.which', lambda x: '/usr/bin/rclone')
resp = client.post('/api/rclone/mounts', json={'name': 'testmount'})
assert resp.status_code == 400
data = resp.get_json()
assert 'remote required' in data['error']


def test_rclone_mounts_create_missing_name(client, monkeypatch):
# Mock rclone available so we can test validation logic
monkeypatch.setattr('scidk.web.routes.api_providers.shutil.which', lambda x: '/usr/bin/rclone')
resp = client.post('/api/rclone/mounts', json={'remote': 'test:'})
assert resp.status_code == 400
data = resp.get_json()
assert 'name required' in data['error']