Add 4 red tests for Phase 2: shared node pool + contextvar HUD

Tests that will pass once implemented:
- pool_creates_shared_nodes: NodePool has shared stateless nodes
- pool_excludes_stateful: sensor/memorizer/ui not shared
- pool_reuses_instances: same pool returns same objects
- contextvar_hud_isolation: concurrent tasks get isolated HUD

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nico 2026-04-03 18:35:48 +02:00
parent b4031611e2
commit d3350fd502

View File

@ -526,6 +526,76 @@ def test_model_override_per_request():
assert result["trace"]["path"] == "reflex"
# --- Phase 2: Shared Node Pool (RED — will fail until implemented) ---
def test_pool_creates_shared_nodes():
"""NodePool creates shared instances for stateless nodes."""
from agent.node_pool import NodePool
pool = NodePool("v4-eras")
# Shared nodes should exist
assert "input" in pool.shared, "input should be shared"
assert "output" in pool.shared, "output should be shared"
assert "pa" in pool.shared, "pa should be shared"
assert "expert_eras" in pool.shared, "expert_eras should be shared"
assert "interpreter" in pool.shared, "interpreter should be shared"
def test_pool_excludes_stateful():
"""NodePool excludes stateful nodes (sensor, memorizer, ui)."""
from agent.node_pool import NodePool
pool = NodePool("v4-eras")
assert "sensor" not in pool.shared, "sensor should NOT be shared"
assert "memorizer" not in pool.shared, "memorizer should NOT be shared"
assert "ui" not in pool.shared, "ui should NOT be shared"
def test_pool_reuses_instances():
"""Two Runtimes using the same pool share node objects."""
from agent.node_pool import NodePool
pool = NodePool("v4-eras")
# Same pool → same node instances
input1 = pool.shared["input"]
input2 = pool.shared["input"]
assert input1 is input2, "pool should return same instance"
def test_contextvar_hud_isolation():
"""Contextvars isolate HUD events between concurrent tasks."""
from agent.nodes.base import _current_hud
results_a = []
results_b = []
async def hud_a(data):
results_a.append(data)
async def hud_b(data):
results_b.append(data)
async def task_a():
_current_hud.set(hud_a)
# Simulate work with a yield point
await asyncio.sleep(0)
hud_fn = _current_hud.get()
await hud_fn({"from": "a"})
async def task_b():
_current_hud.set(hud_b)
await asyncio.sleep(0)
hud_fn = _current_hud.get()
await hud_fn({"from": "b"})
async def run_both():
await asyncio.gather(task_a(), task_b())
asyncio.get_event_loop().run_until_complete(run_both())
assert len(results_a) == 1 and results_a[0]["from"] == "a", \
f"task_a HUD leaked: {results_a}"
assert len(results_b) == 1 and results_b[0]["from"] == "b", \
f"task_b HUD leaked: {results_b}"
# --- Test registry (for run_tests.py) ---
TESTS = {
@ -538,8 +608,13 @@ TESTS = {
'frame_trace_reflex': test_frame_trace_reflex,
'frame_trace_expert': test_frame_trace_expert,
'frame_trace_expert_with_interpreter': test_frame_trace_expert_with_interpreter,
# Red — Phase 1: config-driven models
# Phase 1: config-driven models
'graph_has_models': test_graph_has_models,
'instantiate_applies_graph_models': test_instantiate_applies_graph_models,
'model_override_per_request': test_model_override_per_request,
# Phase 2: shared node pool
'pool_creates_shared_nodes': test_pool_creates_shared_nodes,
'pool_excludes_stateful': test_pool_excludes_stateful,
'pool_reuses_instances': test_pool_reuses_instances,
'contextvar_hud_isolation': test_contextvar_hud_isolation,
}