mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-09-01 05:08:38 +00:00
Fix search result limiting after ranking (#277)
This commit is contained in:
@@ -20,6 +20,10 @@ const rankTorrentsMock = vi.hoisted(() => vi.fn());
|
||||
const groupIndexersMock = vi.hoisted(() => vi.fn());
|
||||
const groupDescriptionMock = vi.hoisted(() => vi.fn(() => 'Group'));
|
||||
|
||||
vi.mock('@/lib/db', () => ({
|
||||
prisma: {},
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/middleware/auth', () => ({
|
||||
requireAuth: requireAuthMock,
|
||||
}));
|
||||
@@ -94,6 +98,45 @@ describe('Audiobooks search torrents route', () => {
|
||||
expect(payload.results[0].rank).toBe(1);
|
||||
expect(rankTorrentsMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ranks all candidates before returning the top 100 with truncation metadata', async () => {
|
||||
authRequest.json.mockResolvedValue({ title: 'Title', author: 'Author' });
|
||||
configServiceMock.get
|
||||
.mockResolvedValueOnce(JSON.stringify([{ id: 1, name: 'Indexer', protocol: 'torrent', priority: 10 }]))
|
||||
.mockResolvedValueOnce(null);
|
||||
groupIndexersMock.mockReturnValue({ groups: [{ categories: [3030], indexerIds: [1] }], skippedIndexers: [] });
|
||||
|
||||
const candidates = Array.from({ length: 101 }, (_, index) => ({
|
||||
title: index === 100 ? 'Best result' : `Result ${index}`,
|
||||
size: 100,
|
||||
indexer: 'Indexer',
|
||||
indexerId: 1,
|
||||
}));
|
||||
const rankedCandidates = [candidates[100], ...candidates.slice(0, 100)].map((result, index) => ({
|
||||
...result,
|
||||
score: 100 - index,
|
||||
breakdown: { matchScore: 50, formatScore: 0, sizeScore: 0, seederScore: 0, notes: [] },
|
||||
bonusPoints: 0,
|
||||
bonusModifiers: [],
|
||||
finalScore: 100 - index,
|
||||
}));
|
||||
prowlarrMock.searchWithVariations.mockResolvedValue(candidates);
|
||||
rankTorrentsMock.mockReturnValue(rankedCandidates);
|
||||
|
||||
const { POST } = await import('@/app/api/audiobooks/search-torrents/route');
|
||||
const response = await POST({} as any);
|
||||
const payload = await response.json();
|
||||
|
||||
expect(rankTorrentsMock.mock.calls[0][0]).toHaveLength(101);
|
||||
expect(prowlarrMock.searchWithVariations).toHaveBeenCalledWith('Title', 'Author', {
|
||||
categories: [3030],
|
||||
indexerIds: [1],
|
||||
});
|
||||
expect(payload.results).toHaveLength(100);
|
||||
expect(payload.results[0]).toEqual(expect.objectContaining({ title: 'Best result', rank: 1 }));
|
||||
expect(payload.truncated).toBe(true);
|
||||
expect(payload.rawCount).toBe(101);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -153,6 +153,51 @@ describe('Request action routes', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('ranks the complete interactive result set before returning the top 100', async () => {
|
||||
authRequest.json.mockResolvedValue({});
|
||||
prismaMock.request.findUnique.mockResolvedValueOnce({
|
||||
id: 'req-complete-ranking',
|
||||
userId: 'user-1',
|
||||
audiobook: { title: 'Title', author: 'Author', audibleAsin: null },
|
||||
});
|
||||
prismaMock.user.findUnique.mockResolvedValueOnce({
|
||||
role: 'user',
|
||||
interactiveSearchAccess: null,
|
||||
});
|
||||
configServiceMock.get.mockResolvedValueOnce(JSON.stringify([{ id: 1, priority: 10, categories: [3030] }]));
|
||||
configServiceMock.get.mockResolvedValueOnce(null);
|
||||
groupIndexersMock.mockReturnValue({ groups: [{ categories: [3030], indexerIds: [1] }], skippedIndexers: [] });
|
||||
|
||||
const candidates = Array.from({ length: 101 }, (_, index) => ({
|
||||
title: index === 100 ? 'Best result' : `Result ${index}`,
|
||||
size: 100,
|
||||
}));
|
||||
const rankedCandidates = [candidates[100], ...candidates.slice(0, 100)].map((result, index) => ({
|
||||
...result,
|
||||
score: 100 - index,
|
||||
breakdown: { matchScore: 50, formatScore: 0, sizeScore: 0, seederScore: 0, notes: [] },
|
||||
bonusPoints: 0,
|
||||
bonusModifiers: [],
|
||||
finalScore: 100 - index,
|
||||
}));
|
||||
prowlarrMock.searchWithVariations.mockResolvedValueOnce(candidates);
|
||||
rankTorrentsMock.mockReturnValueOnce(rankedCandidates);
|
||||
|
||||
const { POST } = await import('@/app/api/requests/[id]/interactive-search/route');
|
||||
const response = await POST({} as any, { params: Promise.resolve({ id: 'req-complete-ranking' }) });
|
||||
const payload = await response.json();
|
||||
|
||||
expect(rankTorrentsMock.mock.calls[0][0]).toHaveLength(101);
|
||||
expect(prowlarrMock.searchWithVariations).toHaveBeenCalledWith('Title', 'Author', {
|
||||
categories: [3030],
|
||||
indexerIds: [1],
|
||||
});
|
||||
expect(payload.results).toHaveLength(100);
|
||||
expect(payload.results[0]).toEqual(expect.objectContaining({ title: 'Best result', rank: 1 }));
|
||||
expect(payload.truncated).toBe(true);
|
||||
expect(payload.rawCount).toBe(101);
|
||||
});
|
||||
|
||||
it('performs interactive search gracefully when runtime fetch fails', async () => {
|
||||
authRequest.json.mockResolvedValue({});
|
||||
prismaMock.request.findUnique.mockResolvedValueOnce({
|
||||
|
||||
Reference in New Issue
Block a user