Files
ReadMeABook/tests/services/library/audiobookshelf-library.service.test.ts
T
kikootwo 94dbaf073b Add backend unit test framework and modularize settings UI
Introduced a Vitest-based backend unit testing framework with supporting scripts, helpers, and GitHub Actions integration. Refactored the admin settings page to a modular architecture, splitting monolithic logic into feature-specific tabs and hooks for improved maintainability and testability. Updated documentation to reflect the new testing setup and settings architecture, and added new dependencies for testing utilities.
2026-01-28 11:41:59 -05:00

151 lines
4.2 KiB
TypeScript

/**
* Component: Audiobookshelf Library Service Tests
* Documentation: documentation/features/audiobookshelf-integration.md
*/
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { AudiobookshelfLibraryService } from '@/lib/services/library/AudiobookshelfLibraryService';
const apiMock = vi.hoisted(() => ({
getABSServerInfo: vi.fn(),
getABSLibraries: vi.fn(),
getABSLibraryItems: vi.fn(),
getABSRecentItems: vi.fn(),
getABSItem: vi.fn(),
searchABSItems: vi.fn(),
triggerABSScan: vi.fn(),
}));
vi.mock('@/lib/services/audiobookshelf/api', () => apiMock);
describe('AudiobookshelfLibraryService', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('tests connection and returns server info', async () => {
apiMock.getABSServerInfo.mockResolvedValue({ name: 'ABS', version: '2.0.0' });
const service = new AudiobookshelfLibraryService();
const result = await service.testConnection();
expect(result.success).toBe(true);
expect(result.serverInfo).toEqual({
name: 'ABS',
version: '2.0.0',
identifier: 'ABS',
});
});
it('returns errors when server info fails', async () => {
apiMock.getABSServerInfo.mockRejectedValue(new Error('No connection'));
const service = new AudiobookshelfLibraryService();
const result = await service.testConnection();
expect(result.success).toBe(false);
expect(result.error).toBe('No connection');
});
it('filters audiobook libraries only', async () => {
apiMock.getABSLibraries.mockResolvedValue([
{ id: 'lib-1', name: 'Books', mediaType: 'book', stats: { totalItems: 10 } },
{ id: 'lib-2', name: 'Podcasts', mediaType: 'podcast', stats: { totalItems: 5 } },
]);
const service = new AudiobookshelfLibraryService();
const libs = await service.getLibraries();
expect(libs).toEqual([
{ id: 'lib-1', name: 'Books', type: 'book', itemCount: 10 },
]);
});
it('maps library items to generic fields', async () => {
apiMock.getABSLibraryItems.mockResolvedValue([
{
id: 'item-1',
addedAt: 1700000000000,
updatedAt: 1700000100000,
media: {
duration: 3600,
coverPath: '/covers/1.jpg',
metadata: {
title: 'Title',
authorName: 'Author',
narratorName: 'Narrator',
description: 'Desc',
asin: 'ASIN1',
isbn: 'ISBN1',
publishedYear: '2020',
},
},
},
]);
const service = new AudiobookshelfLibraryService();
const items = await service.getLibraryItems('lib-1');
expect(items[0]).toEqual({
id: 'item-1',
externalId: 'item-1',
title: 'Title',
author: 'Author',
narrator: 'Narrator',
description: 'Desc',
coverUrl: '/api/items/item-1/cover',
duration: 3600,
asin: 'ASIN1',
isbn: 'ISBN1',
year: 2020,
addedAt: new Date(1700000000000),
updatedAt: new Date(1700000100000),
});
});
it('returns null when item fetch fails', async () => {
apiMock.getABSItem.mockRejectedValue(new Error('missing'));
const service = new AudiobookshelfLibraryService();
const result = await service.getItem('item-1');
expect(result).toBeNull();
});
it('searches items and maps results', async () => {
apiMock.searchABSItems.mockResolvedValue([
{
libraryItem: {
id: 'item-2',
addedAt: 1700000000000,
updatedAt: 1700000000000,
media: {
duration: 200,
metadata: {
title: 'Search Title',
authorName: 'Search Author',
narratorName: '',
description: '',
},
},
},
},
]);
const service = new AudiobookshelfLibraryService();
const items = await service.searchItems('lib-1', 'Search');
expect(items[0].title).toBe('Search Title');
expect(items[0].author).toBe('Search Author');
});
it('triggers library scans', async () => {
apiMock.triggerABSScan.mockResolvedValue(undefined);
const service = new AudiobookshelfLibraryService();
await service.triggerLibraryScan('lib-1');
expect(apiMock.triggerABSScan).toHaveBeenCalledWith('lib-1');
});
});