/** * @module tests/unit/utils/metadataIndex-nested-orderby * @description THE NESTED-FIELD ADDRESSING PIN for ordered reads (the * field-addressing law, dotted-path clause). The defect this keeps dead: * `orderBy` on a nested user metadata field (dotted path, e.g. * `orderBy: 'profile.score'` over `metadata: { profile: { score: 7 } }`) * silently returned insertion order — a no-op sort — because the sort * path's value resolution read flat bag keys only. The law: a dotted user * address is either SERVED CORRECTLY (the batched resolver walks inside * the bag) or REFUSED with a typed UnresolvableFieldError — never a silent * pass-through. Both spellings (`profile.score` / `metadata.profile.score`) * are the same address; the filter side (`where: { 'profile.score': … }`) * obeys the same law. */ import { describe, it, expect, beforeAll, afterAll } from 'vitest' import { Brainy, UnresolvableFieldError } from '../../../src/index.js' import { NounType } from '../../../src/types/graphTypes.js' const ROWS = 30 describe('nested (dotted-path) user field orderBy — the field-addressing law', () => { let brain: Brainy /** id → nested score, for the rows that carry profile.score */ const scoreById = new Map() /** ids of the two rows WITHOUT a profile bag */ let noProfileIds: string[] = [] beforeAll(async () => { brain = new Brainy({ storage: { type: 'memory' }, requireSubtype: false }) await brain.init() for (let i = 0; i < ROWS; i++) { // (i * 11) % 30 is a permutation of 0..29 (gcd(11,30)=1): every score // distinct, insertion order maximally different from value order — a // silent insertion-order pass-through cannot accidentally look sorted. const score = (i * 11) % ROWS const id = await brain.add({ data: `row ${i}`, type: NounType.Document, metadata: { profile: { score }, plain: i } }) scoreById.set(id, score) } const a = await brain.add({ data: 'no-profile a', type: NounType.Document, metadata: { plain: 1000 } }) const b = await brain.add({ data: 'no-profile b', type: NounType.Document, metadata: { plain: 1001 } }) noProfileIds = [a, b].sort() }, 120000) afterAll(async () => { await brain.close().catch(() => {}) }) /** Assert one complete ordered read against the sealed ordering contract. */ function assertOrdered( rows: Array<{ id: string }>, order: 'asc' | 'desc', label: string ): void { // Rows are NEVER dropped: all 30 scored + 2 profile-less rows come back. expect(rows.length, `${label}: complete result`).toBe(ROWS + 2) // Missing-value rows sort LAST in BOTH directions, ties by id ascending. const lastTwo = rows.slice(-2).map((r) => r.id) expect(lastTwo, `${label}: missing-value rows LAST, id asc`).toEqual(noProfileIds) // The scored 30 are ordered by the NESTED value — the exact permutation, // not insertion order. const observed = rows.slice(0, ROWS).map((r) => scoreById.get(r.id)) const wanted = [...scoreById.values()].sort((x, y) => order === 'asc' ? x - y : y - x ) expect(observed, `${label}: nested values in ${order} order`).toEqual(wanted) } it('orderBy: "profile.score" desc — served correctly, missing rows LAST (never a silent insertion-order no-op)', async () => { const rows = await brain.find({ type: NounType.Document, orderBy: 'profile.score', order: 'desc', limit: 40 }) assertOrdered(rows, 'desc', 'bare dotted, desc') }) it('orderBy: "profile.score" asc — same law in the other direction', async () => { const rows = await brain.find({ type: NounType.Document, orderBy: 'profile.score', order: 'asc', limit: 40 }) assertOrdered(rows, 'asc', 'bare dotted, asc') }) it('explicit spelling "metadata.profile.score" is the SAME address — identical result', async () => { const bare = await brain.find({ type: NounType.Document, orderBy: 'profile.score', order: 'desc', limit: 40 }) const explicit = await brain.find({ type: NounType.Document, orderBy: 'metadata.profile.score', order: 'desc', limit: 40 }) assertOrdered(explicit, 'desc', 'metadata.-prefixed, desc') expect( explicit.map((r) => r.id), 'both spellings resolve to the identical ordered id sequence' ).toEqual(bare.map((r) => r.id)) }) it('a dotted path carried by NO entity REFUSES with UnresolvableFieldError — never a silent insertion-order return', async () => { await expect( brain.find({ type: NounType.Document, orderBy: 'no.such.path', order: 'desc', limit: 40 }) ).rejects.toThrow(UnresolvableFieldError) }) it('dotted where: { "profile.score": 7 } finds exactly the right row — the filter side of the same law', async () => { const wantedId = [...scoreById.entries()].find(([, s]) => s === 7)![0] const rows = await brain.find({ type: NounType.Document, where: { 'profile.score': 7 }, limit: 40 }) expect(rows.map((r) => r.id)).toEqual([wantedId]) }) })