mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-07-08 02:11:35 +00:00
tighten postgres compatibility tests and diagnostics
This commit is contained in:
parent
06ad6a722f
commit
7d97d09191
7 changed files with 366 additions and 3 deletions
|
|
@ -62,7 +62,7 @@ describe('libraryItemsBookFilters postgres query safety', () => {
|
|||
booksToExclude: [],
|
||||
bookSeriesToInclude: []
|
||||
})
|
||||
const warnStub = sinon.stub(Logger, 'warn')
|
||||
const debugStub = sinon.stub(Logger, 'debug')
|
||||
|
||||
await libraryItemsBookFilters.getFilteredLibraryItems('library-1', { canAccessExplicitContent: true }, 'authors', 'author-1', 'media.metadata.publishedYear', true, true, [], 20, 0)
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ describe('libraryItemsBookFilters postgres query safety', () => {
|
|||
|
||||
expect(displayTitleExpression).to.include('COALESCE(NULL, libraryItem.title)')
|
||||
expect(displayTitleExpression).to.not.include('IN ()')
|
||||
expect(warnStub.calledOnce).to.equal(true)
|
||||
expect(debugStub.calledWithMatch('collapse-series produced no include IDs')).to.equal(true)
|
||||
})
|
||||
|
||||
it('should escape collapse-series ids safely for postgres subquery', async () => {
|
||||
|
|
|
|||
75
test/server/utils/queries/libraryItemsPodcastFilters.test.js
Normal file
75
test/server/utils/queries/libraryItemsPodcastFilters.test.js
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const Database = require('../../../../server/Database')
|
||||
const libraryItemsPodcastFilters = require('../../../../server/utils/queries/libraryItemsPodcastFilters')
|
||||
|
||||
describe('libraryItemsPodcastFilters dialect behavior', () => {
|
||||
let originalSequelize
|
||||
let originalServerSettings
|
||||
|
||||
beforeEach(() => {
|
||||
originalSequelize = Database.sequelize
|
||||
originalServerSettings = global.ServerSettings
|
||||
global.ServerSettings = { sortingIgnorePrefix: false }
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
Database.sequelize = originalSequelize
|
||||
global.ServerSettings = originalServerSettings
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('should build postgres json tag permission predicate', () => {
|
||||
Database.sequelize = { getDialect: () => 'postgres' }
|
||||
|
||||
const { podcastWhere, replacements } = libraryItemsPodcastFilters.getUserPermissionPodcastWhereQuery({
|
||||
canAccessExplicitContent: true,
|
||||
permissions: {
|
||||
accessAllTags: false,
|
||||
itemTagsSelected: ['fiction'],
|
||||
selectedTagsNotAccessible: false
|
||||
}
|
||||
})
|
||||
|
||||
expect(replacements.userTagsSelected).to.deep.equal(['fiction'])
|
||||
expect(podcastWhere[0].attribute.val).to.include('jsonb_array_elements_text')
|
||||
})
|
||||
|
||||
it('should build sqlite no-case author sort expression', () => {
|
||||
Database.sequelize = { getDialect: () => 'sqlite' }
|
||||
|
||||
const order = libraryItemsPodcastFilters.getOrder('media.metadata.author', false)
|
||||
|
||||
expect(order[0][0].val).to.include('podcast.author COLLATE NOCASE')
|
||||
})
|
||||
|
||||
it('should build postgres lower author sort expression', () => {
|
||||
Database.sequelize = { getDialect: () => 'postgres' }
|
||||
|
||||
const order = libraryItemsPodcastFilters.getOrder('media.metadata.author', false)
|
||||
|
||||
expect(order[0][0].val).to.include('LOWER(podcast.author)')
|
||||
})
|
||||
|
||||
it('should use postgres json duration extraction in podcast stats query', async () => {
|
||||
const queryStub = sinon.stub()
|
||||
queryStub.onFirstCall().resolves([[{ totalSize: 1000 }]])
|
||||
queryStub.onSecondCall().resolves([[{ totalDuration: '12.3', totalItems: '4', numAudioFiles: '9' }]])
|
||||
|
||||
Database.sequelize = {
|
||||
getDialect: () => 'postgres',
|
||||
query: queryStub
|
||||
}
|
||||
|
||||
const result = await libraryItemsPodcastFilters.getPodcastLibraryStats('library-1')
|
||||
|
||||
expect(queryStub.secondCall.args[0]).to.include('NULLIF(pe.audioFile::jsonb #>>')
|
||||
expect(result).to.deep.equal({
|
||||
totalSize: 1000,
|
||||
totalDuration: '12.3',
|
||||
numAudioFiles: '9',
|
||||
totalItems: '4'
|
||||
})
|
||||
})
|
||||
})
|
||||
107
test/server/utils/queries/seriesFilters.test.js
Normal file
107
test/server/utils/queries/seriesFilters.test.js
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const Database = require('../../../../server/Database')
|
||||
const seriesFilters = require('../../../../server/utils/queries/seriesFilters')
|
||||
const libraryItemsBookFilters = require('../../../../server/utils/queries/libraryItemsBookFilters')
|
||||
|
||||
function encodedFilter(group, value) {
|
||||
return `${group}.${encodeURIComponent(Buffer.from(value).toString('base64'))}`
|
||||
}
|
||||
|
||||
describe('seriesFilters dialect behavior', () => {
|
||||
let originalSequelize
|
||||
let originalServerSettings
|
||||
let modelStubs
|
||||
|
||||
function setDialect(dialect) {
|
||||
Database.sequelize = {
|
||||
getDialect: () => dialect,
|
||||
random: sinon.stub(),
|
||||
models: modelStubs
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
originalSequelize = Database.sequelize
|
||||
originalServerSettings = global.ServerSettings
|
||||
modelStubs = {
|
||||
series: {
|
||||
findAndCountAll: sinon.stub().resolves({ rows: [], count: 0 })
|
||||
},
|
||||
bookSeries: {},
|
||||
book: {},
|
||||
libraryItem: {},
|
||||
author: {},
|
||||
feed: {}
|
||||
}
|
||||
|
||||
global.ServerSettings = { sortingIgnorePrefix: false }
|
||||
sinon.stub(libraryItemsBookFilters, 'getUserPermissionBookWhereQuery').returns({
|
||||
bookWhere: [],
|
||||
replacements: {}
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
Database.sequelize = originalSequelize
|
||||
global.ServerSettings = originalServerSettings
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
it('should build postgres progress filter with TRUE and FALSE literals', async () => {
|
||||
setDialect('postgres')
|
||||
|
||||
await seriesFilters.getFilteredSeries(
|
||||
{ id: 'library-1', settings: { hideSingleBookSeries: false } },
|
||||
{ id: 'user-1', canAccessExplicitContent: true, permissions: { accessAllTags: true } },
|
||||
encodedFilter('progress', 'not-started'),
|
||||
'name',
|
||||
false,
|
||||
[],
|
||||
10,
|
||||
0
|
||||
)
|
||||
|
||||
const findOptions = modelStubs.series.findAndCountAll.firstCall.args[0]
|
||||
const progressWhere = findOptions.where[1]
|
||||
|
||||
expect(progressWhere.attribute.val).to.include('mp.isFinished = TRUE')
|
||||
})
|
||||
|
||||
it('should build sqlite no-case name sort expression', async () => {
|
||||
setDialect('sqlite')
|
||||
|
||||
await seriesFilters.getFilteredSeries(
|
||||
{ id: 'library-1', settings: { hideSingleBookSeries: false } },
|
||||
{ id: 'user-1', canAccessExplicitContent: true, permissions: { accessAllTags: true } },
|
||||
null,
|
||||
'name',
|
||||
false,
|
||||
[],
|
||||
10,
|
||||
0
|
||||
)
|
||||
|
||||
const findOptions = modelStubs.series.findAndCountAll.firstCall.args[0]
|
||||
expect(findOptions.order[0][0].val).to.equal('series.name COLLATE NOCASE')
|
||||
})
|
||||
|
||||
it('should build postgres lower-case name sort expression', async () => {
|
||||
setDialect('postgres')
|
||||
|
||||
await seriesFilters.getFilteredSeries(
|
||||
{ id: 'library-1', settings: { hideSingleBookSeries: false } },
|
||||
{ id: 'user-1', canAccessExplicitContent: true, permissions: { accessAllTags: true } },
|
||||
null,
|
||||
'name',
|
||||
false,
|
||||
[],
|
||||
10,
|
||||
0
|
||||
)
|
||||
|
||||
const findOptions = modelStubs.series.findAndCountAll.firstCall.args[0]
|
||||
expect(findOptions.order[0][0].val).to.equal('LOWER(series.name)')
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue