146 lines
4.3 KiB
Python
146 lines
4.3 KiB
Python
import json
|
|
import threading
|
|
import urllib.error
|
|
import urllib.request
|
|
|
|
import pytest
|
|
|
|
from wov_sdk.models import InvokeRequest, InvokeResponse, NodeManifest
|
|
from wov_sdk.server import NodeHTTPServer, create_node_server, run_node
|
|
|
|
|
|
def valid_manifest() -> NodeManifest:
|
|
return NodeManifest(
|
|
id="echo",
|
|
name="Echo",
|
|
version="1.0.0",
|
|
capability="echo",
|
|
command=["python", "-m", "echo"],
|
|
)
|
|
|
|
|
|
def test_create_node_server_validates_manifest() -> None:
|
|
with pytest.raises(ValueError):
|
|
create_node_server(
|
|
NodeManifest(
|
|
id="",
|
|
name="",
|
|
version="",
|
|
capability="",
|
|
command=[],
|
|
),
|
|
lambda request: InvokeResponse(status="completed"),
|
|
)
|
|
|
|
|
|
def _post(server: NodeHTTPServer, path: str, body: dict | None = None) -> tuple[int, dict]:
|
|
data = json.dumps(body or {}).encode("utf-8")
|
|
request = urllib.request.Request(
|
|
f"http://127.0.0.1:{server.server_address[1]}{path}",
|
|
data=data,
|
|
headers={"Content-Type": "application/json"},
|
|
method="POST",
|
|
)
|
|
try:
|
|
with urllib.request.urlopen(request, timeout=5) as response:
|
|
return response.status, json.loads(response.read().decode("utf-8"))
|
|
except urllib.error.HTTPError as exc:
|
|
return exc.code, json.loads(exc.read().decode("utf-8"))
|
|
|
|
|
|
def _get(server: NodeHTTPServer, path: str) -> tuple[int, dict]:
|
|
try:
|
|
with urllib.request.urlopen(
|
|
f"http://127.0.0.1:{server.server_address[1]}{path}", timeout=5
|
|
) as response:
|
|
return response.status, json.loads(response.read().decode("utf-8"))
|
|
except urllib.error.HTTPError as exc:
|
|
return exc.code, json.loads(exc.read().decode("utf-8"))
|
|
|
|
|
|
def test_http_protocol() -> None:
|
|
def handler(request: InvokeRequest) -> InvokeResponse:
|
|
return InvokeResponse(
|
|
status="completed",
|
|
outputs={"text": request.inputs.get("text", "")},
|
|
)
|
|
|
|
server = create_node_server(valid_manifest(), handler)
|
|
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
|
thread.start()
|
|
try:
|
|
status, health = _get(server, "/health")
|
|
assert status == 200
|
|
assert health["node_id"] == "echo"
|
|
|
|
status, invoked = _post(
|
|
server,
|
|
"/invoke",
|
|
{
|
|
"run_id": "run_1",
|
|
"node_instance_id": "ni_1",
|
|
"inputs": {"text": "hello"},
|
|
"output_dir": "out",
|
|
},
|
|
)
|
|
assert status == 200
|
|
assert invoked["outputs"]["text"] == "hello"
|
|
|
|
status, not_found = _get(server, "/missing")
|
|
assert status == 404
|
|
assert not_found["status"] == "error"
|
|
|
|
status, not_found = _post(server, "/missing", {"run_id": "r"})
|
|
assert status == 404
|
|
|
|
status, bad_body = _post(server, "/invoke", {})
|
|
assert status == 500
|
|
assert bad_body["status"] == "failed"
|
|
finally:
|
|
server.shutdown()
|
|
server.server_close()
|
|
thread.join(timeout=5)
|
|
|
|
|
|
def test_http_protocol_handler_error() -> None:
|
|
def failing_handler(request: InvokeRequest) -> InvokeResponse:
|
|
raise RuntimeError("boom")
|
|
|
|
server = create_node_server(valid_manifest(), failing_handler)
|
|
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
|
thread.start()
|
|
try:
|
|
status, payload = _post(
|
|
server,
|
|
"/invoke",
|
|
{
|
|
"run_id": "run_1",
|
|
"node_instance_id": "ni_1",
|
|
"inputs": {},
|
|
"output_dir": "out",
|
|
},
|
|
)
|
|
assert status == 500
|
|
assert payload["status"] == "failed"
|
|
assert "boom" in payload["error"]
|
|
finally:
|
|
server.shutdown()
|
|
server.server_close()
|
|
thread.join(timeout=5)
|
|
|
|
|
|
def test_run_node(monkeypatch, capsys) -> None:
|
|
called = []
|
|
|
|
def fake_serve_forever(server: NodeHTTPServer) -> None:
|
|
called.append(server.server_address[1])
|
|
server.server_close()
|
|
|
|
monkeypatch.setattr(NodeHTTPServer, "serve_forever", fake_serve_forever)
|
|
run_node(
|
|
valid_manifest(),
|
|
lambda request: InvokeResponse(status="completed"),
|
|
)
|
|
assert len(called) == 1
|
|
assert "WOV_NODE_READY" in capsys.readouterr().out
|