Files
codexpi/tests/test_project_settings.py

156 lines
8.0 KiB
Python
Raw Permalink Normal View History

2026-11-14 20:23:59 +01:00
from codexpi.database import Database
def register(logged_in):
client, app, projects, _, headers = logged_in
root = projects / 'effort'
root.mkdir()
project = client.post('/api/projects', headers=headers, json={'name': 'Effort', 'path': str(root)}).json()
return client, app, headers, project['id']
def test_effort_updates_existing_sessions_and_new_defaults(logged_in):
client, app, headers, project_id = register(logged_in)
url = f'/api/projects/{project_id}'
session = client.post(url + '/conversations', headers=headers, json={'title': 'Existing'}).json()
result = client.put(url + '/settings', headers=headers, json={'reasoning_effort': 'high'})
assert result.status_code == 200
assert client.get(url + '/settings').json()['reasoning_effort'] == 'high'
assert client.get(url + '/conversations').json()[0]['reasoning_effort'] == 'high'
created = client.post(url + '/conversations', headers=headers, json={'title': 'New'}).json()
assert created['reasoning_effort'] == 'high'
explicit = client.post(url + '/conversations', headers=headers, json={'title': 'Override', 'reasoning_effort': 'low'}).json()
assert explicit['reasoning_effort'] == 'low'
assert app.state.db.one('SELECT reasoning_effort FROM conversations WHERE id=?', (session['id'],))['reasoning_effort'] == 'high'
def test_project_effort_validates_models_and_csrf(logged_in):
client, app, headers, project_id = register(logged_in)
url = f'/api/projects/{project_id}'
client.post(url + '/conversations', headers=headers, json={'model': 'gpt-5.5'})
assert 'ultra' not in client.get(url + '/settings').json()['efforts']
for effort in ('invalid', 'ultra'):
assert client.put(url + '/settings', headers=headers, json={'reasoning_effort': effort}).status_code == 400
assert client.put(url + '/settings', json={'reasoning_effort': 'high'}).status_code == 403
assert client.get(url + '/settings').json()['reasoning_effort'] == 'medium'
def test_project_effort_migrates_existing_database(tmp_path):
import sqlite3
path = tmp_path / 'old.db'
with sqlite3.connect(path) as conn:
conn.execute('CREATE TABLE projects (id TEXT PRIMARY KEY, name TEXT, path TEXT, full_access INTEGER, created_at TEXT)')
conn.execute("INSERT INTO projects VALUES ('p','Demo','/tmp',0,'2026-01-01')")
db = Database(path)
db.initialize()
db.initialize()
assert db.one("SELECT reasoning_effort FROM projects WHERE id='p'")['reasoning_effort'] == 'medium'
def test_project_model_changes_existing_sessions_and_defaults(logged_in):
client, app, headers, project_id = register(logged_in)
url = f'/api/projects/{project_id}'
client.post(url + '/conversations', headers=headers, json={'model': 'gpt-5.5'})
result = client.put(url + '/settings', headers=headers, json={'model': 'gpt-5.6-sol', 'reasoning_effort': 'high'})
assert result.status_code == 200
assert client.get(url + '/settings').json()['model'] == 'gpt-5.6-sol'
assert client.get(url + '/conversations').json()[0]['model'] == 'gpt-5.6-sol'
assert client.post(url + '/conversations', headers=headers, json={}).json()['model'] == 'gpt-5.6-sol'
assert client.post(url + '/conversations', headers=headers, json={'model': ''}).json()['model'] is None
for model, effort in [('invalid', 'high'), ('gpt-5.5', 'ultra')]:
assert client.put(url + '/settings', headers=headers, json={'model': model, 'reasoning_effort': effort}).status_code == 400
assert client.get(url + '/settings').json()['model'] == 'gpt-5.6-sol'
assert client.put(url + '/settings', headers=headers, json={'model': '', 'reasoning_effort': 'medium'}).status_code == 200
assert all(row['model'] is None for row in client.get(url + '/conversations').json())
def test_model_default_limits_effort_before_first_session(logged_in):
client, app, headers, project_id = register(logged_in)
url = f'/api/projects/{project_id}/settings'
assert client.put(url, headers=headers, json={'model':'gpt-5.5', 'reasoning_effort':'high'}).status_code == 200
assert client.put(url, headers=headers, json={'reasoning_effort':'ultra'}).status_code == 400
assert 'ultra' not in client.get(url).json()['efforts']
def test_project_settings_update_name_and_source_files(logged_in):
client, app, headers, project_id = register(logged_in)
project = app.state.db.one('SELECT path FROM projects WHERE id=?', (project_id,))
root = __import__('pathlib').Path(project['path'])
(root / 'README.md').write_text('# Demo', encoding='utf-8')
(root / 'src').mkdir()
(root / 'src' / 'main.py').write_text('print("ok")', encoding='utf-8')
(root / '.git').mkdir()
(root / '.git' / 'config').write_text('secret', encoding='utf-8')
files = client.get(f'/api/projects/{project_id}/source-files').json()['files']
assert files == ['README.md', 'src/main.py']
result = client.put(
f'/api/projects/{project_id}/settings', headers=headers,
json={
'name': 'Projet renommé', 'reasoning_effort': 'medium',
'execution_group': 'tv',
'source_file': 'README.md', 'additional_files': ['src/main.py'],
},
)
assert result.status_code == 200
saved = client.get(f'/api/projects/{project_id}/settings').json()
assert saved['name'] == 'Projet renommé'
assert saved['execution_group'] == 'tv'
assert saved['source_file'] == 'README.md'
assert saved['additional_files'] == ['src/main.py']
listed = client.get('/api/projects').json()
assert next(item for item in listed if item['id'] == project_id)['additional_files'] == ['src/main.py']
def test_project_settings_reject_source_outside_project(logged_in):
client, _, headers, project_id = register(logged_in)
result = client.put(
f'/api/projects/{project_id}/settings', headers=headers,
json={'reasoning_effort': 'medium', 'source_file': '../secret', 'additional_files': []},
)
assert result.status_code == 400
def test_standby_api_does_not_recover_workers(tmp_path, monkeypatch):
from codexpi.main import should_recover_workers
monkeypatch.setenv('CODEXPI_SKIP_WORKER_RECOVERY', 'true')
assert should_recover_workers() is False
monkeypatch.delenv('CODEXPI_SKIP_WORKER_RECOVERY')
assert should_recover_workers() is True
def test_remote_project_accepts_local_reference_from_file_browser(logged_in):
client, app, headers, project_id = register(logged_in)
project = app.state.db.one('SELECT path FROM projects WHERE id=?', (project_id,))
root = __import__('pathlib').Path(project['path'])
reference = root / 'instructions.md'
reference.write_text('Contexte local', encoding='utf-8')
app.state.db.execute("UPDATE projects SET kind='remote' WHERE id=?", (project_id,))
listing = client.get(f'/api/filesystem/files?path={root}').json()
assert listing['files'] == [{'name': 'instructions.md', 'path': str(reference), 'size': 14}]
saved = client.put(
f'/api/projects/{project_id}/settings', headers=headers,
json={'reasoning_effort': 'medium', 'source_file': '', 'additional_files': [str(reference)]},
)
assert saved.status_code == 200
assert client.get(f'/api/projects/{project_id}/settings').json()['additional_files'] == [str(reference)]
def test_remote_project_accepts_local_directory_as_source(logged_in):
client, app, headers, project_id = register(logged_in)
project = app.state.db.one('SELECT path FROM projects WHERE id=?', (project_id,))
root = __import__('pathlib').Path(project['path'])
source_directory = root / 'documentation'
source_directory.mkdir()
(source_directory / 'guide.md').write_text('Guide local', encoding='utf-8')
app.state.db.execute("UPDATE projects SET kind='remote' WHERE id=?", (project_id,))
saved = client.put(
f'/api/projects/{project_id}/settings', headers=headers,
json={'reasoning_effort': 'medium', 'source_file': '', 'additional_files': [str(source_directory)]},
)
assert saved.status_code == 200
assert client.get(f'/api/projects/{project_id}/settings').json()['additional_files'] == [str(source_directory)]