| 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; |
| 3 | |
| 4 | import { bounded, drift, type Link } from "./branches.ts"; |
| 5 | |
| 6 | /** A history from `[hash, ...parents]` rows. */ |
| 7 | const graph = (...rows: string[][]): Link[] => rows.map(([hash, ...parents]) => ({ hash: hash as string, parents })); |
| 8 | |
| 9 | // main: m1 <- m2 <- m3; the branch left at m2 and added b1 <- b2. |
| 10 | const forked = graph(["m3", "m2"], ["m2", "m1"], ["m1"], ["b2", "b1"], ["b1", "m2"]); |
| 11 | |
| 12 | test("a branch two ahead of where main was, with main one further on", () => { |
| 13 | assert.deepEqual(drift("b2", "m3", forked), { ahead: 2, behind: 1 }); |
| 14 | }); |
| 15 | |
| 16 | test("a branch at main's head is level", () => { |
| 17 | assert.deepEqual(drift("m3", "m3", forked), { ahead: 0, behind: 0 }); |
| 18 | }); |
| 19 | |
| 20 | test("a branch merged long ago is nothing ahead and all of main since behind", () => { |
| 21 | const history = graph(["m5", "m4"], ["m4", "m3"], ["m3", "m2"], ["m2", "m1"]); |
| 22 | // Neither history was read to its start, but they meet in what was. |
| 23 | assert.deepEqual(drift("m2", "m5", history), { ahead: 0, behind: 3 }); |
| 24 | }); |
| 25 | |
| 26 | test("a merge into main counts the merged side once", () => { |
| 27 | // main merged side branch s1 <- s2 at m3; the branch is still at m1. |
| 28 | const history = graph(["m3", "m2", "s2"], ["s2", "s1"], ["s1", "m1"], ["m2", "m1"], ["m1"], ["b1", "m1"]); |
| 29 | assert.deepEqual(drift("b1", "m3", history), { ahead: 1, behind: 4 }); |
| 30 | }); |
| 31 | |
| 32 | test("histories that never meet count everything on each, once read to the start", () => { |
| 33 | const history = graph(["b2", "b1"], ["b1"], ["m2", "m1"], ["m1"]); |
| 34 | assert.deepEqual(drift("b2", "m2", history), { ahead: 2, behind: 2 }); |
| 35 | }); |
| 36 | |
| 37 | test("no answer when what was read stops before the two meet", () => { |
| 38 | // Main's history was read only to m2, whose parent the branch may share. |
| 39 | const history = graph(["m3", "m2"], ["m2", "m1"], ["b2", "b1"], ["b1", "m0"]); |
| 40 | assert.equal(drift("b2", "m3", history), null); |
| 41 | }); |
| 42 | |
| 43 | test("no answer without either head", () => { |
| 44 | assert.equal(drift("b9", "m3", forked), null); |
| 45 | assert.equal(drift("b2", "m9", forked), null); |
| 46 | }); |
| 47 | |
| 48 | test("bounded loads everything in order, never more at once than asked", async () => { |
| 49 | let running = 0; |
| 50 | let most = 0; |
| 51 | const out = await bounded([5, 1, 4, 2, 3], 2, async (wait) => { |
| 52 | running++; |
| 53 | most = Math.max(most, running); |
| 54 | await new Promise((done) => setTimeout(done, wait)); |
| 55 | running--; |
| 56 | return wait * 10; |
| 57 | }); |
| 58 | assert.deepEqual(out, [50, 10, 40, 20, 30]); |
| 59 | assert.equal(most, 2); |
| 60 | }); |