Skip to content

Commit 52d252e

Browse files
test: cover parallel foreign-key edges to the same parent (#1492) (#1514)
Follow-up to #1508. The MultiDiGraph migration enables two foreign keys from one child to the same parent as distinct parallel edges; the prior name-keyed dependency dict collapsed them into one. LocalSynapse (two renamed FKs to Cell) had no direct test for this. Asserts Cell appears as two parent edges of LocalSynapse — across Table.parents()/children(), the dependency MultiDiGraph, and the rendered Diagram — with distinct rename maps and edge keys.
1 parent 3b43a2a commit 52d252e

1 file changed

Lines changed: 38 additions & 0 deletions

File tree

tests/integration/test_foreign_keys.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import datajoint as dj
12
from datajoint.declare import declare
23

34
from tests.schema_advanced import (
@@ -51,3 +52,40 @@ def test_delete(schema_adv):
5152
to_delete = len(parent & "11 in (person_id, parent)")
5253
(person & "person_id=11").delete()
5354
assert to_delete and len(parent) == original_len - to_delete
55+
56+
57+
def test_parallel_edges_to_same_parent(schema_adv):
58+
"""Two foreign keys from one child to the same parent are preserved as two
59+
distinct parallel edges (``nx.MultiDiGraph`` migration, #1492 / PR #1508).
60+
61+
``LocalSynapse`` references ``Cell`` twice —
62+
``-> Cell.proj(presynaptic='cell')`` and ``-> Cell.proj(postsynaptic='cell')``
63+
— so ``Cell`` must appear as TWO parent edges of ``LocalSynapse``. The
64+
pre-migration name-keyed dependency dict collapsed these into a single entry;
65+
this test locks the parallel-edges capability across all three layers that
66+
consume it: the ``Dependencies`` accessors, the dependency graph itself, and
67+
the rendered ``Diagram``.
68+
"""
69+
cell_name = Cell.full_table_name
70+
local_name = LocalSynapse.full_table_name
71+
72+
# (a) Table.parents() surfaces BOTH edges to Cell, with distinct rename maps.
73+
cell_edges = [props for name, props in LocalSynapse().parents(foreign_key_info=True) if name == cell_name]
74+
assert len(cell_edges) == 2, "LocalSynapse must expose two parallel FK edges to Cell"
75+
renamed = {child for props in cell_edges for child, parent in props["attr_map"].items() if parent == "cell"}
76+
assert renamed == {"presynaptic", "postsynaptic"}
77+
# the two edges must carry distinct keys (``tuple(attr_map)``) — the uniqueness
78+
# the keyed-edge migration relies on to keep them apart.
79+
assert len({tuple(sorted(props["attr_map"].items())) for props in cell_edges}) == 2
80+
81+
# (b) The dependency graph keeps both edges — cascade/trace traverse per edge.
82+
deps = LocalSynapse.connection.dependencies
83+
deps.load(force=False)
84+
assert deps.number_of_edges(cell_name, local_name) == 2
85+
# symmetric view from the parent side (children direction)
86+
local_as_child = [name for name, _ in Cell().children(foreign_key_info=True) if name == local_name]
87+
assert len(local_as_child) == 2
88+
89+
# (c) The rendered Diagram (a MultiDiGraph over full table names) draws both.
90+
diagram = dj.Diagram(schema_adv)
91+
assert diagram.number_of_edges(cell_name, local_name) == 2

0 commit comments

Comments
 (0)