| … | … | @@ -163,3 +163,43 @@ test('a corrupt or absent cache file is not fatal', async (t) => { |
| 163 | 163 | recordRefActivity('alpha', 1) |
| 164 | 164 | t.is(getRefActivity('alpha'), 1, 'still usable after a bad load') |
| 165 | 165 | }) |
| 166 | |
| 167 | test('an EXACT tie orders identically on both backends', async (t) => { |
| 168 | // Found in production the hour 0.1.250 shipped. lastRefUpdate agreed to the |
| 169 | // millisecond across tabertron and stortron (1788551868967 for both |
| 170 | // proc-nodes and blender-bridge — one batch publish stamps one timestamp), |
| 171 | // and with no tiebreak the comparator returned 0, so Array.sort left them in |
| 172 | // INPUT order. That input is sync.repos, a Map in readdir order, which is |
| 173 | // per-host: 134 of 135 positions matched and those two were swapped. |
| 174 | // |
| 175 | // The replicated key made ties COMMON — mtimes were near-unique by accident, |
| 176 | // a batch publish is not. Names are unique per daemon, so tie-breaking on |
| 177 | // name makes the order total and host-independent. |
| 178 | const tied = (order) => order.map((name) => ({ name, branches: 1, lastRefUpdate: 1788551868967 })) |
| 179 | |
| 180 | t.alike( |
| 181 | sortReposByRecency(tied(['proc-nodes', 'blender-bridge'])).map((r) => r.name), |
| 182 | sortReposByRecency(tied(['blender-bridge', 'proc-nodes'])).map((r) => r.name), |
| 183 | 'the same tied set orders the same way whatever order it arrives in' |
| 184 | ) |
| 185 | t.alike(sortReposByRecency(tied(['proc-nodes', 'blender-bridge'])).map((r) => r.name), |
| 186 | ['blender-bridge', 'proc-nodes'], 'tie broken by name') |
| 187 | |
| 188 | // The tie is only reached when the recency is equal — it must never outrank |
| 189 | // the timestamp itself. |
| 190 | t.alike( |
| 191 | sortReposByRecency([ |
| 192 | { name: 'aaa', branches: 1, lastRefUpdate: 1 }, |
| 193 | { name: 'zzz', branches: 1, lastRefUpdate: 2 } |
| 194 | ]).map((r) => r.name), |
| 195 | ['zzz', 'aaa'], |
| 196 | 'a newer repo still wins over an alphabetically earlier one' |
| 197 | ) |
| 198 | |
| 199 | // Repos with NO signal at all (both keys absent) tie at 0 — same rule. |
| 200 | t.alike( |
| 201 | sortReposByRecency([{ name: 'b', branches: 1 }, { name: 'a', branches: 1 }]).map((r) => r.name), |
| 202 | ['a', 'b'], |
| 203 | 'repos with no signal at all are also deterministic' |
| 204 | ) |
| 205 | }) |