mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-09-10 17:58:02 +02:00
Added additional unit tests for construction of objects containing deviceId property
This commit is contained in:
parent
3a4aacb7bf
commit
974e17ee3e
@ -71,10 +71,13 @@ class AudioFile {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ index: any; ino: any; deviceId: any; metadata: any; addedAt: any; updatedAt: any; manuallyVerified: any; exclude: any; error: null; trackNumFromMeta: any; discNumFromMeta: any; trackNumFromFilename: any; cdNumFromFilename: undefined; discNumFromFilename: any; format: any; duration: any; bitRate: any; language: any; codec: null; timeBase: any; channels: any; channelLayout: any; chapters: any[]; embeddedCoverArt: null; metaTags: any; }} data
|
||||
*/
|
||||
construct(data) {
|
||||
this.index = data.index
|
||||
this.ino = data.ino
|
||||
this.deviceId = data.dev
|
||||
this.deviceId = data.deviceId
|
||||
this.metadata = new FileMetadata(data.metadata || {})
|
||||
this.addedAt = data.addedAt
|
||||
this.updatedAt = data.updatedAt
|
||||
|
@ -1,6 +1,9 @@
|
||||
const FileMetadata = require('../metadata/FileMetadata')
|
||||
|
||||
class EBookFile {
|
||||
/**
|
||||
* @param {{ ino: any; deviceId: any; isSupplementary?: boolean; addedAt?: number; updatedAt?: number; metadata?: { filename: string; ext: string; path: string; relPath: string; size: number; mtimeMs: number; ctimeMs: number; birthtimeMs: number; }; libraryFolderId?: any; libraryId?: any; mediaType?: any; mtimeMs?: any; ctimeMs?: any; birthtimeMs?: any; path?: any; relPath?: any; isFile?: any; mediaMetadata?: any; libraryFiles?: any; }} file
|
||||
*/
|
||||
constructor(file) {
|
||||
this.ino = null
|
||||
this.deviceId = null
|
||||
@ -14,9 +17,12 @@ class EBookFile {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ ino: any; deviceId: any; isSupplementary?: boolean | undefined; addedAt: any; updatedAt: any; metadata: any; libraryFolderId?: any; libraryId?: any; mediaType?: any; mtimeMs?: any; ctimeMs?: any; birthtimeMs?: any; path?: any; relPath?: any; isFile?: any; mediaMetadata?: any; libraryFiles?: any; ebookFormat?: any; }} file
|
||||
*/
|
||||
construct(file) {
|
||||
this.ino = file.ino
|
||||
this.deviceId = file.dev
|
||||
this.deviceId = file.deviceId
|
||||
this.metadata = new FileMetadata(file.metadata)
|
||||
this.ebookFormat = file.ebookFormat || this.metadata.format
|
||||
this.addedAt = file.addedAt
|
||||
|
@ -4,6 +4,9 @@ const globals = require('../../utils/globals')
|
||||
const FileMetadata = require('../metadata/FileMetadata')
|
||||
|
||||
class LibraryFile {
|
||||
/**
|
||||
* @param {{ ino: any; deviceId: any; metadata?: { filename: any; ext: any; path: any; relPath: any; size: any; mtimeMs: any; ctimeMs: any; birthtimeMs: any; } | { filename: string; ext: string; path: string; relPath: string; size: number; mtimeMs: number; ctimeMs: number; birthtimeMs: number; } | null; isSupplementary?: any; addedAt?: any; updatedAt?: any; fileType?: string; libraryFolderId?: any; libraryId?: any; mediaType?: any; mtimeMs?: any; ctimeMs?: any; birthtimeMs?: any; path?: any; relPath?: any; isFile?: any; mediaMetadata?: any; libraryFiles?: any; } | undefined} [file]
|
||||
*/
|
||||
constructor(file) {
|
||||
this.ino = null
|
||||
this.deviceId = null
|
||||
|
@ -5,11 +5,11 @@ const globals = require('../utils/globals')
|
||||
|
||||
class LibraryItemScanData {
|
||||
/**
|
||||
* @typedef LibraryFileModifiedObject
|
||||
* @typedef {Object} LibraryFileModifiedObject
|
||||
* @property {LibraryItem.LibraryFileObject} old
|
||||
* @property {LibraryItem.LibraryFileObject} new
|
||||
* @param {{ libraryFolderId: any; libraryId: any; mediaType: any; ino: any; deviceId: any; mtimeMs: any; ctimeMs: any; birthtimeMs: any; path: any; relPath: any; isFile: any; mediaMetadata: any; libraryFiles: any; }} data
|
||||
*/
|
||||
|
||||
constructor(data) {
|
||||
/** @type {string} */
|
||||
this.libraryFolderId = data.libraryFolderId
|
||||
@ -20,7 +20,7 @@ class LibraryItemScanData {
|
||||
/** @type {string} */
|
||||
this.ino = data.ino
|
||||
/** @type {string} */
|
||||
this.deviceId = data.dev
|
||||
this.deviceId = data.deviceId
|
||||
/** @type {number} */
|
||||
this.mtimeMs = data.mtimeMs
|
||||
/** @type {number} */
|
||||
@ -179,8 +179,8 @@ class LibraryItemScanData {
|
||||
/**
|
||||
*
|
||||
* @param {LibraryItem} existingLibraryItem
|
||||
* @param {import('./LibraryScan')} libraryScan
|
||||
* @returns {boolean} true if changes found
|
||||
* @param {import('./LibraryScan') | import('./ScanLogger')} libraryScan
|
||||
* @returns {Promise<boolean>} true if changes found
|
||||
*/
|
||||
async checkLibraryItemData(existingLibraryItem, libraryScan) {
|
||||
const keysToCompare = ['libraryFolderId', 'ino', 'deviceId', 'path', 'relPath', 'isFile']
|
||||
|
@ -23,7 +23,7 @@ class LibraryItemScanner {
|
||||
* Scan single library item
|
||||
*
|
||||
* @param {string} libraryItemId
|
||||
* @param {{relPath:string, path:string}} [updateLibraryItemDetails] used by watcher when item folder was renamed
|
||||
* @param {{relPath:string, path:string, isFile: boolean}} [updateLibraryItemDetails] used by watcher when item folder was renamed
|
||||
* @returns {number} ScanResult
|
||||
*/
|
||||
async scanLibraryItem(libraryItemId, updateLibraryItemDetails = null) {
|
||||
@ -190,7 +190,7 @@ class LibraryItemScanner {
|
||||
* @param {import('../models/Library')} library
|
||||
* @param {import('../models/LibraryFolder')} folder
|
||||
* @param {boolean} isSingleMediaItem
|
||||
* @returns {Promise<LibraryItem>} ScanResult
|
||||
* @returns {Promise<LibraryItem | null>} ScanResult
|
||||
*/
|
||||
async scanPotentialNewLibraryItem(libraryItemPath, library, folder, isSingleMediaItem) {
|
||||
const libraryItemScanData = await this.getLibraryItemScanData(libraryItemPath, library, folder, isSingleMediaItem)
|
||||
@ -209,6 +209,13 @@ class LibraryItemScanner {
|
||||
}
|
||||
module.exports = new LibraryItemScanner()
|
||||
|
||||
/**
|
||||
* @param {{ path?: any; relPath?: any; mediaMetadata?: any; }} libraryItemData
|
||||
* @param {import("../models/LibraryFolder")} folder
|
||||
* @param {import("../models/Library")} library
|
||||
* @param {boolean} isSingleMediaItem
|
||||
* @param {LibraryFile[]} libraryFiles
|
||||
*/
|
||||
async function buildLibraryItemScanData(libraryItemData, folder, library, isSingleMediaItem, libraryFiles) {
|
||||
const libraryItemStats = await fileUtils.getFileTimestampsWithIno(libraryItemData.path)
|
||||
return new LibraryItemScanData({
|
||||
|
122
test/server/MockDatabase.js
Normal file
122
test/server/MockDatabase.js
Normal file
@ -0,0 +1,122 @@
|
||||
const Database = require('../../server/Database')
|
||||
const { Sequelize } = require('sequelize')
|
||||
const LibraryFile = require('../../server/objects/files/LibraryFile')
|
||||
const fileUtils = require('../../server/utils/fileUtils')
|
||||
const sinon = require('sinon')
|
||||
|
||||
async function loadTestDatabase(mockFileInfo) {
|
||||
let libraryItem1Id, libraryItem2Id
|
||||
|
||||
let fileInfo = mockFileInfo || getMockFileInfo()
|
||||
let bookLibraryFiles = fileInfo.keys().reduce((acc, key) => {
|
||||
let bookfile = new LibraryFile()
|
||||
bookfile.setDataFromPath(key, key)
|
||||
acc.push(bookfile)
|
||||
return acc
|
||||
}, [])
|
||||
|
||||
global.ServerSettings = {}
|
||||
Database.sequelize = new Sequelize({
|
||||
dialect: 'sqlite',
|
||||
storage: ':memory:',
|
||||
// Choose one of the logging options
|
||||
logging: (...msg) => console.log(msg),
|
||||
logQueryParameters: true
|
||||
})
|
||||
Database.sequelize.uppercaseFirst = (str) => (str ? `${str[0].toUpperCase()}${str.substr(1)}` : '')
|
||||
await Database.buildModels()
|
||||
|
||||
const newLibrary = await Database.libraryModel.create({ name: 'Test Library', mediaType: 'book' })
|
||||
const newLibraryFolder = await Database.libraryFolderModel.create({ path: '/test', libraryId: newLibrary.id })
|
||||
const newLibraryFolder2 = await Database.libraryFolderModel.create({ path: '/mnt/drive', libraryId: newLibrary.id })
|
||||
|
||||
const newBook = await Database.bookModel.create({ title: 'Test Book', audioFiles: [], tags: [], narrators: [], genres: [], chapters: [] })
|
||||
const newLibraryItem = await Database.libraryItemModel.create(buildBookLibraryItemParams(bookLibraryFiles[0], newBook.id, newLibrary.id, newLibraryFolder.id))
|
||||
libraryItem1Id = newLibraryItem.id
|
||||
|
||||
const newBook2 = await Database.bookModel.create({ title: 'Test Book 2', audioFiles: [], tags: [], narrators: [], genres: [], chapters: [] })
|
||||
const newLibraryItem2 = await Database.libraryItemModel.create(buildBookLibraryItemParams(bookLibraryFiles[1], newBook2.id, newLibrary.id, newLibraryFolder2.id))
|
||||
libraryItem2Id = newLibraryItem2.id
|
||||
|
||||
return newLibrary
|
||||
}
|
||||
exports.loadTestDatabase = loadTestDatabase
|
||||
|
||||
/** @returns {Map<string, import('fs').Stats>} */
|
||||
function getMockFileInfo() {
|
||||
// @ts-ignore
|
||||
return new Map([
|
||||
['/test/file.pdf', { path: '/test/file.pdf', isDirectory: () => false, size: 1024, mtimeMs: Date.now(), ino: '1', dev: '100' }],
|
||||
['/mnt/drive/file-same-ino-different-dev.pdf', { path: '/mnt/drive/file-same-ino-different-dev.pdf', isDirectory: () => false, size: 42, mtimeMs: Date.now(), ino: '1', dev: '200' }]
|
||||
])
|
||||
}
|
||||
|
||||
exports.getMockFileInfo = getMockFileInfo
|
||||
/** @returns {Map<string, import('fs').Stats>} */
|
||||
// this has the same data as above except one file has been renamed
|
||||
function getRenamedMockFileInfo() {
|
||||
// @ts-ignore
|
||||
return new Map([
|
||||
['/test/file-renamed.pdf', { path: '/test/file-renamed.pdf', isDirectory: () => false, size: 1024, mtimeMs: Date.now(), ino: '1', dev: '100' }],
|
||||
['/mnt/drive/file-same-ino-different-dev.pdf', { path: '/mnt/drive/file-same-ino-different-dev.pdf', isDirectory: () => false, size: 42, mtimeMs: Date.now(), ino: '1', dev: '200' }]
|
||||
])
|
||||
}
|
||||
exports.getRenamedMockFileInfo = getRenamedMockFileInfo
|
||||
|
||||
/**
|
||||
* @param {LibraryFile} libraryFile
|
||||
* @param {any} bookId
|
||||
* @param {string} libraryId
|
||||
* @param {any} libraryFolderId
|
||||
*/
|
||||
function buildBookLibraryItemParams(libraryFile, bookId, libraryId, libraryFolderId) {
|
||||
return {
|
||||
path: libraryFile.metadata?.path,
|
||||
isFile: true,
|
||||
ino: libraryFile.ino,
|
||||
deviceId: libraryFile.deviceId,
|
||||
libraryFiles: [libraryFile.toJSON()],
|
||||
mediaId: bookId,
|
||||
mediaType: 'book',
|
||||
libraryId: libraryId,
|
||||
libraryFolderId: libraryFolderId
|
||||
}
|
||||
}
|
||||
exports.buildBookLibraryItemParams = buildBookLibraryItemParams
|
||||
|
||||
function stubFileUtils() {
|
||||
let getInoStub, getDeviceIdStub, getFileTimestampsWithInoStub
|
||||
getInoStub = sinon.stub(fileUtils, 'getIno')
|
||||
getInoStub.callsFake((path) => {
|
||||
const normalizedPath = fileUtils.filePathToPOSIX(path).replace(/\/$/, '')
|
||||
const stats = getMockFileInfo().get(normalizedPath)
|
||||
if (stats) {
|
||||
return stats.ino
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
||||
getDeviceIdStub = sinon.stub(fileUtils, 'getDeviceId')
|
||||
getDeviceIdStub.callsFake(async (path) => {
|
||||
const normalizedPath = fileUtils.filePathToPOSIX(path).replace(/\/$/, '')
|
||||
const stats = getMockFileInfo().get(normalizedPath)
|
||||
if (stats) {
|
||||
return stats.dev
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
||||
getFileTimestampsWithInoStub = sinon.stub(fileUtils, 'getFileTimestampsWithIno')
|
||||
getFileTimestampsWithInoStub.callsFake(async (path) => {
|
||||
const normalizedPath = fileUtils.filePathToPOSIX(path).replace(/\/$/, '')
|
||||
const stats = getMockFileInfo().get(normalizedPath)
|
||||
if (stats) {
|
||||
return stats
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
})
|
||||
}
|
||||
exports.stubFileUtils = stubFileUtils
|
@ -1,9 +1,115 @@
|
||||
const chai = require('chai')
|
||||
const expect = chai.expect
|
||||
const sinon = require('sinon')
|
||||
|
||||
const Path = require('path')
|
||||
const Database = require('../../../server/Database')
|
||||
const { loadTestDatabase, stubFileUtils, getMockFileInfo } = require('../MockDatabase')
|
||||
|
||||
// TODO: all of these classes duplicate each other.
|
||||
const LibraryFile = require('../../../server/objects/files/LibraryFile')
|
||||
const EBookFile = require('../../../server/objects/files/EBookFile')
|
||||
const AudioFile = require('../../../server/objects/files/AudioFile')
|
||||
const LibraryItem = require('../../../server/models/LibraryItem')
|
||||
const LibraryItemScanData = require('../../../server/scanner/LibraryItemScanData')
|
||||
const FileMetadata = require('../../../server/objects/metadata/FileMetadata')
|
||||
|
||||
// TODO: all of these duplicate each other. Need to verify that deviceId is set on each when constructing. And that deviceId is populated when using toJSON()
|
||||
const fileProperties = buildFileProperties()
|
||||
const lf = new LibraryFile(fileProperties)
|
||||
const ebf = new EBookFile(fileProperties)
|
||||
const af = new AudioFile(fileProperties)
|
||||
|
||||
// TODO: check that any libraryFiles properties set to JSON contain a LibraryFile which has a deviceId property
|
||||
describe('ObjectSetsDeviceIdWhenConstructed', function () {
|
||||
this.timeout(0)
|
||||
beforeEach(async () => {
|
||||
stubFileUtils()
|
||||
await loadTestDatabase()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
const lisd = new LibraryItemScanData(fileProperties)
|
||||
|
||||
const objects = [lf, ebf, af, lisd]
|
||||
|
||||
objects.forEach((obj) => {
|
||||
it(`${obj.constructor.name}SetsDeviceIdWhenConstructed`, () => {
|
||||
expect(obj.ino).to.equal(fileProperties.ino)
|
||||
expect(obj.deviceId).to.equal(fileProperties.deviceId)
|
||||
})
|
||||
})
|
||||
|
||||
it('LibraryItemSetsDeviceIdWhenConstructed', async () => {
|
||||
const mockFileInfo = getMockFileInfo().get('/test/file.pdf')
|
||||
|
||||
/** @type {import('../../../server/models/LibraryItem') | null} */
|
||||
const li = await Database.libraryItemModel.findOneExpanded({
|
||||
path: '/test/file.pdf'
|
||||
})
|
||||
|
||||
expect(li?.ino).to.equal(mockFileInfo?.ino)
|
||||
expect(li?.deviceId).to.equal(mockFileInfo?.dev)
|
||||
})
|
||||
|
||||
it('LibraryFileJSONHasDeviceId', async () => {
|
||||
const mockFileInfo = getMockFileInfo().get('/test/file.pdf')
|
||||
|
||||
/** @type {import('../../../server/models/LibraryItem') | null} */
|
||||
const li = await Database.libraryItemModel.findOneExpanded({
|
||||
path: '/test/file.pdf'
|
||||
})
|
||||
|
||||
const lf_json = li?.libraryFiles[0]
|
||||
expect(lf_json).to.not.be.null
|
||||
expect(lf_json?.deviceId).to.equal(mockFileInfo?.dev)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ObjectSetsDeviceIdWhenSerialized', () => {
|
||||
const objects = [lf, ebf, af]
|
||||
objects.forEach((obj) => {
|
||||
it(`${obj.constructor.name}SetsDeviceIdWhenSerialized`, () => {
|
||||
const obj_json = obj.toJSON()
|
||||
expect(obj_json.ino).to.equal(fileProperties.ino)
|
||||
expect(obj_json.deviceId).to.equal(fileProperties.deviceId)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
/** @returns {{ libraryFolderId: any; libraryId: any; mediaType: any; ino: any; deviceId: any; mtimeMs: any; ctimeMs: any; birthtimeMs: any; path: any; relPath: any; isFile: any; mediaMetadata: any; libraryFiles: any; }} */
|
||||
function buildFileProperties() {
|
||||
const path = '/tmp/foo.epub'
|
||||
const metadata = new FileMetadata()
|
||||
metadata.filename = Path.basename(path)
|
||||
metadata.path = path
|
||||
metadata.relPath = path
|
||||
metadata.ext = Path.extname(path)
|
||||
|
||||
return {
|
||||
ino: '12345',
|
||||
deviceId: '9876',
|
||||
metadata: metadata,
|
||||
isSupplementary: false,
|
||||
addedAt: Date.now(),
|
||||
updatedAt: Date.now()
|
||||
}
|
||||
}
|
||||
|
||||
function buildLibraryItemProperties(fileProperties) {
|
||||
return {
|
||||
id: '7792E90F-D526-4636-8A38-EA8342E71FEE',
|
||||
path: fileProperties.path,
|
||||
relPath: fileProperties.path,
|
||||
isFile: true,
|
||||
ino: fileProperties.ino,
|
||||
deviceId: fileProperties.dev,
|
||||
libraryFiles: [],
|
||||
mediaId: '7195803A-9974-46E4-A7D1-7A6E1AD7FD4B',
|
||||
mediaType: 'book',
|
||||
libraryId: '907DA361-67E4-47CF-9C67-C8E2E5CA1B15',
|
||||
libraryFolderId: 'E2216F60-8ABF-4E55-BA83-AD077EB907F3',
|
||||
createdAt: Date.now(),
|
||||
updatedAt: Date.now()
|
||||
}
|
||||
}
|
||||
|
@ -1 +1,70 @@
|
||||
// TODO: test buildLibraryItemScanData
|
||||
const chai = require('chai')
|
||||
const expect = chai.expect
|
||||
const sinon = require('sinon')
|
||||
const rewire = require('rewire')
|
||||
const Path = require('path')
|
||||
|
||||
const { stubFileUtils, getMockFileInfo, loadTestDatabase } = require('../MockDatabase')
|
||||
|
||||
const LibraryFile = require('../../../server/objects/files/LibraryFile')
|
||||
const FileMetadata = require('../../../server/objects/metadata/FileMetadata')
|
||||
const LibraryFolder = require('../../../server/models/LibraryFolder')
|
||||
|
||||
describe('buildLibraryItemScanData', () => {
|
||||
let testLibrary = null
|
||||
beforeEach(async () => {
|
||||
stubFileUtils()
|
||||
testLibrary = await loadTestDatabase()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('setsDeviceId', async () => {
|
||||
const libraryItemScanner = rewire('../../../server/scanner/LibraryItemScanner')
|
||||
|
||||
/**
|
||||
* @param {{ path?: any; relPath?: any; mediaMetadata?: any; }} libraryItemData
|
||||
* @param {import("../../../server/models/LibraryFolder")} folder
|
||||
* @param {import("../../../server/models/Library")} library
|
||||
* @param {boolean} isSingleMediaItem
|
||||
* @param {LibraryFile[]} libraryFiles
|
||||
* @return {import('../../../server/scanner/LibraryItemScanData') | null}
|
||||
* */
|
||||
const buildLibraryItemScanData = libraryItemScanner.__get__('buildLibraryItemScanData')
|
||||
|
||||
const mockFileInfo = getMockFileInfo().get('/test/file.pdf')
|
||||
const lf = new LibraryFile()
|
||||
var fileMetadata = new FileMetadata()
|
||||
fileMetadata.setData(mockFileInfo)
|
||||
fileMetadata.filename = Path.basename(mockFileInfo?.path)
|
||||
fileMetadata.path = mockFileInfo?.path
|
||||
fileMetadata.relPath = mockFileInfo?.path
|
||||
fileMetadata.ext = Path.extname(mockFileInfo?.path)
|
||||
lf.ino = mockFileInfo?.ino
|
||||
lf.deviceId = mockFileInfo?.dev
|
||||
lf.metadata = fileMetadata
|
||||
lf.addedAt = Date.now()
|
||||
lf.updatedAt = Date.now()
|
||||
lf.metadata = fileMetadata
|
||||
|
||||
const libraryItemData = {
|
||||
path: mockFileInfo?.path, // full path
|
||||
relPath: mockFileInfo?.path, // only filename
|
||||
mediaMetadata: {
|
||||
title: Path.basename(mockFileInfo?.path, Path.extname(mockFileInfo?.path))
|
||||
}
|
||||
}
|
||||
|
||||
const scanData = await buildLibraryItemScanData(libraryItemData, buildLibraryFolder(), testLibrary, true, [lf.toJSON()])
|
||||
|
||||
expect(scanData).to.not.be.null
|
||||
expect(scanData.deviceId).to.equal(mockFileInfo?.dev)
|
||||
})
|
||||
})
|
||||
|
||||
/** @return {import("../../../server/models/LibraryFolder")} folder */
|
||||
function buildLibraryFolder() {
|
||||
return new LibraryFolder()
|
||||
}
|
||||
|
@ -3,49 +3,18 @@ const expect = chai.expect
|
||||
const sinon = require('sinon')
|
||||
const rewire = require('rewire')
|
||||
const fileUtils = require('../../../server/utils/fileUtils')
|
||||
const Database = require('../../../server/Database')
|
||||
const { Sequelize } = require('sequelize')
|
||||
const LibraryFile = require('../../../server/objects/files/LibraryFile')
|
||||
const LibraryItem = require('../../../server/models/LibraryItem')
|
||||
const FileMetadata = require('../../../server/objects/metadata/FileMetadata')
|
||||
const Path = require('path')
|
||||
const Database = require('../../../server/Database')
|
||||
const { stubFileUtils, loadTestDatabase, getMockFileInfo, getRenamedMockFileInfo, buildBookLibraryItemParams } = require('../MockDatabase')
|
||||
|
||||
describe('LibraryScanner', () => {
|
||||
let getInoStub, getDeviceIdStub, getFileTimestampsWithInoStub, LibraryScanner, testLibrary
|
||||
let LibraryScanner, testLibrary
|
||||
|
||||
beforeEach(async () => {
|
||||
getInoStub = sinon.stub(fileUtils, 'getIno')
|
||||
getInoStub.callsFake((path) => {
|
||||
const normalizedPath = fileUtils.filePathToPOSIX(path).replace(/\/$/, '')
|
||||
const stats = getMockFileInfo().get(normalizedPath)
|
||||
if (stats) {
|
||||
return stats.ino
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
||||
getDeviceIdStub = sinon.stub(fileUtils, 'getDeviceId')
|
||||
getDeviceIdStub.callsFake(async (path) => {
|
||||
const normalizedPath = fileUtils.filePathToPOSIX(path).replace(/\/$/, '')
|
||||
const stats = getMockFileInfo().get(normalizedPath)
|
||||
if (stats) {
|
||||
return stats.dev
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
||||
getFileTimestampsWithInoStub = sinon.stub(fileUtils, 'getFileTimestampsWithIno')
|
||||
getFileTimestampsWithInoStub.callsFake(async (path) => {
|
||||
const normalizedPath = fileUtils.filePathToPOSIX(path).replace(/\/$/, '')
|
||||
const stats = getMockFileInfo().get(normalizedPath)
|
||||
if (stats) {
|
||||
return stats
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
})
|
||||
stubFileUtils()
|
||||
|
||||
LibraryScanner = rewire('../../../server/scanner/LibraryScanner')
|
||||
})
|
||||
@ -245,79 +214,3 @@ describe('LibraryScanner', () => {
|
||||
let ItemToItemInoMatch = LibraryScanner.__get__('ItemToItemInoMatch')
|
||||
})
|
||||
})
|
||||
|
||||
async function loadTestDatabase(mockFileInfo) {
|
||||
let libraryItem1Id, libraryItem2Id
|
||||
|
||||
let fileInfo = mockFileInfo || getMockFileInfo()
|
||||
let bookLibraryFiles = fileInfo.keys().reduce((acc, key) => {
|
||||
let bookfile = new LibraryFile()
|
||||
bookfile.setDataFromPath(key, key)
|
||||
acc.push(bookfile)
|
||||
return acc
|
||||
}, [])
|
||||
|
||||
global.ServerSettings = {}
|
||||
Database.sequelize = new Sequelize({
|
||||
dialect: 'sqlite',
|
||||
storage: ':memory:',
|
||||
// Choose one of the logging options
|
||||
logging: (...msg) => console.log(msg),
|
||||
logQueryParameters: true
|
||||
})
|
||||
Database.sequelize.uppercaseFirst = (str) => (str ? `${str[0].toUpperCase()}${str.substr(1)}` : '')
|
||||
await Database.buildModels()
|
||||
|
||||
const newLibrary = await Database.libraryModel.create({ name: 'Test Library', mediaType: 'book' })
|
||||
const newLibraryFolder = await Database.libraryFolderModel.create({ path: '/test', libraryId: newLibrary.id })
|
||||
const newLibraryFolder2 = await Database.libraryFolderModel.create({ path: '/mnt/drive', libraryId: newLibrary.id })
|
||||
|
||||
const newBook = await Database.bookModel.create({ title: 'Test Book', audioFiles: [], tags: [], narrators: [], genres: [], chapters: [] })
|
||||
const newLibraryItem = await Database.libraryItemModel.create(buildBookLibraryItemParams(bookLibraryFiles[0], newBook.id, newLibrary.id, newLibraryFolder.id))
|
||||
libraryItem1Id = newLibraryItem.id
|
||||
|
||||
const newBook2 = await Database.bookModel.create({ title: 'Test Book 2', audioFiles: [], tags: [], narrators: [], genres: [], chapters: [] })
|
||||
const newLibraryItem2 = await Database.libraryItemModel.create(buildBookLibraryItemParams(bookLibraryFiles[1], newBook2.id, newLibrary.id, newLibraryFolder2.id))
|
||||
libraryItem2Id = newLibraryItem2.id
|
||||
|
||||
return newLibrary
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LibraryFile} libraryFile
|
||||
* @param {any} bookId
|
||||
* @param {string} libraryId
|
||||
* @param {any} libraryFolderId
|
||||
*/
|
||||
function buildBookLibraryItemParams(libraryFile, bookId, libraryId, libraryFolderId) {
|
||||
return {
|
||||
path: libraryFile.metadata.path,
|
||||
isFile: true,
|
||||
ino: libraryFile.ino,
|
||||
deviceId: libraryFile.deviceId,
|
||||
libraryFiles: [libraryFile.toJSON()],
|
||||
mediaId: bookId,
|
||||
mediaType: 'book',
|
||||
libraryId: libraryId,
|
||||
libraryFolderId: libraryFolderId
|
||||
}
|
||||
}
|
||||
|
||||
/** @returns {Map<string, import('fs').Stats>} */
|
||||
function getMockFileInfo() {
|
||||
// @ts-ignore
|
||||
return new Map([
|
||||
['/test/file.pdf', { path: '/test/file.pdf', isDirectory: () => false, size: 1024, mtimeMs: Date.now(), ino: '1', dev: '100' }],
|
||||
['/mnt/drive/file-same-ino-different-dev.pdf', { path: '/mnt/drive/file-same-ino-different-dev.pdf', isDirectory: () => false, size: 42, mtimeMs: Date.now(), ino: '1', dev: '200' }]
|
||||
])
|
||||
}
|
||||
|
||||
/** @returns {Map<string, import('fs').Stats>} */
|
||||
// this has the same data as above except one file has been renamed
|
||||
function getRenamedMockFileInfo() {
|
||||
// @ts-ignore
|
||||
return new Map([
|
||||
['/test/file-renamed.pdf', { path: '/test/file-renamed.pdf', isDirectory: () => false, size: 1024, mtimeMs: Date.now(), ino: '1', dev: '100' }],
|
||||
['/mnt/drive/file-same-ino-different-dev.pdf', { path: '/mnt/drive/file-same-ino-different-dev.pdf', isDirectory: () => false, size: 42, mtimeMs: Date.now(), ino: '1', dev: '200' }]
|
||||
])
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user