<commit_before>// TODO: These are really a mix of unit and integration tests.

var assert = require('assert');
var async = require('async');
var sinon = require('sinon');
var support = require('./support');
var check_err = support.check_err;
var caching = require('../index').caching;
var memory_store = require('../lib/stores/memory');

var methods = {
    get_widget: function(name, cb) {
        cb(null, {name: name});
    }
};

describe("caching", function() {
    var cache;
    var key;
    var ttl = 1;
    var name;
    var value;

    describe("get() and set()", function() {
        ['memory'].forEach(function(store) {
            context("using " + store + " store", function() {
                beforeEach(function() {
                    cache = caching({store: store});
                    key = support.random.string(20);
                    value = support.random.string();
                });

                it("lets us set and get data in cache", function(done) {
                    cache.set(key, value, ttl, function(err) {
                        check_err(err);
                        cache.get(key, function(err, result) {
                            assert.equal(result, value);
                            done();
                        });
                    });
                });

                it("lets us set and get data without a callback", function(done) {
                    cache.set(key, value, ttl);
                    setTimeout(function() {
                        var result = cache.get(key);
                        assert.equal(result, value);
                        done();
                    }, 20);
                });

                it("lets us set and get data without a ttl or callback", function(done) {
                    cache.set(key, value);
                    setTimeout(function() {
                        var result = cache.get(key);
                        assert.equal(result, value);
                        done();
                    }, 20);
                });
            });
        });
    });

    describe("del()", function() {
        ['memory'].forEach(function(store) {
            context("using " + store + " store", function() {
                beforeEach(function(done) {
                    cache = caching({store: store});
                    key = support.random.string(20);
                    value = support.random.string();
                    cache.set(key, value, ttl, function(err) {
                        check_err(err);
                        done();
                    });
                });

                it("deletes data from cache", function(done) {
                    cache.get(key, function(err, result) {
                        assert.equal(result, value);

                        cache.del(key, function(err) {
                            check_err(err);

                            cache.get(key, function(err, result) {
                                assert.ok(!result);
                                done();
                            });
                        });
                    });
                });

                it("lets us delete data without a callback", function(done) {
                    cache.get(key, function(err, result) {
                        assert.equal(result, value);

                        cache.del(key);

                        setTimeout(function() {
                            cache.get(key, function(err, result) {
                                assert.ok(!result);
                                done();
                            });
                        }, 20);
                    });
                });
            });
        });
    });

    describe("reset()", function() {
        var key2;
        var value2;

        beforeEach(function(done) {
            cache = caching({store: 'memory'});
            key = support.random.string(20);
            value = support.random.string();
            cache.set(key, value, ttl, function(err) {
                check_err(err);

                key2 = support.random.string(20);
                value2 = support.random.string();

                cache.set(key2, value2, ttl, done);
            });
        });

        it("clears the cache", function(done) {
            cache.reset(function(err) {
                check_err(err);

                cache.get(key, function(err, result) {
                    assert.ok(!result);

                    cache.get(key2, function(err, result) {
                        assert.ok(!result);
                        done();
                    });
                });
            });
        });

        it("lets us clear the cache without a callback (memory store only)", function(done) {
            cache.reset();
            setTimeout(function() {
                cache.get(key, function(err, result) {
                    assert.ok(!result);

                    cache.get(key2, function(err, result) {
                        assert.ok(!result);
                        done();
                    });
                });
            }, 10);
        });

        context("when store has no del() method", function() {
            var fake_store;

            beforeEach(function() {
                fake_store = {
                    get: function() {},
                    set: function() {},
                };
            });

            it("it doesn't throw an error", function() {
                assert.doesNotThrow(function() {
                    caching({store: fake_store});
                });
            });
        });
    });

    describe("setex()", function() {
        var fake_store;

        beforeEach(function() {
            fake_store = {
                get: function() {},
                set: function() {},
                del: function() {},
                setex: function() {}
            };

            sinon.stub(fake_store, 'setex');

            cache = caching({store: fake_store});
        });

        it("passes the params to the underlying store's setex() method", function() {
            cache.setex('foo', 'bar', 'blah');
            assert.ok(fake_store.setex.calledWith('foo', 'bar', 'blah'));
        });
    });

    describe("keys()", function() {
        var key_count;
        var saved_keys = [];

        beforeEach(function(done) {
            key_count = 10;
            var processed = 0;

            cache = caching({store: 'memory'});

            function is_done() {
                return processed === key_count;
            }

            async.until(is_done, function(cb) {
                processed += 1;
                key = support.random.string(20);
                saved_keys.push(key);
                value = support.random.string();
                cache.set(key, value, ttl, cb);
            }, done);
        });

        it("calls back with all keys in cache", function(done) {
            cache.keys(function(err, keys) {
                check_err(err);
                assert.deepEqual(keys.sort, saved_keys.sort);
                done();
            });
        });

        it("lets us get the keys without a callback (memory store only)", function() {
            var keys = cache.keys();
            assert.deepEqual(keys.sort, saved_keys.sort);
        });
    });

    describe("wrap()", function() {
        describe("using memory (lru-cache) store", function() {
            var memory_store_stub;

            beforeEach(function() {
                ttl = 0.1;
                memory_store_stub = memory_store.create({ttl: ttl});

                sinon.stub(memory_store, 'create').returns(memory_store_stub);

                cache = caching({store: 'memory', ttl: ttl, ignoreCacheErrors: false});
                key = support.random.string(20);
                name = support.random.string();
            });

            afterEach(function() {
                memory_store.create.restore();
            });

            context("calls back with the result of the wrapped function", function() {
                beforeEach(function() {
                    sinon.spy(memory_store_stub, 'set');
                });

                afterEach(function() {
                    memory_store_stub.set.restore();
                });

                it("when a ttl is passed in", function(done) {
                    cache.wrap(key, function(cb) {
                        methods.get_widget(name, cb);
                    }, ttl, function(err, widget) {
                        check_err(err);
                        assert.deepEqual(widget, {name: name});
                        sinon.assert.calledWith(memory_store_stub.set, key, {name: name}, ttl);
                        done();
                    });
                });

                it("when a ttl is not passed in", function(done) {
                    cache.wrap(key, function(cb) {
                        methods.get_widget(name, cb);
                    }, function(err, widget) {
                        check_err(err);
                        assert.deepEqual(widget, {name: name});
                        sinon.assert.calledWith(memory_store_stub.set, key, {name: name}, undefined);
                        done();
                    });
                });
            });

            context("when result is already cached", function() {
                function get_cached_widget(name, cb) {
                    cache.wrap(key, function(cache_cb) {
                        methods.get_widget(name, cache_cb);
                    }, ttl, cb);
                }

                beforeEach(function(done) {
                    get_cached_widget(name, function(err, widget) {
                        check_err(err);
                        assert.ok(widget);

                        memory_store_stub.get(key, function(err, result) {
                            check_err(err);
                            assert.ok(result);

                            sinon.spy(memory_store_stub, 'get');

                            done();
                        });
                    });
                });

                afterEach(function() {
                    memory_store_stub.get.restore();
                });

                it("retrieves data from cache", function(done) {
                    var func_called = false;

                    cache.wrap(key, function(cb) {
                        methods.get_widget(name, function(err, result) {
                            func_called = true;
                            cb(err, result);
                        });
                    }, ttl, function(err, widget) {
                        check_err(err);
                        assert.deepEqual(widget, {name: name});
                        assert.ok(memory_store_stub.get.calledWith(key));
                        assert.ok(!func_called);
                        done();
                    });
                });
            });

            it("expires cached result after ttl seconds", function(done) {
                cache.wrap(key, function(cb) {
                    methods.get_widget(name, cb);
                }, ttl, function(err, widget) {
                    check_err(err);
                    assert.deepEqual(widget, {name: name});

                    memory_store_stub.get(key, function(err, result) {
                        check_err(err);
                        assert.ok(result);

                        var func_called = false;

                        setTimeout(function() {
                            cache.wrap(key, function(cb) {
                                methods.get_widget(name, function(err, result) {
                                    func_called = true;
                                    cb(err, result);
                                });
                            }, function(err, widget) {
                                check_err(err);
                                assert.ok(func_called);
                                assert.deepEqual(widget, {name: name});
                                done();
                            });
                        }, (ttl * 1000 + 10));
                    });
                });
            });

            context("when an error is thrown in the work function", function() {
                var fake_error;

                beforeEach(function() {
                    fake_error = new Error(support.random.string());
                });

                it("bubbles up that error", function(done) {
                    cache.wrap(key, function() {
                        throw fake_error;
                    }, ttl, function(err) {
                        assert.equal(err, fake_error);
                        done();
                    });
                });
            });

            context("when store.get() calls back with an error", function() {
                context("and ignoreCacheErrors is not set (default is false)", function() {
                    it("bubbles up that error", function(done) {
                        var fake_error = new Error(support.random.string());

                        sinon.stub(memory_store_stub, 'get', function(key, cb) {
                            cb(fake_error);
                        });

                        cache.wrap(key, function(cb) {
                            methods.get_widget(name, cb);
                        }, ttl, function(err) {
                            assert.equal(err, fake_error);
                            memory_store_stub.get.restore();
                            done();
                        });
                    });
                });

                context("and ignoreCacheErrors is set to true", function() {
                    it("does not bubble up that error", function(done) {
                        cache = caching({store: 'memory', ttl: ttl, ignoreCacheErrors: true});

                        var fake_error = new Error(support.random.string());

                        sinon.stub(memory_store_stub, 'get', function(key, cb) {
                            cb(fake_error);
                        });

                        cache.wrap(key, function(cb) {
                            methods.get_widget(name, cb);
                        }, ttl, function(err) {
                            assert.equal(err, null);
                            memory_store_stub.get.restore();
                            done();
                        });
                    });
                });
            });

            context("when store.set() calls back with an error", function() {
                context("and ignoreCacheErrors is not set", function() {
                    it("bubbles up that error", function(done) {
                        var fake_error = new Error(support.random.string());

                        sinon.stub(memory_store_stub, 'set', function(key, val, ttl, cb) {
                            cb(fake_error);
                        });

                        cache.wrap(key, function(cb) {
                            methods.get_widget(name, cb);
                        }, ttl, function(err) {
                            assert.equal(err, fake_error);
                            memory_store_stub.set.restore();
                            done();
                        });
                    });
                });

                context("and ignoreCacheErrors is set to true", function() {
                    it("does not bubbles up that error", function(done) {
                        cache = caching({store: 'memory', ttl: ttl, ignoreCacheErrors: true});
                        var fake_error = new Error(support.random.string());

                        sinon.stub(memory_store_stub, 'set', function(key, val, ttl, cb) {
                            cb(fake_error);
                        });

                        cache.wrap(key, function(cb) {
                            methods.get_widget(name, cb);
                        }, ttl, function(err) {
                            assert.equal(err, null);
                            memory_store_stub.set.restore();
                            done();
                        });
                    });
                });
            });

            context("when wrapped function calls back with an error", function() {
                it("calls back with that error", function(done) {
                    var fake_error = new Error(support.random.string());
                    sinon.stub(methods, 'get_widget', function(name, cb) {
                        cb(fake_error, {name: name});
                    });

                    cache.wrap(key, function(cb) {
                        methods.get_widget(name, cb);
                    }, ttl, function(err, widget) {
                        methods.get_widget.restore();
                        assert.equal(err, fake_error);
                        assert.ok(!widget);
                        done();
                    });
                });
            });
        });

        describe("when called multiple times in parallel with same key", function() {
            var construct;

            beforeEach(function() {
                cache = caching({
                    store: 'memory',
                    max: 50,
                    ttl: 5 * 60
                });

                construct = sinon.spy(function(val, cb) {
                    var timeout = support.random.number(100);
                    setTimeout(function() {
                        cb(null, 'value');
                    }, timeout);
                });
            });

            it("calls the wrapped function once", function(done) {
                var values = [];
                for (var i = 0; i < 2; i++) {
                    values.push(i);
                }

                async.each(values, function(val, async_cb) {
                    cache.wrap('key', function(cb) {
                        construct(val, cb);
                    }, ttl, function(err, result) {
                        assert.equal(result, 'value');
                        async_cb(err);
                    });
                }, function(err) {
                    check_err(err);
                    assert.equal(construct.callCount, 1);
                    done();
                });
            });
        });
    });

    describe("instantiating with no store passed in", function() {
        it("defaults to 'memory' store", function() {
            var cache = caching();
            assert.equal(cache.store.name, 'memory');
        });
    });

    describe("instantiating with custom store", function() {
        it("allows us to pass in our own store object", function(done) {
            var store = memory_store.create({ttl: ttl});
            cache = caching({store: store});
            cache.set(key, value, ttl, function(err) {
                check_err(err);
                cache.get(key, function(err, result) {
                    assert.equal(result, value);
                    done();
                });
            });
        });

        it("allows us to pass in a path to our own store", function(done) {
            var store_path = '../lib/stores/memory';
            cache = caching({store: store_path});
            cache.set(key, value, ttl, function(err) {
                check_err(err);
                cache.get(key, function(err, result) {
                    assert.equal(result, value);
                    done();
                });
            });
        });

        it("allows us to pass in a module (uninstantiated)", function(done) {
            var store = memory_store;
            cache = caching({store: store});
            cache.set(key, value, ttl, function(err) {
                check_err(err);
                cache.get(key, function(err, result) {
                    assert.equal(result, value);
                    done();
                });
            });
        });
    });
});
<commit_msg>test case for issue #21<commit_after>// TODO: These are really a mix of unit and integration tests.

var assert = require('assert');
var async = require('async');
var sinon = require('sinon');
var support = require('./support');
var check_err = support.check_err;
var caching = require('../index').caching;
var memory_store = require('../lib/stores/memory');

var methods = {
    get_widget: function(name, cb) {
        cb(null, {name: name});
    }
};

describe("caching", function() {
    var cache;
    var key;
    var ttl = 1;
    var name;
    var value;

    describe("get() and set()", function() {
        ['memory'].forEach(function(store) {
            context("using " + store + " store", function() {
                beforeEach(function() {
                    cache = caching({store: store});
                    key = support.random.string(20);
                    value = support.random.string();
                });

                it("lets us set and get data in cache", function(done) {
                    cache.set(key, value, ttl, function(err) {
                        check_err(err);
                        cache.get(key, function(err, result) {
                            assert.equal(result, value);
                            done();
                        });
                    });
                });

                it("lets us set and get data without a callback", function(done) {
                    cache.set(key, value, ttl);
                    setTimeout(function() {
                        var result = cache.get(key);
                        assert.equal(result, value);
                        done();
                    }, 20);
                });

                it("lets us set and get data without a ttl or callback", function(done) {
                    cache.set(key, value);
                    setTimeout(function() {
                        var result = cache.get(key);
                        assert.equal(result, value);
                        done();
                    }, 20);
                });
            });
        });
    });

    describe("del()", function() {
        ['memory'].forEach(function(store) {
            context("using " + store + " store", function() {
                beforeEach(function(done) {
                    cache = caching({store: store});
                    key = support.random.string(20);
                    value = support.random.string();
                    cache.set(key, value, ttl, function(err) {
                        check_err(err);
                        done();
                    });
                });

                it("deletes data from cache", function(done) {
                    cache.get(key, function(err, result) {
                        assert.equal(result, value);

                        cache.del(key, function(err) {
                            check_err(err);

                            cache.get(key, function(err, result) {
                                assert.ok(!result);
                                done();
                            });
                        });
                    });
                });

                it("lets us delete data without a callback", function(done) {
                    cache.get(key, function(err, result) {
                        assert.equal(result, value);

                        cache.del(key);

                        setTimeout(function() {
                            cache.get(key, function(err, result) {
                                assert.ok(!result);
                                done();
                            });
                        }, 20);
                    });
                });
            });
        });
    });

    describe("reset()", function() {
        var key2;
        var value2;

        beforeEach(function(done) {
            cache = caching({store: 'memory'});
            key = support.random.string(20);
            value = support.random.string();
            cache.set(key, value, ttl, function(err) {
                check_err(err);

                key2 = support.random.string(20);
                value2 = support.random.string();

                cache.set(key2, value2, ttl, done);
            });
        });

        it("clears the cache", function(done) {
            cache.reset(function(err) {
                check_err(err);

                cache.get(key, function(err, result) {
                    assert.ok(!result);

                    cache.get(key2, function(err, result) {
                        assert.ok(!result);
                        done();
                    });
                });
            });
        });

        it("lets us clear the cache without a callback (memory store only)", function(done) {
            cache.reset();
            setTimeout(function() {
                cache.get(key, function(err, result) {
                    assert.ok(!result);

                    cache.get(key2, function(err, result) {
                        assert.ok(!result);
                        done();
                    });
                });
            }, 10);
        });

        context("when store has no del() method", function() {
            var fake_store;

            beforeEach(function() {
                fake_store = {
                    get: function() {},
                    set: function() {},
                };
            });

            it("it doesn't throw an error", function() {
                assert.doesNotThrow(function() {
                    caching({store: fake_store});
                });
            });
        });
    });

    describe("setex()", function() {
        var fake_store;

        beforeEach(function() {
            fake_store = {
                get: function() {},
                set: function() {},
                del: function() {},
                setex: function() {}
            };

            sinon.stub(fake_store, 'setex');

            cache = caching({store: fake_store});
        });

        it("passes the params to the underlying store's setex() method", function() {
            cache.setex('foo', 'bar', 'blah');
            assert.ok(fake_store.setex.calledWith('foo', 'bar', 'blah'));
        });
    });

    describe("keys()", function() {
        var key_count;
        var saved_keys = [];

        beforeEach(function(done) {
            key_count = 10;
            var processed = 0;

            cache = caching({store: 'memory'});

            function is_done() {
                return processed === key_count;
            }

            async.until(is_done, function(cb) {
                processed += 1;
                key = support.random.string(20);
                saved_keys.push(key);
                value = support.random.string();
                cache.set(key, value, ttl, cb);
            }, done);
        });

        it("calls back with all keys in cache", function(done) {
            cache.keys(function(err, keys) {
                check_err(err);
                assert.deepEqual(keys.sort, saved_keys.sort);
                done();
            });
        });

        it("lets us get the keys without a callback (memory store only)", function() {
            var keys = cache.keys();
            assert.deepEqual(keys.sort, saved_keys.sort);
        });
    });

    describe("wrap()", function() {
        describe("using memory (lru-cache) store", function() {
            var memory_store_stub;

            beforeEach(function() {
                ttl = 0.1;
                memory_store_stub = memory_store.create({ttl: ttl});

                sinon.stub(memory_store, 'create').returns(memory_store_stub);

                cache = caching({store: 'memory', ttl: ttl, ignoreCacheErrors: false});
                key = support.random.string(20);
                name = support.random.string();
            });

            afterEach(function() {
                memory_store.create.restore();
            });

            context("calls back with the result of the wrapped function", function() {
                beforeEach(function() {
                    sinon.spy(memory_store_stub, 'set');
                });

                afterEach(function() {
                    memory_store_stub.set.restore();
                });

                it("when a ttl is passed in", function(done) {
                    cache.wrap(key, function(cb) {
                        methods.get_widget(name, cb);
                    }, ttl, function(err, widget) {
                        check_err(err);
                        assert.deepEqual(widget, {name: name});
                        sinon.assert.calledWith(memory_store_stub.set, key, {name: name}, ttl);
                        done();
                    });
                });

                it("when a ttl is not passed in", function(done) {
                    cache.wrap(key, function(cb) {
                        methods.get_widget(name, cb);
                    }, function(err, widget) {
                        check_err(err);
                        assert.deepEqual(widget, {name: name});
                        sinon.assert.calledWith(memory_store_stub.set, key, {name: name}, undefined);
                        done();
                    });
                });
            });

            context("when result is already cached", function() {
                function get_cached_widget(name, cb) {
                    cache.wrap(key, function(cache_cb) {
                        methods.get_widget(name, cache_cb);
                    }, ttl, cb);
                }

                beforeEach(function(done) {
                    get_cached_widget(name, function(err, widget) {
                        check_err(err);
                        assert.ok(widget);

                        memory_store_stub.get(key, function(err, result) {
                            check_err(err);
                            assert.ok(result);

                            sinon.spy(memory_store_stub, 'get');

                            done();
                        });
                    });
                });

                afterEach(function() {
                    memory_store_stub.get.restore();
                });

                it("retrieves data from cache", function(done) {
                    var func_called = false;

                    cache.wrap(key, function(cb) {
                        methods.get_widget(name, function(err, result) {
                            func_called = true;
                            cb(err, result);
                        });
                    }, ttl, function(err, widget) {
                        check_err(err);
                        assert.deepEqual(widget, {name: name});
                        assert.ok(memory_store_stub.get.calledWith(key));
                        assert.ok(!func_called);
                        done();
                    });
                });
            });

            it("lets us make nested calls", function(done) {
                function get_cached_widget(name, cb) {
                    cache.wrap(key, function(cache_cb) {
                        methods.get_widget(name, cache_cb);
                    }, cb);
                }

                get_cached_widget(name, function(err, widget) {
                    check_err(err);
                    assert.equal(widget.name, name);

                    get_cached_widget(name, function(err, widget) {
                        check_err(err);
                        assert.equal(widget.name, name);

                        get_cached_widget(name, function(err, widget) {
                            check_err(err);
                            assert.equal(widget.name, name);
                            done();
                        });
                    });
                });
            });

            it("expires cached result after ttl seconds", function(done) {
                cache.wrap(key, function(cb) {
                    methods.get_widget(name, cb);
                }, ttl, function(err, widget) {
                    check_err(err);
                    assert.deepEqual(widget, {name: name});

                    memory_store_stub.get(key, function(err, result) {
                        check_err(err);
                        assert.ok(result);

                        var func_called = false;

                        setTimeout(function() {
                            cache.wrap(key, function(cb) {
                                methods.get_widget(name, function(err, result) {
                                    func_called = true;
                                    cb(err, result);
                                });
                            }, function(err, widget) {
                                check_err(err);
                                assert.ok(func_called);
                                assert.deepEqual(widget, {name: name});
                                done();
                            });
                        }, (ttl * 1000 + 10));
                    });
                });
            });

            context("when an error is thrown in the work function", function() {
                var fake_error;

                beforeEach(function() {
                    fake_error = new Error(support.random.string());
                });

                it("bubbles up that error", function(done) {
                    cache.wrap(key, function() {
                        throw fake_error;
                    }, ttl, function(err) {
                        assert.equal(err, fake_error);
                        done();
                    });
                });
            });

            context("when store.get() calls back with an error", function() {
                context("and ignoreCacheErrors is not set (default is false)", function() {
                    it("bubbles up that error", function(done) {
                        var fake_error = new Error(support.random.string());

                        sinon.stub(memory_store_stub, 'get', function(key, cb) {
                            cb(fake_error);
                        });

                        cache.wrap(key, function(cb) {
                            methods.get_widget(name, cb);
                        }, ttl, function(err) {
                            assert.equal(err, fake_error);
                            memory_store_stub.get.restore();
                            done();
                        });
                    });
                });

                context("and ignoreCacheErrors is set to true", function() {
                    it("does not bubble up that error", function(done) {
                        cache = caching({store: 'memory', ttl: ttl, ignoreCacheErrors: true});

                        var fake_error = new Error(support.random.string());

                        sinon.stub(memory_store_stub, 'get', function(key, cb) {
                            cb(fake_error);
                        });

                        cache.wrap(key, function(cb) {
                            methods.get_widget(name, cb);
                        }, ttl, function(err) {
                            assert.equal(err, null);
                            memory_store_stub.get.restore();
                            done();
                        });
                    });
                });
            });

            context("when store.set() calls back with an error", function() {
                context("and ignoreCacheErrors is not set", function() {
                    it("bubbles up that error", function(done) {
                        var fake_error = new Error(support.random.string());

                        sinon.stub(memory_store_stub, 'set', function(key, val, ttl, cb) {
                            cb(fake_error);
                        });

                        cache.wrap(key, function(cb) {
                            methods.get_widget(name, cb);
                        }, ttl, function(err) {
                            assert.equal(err, fake_error);
                            memory_store_stub.set.restore();
                            done();
                        });
                    });
                });

                context("and ignoreCacheErrors is set to true", function() {
                    it("does not bubbles up that error", function(done) {
                        cache = caching({store: 'memory', ttl: ttl, ignoreCacheErrors: true});
                        var fake_error = new Error(support.random.string());

                        sinon.stub(memory_store_stub, 'set', function(key, val, ttl, cb) {
                            cb(fake_error);
                        });

                        cache.wrap(key, function(cb) {
                            methods.get_widget(name, cb);
                        }, ttl, function(err) {
                            assert.equal(err, null);
                            memory_store_stub.set.restore();
                            done();
                        });
                    });
                });
            });

            context("when wrapped function calls back with an error", function() {
                it("calls back with that error", function(done) {
                    var fake_error = new Error(support.random.string());
                    sinon.stub(methods, 'get_widget', function(name, cb) {
                        cb(fake_error, {name: name});
                    });

                    cache.wrap(key, function(cb) {
                        methods.get_widget(name, cb);
                    }, ttl, function(err, widget) {
                        methods.get_widget.restore();
                        assert.equal(err, fake_error);
                        assert.ok(!widget);
                        done();
                    });
                });
            });
        });

        describe("when called multiple times in parallel with same key", function() {
            var construct;

            beforeEach(function() {
                cache = caching({
                    store: 'memory',
                    max: 50,
                    ttl: 5 * 60
                });

                construct = sinon.spy(function(val, cb) {
                    var timeout = support.random.number(100);
                    setTimeout(function() {
                        cb(null, 'value');
                    }, timeout);
                });
            });

            it("calls the wrapped function once", function(done) {
                var values = [];
                for (var i = 0; i < 2; i++) {
                    values.push(i);
                }

                async.each(values, function(val, async_cb) {
                    cache.wrap('key', function(cb) {
                        construct(val, cb);
                    }, ttl, function(err, result) {
                        assert.equal(result, 'value');
                        async_cb(err);
                    });
                }, function(err) {
                    check_err(err);
                    assert.equal(construct.callCount, 1);
                    done();
                });
            });
        });
    });

    describe("instantiating with no store passed in", function() {
        it("defaults to 'memory' store", function() {
            var cache = caching();
            assert.equal(cache.store.name, 'memory');
        });
    });

    describe("instantiating with custom store", function() {
        it("allows us to pass in our own store object", function(done) {
            var store = memory_store.create({ttl: ttl});
            cache = caching({store: store});
            cache.set(key, value, ttl, function(err) {
                check_err(err);
                cache.get(key, function(err, result) {
                    assert.equal(result, value);
                    done();
                });
            });
        });

        it("allows us to pass in a path to our own store", function(done) {
            var store_path = '../lib/stores/memory';
            cache = caching({store: store_path});
            cache.set(key, value, ttl, function(err) {
                check_err(err);
                cache.get(key, function(err, result) {
                    assert.equal(result, value);
                    done();
                });
            });
        });

        it("allows us to pass in a module (uninstantiated)", function(done) {
            var store = memory_store;
            cache = caching({store: store});
            cache.set(key, value, ttl, function(err) {
                check_err(err);
                cache.get(key, function(err, result) {
                    assert.equal(result, value);
                    done();
                });
            });
        });
    });
});

{'fraction_non_alphanumeric': 0.11662930110147188, 'fraction_numerical': 0.0025830977678136273, 'mean_word_length': 1.0410325276037005, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7517622', 'n_tokens_mistral': 10052, 'n_tokens_neox': 9241, 'n_words': 2005}