/** * @module tests/integration/related-verb-array * @description related() honours EVERY verb type in an array (10.4.9). * * The storage fast paths for `sourceId + verbType` and `verbType` collapsed a * verb-type ARRAY to its first element — `related({ from, type: [a, b] })` * silently returned only `a` edges, whichever order the array came in. The * same quiet-loss class as the graph-first paging defect, one seam over. * These pins seed a store where the SECOND requested type's edge must come * back, on every path the collapse lived in. */ import { describe, it, expect, beforeAll, afterAll } from 'vitest' import { Brainy } from '../../src/brainy' import { NounType, VerbType } from '../../src/types/graphTypes' import { v5 } from '../../src/universal/uuid' describe('related() with a verb-type array returns every requested type', () => { let brain: Brainy beforeAll(async () => { brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' } }) await brain.init() for (const id of ['a', 'b', 'c', 'd']) { await brain.add({ id, data: `node ${id}`, type: NounType.Person }) } await brain.relate({ from: 'a', to: 'b', type: VerbType.Supports }) await brain.relate({ from: 'a', to: 'c', type: VerbType.RelatedTo }) await brain.relate({ from: 'a', to: 'd', type: VerbType.Knows }) await brain.relate({ from: 'b', to: 'c', type: VerbType.RelatedTo }) }) afterAll(async () => { brain = null as any }) it('from + type array: the second type\'s edge comes back, both orders', async () => { for (const types of [ [VerbType.Supports, VerbType.RelatedTo], [VerbType.RelatedTo, VerbType.Supports] ]) { const edges = await brain.related({ from: 'a', type: types }) const targets = new Set(edges.map((e) => e.to)) expect(targets.has(v5('b')), `types [${types}] missing Supports edge`).toBe(true) expect(targets.has(v5('c')), `types [${types}] missing RelatedTo edge`).toBe(true) expect(targets.has(v5('d'))).toBe(false) expect(edges).toHaveLength(2) } }) it('a single-element array behaves exactly like the scalar', async () => { const scalar = await brain.related({ from: 'a', type: VerbType.Supports }) const array = await brain.related({ from: 'a', type: [VerbType.Supports] }) expect(array.map((e) => e.id).sort()).toEqual(scalar.map((e) => e.id).sort()) expect(array).toHaveLength(1) }) it('no duplicate edges when types overlap the same edge set', async () => { const edges = await brain.related({ from: 'a', type: [VerbType.Supports, VerbType.RelatedTo, VerbType.Knows] }) const ids = edges.map((e) => e.id) expect(new Set(ids).size).toBe(ids.length) expect(edges).toHaveLength(3) }) it('type-only asks (no anchor) honour the whole array too', async () => { const edges = await brain.related({ type: [VerbType.Supports, VerbType.Knows] }) const verbs = new Set(edges.map((e) => e.type)) expect(verbs.has(VerbType.Supports)).toBe(true) expect(verbs.has(VerbType.Knows)).toBe(true) expect(edges).toHaveLength(2) }) it('pagination stays consistent across the union', async () => { const page1 = await brain.related({ from: 'a', type: [VerbType.Supports, VerbType.RelatedTo, VerbType.Knows], limit: 2 }) const page2 = await brain.related({ from: 'a', type: [VerbType.Supports, VerbType.RelatedTo, VerbType.Knows], limit: 2, offset: 2 }) const all = [...page1, ...page2].map((e) => e.id) expect(new Set(all).size).toBe(3) }) })