Skip to content

Commit 56b6a63

Browse files
authored
Merge pull request #908 from fatkobra/test/palace-graph-tunnels
test: add palace_graph tunnel helper coverage
2 parents b524b31 + 966937d commit 56b6a63

1 file changed

Lines changed: 137 additions & 0 deletions

File tree

tests/test_palace_graph_tunnels.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
"""Tests for explicit tunnel helpers in mempalace.palace_graph."""
2+
3+
from unittest.mock import MagicMock, patch
4+
5+
import pytest
6+
7+
with patch.dict("sys.modules", {"chromadb": MagicMock()}):
8+
import mempalace.palace_graph as palace_graph
9+
10+
11+
def _use_tmp_tunnel_file(monkeypatch, tmp_path):
12+
tunnel_file = tmp_path / "tunnels.json"
13+
monkeypatch.setattr(palace_graph, "_TUNNEL_FILE", str(tunnel_file))
14+
return tunnel_file
15+
16+
17+
class TestTunnelStorage:
18+
def test_load_tunnels_missing_file_returns_empty_list(self, tmp_path, monkeypatch):
19+
_use_tmp_tunnel_file(monkeypatch, tmp_path)
20+
assert palace_graph._load_tunnels() == []
21+
22+
def test_load_tunnels_corrupt_file_returns_empty_list(self, tmp_path, monkeypatch):
23+
tunnel_file = _use_tmp_tunnel_file(monkeypatch, tmp_path)
24+
tunnel_file.write_text("{not valid json", encoding="utf-8")
25+
assert palace_graph._load_tunnels() == []
26+
27+
def test_save_and_load_round_trip(self, tmp_path, monkeypatch):
28+
_use_tmp_tunnel_file(monkeypatch, tmp_path)
29+
tunnels = [
30+
{
31+
"id": "abc123",
32+
"source": {"wing": "wing_code", "room": "auth"},
33+
"target": {"wing": "wing_people", "room": "users"},
34+
"label": "same concept",
35+
}
36+
]
37+
palace_graph._save_tunnels(tunnels)
38+
assert palace_graph._load_tunnels() == tunnels
39+
40+
41+
class TestExplicitTunnels:
42+
def test_create_tunnel_deduplicates_reverse_order_and_updates_label(
43+
self, tmp_path, monkeypatch
44+
):
45+
_use_tmp_tunnel_file(monkeypatch, tmp_path)
46+
47+
first = palace_graph.create_tunnel(
48+
"wing_code", "auth", "wing_people", "users", label="same concept"
49+
)
50+
second = palace_graph.create_tunnel(
51+
"wing_people", "users", "wing_code", "auth", label="updated label"
52+
)
53+
54+
assert first["id"] == second["id"]
55+
assert len(palace_graph.list_tunnels()) == 1
56+
assert second["label"] == "updated label"
57+
assert second["created_at"] == first["created_at"]
58+
assert "updated_at" in second
59+
60+
def test_create_tunnel_rejects_empty_names(self, tmp_path, monkeypatch):
61+
_use_tmp_tunnel_file(monkeypatch, tmp_path)
62+
63+
with pytest.raises(ValueError):
64+
palace_graph.create_tunnel("", "auth", "wing_people", "users")
65+
66+
def test_list_tunnels_filters_by_either_side(self, tmp_path, monkeypatch):
67+
_use_tmp_tunnel_file(monkeypatch, tmp_path)
68+
69+
palace_graph.create_tunnel("wing_code", "auth", "wing_people", "users", label="A")
70+
palace_graph.create_tunnel("wing_ops", "deploy", "wing_people", "users", label="B")
71+
72+
assert len(palace_graph.list_tunnels()) == 2
73+
assert len(palace_graph.list_tunnels("wing_people")) == 2
74+
assert len(palace_graph.list_tunnels("wing_code")) == 1
75+
76+
def test_delete_tunnel_removes_saved_tunnel(self, tmp_path, monkeypatch):
77+
_use_tmp_tunnel_file(monkeypatch, tmp_path)
78+
79+
tunnel = palace_graph.create_tunnel(
80+
"wing_code", "auth", "wing_people", "users", label="same concept"
81+
)
82+
83+
assert palace_graph.delete_tunnel(tunnel["id"]) == {"deleted": tunnel["id"]}
84+
assert palace_graph.list_tunnels() == []
85+
86+
def test_follow_tunnels_returns_direction_and_preview(self, tmp_path, monkeypatch):
87+
_use_tmp_tunnel_file(monkeypatch, tmp_path)
88+
89+
palace_graph.create_tunnel(
90+
"wing_code",
91+
"auth",
92+
"wing_people",
93+
"users",
94+
label="same concept",
95+
target_drawer_id="drawer_users_1",
96+
)
97+
98+
col = MagicMock()
99+
col.get.return_value = {
100+
"ids": ["drawer_users_1"],
101+
"documents": ["A" * 400],
102+
"metadatas": [{}],
103+
}
104+
105+
outgoing = palace_graph.follow_tunnels("wing_code", "auth", col=col)
106+
assert len(outgoing) == 1
107+
assert outgoing[0]["direction"] == "outgoing"
108+
assert outgoing[0]["connected_wing"] == "wing_people"
109+
assert outgoing[0]["connected_room"] == "users"
110+
assert outgoing[0]["drawer_id"] == "drawer_users_1"
111+
assert len(outgoing[0]["drawer_preview"]) == 300
112+
113+
incoming = palace_graph.follow_tunnels("wing_people", "users", col=col)
114+
assert len(incoming) == 1
115+
assert incoming[0]["direction"] == "incoming"
116+
assert incoming[0]["connected_wing"] == "wing_code"
117+
118+
def test_follow_tunnels_returns_connections_even_if_collection_lookup_fails(
119+
self, tmp_path, monkeypatch
120+
):
121+
_use_tmp_tunnel_file(monkeypatch, tmp_path)
122+
123+
palace_graph.create_tunnel(
124+
"wing_code",
125+
"auth",
126+
"wing_people",
127+
"users",
128+
label="same concept",
129+
target_drawer_id="drawer_users_1",
130+
)
131+
132+
col = MagicMock()
133+
col.get.side_effect = RuntimeError("boom")
134+
135+
connections = palace_graph.follow_tunnels("wing_code", "auth", col=col)
136+
assert len(connections) == 1
137+
assert "drawer_preview" not in connections[0]

0 commit comments

Comments
 (0)