Files
microdao-daarion/tests/test_matrix_bridge_room_mapping.py
Apple dbfab78f02 feat(matrix-bridge-dagi): add room mapping, ingress loop, synapse setup (PR-M1.2 + PR-M1.3)
PR-M1.2 — room-to-agent mapping:
- adds room_mapping.py: parse BRIDGE_ROOM_MAP (format: agent:!room_id:server)
- RoomMappingConfig with O(1) room→agent lookup, agent allowlist check
- /bridge/mappings endpoint (read-only ops summary, no secrets)
- health endpoint now includes mappings_count
- 21 tests for parsing, validation, allowlist, summary

PR-M1.3 — Matrix ingress loop:
- adds ingress.py: MatrixIngressLoop asyncio task
- sync_poll → extract → dedupe → _invoke_gateway (POST /v1/invoke)
- gateway payload: agent_id, node_id, message, metadata (transport, room_id, event_id, sender)
- exponential backoff on errors (2s..60s)
- joins all mapped rooms at startup
- metric callbacks: on_message_received, on_gateway_error
- graceful shutdown via asyncio.Event
- 5 ingress tests (invoke, dedupe, callbacks, empty-map idle)

Synapse setup (docker-compose.synapse-node1.yml):
- fixed volume: bind mount ./synapse-data instead of named volume
- added port mapping 127.0.0.1:8008:8008

Synapse running on NODA1 (localhost:8008), bot @dagi_bridge:daarion.space created,
room !QwHczWXgefDHBEVkTH:daarion.space created, all 4 values in .env on NODA1.

Made-with: Cursor
2026-03-03 07:51:13 -08:00

152 lines
5.1 KiB
Python

"""
Tests for services/matrix-bridge-dagi/app/room_mapping.py
"""
import sys
from pathlib import Path
import pytest
_BRIDGE = Path(__file__).parent.parent / "services" / "matrix-bridge-dagi"
if str(_BRIDGE) not in sys.path:
sys.path.insert(0, str(_BRIDGE))
from app.room_mapping import RoomMappingConfig, parse_room_map, RoomMapping # noqa: E402
ALLOWED = frozenset({"sofiia", "druid"})
ROOM1 = "!QwHczWXgefDHBEVkTH:daarion.space"
ROOM2 = "!AnotherRoom123:daarion.space"
# ── Parsing — valid ────────────────────────────────────────────────────────────
def test_parse_single_mapping():
cfg = parse_room_map(f"sofiia:{ROOM1}", ALLOWED)
assert cfg.total_mappings == 1
assert cfg.mappings[0].agent_id == "sofiia"
assert cfg.mappings[0].room_id == ROOM1
def test_parse_multiple_mappings():
raw = f"sofiia:{ROOM1},druid:{ROOM2}"
cfg = parse_room_map(raw, ALLOWED)
assert cfg.total_mappings == 2
def test_parse_empty_string():
cfg = parse_room_map("", ALLOWED)
assert cfg.total_mappings == 0
def test_parse_whitespace_only():
cfg = parse_room_map(" ", ALLOWED)
assert cfg.total_mappings == 0
def test_parse_trailing_comma():
cfg = parse_room_map(f"sofiia:{ROOM1},", ALLOWED)
assert cfg.total_mappings == 1
def test_parse_spaces_around_entries():
cfg = parse_room_map(f" sofiia:{ROOM1} , druid:{ROOM2} ", ALLOWED)
assert cfg.total_mappings == 2
# ── Parsing — invalid ─────────────────────────────────────────────────────────
def test_parse_missing_colon_raises():
with pytest.raises(ValueError, match="parse errors"):
parse_room_map("sofiia_no_colon", ALLOWED)
def test_parse_invalid_room_id_format_raises():
with pytest.raises(ValueError, match="invalid room_id format"):
# Room ID must start with !
parse_room_map(f"sofiia:#badroom:server", ALLOWED)
def test_parse_absolute_path_as_room_raises():
with pytest.raises(ValueError, match="invalid room_id format"):
parse_room_map("sofiia:/etc/passwd", ALLOWED)
def test_parse_empty_agent_id_raises():
with pytest.raises(ValueError, match="parse errors"):
parse_room_map(f":{ROOM1}", ALLOWED)
def test_parse_empty_room_id_raises():
with pytest.raises(ValueError, match="parse errors"):
parse_room_map("sofiia:", ALLOWED)
# ── agent_for_room ────────────────────────────────────────────────────────────
def test_agent_for_room_found():
cfg = parse_room_map(f"sofiia:{ROOM1}", ALLOWED)
assert cfg.agent_for_room(ROOM1) == "sofiia"
def test_agent_for_room_not_found():
cfg = parse_room_map(f"sofiia:{ROOM1}", ALLOWED)
assert cfg.agent_for_room("!unknownroom:server") is None
def test_agent_for_room_not_allowed():
"""Agent in mapping but not in allowed_agents → None."""
cfg = parse_room_map(f"druid:{ROOM1}", frozenset({"sofiia"})) # druid not allowed
# mapping is accepted but agent_for_room returns None
assert cfg.agent_for_room(ROOM1) is None
def test_agent_for_room_allowed_when_in_set():
cfg = parse_room_map(f"druid:{ROOM1}", frozenset({"druid"}))
assert cfg.agent_for_room(ROOM1) == "druid"
# ── rooms_for_agent ───────────────────────────────────────────────────────────
def test_rooms_for_agent_single():
cfg = parse_room_map(f"sofiia:{ROOM1}", ALLOWED)
assert cfg.rooms_for_agent("sofiia") == [ROOM1]
def test_rooms_for_agent_multiple():
cfg = parse_room_map(f"sofiia:{ROOM1},sofiia:{ROOM2}", ALLOWED)
rooms = cfg.rooms_for_agent("sofiia")
assert ROOM1 in rooms
assert ROOM2 in rooms
def test_rooms_for_agent_unknown():
cfg = parse_room_map(f"sofiia:{ROOM1}", ALLOWED)
assert cfg.rooms_for_agent("nonexistent") == []
# ── as_summary ────────────────────────────────────────────────────────────────
def test_summary_contains_expected_fields():
cfg = parse_room_map(f"sofiia:{ROOM1}", ALLOWED)
summary = cfg.as_summary()
assert len(summary) == 1
entry = summary[0]
assert entry["room_id"] == ROOM1
assert entry["agent_id"] == "sofiia"
assert entry["allowed"] is True
def test_summary_allowed_false_for_unknown_agent():
cfg = parse_room_map(f"druid:{ROOM1}", frozenset({"sofiia"}))
summary = cfg.as_summary()
assert summary[0]["allowed"] is False
def test_summary_no_tokens_in_output():
"""Access tokens must never appear in summary."""
cfg = parse_room_map(f"sofiia:{ROOM1}", ALLOWED)
summary = cfg.as_summary()
for entry in summary:
assert "token" not in str(entry).lower()
assert "secret" not in str(entry).lower()