test(04-07): add failing collation regression test (LIST-03)

- Seed items with ranks 'a0' and 'a1', drag second to top via rank 'Zz'
- Assert 'Zz' < 'a0' is true in JS (documents uppercase-before-lowercase intent)
- GET /api/lists/:id/items must return Zz-ranked item at index 0
- Fails now because MariaDB utf8mb4_uca1400_ai_ci sorts 'Zz' after 'a0'
- Will pass once rank column gets COLLATE utf8mb4_bin via migration
This commit is contained in:
Lucas Berger
2026-06-09 14:19:27 -04:00
parent 797338424d
commit ece663d1df
+34
View File
@@ -1037,4 +1037,38 @@ describe('PATCH /api/list-items/:id { position } — reorder ordering (LIST-03,
const res = await app.request(jsonRequest('PATCH', `/api/list-items/${itemId}`, { position: 'a1', checked: true })) const res = await app.request(jsonRequest('PATCH', `/api/list-items/${itemId}`, { position: 'a1', checked: true }))
expect(res.status).toBe(400) expect(res.status).toBe(400)
}) })
it('drag-to-top: uppercase-prefixed rank sorts above lowercase ranks (LIST-03 collation regression)', async () => {
const ownerId = await seedUser('reorder-collation')
currentDevUserId = ownerId
const listId = await seedList(ownerId, 'Collation Test', false)
// Seed two active items: item1 has lowercase rank 'a0', item2 has rank 'a1'
const id1 = await seedItem(listId, 'first', 'a0')
const id2 = await seedItem(listId, 'second', 'a1')
// JS string comparison: 'Zz' < 'a0' is true (uppercase sorts before lowercase in JS/Unicode)
// This is what fractional-indexing produces for generateKeyBetween(null, 'a0') — a drag-to-top
expect('Zz' < 'a0').toBe(true)
// Drag item2 to top by assigning it the uppercase-prefixed rank 'Zz'
const app = await getApp()
const patchRes = await app.request(jsonRequest('PATCH', `/api/list-items/${id2}`, { position: 'Zz' }))
expect(patchRes.status).toBe(200)
const patchBody = await patchRes.json() as { rank: string }
expect(patchBody.rank).toBe('Zz')
// GET /api/lists/:listId/items — the dragged item (rank 'Zz') MUST be returned first (index 0)
// This exercises the real DB ORDER BY rank. Without utf8mb4_bin collation, MariaDB's
// case-insensitive default collation sorts 'Zz' AFTER 'a0', placing the item last.
const getRes = await app.request(`/api/lists/${listId}/items`)
expect(getRes.status).toBe(200)
const getBody = await getRes.json() as { items: Array<{ id: number; rank: string }> }
expect(getBody.items).toHaveLength(2)
// item2 (rank 'Zz') must be first — drag-to-top persists
expect(getBody.items[0].id).toBe(id2)
expect(getBody.items[0].rank).toBe('Zz')
// item1 (rank 'a0') must be second
expect(getBody.items[1].id).toBe(id1)
})
}) })