mirror of
https://github.com/deepmodeling/Uni-Lab-OS
synced 2026-05-23 19:09:57 +00:00
135 lines
4.3 KiB
Python
135 lines
4.3 KiB
Python
"""Tests for /interpret and /interpret/schema API endpoints."""
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
from ..server import app
|
|
|
|
client = TestClient(app)
|
|
|
|
|
|
def test_interpret_reachable_by():
|
|
resp = client.post("/interpret", json={
|
|
"intents": [
|
|
{
|
|
"intent": "reachable_by",
|
|
"params": {
|
|
"arm": "arm_slider",
|
|
"targets": ["opentrons_liquid_handler", "inheco_odtc_96xl"],
|
|
},
|
|
"description": "Arm must reach liquid handler and thermal cycler",
|
|
}
|
|
]
|
|
})
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert len(data["constraints"]) == 2
|
|
assert all(c["rule_name"] == "reachability" for c in data["constraints"])
|
|
assert len(data["translations"]) == 1
|
|
assert data["translations"][0]["source_intent"] == "reachable_by"
|
|
assert len(data["errors"]) == 0
|
|
|
|
|
|
def test_interpret_pcr_workflow():
|
|
"""Full PCR: reachability + workflow_hint + close_together."""
|
|
resp = client.post("/interpret", json={
|
|
"intents": [
|
|
{
|
|
"intent": "reachable_by",
|
|
"params": {
|
|
"arm": "arm_slider",
|
|
"targets": [
|
|
"opentrons_liquid_handler",
|
|
"inheco_odtc_96xl",
|
|
"agilent_plateloc",
|
|
"thermo_orbitor_rs2_hotel",
|
|
],
|
|
},
|
|
},
|
|
{
|
|
"intent": "workflow_hint",
|
|
"params": {
|
|
"workflow": "pcr",
|
|
"devices": [
|
|
"opentrons_liquid_handler",
|
|
"inheco_odtc_96xl",
|
|
"agilent_plateloc",
|
|
"thermo_orbitor_rs2_hotel",
|
|
],
|
|
},
|
|
},
|
|
{
|
|
"intent": "close_together",
|
|
"params": {
|
|
"devices": ["opentrons_liquid_handler", "inheco_odtc_96xl"],
|
|
"priority": "high",
|
|
},
|
|
},
|
|
]
|
|
})
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
# 4 reachability + 3 workflow + 1 close = 8
|
|
assert len(data["constraints"]) == 8
|
|
assert len(data["workflow_edges"]) == 3
|
|
assert len(data["translations"]) == 3
|
|
assert len(data["errors"]) == 0
|
|
|
|
|
|
def test_interpret_returns_errors_for_bad_intents():
|
|
resp = client.post("/interpret", json={
|
|
"intents": [
|
|
{"intent": "reachable_by", "params": {}},
|
|
{"intent": "nonexistent_intent"},
|
|
]
|
|
})
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert len(data["errors"]) == 2
|
|
assert len(data["constraints"]) == 0
|
|
|
|
|
|
def test_interpret_empty_intents():
|
|
resp = client.post("/interpret", json={"intents": []})
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert data["constraints"] == []
|
|
assert data["translations"] == []
|
|
assert data["errors"] == []
|
|
|
|
|
|
def test_interpret_schema_returns_all_intents():
|
|
resp = client.get("/interpret/schema")
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
intents = data["intents"]
|
|
expected = {
|
|
"reachable_by", "close_together", "far_apart",
|
|
"max_distance", "min_distance", "min_spacing",
|
|
"workflow_hint", "face_outward", "face_inward", "align_cardinal",
|
|
}
|
|
assert set(intents.keys()) == expected
|
|
|
|
|
|
def test_interpret_constraints_passable_to_optimize():
|
|
"""Constraints from /interpret should be directly usable in /optimize."""
|
|
# Step 1: interpret
|
|
interpret_resp = client.post("/interpret", json={
|
|
"intents": [
|
|
{"intent": "close_together", "params": {"devices": ["dev_a", "dev_b"]}},
|
|
]
|
|
})
|
|
constraints = interpret_resp.json()["constraints"]
|
|
|
|
# Step 2: pass to optimize (verify it accepts the format)
|
|
optimize_resp = client.post("/optimize", json={
|
|
"devices": [
|
|
{"id": "dev_a", "name": "Device A", "size": [0.5, 0.4]},
|
|
{"id": "dev_b", "name": "Device B", "size": [0.5, 0.4]},
|
|
],
|
|
"lab": {"width": 4.0, "depth": 3.0},
|
|
"constraints": constraints,
|
|
"run_de": False,
|
|
})
|
|
assert optimize_resp.status_code == 200
|
|
assert len(optimize_resp.json()["placements"]) == 2
|