From 0f3d17ab7eade20b289bfec5f7204dd99c464cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Wed, 7 Oct 2026 18:42:56 +0200 Subject: [PATCH 1/2] test(depgraph): pin computeLevels on a diamond and on a value cycle Co-Authored-By: Claude Opus 5.5 (1M context) --- scripts/depgraph/model.test.ts | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/scripts/depgraph/model.test.ts b/scripts/depgraph/model.test.ts index 55faa9bf65..4bc7e38f32 100644 --- a/scripts/depgraph/model.test.ts +++ b/scripts/depgraph/model.test.ts @@ -12,6 +12,7 @@ import { buildGraph, collapseEdges, collectCycles, + computeLevels, markTransitivelyReachableEdges, typeInversionsByPair, } from './model.ts'; @@ -270,6 +271,47 @@ test('buildGraph still reports a value cycle and leaves edges unmarked', () => { ); }); +test('computeLevels takes the longest value path to a sink and ignores type edges', () => { + const files = sources({ + 'src/core/a.ts': [ + "import { b } from './b.ts';", + "import { c } from './c.ts';", + "import { d } from './d.ts';", + "import type { E } from './e.ts';", + 'export const a = [b, c, d];', + ].join('\n'), + 'src/core/b.ts': "import { d } from './d.ts';\nexport const b = d;", + 'src/core/c.ts': "import { d } from './d.ts';\nexport const c = d;", + 'src/core/d.ts': 'export const d = 1;', + 'src/core/e.ts': "import type { A } from './a.ts';\nexport type E = A;", + 'src/core/lone.ts': 'export const lone = 1;', + }); + const graph = buildGraph(files, resolveImportEdges(files)); + + assert.deepEqual(Object.fromEntries(computeLevels(graph.nodes, graph.edges)), { + 'src/core/a.ts': 2, + 'src/core/b.ts': 1, + 'src/core/c.ts': 1, + 'src/core/d.ts': 0, + 'src/core/e.ts': 0, + 'src/core/lone.ts': 0, + }); +}); + +test('computeLevels stays finite on a value cycle, where the closing edge adds no height', () => { + const files = sources({ + ...Object.fromEntries(valueCycleFixture()), + 'src/core/z.ts': "import { a } from './a.ts';\nexport const z = a;", + }); + const graph = buildGraph(files, resolveImportEdges(files)); + + assert.deepEqual(Object.fromEntries(computeLevels(graph.nodes, graph.edges)), { + 'src/core/a.ts': 1, + 'src/core/b.ts': 0, + 'src/core/z.ts': 2, + }); +}); + test('a type-only shortcut is never flagged against a value path', () => { const edges = collapseEdges( resolveImportEdges( From b76abe0a47328003b5ada3bb8d5bd8d2f3bfee49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Wed, 7 Oct 2026 20:21:06 +0200 Subject: [PATCH 2/2] test(depgraph): assert cycle levels independently of node order Co-Authored-By: Claude Opus 5.5 (1M context) --- scripts/depgraph/model.test.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/depgraph/model.test.ts b/scripts/depgraph/model.test.ts index 4bc7e38f32..46c99b501f 100644 --- a/scripts/depgraph/model.test.ts +++ b/scripts/depgraph/model.test.ts @@ -305,11 +305,10 @@ test('computeLevels stays finite on a value cycle, where the closing edge adds n }); const graph = buildGraph(files, resolveImportEdges(files)); - assert.deepEqual(Object.fromEntries(computeLevels(graph.nodes, graph.edges)), { - 'src/core/a.ts': 1, - 'src/core/b.ts': 0, - 'src/core/z.ts': 2, - }); + const levels = computeLevels(graph.nodes, graph.edges); + + assert.deepEqual([levels.get('src/core/a.ts'), levels.get('src/core/b.ts')].sort(), [0, 1]); + assert.equal(levels.get('src/core/z.ts'), 2); }); test('a type-only shortcut is never flagged against a value path', () => {