ouroboros / tests /test_server.py
REXWind's picture
Improve hard examples and default sampling in showcase
81330fa verified
Raw History Blame Contribute Delete
4.32 kB
import threading
import time
import pytest
from fastapi import HTTPException
from fastapi.testclient import TestClient
from server import GenerationRequest, InferenceEngine, create_app, normalize_input
def test_protein_validation_preserves_one_sequence_and_reports_normalization():
request = GenerationRequest(direction='p2t', input='>candidate\nmaK uZob\nACD')
assert request.greedy is False
assert request.temperature == 1
assert request.max_length == 512
normalized, note = normalize_input(request)
assert normalized == 'MAKXXXXACD'
assert note
for sequence in ['>one\nMAK\n>two\nMAK', 'MAK123', 'A' * 512, '>header']:
with pytest.raises(HTTPException) as error:
normalize_input(GenerationRequest(direction='p2t', input=sequence))
assert error.value.status_code == 422
def test_api_rejects_invalid_requests_and_does_not_fake_preview_results():
with TestClient(create_app(InferenceEngine(disabled=True))) as client:
assert client.get('/api/health').json()['inference'] == 'disabled'
assert client.post('/api/jobs', json={'direction': 't2p', 'input': 'PET hydrolase'}).status_code == 503
for payload in [
{'direction': 'invalid', 'input': 'abc'},
{'direction': 't2p', 'input': ' '},
{'direction': 't2p', 'input': 'x', 'max_length': 10000},
{'direction': 'p2t', 'input': 'MAK9'},
]:
assert client.post('/api/jobs', json=payload).status_code == 422
assert client.get('/api/jobs/nonexistent').status_code == 404
def test_queue_is_bounded():
engine = InferenceEngine(disabled=False)
for _ in range(5):
engine.submit(GenerationRequest(direction='t2p', input='Hydrolase'))
with pytest.raises(HTTPException) as error:
engine.submit(GenerationRequest(direction='t2p', input='Hydrolase'))
assert error.value.status_code == 429
assert len(engine.jobs) == 5
class RecordingRunner:
def __init__(self):
self.loads = 0
self.active = 0
self.max_active = 0
self.calls = []
def load(self):
self.loads += 1
def generate(self, request, sequence):
self.active += 1
self.max_active = max(self.active, self.max_active)
self.calls.append((request.direction, sequence))
time.sleep(.03)
self.active -= 1
if sequence == 'fail':
raise ValueError('Please shorten the description.')
return 'MAK' if request.direction == 't2p' else 'A test annotation.'
def test_jobs_run_serially_reuse_model_and_preserve_input_provenance():
runner = RecordingRunner()
with TestClient(create_app(InferenceEngine(runner, disabled=False))) as client:
first = client.post('/api/jobs', json={'direction': 't2p', 'input': 'Hydrolase'}).json()['id']
second = client.post('/api/jobs', json={'direction': 'p2t', 'input': '>candidate\nma u'}).json()['id']
deadline = time.monotonic() + 3
while time.monotonic() < deadline:
b = client.get('/api/jobs/' + second).json()
if b['status'] == 'completed':
break
time.sleep(.02)
a = client.get('/api/jobs/' + first).json()
assert a['status'] == b['status'] == 'completed'
assert a['output'] == 'MAK'
assert b['normalized_input'] == 'MAX'
assert b['normalization_note']
assert runner.loads == 1 and runner.max_active == 1
assert runner.calls == [('t2p', 'Hydrolase'), ('p2t', 'MAX')]
def test_failed_generation_does_not_become_a_success_or_poison_the_model():
runner = RecordingRunner()
engine = InferenceEngine(runner, disabled=False)
engine.start()
try:
first = engine.submit(GenerationRequest(direction='t2p', input='fail'))['id']
second = engine.submit(GenerationRequest(direction='t2p', input='hydrolase'))['id']
deadline = time.monotonic() + 3
while time.monotonic() < deadline and engine.read(second)['status'] != 'completed':
time.sleep(.02)
assert engine.read(first)['status'] == 'failed'
assert engine.read(first)['output'] is None
assert engine.read(second)['status'] == 'completed'
assert runner.loads == 1
finally:
engine.close()