feat(matrix-bridge-dagi): support N rooms in BRIDGE_ROOM_MAP, reject duplicate room_id (M2.0)
Made-with: Cursor
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Room-to-Agent Mapping — Phase M1
|
Room-to-Agent Mapping — Phase M2.0 (N rooms, 1 agent per room)
|
||||||
|
|
||||||
Parses BRIDGE_ROOM_MAP env var and provides:
|
Parses BRIDGE_ROOM_MAP env var and provides:
|
||||||
- room_id → agent_id lookup
|
- room_id → agent_id lookup
|
||||||
@@ -148,6 +148,16 @@ def parse_room_map(raw: str, allowed_agents: FrozenSet[str]) -> RoomMappingConfi
|
|||||||
if errors:
|
if errors:
|
||||||
raise ValueError(f"BRIDGE_ROOM_MAP parse errors: {'; '.join(errors)}")
|
raise ValueError(f"BRIDGE_ROOM_MAP parse errors: {'; '.join(errors)}")
|
||||||
|
|
||||||
|
# M2.0: fail fast on duplicate room_id (1 room must map to exactly 1 agent)
|
||||||
|
seen_rooms: Dict[str, str] = {}
|
||||||
|
for m in mappings:
|
||||||
|
if m.room_id in seen_rooms:
|
||||||
|
raise ValueError(
|
||||||
|
f"Duplicate room_id {m.room_id!r}: already bound to agent "
|
||||||
|
f"{seen_rooms[m.room_id]!r}, cannot rebind to {m.agent_id!r}"
|
||||||
|
)
|
||||||
|
seen_rooms[m.room_id] = m.agent_id
|
||||||
|
|
||||||
config = RoomMappingConfig(mappings=mappings, allowed_agents=allowed_agents)
|
config = RoomMappingConfig(mappings=mappings, allowed_agents=allowed_agents)
|
||||||
logger.info(
|
logger.info(
|
||||||
"Room mapping loaded: %d entries, allowed_agents=%s",
|
"Room mapping loaded: %d entries, allowed_agents=%s",
|
||||||
|
|||||||
@@ -149,3 +149,79 @@ def test_summary_no_tokens_in_output():
|
|||||||
for entry in summary:
|
for entry in summary:
|
||||||
assert "token" not in str(entry).lower()
|
assert "token" not in str(entry).lower()
|
||||||
assert "secret" not in str(entry).lower()
|
assert "secret" not in str(entry).lower()
|
||||||
|
|
||||||
|
|
||||||
|
# ── M2.0: N rooms, duplicate validation, multi-room lookup ────────────────────
|
||||||
|
|
||||||
|
ROOM3 = "!ThirdRoom456:daarion.space"
|
||||||
|
ROOM4 = "!FourthRoom789:daarion.space"
|
||||||
|
ROOM5 = "!FifthRoom000:daarion.space"
|
||||||
|
ALLOWED_MULTI = frozenset({"sofiia", "druid", "helion", "nutra", "alateya"})
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_five_rooms():
|
||||||
|
"""N rooms (up to 5) should all parse correctly."""
|
||||||
|
raw = (
|
||||||
|
f"sofiia:{ROOM1},druid:{ROOM2},helion:{ROOM3},"
|
||||||
|
f"nutra:{ROOM4},alateya:{ROOM5}"
|
||||||
|
)
|
||||||
|
cfg = parse_room_map(raw, ALLOWED_MULTI)
|
||||||
|
assert cfg.total_mappings == 5
|
||||||
|
assert cfg.agent_for_room(ROOM1) == "sofiia"
|
||||||
|
assert cfg.agent_for_room(ROOM2) == "druid"
|
||||||
|
assert cfg.agent_for_room(ROOM3) == "helion"
|
||||||
|
assert cfg.agent_for_room(ROOM4) == "nutra"
|
||||||
|
assert cfg.agent_for_room(ROOM5) == "alateya"
|
||||||
|
|
||||||
|
|
||||||
|
def test_duplicate_room_id_raises():
|
||||||
|
"""Same room_id bound to two agents must raise ValueError (M2.0 fail-fast)."""
|
||||||
|
raw = f"sofiia:{ROOM1},druid:{ROOM1}"
|
||||||
|
with pytest.raises(ValueError, match="Duplicate room_id"):
|
||||||
|
parse_room_map(raw, ALLOWED_MULTI)
|
||||||
|
|
||||||
|
|
||||||
|
def test_duplicate_room_id_same_agent_raises():
|
||||||
|
"""Even same agent repeated for same room must raise — 1 room = 1 agent."""
|
||||||
|
raw = f"sofiia:{ROOM1},sofiia:{ROOM1}"
|
||||||
|
with pytest.raises(ValueError, match="Duplicate room_id"):
|
||||||
|
parse_room_map(raw, ALLOWED_MULTI)
|
||||||
|
|
||||||
|
|
||||||
|
def test_multi_room_o1_lookup():
|
||||||
|
"""agent_for_room must return correct agent for each of N rooms (O(1) index)."""
|
||||||
|
raw = f"sofiia:{ROOM1},druid:{ROOM2},helion:{ROOM3}"
|
||||||
|
cfg = parse_room_map(raw, ALLOWED_MULTI)
|
||||||
|
assert cfg.agent_for_room(ROOM1) == "sofiia"
|
||||||
|
assert cfg.agent_for_room(ROOM2) == "druid"
|
||||||
|
assert cfg.agent_for_room(ROOM3) == "helion"
|
||||||
|
assert cfg.agent_for_room("!unknown:server") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_rooms_for_agent_multi_room():
|
||||||
|
"""rooms_for_agent returns all rooms bound to a given agent."""
|
||||||
|
raw = f"sofiia:{ROOM1},sofiia:{ROOM2},druid:{ROOM3}"
|
||||||
|
allowed = frozenset({"sofiia", "druid"})
|
||||||
|
cfg = parse_room_map(raw, allowed)
|
||||||
|
sofiia_rooms = cfg.rooms_for_agent("sofiia")
|
||||||
|
assert set(sofiia_rooms) == {ROOM1, ROOM2}
|
||||||
|
assert cfg.rooms_for_agent("druid") == [ROOM3]
|
||||||
|
|
||||||
|
|
||||||
|
def test_multi_room_summary_count():
|
||||||
|
"""as_summary() must return one entry per mapping."""
|
||||||
|
raw = f"sofiia:{ROOM1},druid:{ROOM2},helion:{ROOM3}"
|
||||||
|
cfg = parse_room_map(raw, ALLOWED_MULTI)
|
||||||
|
summary = cfg.as_summary()
|
||||||
|
assert len(summary) == 3
|
||||||
|
room_ids = {s["room_id"] for s in summary}
|
||||||
|
assert room_ids == {ROOM1, ROOM2, ROOM3}
|
||||||
|
|
||||||
|
|
||||||
|
def test_multi_room_unknown_agent_filtered():
|
||||||
|
"""agent_for_room returns None if agent not in allowed_agents (even if mapping exists)."""
|
||||||
|
raw = f"sofiia:{ROOM1},unknown_bot:{ROOM2}"
|
||||||
|
allowed = frozenset({"sofiia"}) # unknown_bot not allowed
|
||||||
|
cfg = parse_room_map(raw, allowed)
|
||||||
|
assert cfg.agent_for_room(ROOM1) == "sofiia"
|
||||||
|
assert cfg.agent_for_room(ROOM2) is None # not in allowed_agents
|
||||||
|
|||||||
Reference in New Issue
Block a user