mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-02 20:30:10 +00:00
a97979358f
Adds file hash-based matching for Audiobookshelf library items to ensure 100% accurate ASIN assignment for RMAB-organized content. Removes fuzzy matching from library availability checks, making all matching ASIN-only to eliminate false positives and race conditions. Updates database schema, processors, and matcher utilities; adds new tests and documentation for the new matching strategy. Removes obsolete scripts, Dockerfile, and related tests; updates docker-compose for test environments.
150 lines
4.6 KiB
TypeScript
150 lines
4.6 KiB
TypeScript
/**
|
|
* Component: Ebook Settings Hook Tests
|
|
* Documentation: documentation/settings-pages.md
|
|
*/
|
|
|
|
// @vitest-environment jsdom
|
|
|
|
import React from 'react';
|
|
import { act, render } from '@testing-library/react';
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const fetchWithAuthMock = vi.hoisted(() => vi.fn());
|
|
|
|
vi.mock('@/lib/utils/api', () => ({
|
|
fetchWithAuth: fetchWithAuthMock,
|
|
}));
|
|
|
|
const renderHook = <T,>(hook: () => T) => {
|
|
const result = { current: undefined as T };
|
|
function Probe() {
|
|
result.current = hook();
|
|
return null;
|
|
}
|
|
render(<Probe />);
|
|
return result;
|
|
};
|
|
|
|
const baseEbook = {
|
|
enabled: true,
|
|
preferredFormat: 'epub',
|
|
baseUrl: 'https://annas-archive.li',
|
|
flaresolverrUrl: 'http://flare',
|
|
};
|
|
|
|
describe('useEbookSettings', () => {
|
|
const onChange = vi.fn();
|
|
const onSuccess = vi.fn();
|
|
const onError = vi.fn();
|
|
const markAsSaved = vi.fn();
|
|
|
|
beforeEach(() => {
|
|
fetchWithAuthMock.mockReset();
|
|
onChange.mockReset();
|
|
onSuccess.mockReset();
|
|
onError.mockReset();
|
|
markAsSaved.mockReset();
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
it('updates ebook settings and clears flaresolverr test results when URL changes', async () => {
|
|
const { useEbookSettings } = await import('@/app/admin/settings/tabs/EbookTab/useEbookSettings');
|
|
const result = renderHook(() =>
|
|
useEbookSettings({ ebook: baseEbook, onChange, onSuccess, onError, markAsSaved })
|
|
);
|
|
|
|
act(() => {
|
|
result.current.updateEbook('flaresolverrUrl', 'http://new');
|
|
});
|
|
|
|
expect(onChange).toHaveBeenCalledWith({ ...baseEbook, flaresolverrUrl: 'http://new' });
|
|
expect(result.current.flaresolverrTestResult).toBeNull();
|
|
});
|
|
|
|
it('returns an error when testing FlareSolverr without a URL', async () => {
|
|
const { useEbookSettings } = await import('@/app/admin/settings/tabs/EbookTab/useEbookSettings');
|
|
const result = renderHook(() =>
|
|
useEbookSettings({ ebook: { ...baseEbook, flaresolverrUrl: '' }, onChange, onSuccess, onError, markAsSaved })
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.testFlaresolverrConnection();
|
|
});
|
|
|
|
expect(result.current.flaresolverrTestResult?.success).toBe(false);
|
|
expect(result.current.flaresolverrTestResult?.message).toContain('Please enter a FlareSolverr URL');
|
|
});
|
|
|
|
it('tests FlareSolverr connection successfully', async () => {
|
|
fetchWithAuthMock.mockResolvedValueOnce({
|
|
ok: true,
|
|
json: async () => ({ success: true, message: 'OK' }),
|
|
});
|
|
|
|
const { useEbookSettings } = await import('@/app/admin/settings/tabs/EbookTab/useEbookSettings');
|
|
const result = renderHook(() =>
|
|
useEbookSettings({ ebook: baseEbook, onChange, onSuccess, onError, markAsSaved })
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.testFlaresolverrConnection();
|
|
});
|
|
|
|
expect(result.current.flaresolverrTestResult?.success).toBe(true);
|
|
});
|
|
|
|
it('handles FlareSolverr test failures', async () => {
|
|
fetchWithAuthMock.mockRejectedValueOnce(new Error('flare down'));
|
|
|
|
const { useEbookSettings } = await import('@/app/admin/settings/tabs/EbookTab/useEbookSettings');
|
|
const result = renderHook(() =>
|
|
useEbookSettings({ ebook: baseEbook, onChange, onSuccess, onError, markAsSaved })
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.testFlaresolverrConnection();
|
|
});
|
|
|
|
expect(result.current.flaresolverrTestResult?.message).toBe('flare down');
|
|
});
|
|
|
|
it('saves ebook settings and clears success banner after delay', async () => {
|
|
vi.useFakeTimers();
|
|
fetchWithAuthMock.mockResolvedValueOnce({ ok: true, json: async () => ({}) });
|
|
|
|
const { useEbookSettings } = await import('@/app/admin/settings/tabs/EbookTab/useEbookSettings');
|
|
const result = renderHook(() =>
|
|
useEbookSettings({ ebook: baseEbook, onChange, onSuccess, onError, markAsSaved })
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.saveSettings();
|
|
});
|
|
|
|
expect(onSuccess).toHaveBeenCalledWith('E-book sidecar settings saved successfully!');
|
|
expect(markAsSaved).toHaveBeenCalled();
|
|
|
|
act(() => {
|
|
vi.advanceTimersByTime(3000);
|
|
});
|
|
|
|
expect(onSuccess).toHaveBeenCalledWith('');
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
it('surfaces save errors', async () => {
|
|
fetchWithAuthMock.mockResolvedValueOnce({ ok: false, json: async () => ({}) });
|
|
|
|
const { useEbookSettings } = await import('@/app/admin/settings/tabs/EbookTab/useEbookSettings');
|
|
const result = renderHook(() =>
|
|
useEbookSettings({ ebook: baseEbook, onChange, onSuccess, onError, markAsSaved })
|
|
);
|
|
|
|
await act(async () => {
|
|
await result.current.saveSettings();
|
|
});
|
|
|
|
expect(onError).toHaveBeenCalledWith('Failed to save e-book settings');
|
|
});
|
|
});
|