Skip to content

Commit

Permalink
Added tests for file implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
mallocator committed Mar 20, 2017
1 parent 234c2dc commit 39db6b1
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 25 deletions.
9 changes: 9 additions & 0 deletions cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class Cache {
* @returns {Promise.<Payload>}
*/
store(type, id, payload, cb) {
type = typeof type === 'string' ? type : String(type);
id = typeof id === 'string' ? id : String(id);
if (this._ttl) {
this._timeouts[type] = this._timeouts[type] || {};
this._timeouts[type][id] && clearTimeout(this._timeouts[type][id]);
Expand All @@ -72,6 +74,8 @@ class Cache {
* @returns {Promise.<Payload>}
*/
fetch(type, id, cb) {
type = typeof type === 'string' ? type : String(type);
id = typeof id === 'string' ? id : String(id);
if (this._ttl) {
this._timeouts[type][id] && clearTimeout(this._timeouts[type][id]);
this._timeouts[type][id] = setTimeout(this.remove.bind(this, type, id), this._ttl);
Expand All @@ -91,6 +95,7 @@ class Cache {
* @returns {Promise.<Payload[]>}
*/
list(type, cb) {
type = typeof type === 'string' ? type : String(type);
if (cb) {
this._list(type).then(result => cb(null, result)).catch(cb);
} else {
Expand All @@ -106,6 +111,7 @@ class Cache {
* @returns {Promise.<Object<string, Payload>>}
*/
map(type, cb) {
type = typeof type === 'string' ? type : String(type);
if (cb) {
this._map(type).then(result => cb(null, result)).catch(cb);
} else {
Expand All @@ -122,6 +128,8 @@ class Cache {
* @returns {Promise.<Payload>}
*/
remove(type, id, cb) {
type = typeof type === 'string' ? type : String(type);
id = typeof id === 'string' ? id : String(id);
if (this._timeouts[type]) {
this._timeouts[type][id] && clearTimeout(this._timeouts[type][id]);
delete this._timeouts[type][id];
Expand All @@ -141,6 +149,7 @@ class Cache {
* @returns {*}
*/
clear(type, cb) {
type = typeof type === 'string' ? type : String(type);
if (this._timeouts[type]) {
this._timeouts[type].forEach(clearTimeout);
delete this._timeouts[type];
Expand Down
57 changes: 32 additions & 25 deletions file.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ class FileCache extends Cache {
*/
constructor(config, name) {
super({ ttl: true }, config, name);
this._path = FileCache._mkDir(config && config.path || 'queues');
this._types = {};
this._path = FileCache._mkDir(config && config.path || 'cache');
}

/**
Expand All @@ -45,62 +44,70 @@ class FileCache extends Cache {
/**
* Stores the payload in cache.
* @param {string} type The type/group to cache for
* @param {string} id The id of the document to store
* @param {object} payload The data to cache
* @param {Callback} [cb] Callback to be notified on async store operations
* @param {string|number} id The id of the document to store
* @param {Payload} payload The data to cache
* @returns {Promise.<Payload>}
* @private
*/
store(type, id, payload, cb) {
this._types[type][id] = payload;
async _store(type, id, payload) {
let fullPath = path.join(this._path, type);
fs.existsSync(fullPath) || fs.mkdirSync(fullPath);
fs.writeFile(path.join(fullPath, id), JSON.stringify(payload), 'utf8', err => cb && cb(err));
fs.writeFileSync(path.join(fullPath, id), JSON.stringify(payload), 'utf8');
return payload;
}

async _fetch(type, id) {
let file = path.join(this._path, type, id);
return fs.existsSync(file) && JSON.parse(fs.readFileSync(file, 'utf8'))
}

/**
* Returns all cached messages and listeners
* @param {string} type The id/name of the type for which to fetch data
* @param {Callback} [cb] Callback function for async fetching
* @returns {Object<string, Object>} A map with message id's mapping to payloads
*/
get(type, cb) {
if (this._types[type]) {
cb(null, this._types[type]);
return this._types[type];
}
async _map(type) {
let fullPath = path.join(this._path, type);
let files = fs.readdirSync(fullPath);
let response = {};
for (let file of files) {
let payload = JSON.parse(fs.readFileSync(path.join(fullPath, file), 'utf8'));
response[payload.id] = payload;
response[file] = payload;
}
return response;
}

async _list(type) {
let fullPath = path.join(this._path, type);
let files = fs.readdirSync(fullPath);
let response = [];
for (let file of files) {
let payload = JSON.parse(fs.readFileSync(path.join(fullPath, file), 'utf8'));
response.push(payload);
}
cb && cb(null, response);
return response;
}

/**
* Removes all cached data from a type
* @param {string} type The id/name of the type to clear
* @param {Callback} [cb] Callback to be notified on async clear operations
*/
clear(type, cb) {
async _clear(type) {
let fullPath = path.join(this._path, type);
fs.existsSync(fullPath) && fs.unlinkSync(fullPath);
delete this._types[type];
cb && cb();
for (let file of fs.readdirSync(fullPath)) {
await this._remove(type, file);
}
fs.existsSync(fullPath) && fs.rmdirSync(fullPath);
}

/**
* Remove an entry from cache.
* @param {string} type The id/name of the type from which to remove the message
* @param {string} id The id of the message to remove
* @param {Callback} [cb] Callback to be notified on async remove operations
*/
remove(type, id, cb) {
async _remove(type, id) {
let fullPath = path.join(this._path, type, id);
fs.existsSync(fullPath) && fs.unlinkSync(fullPath);
delete this._types[type][id];
cb && cb();
}

/**
Expand Down
61 changes: 61 additions & 0 deletions test/file.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* global describe, it, after, before, afterEach, beforeEach */
const expect = require('chai').expect;
const fs = require('fs');
const path = require('path');

const File = require('../file');


describe('File', () => {
it('should create a file cache instance', () => {
let file = new File({type: 'file'}, 'Test');
expect(file.type).to.equal('file');
expect(file.name).to.equal('Test');
expect(file).to.respondTo('_store');
expect(file).to.respondTo('_fetch');
expect(file).to.respondTo('_list');
expect(file).to.respondTo('_map');
expect(file).to.respondTo('_clear');
expect(file).to.respondTo('_remove');
});

it('should store and retrieve a cache value using callbacks', done => {
let file = new File({type: 'file'}, 'Test');
file.store('TestType', 1, {a: 'Test'}, () => {
file.fetch('TestType', 1, (err, result) => {
expect(result).to.deep.equal({a: 'Test'});
done();
});
});
});

it('should store and retrieve a cache value using promises', async () => {
let file = new File({type: 'file'}, 'Test');
await file.store('TestType', 1, {a: 'Test'});
let result = await file.fetch('TestType', 1);
expect(result).to.deep.equal({a: 'Test'});
});

it('should support all standard operation for caching', async () => {
let file = new File({type: 'file'}, 'Test');
await file.store('TestType', 1, {a: 'Test1'});
await file.store('TestType', 2, {a: 'Test2'});

let list = await file.list('TestType');
expect(list.length).to.equal(2);
expect(list).to.include({a: 'Test1'});
expect(list).to.include({a: 'Test2'});

await file.remove('TestType', 1);
let map = await file.map('TestType');
expect(map).to.have.any.key('2');
expect(map['2']).to.deep.equal({a: 'Test2'});

await file.clear('TestType');
let entry1 = await file.fetch('TestType', 1);
expect(entry1).to.be.not.ok;
let entry2 = await file.fetch('TestType', 2);
expect(entry2).to.be.not.ok;
expect(fs.existsSync(path.join(file.path, 'TestType'))).to.be.not.ok;
});
});
6 changes: 6 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,10 @@ describe('Manager', () => {
Cache.configure({ myfiles: { type: 'file' }});
expect(Cache.instance('myfiles').type).to.equal('file');
});

it('should by default call the instance _default_', () => {
let instance = Cache.instance({type: 'file'});
expect(instance.name).to.equal('_default_');
expect(instance.type).to.equal('file');
})
});

0 comments on commit 39db6b1

Please sign in to comment.