initial commit - far from runnable

This commit is contained in:
Anika Raemer 2025-09-21 12:39:54 +02:00
commit db057ce342
8614 changed files with 1032171 additions and 0 deletions

18
node_modules/memory-cache/.jsbeautifyrc generated vendored Normal file
View file

@ -0,0 +1,18 @@
{
"indent_size": 2,
"indent_char": " ",
"indent_level": 0,
"indent_with_tabs": false,
"preserve_newlines": true,
"max_preserve_newlines": 10,
"jslint_happy": false,
"brace_style": "collapse",
"keep_array_indentation": false,
"keep_function_indentation": false,
"space_in_paren": false,
"space_before_conditional": true,
"break_chained_methods": false,
"eval_code": false,
"unescape_strings": false,
"wrap_line_length": 0
}

10
node_modules/memory-cache/.jshintrc generated vendored Normal file
View file

@ -0,0 +1,10 @@
{
"node": true,
"undef": true,
"unused": true,
"esnext": true,
"predef": [ "require", "module" ],
"globalstrict": true,
"trailing": true,
"-W097": true
}

3
node_modules/memory-cache/.npmignore generated vendored Normal file
View file

@ -0,0 +1,3 @@
coverage/
node_modules/
.idea

1
node_modules/memory-cache/.travis.yml generated vendored Normal file
View file

@ -0,0 +1 @@
language: node_js

23
node_modules/memory-cache/LICENSE.md generated vendored Normal file
View file

@ -0,0 +1,23 @@
Copyright (c) 2013, Paul Tarjan
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

125
node_modules/memory-cache/README.md generated vendored Normal file
View file

@ -0,0 +1,125 @@
# memory-cache [![Build Status](https://travis-ci.org/ptarjan/node-cache.svg?branch=master)](https://travis-ci.org/ptarjan/node-cache)
A simple in-memory cache for node.js
## Installation
npm install memory-cache --save
## Usage
```javascript
var cache = require('memory-cache');
// now just use the cache
cache.put('foo', 'bar');
console.log(cache.get('foo'));
// that wasn't too interesting, here's the good part
cache.put('houdini', 'disappear', 100, function(key, value) {
console.log(key + ' did ' + value);
}); // Time in ms
console.log('Houdini will now ' + cache.get('houdini'));
setTimeout(function() {
console.log('Houdini is ' + cache.get('houdini'));
}, 200);
// create new cache instance
var newCache = new cache.Cache();
newCache.put('foo', 'newbaz');
setTimeout(function() {
console.log('foo in old cache is ' + cache.get('foo'));
console.log('foo in new cache is ' + newCache.get('foo'));
}, 200);
```
which should print
bar
Houdini will now disappear
houdini did disappear
Houdini is null
foo in old cache is baz
foo in new cache is newbaz
## API
### put = function(key, value, time, timeoutCallback)
* Simply stores a value
* If time isn't passed in, it is stored forever
* Will actually remove the value in the specified time in ms (via `setTimeout`)
* timeoutCallback is optional function fired after entry has expired with key and value passed (`function(key, value) {}`)
* Returns the cached value
### get = function(key)
* Retrieves a value for a given key
* If value isn't cached, returns `null`
### del = function(key)
* Deletes a key, returns a boolean specifying whether or not the key was deleted
### clear = function()
* Deletes all keys
### size = function()
* Returns the current number of entries in the cache
### memsize = function()
* Returns the number of entries taking up space in the cache
* Will usually `== size()` unless a `setTimeout` removal went wrong
### debug = function(bool)
* Turns on or off debugging
### hits = function()
* Returns the number of cache hits (only monitored in debug mode)
### misses = function()
* Returns the number of cache misses (only monitored in debug mode)
### keys = function()
* Returns all the cache keys
### exportJson = function()
* Returns a JSON string representing all the cache data
* Any timeoutCallbacks will be ignored
### importJson = function(json: string, options: { skipDuplicates: boolean })
* Merges all the data from a previous call to `export` into the cache
* Any existing entries before an `import` will remain in the cache
* Any duplicate keys will be overwritten, unless `skipDuplicates` is `true`
* Any entries that would have expired since being exported will expire upon being imported (but their callbacks will not be invoked)
* Available `options`:
* `skipDuplicates`: If `true`, any duplicate keys will be ignored when importing them. Defaults to `false`.
* Returns the new size of the cache
### Cache = function()
* Cache constructor
* note that `require('cache')` would return the default instance of Cache
* while `require('cache').Cache` is the actual class
## Note on Patches/Pull Requests
* Fork the project.
* Make your feature addition or bug fix.
* Send me a pull request.

65
node_modules/memory-cache/gulpfile.js generated vendored Normal file
View file

@ -0,0 +1,65 @@
/**************/
/* REQUIRES */
/**************/
var gulp = require('gulp');
// File I/O
var exit = require('gulp-exit');
var jshint = require('gulp-jshint');
// Testing
var mocha = require('gulp-mocha');
var istanbul = require('gulp-istanbul');
/****************/
/* FILE PATHS */
/****************/
var paths = {
js: [
'index.js'
],
tests: [
'test.js'
]
};
/***********/
/* TASKS */
/***********/
// Lints the JavaScript files
gulp.task('lint', function() {
return gulp.src(paths.js)
.pipe(jshint())
.pipe(jshint.reporter('jshint-stylish'))
.pipe(jshint.reporter('fail'))
.on('error', function(error) {
throw error;
});
});
// Runs the Mocha test suite
gulp.task('test', function() {
return gulp.src(paths.js)
.pipe(istanbul())
.pipe(istanbul.hookRequire())
.on('finish', function () {
gulp.src(paths.tests)
.pipe(mocha({
reporter: 'spec',
timeout: 5000
}))
.pipe(istanbul.writeReports())
.pipe(exit());
});
});
// Re-runs the linter every time a JavaScript file changes
gulp.task('watch', function() {
gulp.watch(paths.js, ['lint']);
});
// Default task
gulp.task('default', ['lint', 'test']);

190
node_modules/memory-cache/index.js generated vendored Normal file
View file

@ -0,0 +1,190 @@
'use strict';
function Cache () {
var _cache = Object.create(null);
var _hitCount = 0;
var _missCount = 0;
var _size = 0;
var _debug = false;
this.put = function(key, value, time, timeoutCallback) {
if (_debug) {
console.log('caching: %s = %j (@%s)', key, value, time);
}
if (typeof time !== 'undefined' && (typeof time !== 'number' || isNaN(time) || time <= 0)) {
throw new Error('Cache timeout must be a positive number');
} else if (typeof timeoutCallback !== 'undefined' && typeof timeoutCallback !== 'function') {
throw new Error('Cache timeout callback must be a function');
}
var oldRecord = _cache[key];
if (oldRecord) {
clearTimeout(oldRecord.timeout);
} else {
_size++;
}
var record = {
value: value,
expire: time + Date.now()
};
if (!isNaN(record.expire)) {
record.timeout = setTimeout(function() {
_del(key);
if (timeoutCallback) {
timeoutCallback(key, value);
}
}.bind(this), time);
}
_cache[key] = record;
return value;
};
this.del = function(key) {
var canDelete = true;
var oldRecord = _cache[key];
if (oldRecord) {
clearTimeout(oldRecord.timeout);
if (!isNaN(oldRecord.expire) && oldRecord.expire < Date.now()) {
canDelete = false;
}
} else {
canDelete = false;
}
if (canDelete) {
_del(key);
}
return canDelete;
};
function _del(key){
_size--;
delete _cache[key];
}
this.clear = function() {
for (var key in _cache) {
clearTimeout(_cache[key].timeout);
}
_size = 0;
_cache = Object.create(null);
if (_debug) {
_hitCount = 0;
_missCount = 0;
}
};
this.get = function(key) {
var data = _cache[key];
if (typeof data != "undefined") {
if (isNaN(data.expire) || data.expire >= Date.now()) {
if (_debug) _hitCount++;
return data.value;
} else {
// free some space
if (_debug) _missCount++;
_size--;
delete _cache[key];
}
} else if (_debug) {
_missCount++;
}
return null;
};
this.size = function() {
return _size;
};
this.memsize = function() {
var size = 0,
key;
for (key in _cache) {
size++;
}
return size;
};
this.debug = function(bool) {
_debug = bool;
};
this.hits = function() {
return _hitCount;
};
this.misses = function() {
return _missCount;
};
this.keys = function() {
return Object.keys(_cache);
};
this.exportJson = function() {
var plainJsCache = {};
// Discard the `timeout` property.
// Note: JSON doesn't support `NaN`, so convert it to `'NaN'`.
for (var key in _cache) {
var record = _cache[key];
plainJsCache[key] = {
value: record.value,
expire: record.expire || 'NaN',
};
}
return JSON.stringify(plainJsCache);
};
this.importJson = function(jsonToImport, options) {
var cacheToImport = JSON.parse(jsonToImport);
var currTime = Date.now();
var skipDuplicates = options && options.skipDuplicates;
for (var key in cacheToImport) {
if (cacheToImport.hasOwnProperty(key)) {
if (skipDuplicates) {
var existingRecord = _cache[key];
if (existingRecord) {
if (_debug) {
console.log('Skipping duplicate imported key \'%s\'', key);
}
continue;
}
}
var record = cacheToImport[key];
// record.expire could be `'NaN'` if no expiry was set.
// Try to subtract from it; a string minus a number is `NaN`, which is perfectly fine here.
var remainingTime = record.expire - currTime;
if (remainingTime <= 0) {
// Delete any record that might exist with the same key, since this key is expired.
this.del(key);
continue;
}
// Remaining time must now be either positive or `NaN`,
// but `put` will throw an error if we try to give it `NaN`.
remainingTime = remainingTime > 0 ? remainingTime : undefined;
this.put(key, record.value, remainingTime);
}
}
return this.size();
};
}
module.exports = new Cache();
module.exports.Cache = Cache;

42
node_modules/memory-cache/package.json generated vendored Normal file
View file

@ -0,0 +1,42 @@
{
"name": "memory-cache",
"description": "A simple in-memory cache. put(), get() and del()",
"author": "Paul Tarjan <npm@paulisageek.com>",
"contributors": [
{
"name": "Ramon Snir",
"email": "ramon@dynamicyield.com"
},
{
"name": "Jacob Wenger",
"email": "wenger.jacob@gmail.com"
}
],
"keywords": [
"cache",
"ram",
"simple",
"storage"
],
"main": "./index.js",
"version": "0.2.0",
"repository": {
"type": "git",
"url": "git://github.com/ptarjan/node-cache.git"
},
"scripts": {
"test": "./node_modules/.bin/gulp test"
},
"license": "BSD-2-Clause",
"devDependencies": {
"chai": "^2.2.0",
"gulp": "^3.8.11",
"gulp-exit": "0.0.2",
"gulp-istanbul": "^0.7.0",
"gulp-jshint": "^1.10.0",
"gulp-mocha": "^2.0.1",
"jshint-stylish": "^1.0.1",
"sinon": "^1.14.1",
"sinon-chai": "^2.7.0"
}
}

839
node_modules/memory-cache/test.js generated vendored Normal file
View file

@ -0,0 +1,839 @@
/* global describe, it, before, beforeEach, afterEach */
'use strict';
var chai = require('chai'),
expect = chai.expect,
sinon = require('sinon'),
sinonChai = require('sinon-chai'),
Cache = require('./index').Cache,
cache = new Cache(),
clock;
chai.use(sinonChai);
describe('node-cache', function() {
beforeEach(function() {
clock = sinon.useFakeTimers();
cache.clear();
});
afterEach(function() {
clock.restore();
});
describe('put()', function() {
before(function() {
cache.debug(false);
});
it('should allow adding a new item to the cache', function() {
expect(function() {
cache.put('key', 'value');
}).to.not.throw();
});
it('should allow adding a new item to the cache with a timeout', function() {
expect(function() {
cache.put('key', 'value', 100);
}).to.not.throw();
});
it('should allow adding a new item to the cache with a timeout callback', function() {
expect(function() {
cache.put('key', 'value', 100, function() {});
}).to.not.throw();
});
it('should throw an error given a non-numeric timeout', function() {
expect(function() {
cache.put('key', 'value', 'foo');
}).to.throw();
});
it('should throw an error given a timeout of NaN', function() {
expect(function() {
cache.put('key', 'value', NaN);
}).to.throw();
});
it('should throw an error given a timeout of 0', function() {
expect(function() {
cache.put('key', 'value', 0);
}).to.throw();
});
it('should throw an error given a negative timeout', function() {
expect(function() {
cache.put('key', 'value', -100);
}).to.throw();
});
it('should throw an error given a non-function timeout callback', function() {
expect(function() {
cache.put('key', 'value', 100, 'foo');
}).to.throw();
});
it('should cause the timeout callback to fire once the cache item expires', function() {
var spy = sinon.spy();
cache.put('key', 'value', 1000, spy);
clock.tick(999);
expect(spy).to.not.have.been.called;
clock.tick(1);
expect(spy).to.have.been.calledOnce.and.calledWith('key', 'value');
});
it('should override the timeout callback on a new put() with a different timeout callback', function() {
var spy1 = sinon.spy();
var spy2 = sinon.spy();
cache.put('key', 'value', 1000, spy1);
clock.tick(999);
cache.put('key', 'value', 1000, spy2)
clock.tick(1001);
expect(spy1).to.not.have.been.called;
expect(spy2).to.have.been.calledOnce.and.calledWith('key', 'value');
});
it('should cancel the timeout callback on a new put() without a timeout callback', function() {
var spy = sinon.spy();
cache.put('key', 'value', 1000, spy);
clock.tick(999);
cache.put('key', 'value');
clock.tick(1);
expect(spy).to.not.have.been.called;
});
it('should return the cached value', function() {
expect(cache.put('key', 'value')).to.equal('value');
});
});
describe('del()', function() {
before(function() {
cache.debug(false);
});
it('should return false given a key for an empty cache', function() {
expect(cache.del('miss')).to.be.false;
});
it('should return false given a key not in a non-empty cache', function() {
cache.put('key', 'value');
expect(cache.del('miss')).to.be.false;
});
it('should return true given a key in the cache', function() {
cache.put('key', 'value');
expect(cache.del('key')).to.be.true;
});
it('should remove the provided key from the cache', function() {
cache.put('key', 'value');
expect(cache.get('key')).to.equal('value');
expect(cache.del('key')).to.be.true;
expect(cache.get('key')).to.be.null;
});
it('should decrement the cache size by 1', function() {
cache.put('key', 'value');
expect(cache.size()).to.equal(1);
expect(cache.del('key')).to.be.true;
expect(cache.size()).to.equal(0);
});
it('should not remove other keys in the cache', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2');
cache.put('key3', 'value3');
expect(cache.get('key1')).to.equal('value1');
expect(cache.get('key2')).to.equal('value2');
expect(cache.get('key3')).to.equal('value3');
cache.del('key1');
expect(cache.get('key1')).to.be.null;
expect(cache.get('key2')).to.equal('value2');
expect(cache.get('key3')).to.equal('value3');
});
it('should only delete a key from the cache once even if called multiple times in a row', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2');
cache.put('key3', 'value3');
expect(cache.size()).to.equal(3);
cache.del('key1');
cache.del('key1');
cache.del('key1');
expect(cache.size()).to.equal(2);
});
it('should handle deleting keys which were previously deleted and then re-added to the cache', function() {
cache.put('key', 'value');
expect(cache.get('key')).to.equal('value');
cache.del('key');
expect(cache.get('key')).to.be.null;
cache.put('key', 'value');
expect(cache.get('key')).to.equal('value');
cache.del('key');
expect(cache.get('key')).to.be.null;
});
it('should return true given an non-expired key', function() {
cache.put('key', 'value', 1000);
clock.tick(999);
expect(cache.del('key')).to.be.true;
});
it('should return false given an expired key', function() {
cache.put('key', 'value', 1000);
clock.tick(1000);
expect(cache.del('key')).to.be.false;
});
it('should cancel the timeout callback for the deleted key', function() {
var spy = sinon.spy();
cache.put('key', 'value', 1000, spy);
cache.del('key');
clock.tick(1000);
expect(spy).to.not.have.been.called;
});
it('should handle deletion of many items', function(done) {
clock.restore();
var num = 1000;
for(var i = 0; i < num; i++){
cache.put('key' + i, i, 1000);
}
expect(cache.size()).to.equal(num);
setTimeout(function(){
expect(cache.size()).to.equal(0);
done();
}, 1000);
});
});
describe('clear()', function() {
before(function() {
cache.debug(false);
});
it('should have no effect given an empty cache', function() {
expect(cache.size()).to.equal(0);
cache.clear();
expect(cache.size()).to.equal(0);
});
it('should remove all existing keys in the cache', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2');
cache.put('key3', 'value3');
expect(cache.size()).to.equal(3);
cache.clear();
expect(cache.size()).to.equal(0);
});
it('should remove the keys in the cache', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2');
cache.put('key3', 'value3');
expect(cache.get('key1')).to.equal('value1');
expect(cache.get('key2')).to.equal('value2');
expect(cache.get('key3')).to.equal('value3');
cache.clear();
expect(cache.get('key1')).to.be.null;
expect(cache.get('key2')).to.be.null;
expect(cache.get('key3')).to.be.null;
});
it('should reset the cache size to 0', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2');
cache.put('key3', 'value3');
expect(cache.size()).to.equal(3);
cache.clear();
expect(cache.size()).to.equal(0);
});
it('should reset the debug cache hits', function() {
cache.debug(true);
cache.put('key', 'value');
cache.get('key');
expect(cache.hits()).to.equal(1);
cache.clear();
expect(cache.hits()).to.equal(0);
});
it('should reset the debug cache misses', function() {
cache.debug(true);
cache.put('key', 'value');
cache.get('miss1');
expect(cache.misses()).to.equal(1);
cache.clear();
expect(cache.misses()).to.equal(0);
});
it('should cancel the timeout callbacks for all existing keys', function() {
var spy1 = sinon.spy();
var spy2 = sinon.spy();
var spy3 = sinon.spy();
cache.put('key1', 'value1', 1000, spy1);
cache.put('key2', 'value2', 1000, spy2);
cache.put('key3', 'value3', 1000, spy3);
cache.clear();
clock.tick(1000);
expect(spy1).to.not.have.been.called;
expect(spy2).to.not.have.been.called;
expect(spy3).to.not.have.been.called;
});
});
describe('get()', function() {
before(function() {
cache.debug(false);
});
it('should return null given a key for an empty cache', function() {
expect(cache.get('miss')).to.be.null;
});
it('should return null given a key not in a non-empty cache', function() {
cache.put('key', 'value');
expect(cache.get('miss')).to.be.null;
});
it('should return the corresponding value of a key in the cache', function() {
cache.put('key', 'value');
expect(cache.get('key')).to.equal('value');
});
it('should return the latest corresponding value of a key in the cache', function() {
cache.put('key', 'value1');
cache.put('key', 'value2');
cache.put('key', 'value3');
expect(cache.get('key')).to.equal('value3');
});
it('should handle various types of cache keys', function() {
var keys = [null, undefined, NaN, true, false, 0, 1, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY, '', 'a', [], {}, [1, 'a', false], {a:1,b:'a',c:false}, function() {}];
keys.forEach(function(key, index) {
var value = 'value' + index;
cache.put(key, value);
expect(cache.get(key)).to.deep.equal(value);
});
});
it('should handle various types of cache values', function() {
var values = [null, undefined, NaN, true, false, 0, 1, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY, '', 'a', [], {}, [1, 'a', false], {a:1,b:'a',c:false}, function() {}];
values.forEach(function(value, index) {
var key = 'key' + index;
cache.put(key, value);
expect(cache.get(key)).to.deep.equal(value);
});
});
it('should not set a timeout given no expiration time', function() {
cache.put('key', 'value');
clock.tick(1000);
expect(cache.get('key')).to.equal('value');
});
it('should return the corresponding value of a non-expired key in the cache', function() {
cache.put('key', 'value', 1000);
clock.tick(999);
expect(cache.get('key')).to.equal('value');
});
it('should return null given an expired key', function() {
cache.put('key', 'value', 1000);
clock.tick(1000);
expect(cache.get('key')).to.be.null;
});
it('should return null given an expired key', function() {
cache.put('key', 'value', 1000);
clock.tick(1000);
expect(cache.get('key')).to.be.null;
});
it('should return null given a key which is a property on the Object prototype', function() {
expect(cache.get('toString')).to.be.null;
});
it('should allow reading the value for a key which is a property on the Object prototype', function() {
cache.put('toString', 'value');
expect(cache.get('toString')).to.equal('value');
});
});
describe('size()', function() {
before(function() {
cache.debug(false);
});
it('should return 0 given a fresh cache', function() {
expect(cache.size()).to.equal(0);
});
it('should return 1 after adding a single item to the cache', function() {
cache.put('key', 'value');
expect(cache.size()).to.equal(1);
});
it('should return 3 after adding three items to the cache', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2');
cache.put('key3', 'value3');
expect(cache.size()).to.equal(3);
});
it('should not multi-count duplicate items added to the cache', function() {
cache.put('key', 'value1');
expect(cache.size()).to.equal(1);
cache.put('key', 'value2');
expect(cache.size()).to.equal(1);
});
it('should update when a key in the cache expires', function() {
cache.put('key', 'value', 1000);
expect(cache.size()).to.equal(1);
clock.tick(999);
expect(cache.size()).to.equal(1);
clock.tick(1);
expect(cache.size()).to.equal(0);
});
});
describe('memsize()', function() {
before(function() {
cache.debug(false);
});
it('should return 0 given a fresh cache', function() {
expect(cache.memsize()).to.equal(0);
});
it('should return 1 after adding a single item to the cache', function() {
cache.put('key', 'value');
expect(cache.memsize()).to.equal(1);
});
it('should return 3 after adding three items to the cache', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2');
cache.put('key3', 'value3');
expect(cache.memsize()).to.equal(3);
});
it('should not multi-count duplicate items added to the cache', function() {
cache.put('key', 'value1');
expect(cache.memsize()).to.equal(1);
cache.put('key', 'value2');
expect(cache.memsize()).to.equal(1);
});
it('should update when a key in the cache expires', function() {
cache.put('key', 'value', 1000);
expect(cache.memsize()).to.equal(1);
clock.tick(999);
expect(cache.memsize()).to.equal(1);
clock.tick(1);
expect(cache.memsize()).to.equal(0);
});
});
describe('debug()', function() {
it('should not count cache hits when false', function() {
cache.debug(false);
cache.put('key', 'value');
cache.get('key');
expect(cache.hits()).to.equal(0);
});
it('should not count cache misses when false', function() {
cache.debug(false);
cache.put('key', 'value');
cache.get('miss1');
expect(cache.misses()).to.equal(0);
});
it('should count cache hits when true', function() {
cache.debug(true);
cache.put('key', 'value');
cache.get('key');
expect(cache.hits()).to.equal(1);
});
it('should count cache misses when true', function() {
cache.debug(true);
cache.put('key', 'value');
cache.get('miss1');
expect(cache.misses()).to.equal(1);
});
});
describe('hits()', function() {
before(function() {
cache.debug(true);
});
it('should return 0 given an empty cache', function() {
expect(cache.hits()).to.equal(0);
});
it('should return 0 given a non-empty cache which has not been accessed', function() {
cache.put('key', 'value');
expect(cache.hits()).to.equal(0);
});
it('should return 0 given a non-empty cache which has had only misses', function() {
cache.put('key', 'value');
cache.get('miss1');
cache.get('miss2');
cache.get('miss3');
expect(cache.hits()).to.equal(0);
});
it('should return 1 given a non-empty cache which has had a single hit', function() {
cache.put('key', 'value');
cache.get('key');
expect(cache.hits()).to.equal(1);
});
it('should return 3 given a non-empty cache which has had three hits on the same key', function() {
cache.put('key', 'value');
cache.get('key');
cache.get('key');
cache.get('key');
expect(cache.hits()).to.equal(3);
});
it('should return 3 given a non-empty cache which has had three hits across many keys', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2');
cache.put('key3', 'value3');
cache.get('key1');
cache.get('key2');
cache.get('key3');
expect(cache.hits()).to.equal(3);
});
it('should return the correct value after a sequence of hits and misses', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2');
cache.put('key3', 'value3');
cache.get('key1');
cache.get('miss');
cache.get('key3');
expect(cache.hits()).to.equal(2);
});
it('should not count hits for expired keys', function() {
cache.put('key', 'value', 1000);
cache.get('key');
expect(cache.hits()).to.equal(1);
clock.tick(999);
cache.get('key');
expect(cache.hits()).to.equal(2);
clock.tick(1);
cache.get('key');
expect(cache.hits()).to.equal(2);
});
});
describe('misses()', function() {
before(function() {
cache.debug(true);
});
it('should return 0 given an empty cache', function() {
expect(cache.misses()).to.equal(0);
});
it('should return 0 given a non-empty cache which has not been accessed', function() {
cache.put('key', 'value');
expect(cache.misses()).to.equal(0);
});
it('should return 0 given a non-empty cache which has had only hits', function() {
cache.put('key', 'value');
cache.get('key');
cache.get('key');
cache.get('key');
expect(cache.misses()).to.equal(0);
});
it('should return 1 given a non-empty cache which has had a single miss', function() {
cache.put('key', 'value');
cache.get('miss');
expect(cache.misses()).to.equal(1);
});
it('should return 3 given a non-empty cache which has had three misses', function() {
cache.put('key', 'value');
cache.get('miss1');
cache.get('miss2');
cache.get('miss3');
expect(cache.misses()).to.equal(3);
});
it('should return the correct value after a sequence of hits and misses', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2');
cache.put('key3', 'value3');
cache.get('key1');
cache.get('miss');
cache.get('key3');
expect(cache.misses()).to.equal(1);
});
it('should count misses for expired keys', function() {
cache.put('key', 'value', 1000);
cache.get('key');
expect(cache.misses()).to.equal(0);
clock.tick(999);
cache.get('key');
expect(cache.misses()).to.equal(0);
clock.tick(1);
cache.get('key');
expect(cache.misses()).to.equal(1);
});
});
describe('keys()', function() {
before(function() {
cache.debug(false);
});
it('should return an empty array given an empty cache', function() {
expect(cache.keys()).to.deep.equal([]);
});
it('should return a single key after adding a single item to the cache', function() {
cache.put('key', 'value');
expect(cache.keys()).to.deep.equal(['key']);
});
it('should return 3 keys after adding three items to the cache', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2');
cache.put('key3', 'value3');
expect(cache.keys()).to.deep.equal(['key1', 'key2', 'key3']);
});
it('should not multi-count duplicate items added to the cache', function() {
cache.put('key', 'value1');
expect(cache.keys()).to.deep.equal(['key']);
cache.put('key', 'value2');
expect(cache.keys()).to.deep.equal(['key']);
});
it('should update when a key in the cache expires', function() {
cache.put('key', 'value', 1000);
expect(cache.keys()).to.deep.equal(['key']);
clock.tick(999);
expect(cache.keys()).to.deep.equal(['key']);
clock.tick(1);
expect(cache.keys()).to.deep.equal([]);
});
});
describe('export()', function() {
var START_TIME = 10000;
var BASIC_EXPORT = JSON.stringify({
key: {
value: 'value',
expire: START_TIME + 1000,
},
});
before(function() {
cache.debug(false);
});
beforeEach(function() {
clock.tick(START_TIME);
});
it('should return an empty object given an empty cache', function() {
expect(cache.exportJson()).to.equal(JSON.stringify({}));
});
it('should return a single record after adding a single item to the cache', function() {
cache.put('key', 'value', 1000);
expect(cache.exportJson()).to.equal(BASIC_EXPORT);
});
it('should return multiple records with expiry', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2', 1000);
expect(cache.exportJson()).to.equal(JSON.stringify({
key1: {
value: 'value1',
expire: 'NaN',
},
key2: {
value: 'value2',
expire: START_TIME + 1000,
},
}));
});
it('should update when a key in the cache expires', function() {
cache.put('key', 'value', 1000);
expect(cache.exportJson()).to.equal(BASIC_EXPORT);
clock.tick(999);
expect(cache.exportJson()).to.equal(BASIC_EXPORT);
clock.tick(1);
expect(cache.exportJson()).to.equal(JSON.stringify({}));
});
});
describe('import()', function() {
var START_TIME = 10000;
var BASIC_EXPORT = JSON.stringify({
key: {
value: 'value',
expire: START_TIME + 1000,
},
});
before(function() {
cache.debug(false);
});
beforeEach(function() {
clock.tick(START_TIME);
});
it('should import an empty object into an empty cache', function() {
var exportedJson = cache.exportJson();
cache.clear();
cache.importJson(exportedJson);
expect(cache.exportJson()).to.equal(JSON.stringify({}));
});
it('should import records into an empty cache', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2', 1000);
var exportedJson = cache.exportJson();
cache.clear();
cache.importJson(exportedJson);
expect(cache.exportJson()).to.equal(JSON.stringify({
key1: {
value: 'value1',
expire: 'NaN',
},
key2: {
value: 'value2',
expire: START_TIME + 1000,
},
}));
});
it('should import records into an already-existing cache', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2', 1000);
var exportedJson = cache.exportJson();
cache.put('key1', 'changed value', 5000);
cache.put('key3', 'value3', 500);
cache.importJson(exportedJson);
expect(cache.exportJson()).to.equal(JSON.stringify({
key1: {
value: 'value1',
expire: 'NaN',
},
key2: {
value: 'value2',
expire: START_TIME + 1000,
},
key3: {
value: 'value3',
expire: START_TIME + 500,
},
}));
});
it('should import records into an already-existing cache and skip duplicates', function() {
cache.debug(true);
cache.put('key1', 'value1');
cache.put('key2', 'value2', 1000);
var exportedJson = cache.exportJson();
cache.clear();
cache.put('key1', 'changed value', 5000);
cache.put('key3', 'value3', 500);
cache.importJson(exportedJson, { skipDuplicates: true });
expect(cache.exportJson()).to.equal(JSON.stringify({
key1: {
value: 'changed value',
expire: START_TIME + 5000,
},
key3: {
value: 'value3',
expire: START_TIME + 500,
},
key2: {
value: 'value2',
expire: START_TIME + 1000,
},
}));
});
it('should import with updated expire times', function() {
cache.put('key1', 'value1', 500);
cache.put('key2', 'value2', 1000);
var exportedJson = cache.exportJson();
var tickAmount = 750;
clock.tick(tickAmount);
cache.importJson(exportedJson);
expect(cache.exportJson()).to.equal(JSON.stringify({
key2: {
value: 'value2',
expire: START_TIME + tickAmount + 250,
},
}));
});
it('should return the new size', function() {
cache.put('key1', 'value1', 500);
var exportedJson = cache.exportJson();
cache.clear();
cache.put('key2', 'value2', 1000);
expect(cache.size()).to.equal(1);
var size = cache.importJson(exportedJson);
expect(size).to.equal(2);
expect(cache.size()).to.equal(2);
});
});
describe('Cache()', function() {
it('should return a new cache instance when called', function() {
var cache1 = new Cache(),
cache2 = new Cache();
cache1.put('key', 'value1');
expect(cache1.keys()).to.deep.equal(['key']);
expect(cache2.keys()).to.deep.equal([]);
cache2.put('key', 'value2');
expect(cache1.get('key')).to.equal('value1');
expect(cache2.get('key')).to.equal('value2');
});
});
});