7f4ceaab2b
One ASGI app fronting BOTH the memory.* and affect.* planes (:8392), so a single bound Worldtree session both remembers AND shows live PAD. Closes #18 end-to-end (D2 PAD read-endpoint shipped v0.17.14; D1 was bifrost-blocked, now unparked by bifrost 0.10.0's public build_combined_app + FR-1 resolved — zero Worldtree change). - provider/combined.py: build_combined_provider_app wraps bifrost.consumer.build_combined_app over both stores + mounts the shared affect read route. Advertises both caps by store presence; per-route call-time isolation is bifrost's (INV-013). - affect_store.py: extract add_affect_read_route shared helper (the D2 INV-007 promise — composite + standalone mount the SAME read route over the same affect.db, INV-011). - opfeed.py: plane='combined' derives the OpEvent plane per request path (memory-call->memory, affect-call->affect, handshake->combined; INV-012). - serve_combined.py + ratatoskr-combined-provider console script on :8392 (additive — standalone :8390/:8391 untouched, INV-014). - contract: 18.contract.md § Deliverable 1 (INV-009..INV-014); D1 un-deferred. Latent bug fixed (exposed by the contract-mandated memory `search` dispatch test running through TestClient = a worker thread): open_memory_store lacked check_same_thread=False — the SAME sqlite thread-safety bug already fixed in the affect store (D2). The composite serves the memory plane over HTTP, so a memory-call on uvicorn's threadpool would trip it. Fix: check_same_thread=False + PRAGMA busy_timeout=5000 (memory contract Concurrency note). heid-code-review panel (Groa/Hulda/Regin): ZERO drift findings; the implementation matches INV-009..INV-014 at function-block level. Folded the genuine test-fidelity fix (memory leg describe_store -> search per the contract TEST) + added the PRE-001/PRE-002 guard tests. Suite 486 -> 502 green.
45 lines
1.4 KiB
Python
45 lines
1.4 KiB
Python
"""Tests for the combined-provider serve entrypoint (ratatoskr.provider.serve_combined).
|
|
|
|
Only the env -> app seam is unit-tested; uvicorn.run is the untestable shell.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from ratatoskr.provider.serve_combined import build_combined_app_from_env
|
|
|
|
_ENV = {
|
|
"RATATOSKR_HEIMDALL_KEY": "shared-secret",
|
|
"RATATOSKR_MEMORY_EMBEDDING_DIM": "8",
|
|
"RATATOSKR_AFFECT_DB": ":memory:",
|
|
"RATATOSKR_MEMORY_DB": ":memory:",
|
|
}
|
|
|
|
|
|
def test_requires_heimdall_key():
|
|
env = {k: v for k, v in _ENV.items() if k != "RATATOSKR_HEIMDALL_KEY"}
|
|
with pytest.raises(RuntimeError):
|
|
build_combined_app_from_env(env)
|
|
|
|
|
|
def test_requires_embedding_dim():
|
|
env = {k: v for k, v in _ENV.items() if k != "RATATOSKR_MEMORY_EMBEDDING_DIM"}
|
|
with pytest.raises(RuntimeError):
|
|
build_combined_app_from_env(env)
|
|
|
|
|
|
def test_builds_app_with_all_routes():
|
|
app = build_combined_app_from_env(dict(_ENV))
|
|
paths = {getattr(r, "path", None) for r in app.routes}
|
|
assert "/bifrost/handshake" in paths
|
|
assert "/bifrost/memory-call" in paths
|
|
assert "/bifrost/affect-call" in paths
|
|
assert "/affect/state/{agent_id}" in paths
|
|
|
|
|
|
def test_opfeed_path_wraps_app(tmp_path):
|
|
env = dict(_ENV)
|
|
env["RATATOSKR_OPFEED_PATH"] = str(tmp_path / "ops.jsonl")
|
|
app = build_combined_app_from_env(env)
|
|
assert not hasattr(app, "routes") # wrapped: a bare ASGI callable (plane='combined')
|