Skip to content

Commit

Permalink
#36 change cache mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
FrankDeGroot committed Mar 4, 2021
1 parent 6363a48 commit 7943c2d
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 45 deletions.
18 changes: 9 additions & 9 deletions server/service/rcon.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@

const { Rcon: { connect } } = require('rcon-client')
const { readServerProperties } = require('../worlds/serverproperties')
const { deleteCachedItem, getCachedItem, setCachedItem } = require('../utils/cache')
const { cache } = require('../utils/cache')

const connection = 'connection'
const connectionCache = cache()

exports.connect = async () => {
if (!getCachedItem(connection)) {
await connectionCache.store(async () => {
const { 'rcon.port': port, 'rcon.password': password } = await readServerProperties()
setCachedItem(connection, await connect({ host: 'localhost', port, password }))
}
return await connect({ host: 'localhost', port, password })
})
}

exports.send = async request => {
if (!getCachedItem(connection)) {
const connection = connectionCache.read(() => {
throw new Error('Not connected')
}
})
try {
return await getCachedItem(connection).send(request)
return await connection.send(request)
} catch (error) {
deleteCachedItem(connection)
connectionCache.evict()
throw error
}
}
61 changes: 41 additions & 20 deletions server/service/rcon.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,70 @@ jest.mock('../utils/cache')

const { Rcon: { connect: rconConnect } } = require('rcon-client')
const { readServerProperties } = require('../worlds/serverproperties')
const { getCachedItem, setCachedItem } = require('../utils/cache')
const { cache } = require('../utils/cache')

const connectionCache = {
store: jest.fn(),
read: jest.fn(),
evict: jest.fn()
}
cache.mockReturnValue(connectionCache)

const { connect, send } = require('./rcon')

describe('send', () => {
describe('rcon', () => {
const port = 123
const password = 'password'
const message = 'message'
const response = 'response'
const connection = {
send: jest.fn()
}
const request = 'request'
const response = 'response'
const sendError = new Error('Send error')
beforeEach(() => {
getCachedItem.mockReset()
setCachedItem.mockReset()
connectionCache.store.mockReset()
connectionCache.read.mockReset()
connectionCache.evict.mockReset()
readServerProperties.mockReset()
rconConnect.mockReset()
connection.send.mockReset()
})
it('should cache connection on connect', async () => {
readServerProperties
.mockResolvedValue({ 'rcon.port': port, 'rcon.password': password })
rconConnect.mockReturnValue(connection)
connectionCache.store.mockImplementation(async create => {
await expect(create()).resolves.toBe(connection)
})
readServerProperties.mockResolvedValue({
'rcon.port': port,
'rcon.password': password
})
rconConnect.mockResolvedValue(connection)

await connect()

expect(readServerProperties).toHaveBeenCalled()
expect(rconConnect).toHaveBeenCalledWith({ host: 'localhost', port, password })
expect(setCachedItem).toHaveBeenCalledWith('connection', connection)
})
it('should not connect again', async () => {
getCachedItem.mockReturnValue(connection)
it('should send on cached connection', async () => {
connectionCache.read.mockImplementation(otherwise => {
expect(otherwise).toThrow(new Error('Not connected'))
return connection
})
connection.send.mockResolvedValue(response)

await connect()
await expect(send(request)).resolves.toBe(response)

expect(readServerProperties).not.toHaveBeenCalled()
expect(rconConnect).not.toHaveBeenCalled()
expect(connection.send).toHaveBeenCalledWith(request)
})
it('should send on cached connection', async () => {
getCachedItem.mockReturnValue(connection)
connection.send.mockResolvedValue(response)
it('should evict connection on send error', async () => {
connectionCache.read.mockImplementation(otherwise => {
expect(otherwise).toThrow(new Error('Not connected'))
return connection
})
connection.send.mockRejectedValue(sendError)

await expect(send(message)).resolves.toBe(response)
await expect(send(request)).rejects.toBe(sendError)

expect(connection.send).toHaveBeenCalledWith(message)
expect(connection.send).toHaveBeenCalledWith(request)
expect(connectionCache.evict).toHaveBeenCalled()
})
})
18 changes: 11 additions & 7 deletions server/utils/cache.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
'use strict'

const cache = {}

exports.getCachedItem = key => cache[key]
exports.setCachedItem = (key, value) => {
cache[key] = value
return value
exports.cache = () => {
let storage = null
return {
store: async create => {
return storage || (storage = await create())
},
read: otherwise => storage || otherwise(),
evict: () => {
storage = null
}
}
}
exports.deleteCachedItem = key => delete cache[key]
30 changes: 21 additions & 9 deletions server/utils/cache.test.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
'use strict'

const { deleteCachedItem, getCachedItem, setCachedItem } = require('./cache')
const { cache } = require('./cache')

describe('cache', () => {
const key = 'item'
const value = 'value'
it('should cache', () => {
expect(getCachedItem(key)).toBe(undefined)
it('should store, read and evict', async () => {
const myCache = cache()
const value = 'value'

expect(setCachedItem(key, value)).toBe(value)
const create = jest.fn()
create.mockResolvedValue(value)
await expect(myCache.store(create)).resolves.toBe(value)
await expect(myCache.store(create)).resolves.toBe(value)

expect(getCachedItem(key)).toBe(value)
expect(create).toHaveBeenCalledTimes(1)

deleteCachedItem(key)
const otherwise = jest.fn()
expect(myCache.read(otherwise)).toBe(value)

expect(getCachedItem(key)).toBe(undefined)
myCache.evict()

otherwise.mockImplementation(() => {
throw new Error('no value')
})
expect(() => myCache.read(otherwise)).toThrow(new Error('no value'))

await expect(myCache.store(create)).resolves.toBe(value)

expect(create).toHaveBeenCalledTimes(2)
})
})

0 comments on commit 7943c2d

Please sign in to comment.