mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-03 00:06:46 +01:00
Add ApiCacheManager unit test
This commit is contained in:
parent
d944ecaa21
commit
5e1e748c71
@ -3,12 +3,16 @@ const Logger = require('../Logger')
|
|||||||
const Database = require('../Database')
|
const Database = require('../Database')
|
||||||
|
|
||||||
class ApiCacheManager {
|
class ApiCacheManager {
|
||||||
constructor(options = { max: 1000, maxSize: 10 * 1000 * 1000, sizeCalculation: item => item.length }) {
|
|
||||||
this.options = options
|
defaultCacheOptions = { max: 1000, maxSize: 10 * 1000 * 1000, sizeCalculation: item => item.length }
|
||||||
|
defaultTtlOptions = { ttl: 30 * 60 * 1000 }
|
||||||
|
|
||||||
|
constructor(cache = new LRUCache(this.defaultCacheOptions), ttlOptions = this.defaultTtlOptions) {
|
||||||
|
this.cache = cache
|
||||||
|
this.ttlOptions = ttlOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
init(database = Database) {
|
init(database = Database) {
|
||||||
this.cache = new LRUCache(this.options)
|
|
||||||
let hooks = ['afterCreate', 'afterUpdate', 'afterDestroy', 'afterBulkCreate', 'afterBulkUpdate', 'afterBulkDestroy']
|
let hooks = ['afterCreate', 'afterUpdate', 'afterDestroy', 'afterBulkCreate', 'afterBulkUpdate', 'afterBulkDestroy']
|
||||||
hooks.forEach(hook => database.sequelize.addHook(hook, (model) => this.clear(model, hook)))
|
hooks.forEach(hook => database.sequelize.addHook(hook, (model) => this.clear(model, hook)))
|
||||||
}
|
}
|
||||||
@ -23,23 +27,22 @@ class ApiCacheManager {
|
|||||||
const key = { user: req.user.username, url: req.url }
|
const key = { user: req.user.username, url: req.url }
|
||||||
const stringifiedKey = JSON.stringify(key)
|
const stringifiedKey = JSON.stringify(key)
|
||||||
Logger.debug(`[ApiCacheManager] count: ${this.cache.size} size: ${this.cache.calculatedSize}`)
|
Logger.debug(`[ApiCacheManager] count: ${this.cache.size} size: ${this.cache.calculatedSize}`)
|
||||||
Logger.debug(`[ApiCacheManager] Cache key: ${stringifiedKey}`)
|
|
||||||
const cached = this.cache.get(stringifiedKey)
|
const cached = this.cache.get(stringifiedKey)
|
||||||
if (cached) {
|
if (cached) {
|
||||||
Logger.debug(`[ApiCacheManager] Cache hit: ${stringifiedKey}`)
|
Logger.debug(`[ApiCacheManager] Cache hit: ${stringifiedKey}`)
|
||||||
res.send(cached)
|
res.send(cached)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res.sendResponse = res.send
|
res.originalSend = res.send
|
||||||
res.send = (body) => {
|
res.send = (body) => {
|
||||||
Logger.debug(`[ApiCacheManager] Cache miss: ${stringifiedKey}`)
|
Logger.debug(`[ApiCacheManager] Cache miss: ${stringifiedKey}`)
|
||||||
if (key.url.search(/^\/libraries\/.*?\/personalized/) !== -1) {
|
if (key.url.search(/^\/libraries\/.*?\/personalized/) !== -1) {
|
||||||
Logger.debug(`[ApiCacheManager] Caching personalized with 30 minues TTL`)
|
Logger.debug(`[ApiCacheManager] Caching with ${this.ttlOptions.ttl} ms TTL`)
|
||||||
this.cache.set(stringifiedKey, body, { ttl: 30 * 60 * 1000 })
|
this.cache.set(stringifiedKey, body, this.ttlOptions)
|
||||||
} else {
|
} else {
|
||||||
this.cache.set(stringifiedKey, body)
|
this.cache.set(stringifiedKey, body)
|
||||||
}
|
}
|
||||||
res.sendResponse(body)
|
res.originalSend(body)
|
||||||
}
|
}
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
|
85
test/server/managers/ApiCacheManager.test.js
Normal file
85
test/server/managers/ApiCacheManager.test.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// Import dependencies and modules for testing
|
||||||
|
const { expect } = require('chai')
|
||||||
|
const sinon = require('sinon')
|
||||||
|
const ApiCacheManager = require('../../../server/managers/ApiCacheManager')
|
||||||
|
|
||||||
|
describe('ApiCacheManager', () => {
|
||||||
|
let cache
|
||||||
|
let req
|
||||||
|
let res
|
||||||
|
let next
|
||||||
|
let manager
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cache = { get: sinon.stub(), set: sinon.spy() }
|
||||||
|
req = { user: { username: 'testUser' }, url: '/test-url' }
|
||||||
|
res = { send: sinon.spy() }
|
||||||
|
next = sinon.spy()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('middleware', () => {
|
||||||
|
it('should send cached data if available', () => {
|
||||||
|
// Arrange
|
||||||
|
const cachedData = { data: 'cached data' }
|
||||||
|
cache.get.returns(cachedData)
|
||||||
|
const key = JSON.stringify({ user: req.user.username, url: req.url })
|
||||||
|
manager = new ApiCacheManager(cache)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
manager.middleware(req, res, next)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(cache.get.calledOnce).to.be.true
|
||||||
|
expect(cache.get.calledWith(key)).to.be.true
|
||||||
|
expect(res.send.calledOnce).to.be.true
|
||||||
|
expect(res.send.calledWith(cachedData)).to.be.true
|
||||||
|
expect(res.originalSend).to.be.undefined
|
||||||
|
expect(next.called).to.be.false
|
||||||
|
expect(cache.set.called).to.be.false
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should cache and send response if data is not cached', () => {
|
||||||
|
// Arrange
|
||||||
|
cache.get.returns(null)
|
||||||
|
const responseData = { data: 'response data' }
|
||||||
|
const key = JSON.stringify({ user: req.user.username, url: req.url })
|
||||||
|
manager = new ApiCacheManager(cache)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
manager.middleware(req, res, next)
|
||||||
|
res.send(responseData)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(cache.get.calledOnce).to.be.true
|
||||||
|
expect(cache.get.calledWith(key)).to.be.true
|
||||||
|
expect(next.calledOnce).to.be.true
|
||||||
|
expect(cache.set.calledOnce).to.be.true
|
||||||
|
expect(cache.set.calledWith(key, responseData)).to.be.true
|
||||||
|
expect(res.originalSend.calledOnce).to.be.true
|
||||||
|
expect(res.originalSend.calledWith(responseData)).to.be.true
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should cache personalized response with 30 minutes TTL', () => {
|
||||||
|
// Arrange
|
||||||
|
cache.get.returns(null)
|
||||||
|
const responseData = { data: 'personalized data' }
|
||||||
|
req.url = '/libraries/id/personalized'
|
||||||
|
const key = JSON.stringify({ user: req.user.username, url: req.url })
|
||||||
|
const ttlOptions = { ttl: 30 * 60 * 1000 }
|
||||||
|
manager = new ApiCacheManager(cache, ttlOptions)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
manager.middleware(req, res, next)
|
||||||
|
res.send(responseData)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(cache.get.calledOnce).to.be.true
|
||||||
|
expect(cache.get.calledWith(key)).to.be.true
|
||||||
|
expect(next.calledOnce).to.be.true
|
||||||
|
expect(cache.set.calledOnce).to.be.true
|
||||||
|
expect(cache.set.calledWith(key, responseData, ttlOptions)).to.be.true
|
||||||
|
expect(res.originalSend.calledOnce).to.be.true
|
||||||
|
expect(res.originalSend.calledWith(responseData)).to.be.true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user