audiobookshelf/test/server/scanner/LibraryScanner.placeholders.test.js

260 lines
8.3 KiB
JavaScript
Raw Normal View History

const chai = require('chai')
const sinon = require('sinon')
const LibraryScanner = require('../../../server/scanner/LibraryScanner')
const LibraryScan = require('../../../server/scanner/LibraryScan')
const LibraryItemScanner = require('../../../server/scanner/LibraryItemScanner')
const Database = require('../../../server/Database')
const libraryFilters = require('../../../server/utils/queries/libraryFilters')
const SocketAuthority = require('../../../server/SocketAuthority')
const fs = require('../../../server/libs/fsExtra')
const { ScanResult } = require('../../../server/utils/constants')
const expect = chai.expect
describe('LibraryScanner placeholder handling', () => {
let originalSequelize
beforeEach(() => {
originalSequelize = Database.sequelize
Database.sequelize = {
models: {
libraryItem: {
findAll: () => {},
getExpandedById: () => {},
update: () => {},
findOneExpanded: () => {},
findOne: () => {}
}
}
}
})
afterEach(() => {
Database.sequelize = originalSequelize
sinon.restore()
})
it('skips missing checks for placeholder items during full scans', async () => {
const library = {
id: 'library-1',
name: 'Test Library',
mediaType: 'book',
settings: {},
libraryFolders: [
{
id: 'folder-1',
path: '/library'
}
]
}
const libraryScan = new LibraryScan()
libraryScan.setData(library)
const placeholderItem = {
id: 'item-1',
path: '/library/Series/Placeholder',
relPath: 'Series/Placeholder',
ino: 123,
isPlaceholder: true,
isMissing: false,
libraryFiles: [],
save: sinon.stub().resolves(),
changed: sinon.stub()
}
sinon.stub(libraryFilters, 'getFilterData').resolves()
sinon.stub(LibraryScanner, 'scanFolder').resolves([])
const findAllStub = sinon.stub(Database.libraryItemModel, 'findAll').resolves([placeholderItem])
const getExpandedStub = sinon.stub(Database.libraryItemModel, 'getExpandedById').resolves(null)
const updateStub = sinon.stub(Database.libraryItemModel, 'update').resolves()
const checkAuthorsStub = sinon.stub(LibraryItemScanner, 'checkAuthorsAndSeriesRemovedFromBooks').resolves()
await LibraryScanner.scanLibrary(libraryScan, false)
expect(findAllStub.calledOnce).to.be.true
expect(getExpandedStub.called).to.be.false
expect(updateStub.called).to.be.false
expect(checkAuthorsStub.calledOnce).to.be.true
expect(libraryScan.resultsMissing).to.equal(0)
})
it('promotes placeholders when audio files are detected in folder updates', async () => {
const library = {
id: 'library-1',
name: 'Test Library',
mediaType: 'book',
settings: {}
}
const folder = {
id: 'folder-1',
path: '/library',
libraryId: library.id
}
const placeholderItem = {
id: 'item-1',
path: '/library/Series/Placeholder',
relPath: 'Series/Placeholder',
isPlaceholder: true,
isMissing: true,
media: { title: 'Placeholder Book' },
save: sinon.stub().resolves(),
changed: sinon.stub()
}
sinon.stub(libraryFilters, 'getFilterData').resolves()
sinon.stub(Database.libraryItemModel, 'findOneExpanded').resolves(placeholderItem)
sinon.stub(Database.libraryItemModel, 'findOne').resolves(null)
sinon.stub(fs, 'pathExists').resolves(true)
const scanLibraryItemStub = sinon.stub(LibraryItemScanner, 'scanLibraryItem').resolves(ScanResult.UPDATED)
sinon.stub(SocketAuthority, 'libraryItemEmitter')
const fileUpdateGroup = {
'Series/Placeholder': ['track-01.mp3']
}
const results = await LibraryScanner.scanFolderUpdates(library, folder, fileUpdateGroup)
expect(placeholderItem.isPlaceholder).to.be.false
expect(placeholderItem.isMissing).to.be.false
expect(placeholderItem.save.calledOnce).to.be.true
expect(scanLibraryItemStub.calledOnce).to.be.true
expect(results['Series/Placeholder']).to.equal(ScanResult.UPDATED)
})
it('promotes placeholders during full scans when media is present', async () => {
const library = {
id: 'library-1',
name: 'Test Library',
mediaType: 'book',
settings: {},
libraryFolders: [
{
id: 'folder-1',
path: '/library'
}
]
}
const libraryScan = new LibraryScan()
libraryScan.setData(library)
const placeholderItem = {
id: 'item-1',
path: '/library/Series/Placeholder',
relPath: 'Series/Placeholder',
isPlaceholder: true,
isMissing: true,
save: sinon.stub().resolves(),
changed: sinon.stub()
}
const libraryItemData = {
path: placeholderItem.path,
audioLibraryFiles: [{ metadata: { ext: '.mp3' } }],
ebookLibraryFiles: [],
hasLibraryFileChanges: true,
hasPathChange: false,
checkLibraryItemData: sinon.stub().resolves(true)
}
sinon.stub(libraryFilters, 'getFilterData').resolves()
sinon.stub(LibraryScanner, 'scanFolder').resolves([libraryItemData])
sinon.stub(Database.libraryItemModel, 'findAll').resolves([placeholderItem])
const rescanStub = sinon.stub(LibraryItemScanner, 'rescanLibraryItemMedia').resolves({ libraryItem: placeholderItem, wasUpdated: true })
sinon.stub(LibraryItemScanner, 'checkAuthorsAndSeriesRemovedFromBooks').resolves()
sinon.stub(SocketAuthority, 'libraryItemsEmitter')
await LibraryScanner.scanLibrary(libraryScan, false)
expect(placeholderItem.isPlaceholder).to.be.false
expect(placeholderItem.isMissing).to.be.false
expect(placeholderItem.save.calledOnce).to.be.true
expect(rescanStub.calledOnce).to.be.true
})
it('skips placeholder scans on file updates without audio', async () => {
const library = {
id: 'library-1',
name: 'Test Library',
mediaType: 'book',
settings: {}
}
const folder = {
id: 'folder-1',
path: '/library',
libraryId: library.id
}
const placeholderItem = {
id: 'item-1',
path: '/library/Series/Placeholder',
relPath: 'Series/Placeholder',
isPlaceholder: true,
isMissing: false,
media: { title: 'Placeholder Book' },
save: sinon.stub().resolves(),
changed: sinon.stub()
}
sinon.stub(libraryFilters, 'getFilterData').resolves()
sinon.stub(Database.libraryItemModel, 'findOneExpanded').resolves(placeholderItem)
sinon.stub(Database.libraryItemModel, 'findOne').resolves(null)
sinon.stub(fs, 'pathExists').resolves(true)
const scanLibraryItemStub = sinon.stub(LibraryItemScanner, 'scanLibraryItem').resolves(ScanResult.UPDATED)
sinon.stub(SocketAuthority, 'libraryItemEmitter')
const fileUpdateGroup = {
'Series/Placeholder': ['cover.jpg']
}
const results = await LibraryScanner.scanFolderUpdates(library, folder, fileUpdateGroup)
expect(scanLibraryItemStub.called).to.be.false
expect(results['Series/Placeholder']).to.equal(ScanResult.NOTHING)
})
it('ignores missing checks for placeholders on file updates when path is gone', async () => {
const library = {
id: 'library-1',
name: 'Test Library',
mediaType: 'book',
settings: {}
}
const folder = {
id: 'folder-1',
path: '/library',
libraryId: library.id
}
const placeholderItem = {
id: 'item-1',
path: '/library/Series/Placeholder',
relPath: 'Series/Placeholder',
isPlaceholder: true,
isMissing: false,
media: { title: 'Placeholder Book' },
save: sinon.stub().resolves(),
changed: sinon.stub()
}
sinon.stub(libraryFilters, 'getFilterData').resolves()
sinon.stub(Database.libraryItemModel, 'findOneExpanded').resolves(placeholderItem)
sinon.stub(Database.libraryItemModel, 'findOne').resolves(null)
sinon.stub(fs, 'pathExists').resolves(false)
const scanLibraryItemStub = sinon.stub(LibraryItemScanner, 'scanLibraryItem').resolves(ScanResult.UPDATED)
sinon.stub(SocketAuthority, 'libraryItemEmitter')
const fileUpdateGroup = {
'Series/Placeholder': ['track-01.mp3']
}
const results = await LibraryScanner.scanFolderUpdates(library, folder, fileUpdateGroup)
expect(scanLibraryItemStub.called).to.be.false
expect(results['Series/Placeholder']).to.equal(ScanResult.NOTHING)
})
})