Files
ratatoskr/tests/test_provider_serve_combined.py
vh 7f4ceaab2b feat(#18): composite Bifrost endpoint — build_combined_app (Deliverable 1)
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.
2026-06-19 23:32:47 -07:00

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')