test(04-05): server-side reorder ordering + rank precision tests (LIST-03, D-13)
- rank.test.ts: 100-iteration zipper mid-point insert precision test (Pitfall 2); rank-between-neighbors contract test; total 10 tests (was 8) - lists.test.ts: 5 new LIST-03 ordering tests — PATCH position updates only rank and GET returns new ASC order; one-row write asserts other items unchanged; LWW (D-15): second PATCH overwrites first; T-04-07 two-field position PATCH → 400 - Note: tests use a0–a5 rank range (avoids uppercase ranks that sort differently under MariaDB utf8mb4_unicode_ci vs JS lexicographic order)
This commit is contained in:
@@ -75,4 +75,48 @@ describe('rankBetween', () => {
|
||||
expect(c < e).toBe(true)
|
||||
expect(e < b).toBe(true)
|
||||
})
|
||||
|
||||
/**
|
||||
* Precision regression test — LIST-03, Pitfall 2.
|
||||
*
|
||||
* Repeated "zipper" inserts (always between the first two items) must produce
|
||||
* strictly increasing, unique, non-empty rank strings across many iterations.
|
||||
* Float-based approaches would exhaust precision around iteration 52; the
|
||||
* fractional-indexing string approach degrades gracefully by growing string
|
||||
* length instead.
|
||||
*/
|
||||
it('repeated mid-point inserts produce unique strictly-increasing ranks over 100 iterations (precision — Pitfall 2)', () => {
|
||||
const ranks: string[] = [rankBetween(null, null), rankBetween(null, null)]
|
||||
// Set up: two items with known ranks
|
||||
ranks[0] = rankBetween(null, null) // 'a0'
|
||||
ranks[1] = rankBetween(ranks[0], null) // 'a1'
|
||||
|
||||
// Insert 100 times between the first item and the second item
|
||||
// This is the worst-case "zipper" pattern — always inserting at the same gap
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const newRank = rankBetween(ranks[0], ranks[1])
|
||||
// Must be strictly between
|
||||
expect(newRank > ranks[0]).toBe(true)
|
||||
expect(newRank < ranks[1]).toBe(true)
|
||||
// Must be a non-empty string
|
||||
expect(newRank.length).toBeGreaterThan(0)
|
||||
// Must be unique (not equal to any existing rank)
|
||||
expect(ranks).not.toContain(newRank)
|
||||
// New item goes at index 1 (after first, before old second) — shift old items
|
||||
ranks.splice(1, 0, newRank)
|
||||
}
|
||||
|
||||
// Verify the final list is fully sorted ASC
|
||||
for (let i = 1; i < ranks.length; i++) {
|
||||
expect(ranks[i] > ranks[i - 1]).toBe(true)
|
||||
}
|
||||
})
|
||||
|
||||
it('rank between two neighbors is strictly between them (D-13 reorder contract)', () => {
|
||||
const prev = 'a0'
|
||||
const next = 'a3'
|
||||
const middle = rankBetween(prev, next)
|
||||
expect(middle > prev).toBe(true)
|
||||
expect(middle < next).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user