diff --git a/src/core/pinecone/rerank.test.ts b/src/core/pinecone/rerank.test.ts index c26c63e..6156d30 100644 --- a/src/core/pinecone/rerank.test.ts +++ b/src/core/pinecone/rerank.test.ts @@ -6,6 +6,10 @@ const sampleMerged: MergedHit[] = [ { _id: '1', _score: 0.5, chunk_text: 'hello', metadata: { k: 'v' } }, ]; +function makePc(rerank: ReturnType) { + return { inference: { rerank } } as Parameters[0]; +} + describe('rerankResults', () => { it('returns empty outcome when there are no merged hits', async () => { const pc = {} as Parameters[0]; @@ -23,7 +27,7 @@ describe('rerankResults', () => { }, ], }); - const pc = { inference: { rerank } } as Parameters[0]; + const pc = makePc(rerank); const out = await rerankResults(pc, 'm', 'q', sampleMerged, 5); @@ -37,7 +41,7 @@ describe('rerankResults', () => { it('returns unreranked slice with degraded when rerank throws', async () => { const rerank = vi.fn().mockRejectedValue(new Error('rerank unavailable')); - const pc = { inference: { rerank } } as Parameters[0]; + const pc = makePc(rerank); const out = await rerankResults(pc, 'm', 'q', sampleMerged, 5); @@ -47,4 +51,64 @@ describe('rerankResults', () => { expect(out.results[0]?.reranked).toBe(false); expect(out.results[0]?.content).toBe('hello'); }); + + it('returns empty reranked results with degraded=false when data is an empty array', async () => { + const rerank = vi.fn().mockResolvedValue({ data: [] }); + const pc = makePc(rerank); + + const out = await rerankResults(pc, 'm', 'q', sampleMerged, 5); + + expect(out.results).toEqual([]); + expect(out.degraded).toBe(false); + expect(out.degradation_reason).toBeUndefined(); + }); + + it('returns empty reranked results with degraded=false when data is absent', async () => { + const rerank = vi.fn().mockResolvedValue({}); + const pc = makePc(rerank); + + const out = await rerankResults(pc, 'm', 'q', sampleMerged, 5); + + expect(out.results).toEqual([]); + expect(out.degraded).toBe(false); + }); + + it('maps a rerank item with no document to an empty id/content result instead of throwing', async () => { + const rerank = vi.fn().mockResolvedValue({ + data: [{ score: 0.42 }], + }); + const pc = makePc(rerank); + + const out = await rerankResults(pc, 'm', 'q', sampleMerged, 5); + + expect(out.degraded).toBe(false); + expect(out.results).toHaveLength(1); + expect(out.results[0]?.id).toBe(''); + expect(out.results[0]?.content).toBe(''); + expect(out.results[0]?.metadata).toEqual({}); + expect(out.results[0]?.reranked).toBe(true); + expect(out.results[0]?.score).toBeCloseTo(0.42); + }); + + it('maps duplicate documents in the rerank output to one result per data item', async () => { + const document = { _id: '1', chunk_text: 'hello', metadata: { k: 'v' } }; + const rerank = vi.fn().mockResolvedValue({ + data: [ + { score: 0.9, document }, + { score: 0.8, document }, + ], + }); + const pc = makePc(rerank); + + const out = await rerankResults(pc, 'm', 'q', sampleMerged, 5); + + expect(out.degraded).toBe(false); + expect(out.results).toHaveLength(2); + expect(out.results.map((r) => r.id)).toEqual(['1', '1']); + expect(out.results.map((r) => r.content)).toEqual(['hello', 'hello']); + expect(out.results[0]?.metadata).toEqual({ k: 'v' }); + expect(out.results[0]?.score).toBeCloseTo(0.9); + expect(out.results[1]?.score).toBeCloseTo(0.8); + expect(out.results.every((r) => r.reranked === true)).toBe(true); + }); });