import { describe, expect, it } from "vitest"; import { fuseRrf } from "../rrf-fusion.js"; describe("fuseRrf", () => { it("sums 1/(k+rank) per node across lists", () => { const listA = [{ nodeId: "a" }, { nodeId: "b" }, { nodeId: "c" }]; const listB = [{ nodeId: "b" }, { nodeId: "d" }, { nodeId: "a" }]; const fused = fuseRrf([listA, listB], 60); // a: list A rank 0 + list B rank 2 = 1/60 + 1/62 // b: list A rank 1 + list B rank 0 = 1/61 + 1/60 // c: list A rank 2 only = 1/62 // d: list B rank 1 only = 1/61 const byId = new Map(fused.map((f) => [f.nodeId, f.rrfScore])); expect(byId.get("a")).toBeCloseTo(1 / 60 + 1 / 62, 6); expect(byId.get("b")).toBeCloseTo(1 / 61 + 1 / 60, 6); expect(byId.get("c")).toBeCloseTo(1 / 62, 6); expect(byId.get("d")).toBeCloseTo(1 / 61, 6); }); it("sorts by descending RRF score", () => { const listA = [{ nodeId: "a" }, { nodeId: "b" }]; const listB = [{ nodeId: "b" }, { nodeId: "a" }]; const fused = fuseRrf([listA, listB], 60); // Both appear in both lists at ranks (0,1) and (1,0) → same score; tie broken by first-seen → "a" first. expect(fused.map((f) => f.nodeId)).toEqual(["a", "b"]); }); it("honours configurable k", () => { const listA = [{ nodeId: "a" }]; const fused = fuseRrf([listA], 10); expect(fused[0].rrfScore).toBeCloseTo(1 / 10, 6); }); it("returns [] for empty input", () => { expect(fuseRrf([])).toEqual([]); expect(fuseRrf([[], []])).toEqual([]); }); });