mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-02 20:30:10 +00:00
SABnzbd path mapping + ASIN-based request deletion
Add bidirectional path mapping and complete_dir-aware category sync to the SABnzbd integration. Introduces PathMapper usage, complete_dir extraction, calculateCategoryPath(), and ensureCategory() logic to choose empty/relative/absolute category paths; ensureCategory is invoked before adding NZBs. Update singleton factory to load download_dir and path-mapping config from DownloadClientManager and recreate the service when config is not loaded. Make DownloadClientManager pass path-mapping config into the SABnzbd service. Change request deletion to remove plex_library records by ASIN (deleteMany) with a fallback to exact title/author matches so availability checks and deletions are consistent. Update documentation and tests to reflect the new behavior and APIs.
This commit is contained in:
@@ -44,7 +44,8 @@ describe('IndexerConfigModal', () => {
|
||||
priority: 25,
|
||||
seedingTimeMinutes: 0,
|
||||
rssEnabled: false,
|
||||
categories: expect.arrayContaining([3030]),
|
||||
audiobookCategories: expect.arrayContaining([3030]),
|
||||
ebookCategories: expect.arrayContaining([7020]),
|
||||
})
|
||||
);
|
||||
expect(onClose).toHaveBeenCalledTimes(1);
|
||||
@@ -63,6 +64,7 @@ describe('IndexerConfigModal', () => {
|
||||
/>
|
||||
);
|
||||
|
||||
// Find the Audiobook toggle in the category tree and click it to deselect
|
||||
const audiobookLabel = screen.getByText('Audiobook');
|
||||
const audiobookRow = audiobookLabel.closest('div')?.parentElement;
|
||||
if (!audiobookRow) {
|
||||
@@ -72,7 +74,8 @@ describe('IndexerConfigModal', () => {
|
||||
fireEvent.click(within(audiobookRow).getByRole('switch'));
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Add Indexer' }));
|
||||
|
||||
expect(screen.getByText('At least one category must be selected')).toBeInTheDocument();
|
||||
// Component now shows specific error for audiobook categories
|
||||
expect(screen.getByText('At least one audiobook category must be selected')).toBeInTheDocument();
|
||||
expect(onSave).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
const useAuthMock = vi.hoisted(() => vi.fn());
|
||||
const useAudiobookDetailsMock = vi.hoisted(() => vi.fn());
|
||||
const createRequestMock = vi.hoisted(() => vi.fn());
|
||||
const fetchEbookMock = vi.hoisted(() => vi.fn());
|
||||
const revalidateEbookStatusMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock('@/contexts/AuthContext', () => ({
|
||||
useAuth: () => useAuthMock(),
|
||||
@@ -23,6 +25,11 @@ vi.mock('@/lib/hooks/useAudiobooks', () => ({
|
||||
|
||||
vi.mock('@/lib/hooks/useRequests', () => ({
|
||||
useCreateRequest: () => ({ createRequest: createRequestMock, isLoading: false }),
|
||||
useEbookStatus: () => ({
|
||||
ebookStatus: { ebookSourcesEnabled: false, hasActiveEbookRequest: false },
|
||||
revalidate: revalidateEbookStatusMock,
|
||||
}),
|
||||
useFetchEbookByAsin: () => ({ fetchEbook: fetchEbookMock, isLoading: false }),
|
||||
}));
|
||||
|
||||
vi.mock('@/components/requests/InteractiveTorrentSearchModal', () => ({
|
||||
|
||||
@@ -13,6 +13,10 @@ const searchByRequestMock = vi.hoisted(() => vi.fn());
|
||||
const selectTorrentMock = vi.hoisted(() => vi.fn());
|
||||
const searchByAudiobookMock = vi.hoisted(() => vi.fn());
|
||||
const requestWithTorrentMock = vi.hoisted(() => vi.fn());
|
||||
const searchEbooksMock = vi.hoisted(() => vi.fn());
|
||||
const selectEbookMock = vi.hoisted(() => vi.fn());
|
||||
const searchEbooksByAsinMock = vi.hoisted(() => vi.fn());
|
||||
const selectEbookByAsinMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock('@/lib/hooks/useRequests', () => ({
|
||||
useInteractiveSearch: () => ({
|
||||
@@ -35,6 +39,26 @@ vi.mock('@/lib/hooks/useRequests', () => ({
|
||||
isLoading: false,
|
||||
error: null,
|
||||
}),
|
||||
useInteractiveSearchEbook: () => ({
|
||||
searchEbooks: searchEbooksMock,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
}),
|
||||
useSelectEbook: () => ({
|
||||
selectEbook: selectEbookMock,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
}),
|
||||
useInteractiveSearchEbookByAsin: () => ({
|
||||
searchEbooks: searchEbooksByAsinMock,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
}),
|
||||
useSelectEbookByAsin: () => ({
|
||||
selectEbook: selectEbookByAsinMock,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
}),
|
||||
}));
|
||||
|
||||
const baseResult = {
|
||||
|
||||
Reference in New Issue
Block a user