From 0cd4882ef43cb1dabc1fb47a78bcbdc61574b072 Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 4 Jun 2026 13:37:20 +0200 Subject: [PATCH] test(stammbaum): no two nodes overlap on the same row (#724) O(n^2) sweep over canonical + synthetic: any two nodes sharing a y are at least NODE_W + COL_GAP apart. Co-Authored-By: Claude Opus 4.8 --- .../genealogy/layout/buildLayout.test.ts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/frontend/src/lib/person/genealogy/layout/buildLayout.test.ts b/frontend/src/lib/person/genealogy/layout/buildLayout.test.ts index 21d5b958..cdc5f611 100644 --- a/frontend/src/lib/person/genealogy/layout/buildLayout.test.ts +++ b/frontend/src/lib/person/genealogy/layout/buildLayout.test.ts @@ -501,4 +501,42 @@ describe('buildLayout — ancestor centring invariant (#724)', () => { forest.roots.forEach(walk); } }); + + it('no two nodes on the same row overlap (canonical + synthetic)', () => { + const cases: [string, PersonNodeDTO[], RelationshipDTO[]][] = [ + ['canonical', fixtureNodes, fixtureEdges], + [ + 'synthetic deep', + [ + node('R', 'R', 0), + node('p1', 'p1', 1), + node('p2', 'p2', 1), + node('g1', 'g1', 2), + node('g2', 'g2', 2) + ], + [ + parentEdge('R', 'p1'), + parentEdge('R', 'p2'), + parentEdge('p1', 'g1'), + parentEdge('p1', 'g2') + ] + ] + ]; + + for (const [label, nodes, edges] of cases) { + const layout = buildLayout(nodes, edges); + const entries = [...layout.positions.entries()]; + for (let i = 0; i < entries.length; i++) { + for (let j = i + 1; j < entries.length; j++) { + const [, a] = entries[i]; + const [, b] = entries[j]; + if (a.y !== b.y) continue; + expect( + Math.abs(a.x - b.x), + `${label}: ${entries[i][0]} vs ${entries[j][0]} overlap on y=${a.y}` + ).toBeGreaterThanOrEqual(NODE_W + COL_GAP); + } + } + } + }); });