Comparing version 0.0.3 to 0.1.0
142
lib/index.js
@@ -13,6 +13,6 @@ 'use strict'; | ||
if(!this instanceof Stillframe) | ||
if (!this instanceof Stillframe) | ||
return new Stillframe(config); | ||
if(!store || !engines || !config) | ||
if (!store || !engines || !config) | ||
throw new Error('Missing required arguments.'); | ||
@@ -33,9 +33,9 @@ | ||
// lookup an entry in the cache manifest | ||
Stillframe.prototype.lookup = function lookup(engineId, hash, timestamp, ttl, callback) { | ||
Stillframe.prototype.lookup = function lookup(hash, timestamp, ttl, callback) { | ||
return this.redis.eval( | ||
lookupScript, 1, [this.config.prefix, engineId, hash].join(':'), | ||
timestamp, // current request entry | ||
timestamp - ttl, // oldest acceptable complete entry | ||
timestamp - this.config.timeout, // oldest acceptable pending entry | ||
timestamp + this.config.ttl, // entry expiration | ||
lookupScript, 1, [this.config.prefix, hash].join(':'), | ||
timestamp, // current request entry | ||
timestamp - ttl, // oldest acceptable complete entry | ||
timestamp - this.config.timeout, // oldest acceptable pending entry | ||
timestamp + this.config.ttl, // entry expiration | ||
callback | ||
@@ -46,7 +46,7 @@ ); | ||
// resolve a pending entry in the cache manifest | ||
Stillframe.prototype.resolve = function resolve(engineId, hash, timestamp, callback) { | ||
Stillframe.prototype.resolve = function resolve(hash, timestamp, callback) { | ||
return this.redis.eval( | ||
resolveScript, 1, [this.config.prefix, engineId, hash].join(':'), | ||
timestamp, // current request entry | ||
timestamp + this.config.ttl, // entry expiration | ||
resolveScript, 1, [this.config.prefix, hash].join(':'), | ||
timestamp, // current request entry | ||
timestamp + this.config.ttl, // entry expiration | ||
callback | ||
@@ -60,10 +60,13 @@ ); | ||
var switchboard = new stream.PassThrough(); | ||
process.nextTick(function(){ | ||
process.nextTick(function() { | ||
// make sure the engine exists | ||
if(!self.engines[engineId]) | ||
return error(new Error('The engine "' + engineId + '" does not exist.')); | ||
var engine = self.engines[engineId]; | ||
if (!engine) return error(new Error('The engine "' + engineId + '" does not exist.')); | ||
// generate the hash | ||
var hash = hasher.sha1({request: request, options: options}); | ||
var hash = hasher.sha1({ | ||
request: request, | ||
options: options | ||
}); | ||
@@ -76,4 +79,3 @@ // get the timestamp for this transaction | ||
function error(err) { | ||
if(typeof callback === 'function') { | ||
if (typeof callback === 'function') { | ||
callback(err); | ||
@@ -83,3 +85,3 @@ | ||
// an error if there are listeners | ||
if(switchboard.listeners('error').length) | ||
if (switchboard.listeners('error').length) | ||
switchboard.emit('error', err); | ||
@@ -90,9 +92,9 @@ | ||
function run(){ | ||
function run() { | ||
// lookup hash in cache manifest | ||
return self.lookup(engineId, hash, timestamp, ttl, function(err, entry){ | ||
if(err) return error(err); | ||
return self.lookup(hash, timestamp, ttl, function(err, entry) { | ||
var readStream, writeStream, buffer = new stream.PassThrough(); | ||
if (err) return error(err); | ||
var engine = self.engines[engineId]; | ||
@@ -102,15 +104,47 @@ | ||
// a cache entry exists | ||
if(typeof entry === 'string') { | ||
if (typeof entry === 'string') { | ||
entry = entry.split(':'); | ||
// callback with a snapshot | ||
if(typeof callback === 'function') | ||
callback(null, {status: entry[0], created: parseInt(entry[1], 10)}); | ||
if (typeof callback === 'function') | ||
callback(null, { | ||
status: entry[0], | ||
created: parseInt(entry[1], 10) | ||
}); | ||
// cache is complete | ||
else if(entry[0] === 'complete') | ||
self.store.fetch(engineId, engine, hash, parseInt(entry[1])).pipe(switchboard); | ||
else if (entry[0] === 'complete') { | ||
readStream = self.store.fetch(hash, parseInt(entry[1], 10)); | ||
readStream.pipe(buffer); | ||
// cache is pending; wait and try again... | ||
// read from store | ||
readStream | ||
.on('error', error) | ||
// forward progress events | ||
.on('progress', function(p) { | ||
switchboard.emit('progress', p); | ||
}) | ||
// start piping after metadata | ||
.once('metadata', function(m) { | ||
// forward metadata events | ||
switchboard.emit('metadata', m); | ||
// pipe to the switchboard | ||
buffer.pipe(switchboard); | ||
}); | ||
} | ||
// TODO: instead of polling for completion, use redis pub/sub | ||
// to listen for and emit progress updates, fetching the file | ||
// once it's complete. | ||
// cache is pending; wait and try again | ||
else setTimeout(run, self.config.retry); | ||
@@ -126,27 +160,36 @@ } | ||
var readStream = engine.run(request, options); | ||
var writeStream = self.store.save(engineId, engine, hash, timestamp, ttl); | ||
// read from engineId | ||
readStream = engine.run(request, options); | ||
// read from engine | ||
readStream | ||
.on('error', function(e){ | ||
err = e; switchboard.emit('error', err); | ||
.on('error', error) | ||
// forward progress events | ||
.on('progress', function(p) { | ||
// TODO: write progress to redis entry and emit the progress | ||
// update using redis pub/sub | ||
switchboard.emit('progress', p); | ||
}) | ||
// pipe to the switchboard | ||
.pipe(switchboard) | ||
// start piping after metadata | ||
.once('metadata', function(m) { | ||
// write to the store | ||
.pipe(writeStream) | ||
.on('error', function(e){ | ||
err = e; switchboard.emit('error', err); | ||
// forward metadata events | ||
switchboard.emit('metadata', m); | ||
// write to the store | ||
writeStream = self.store.save(hash, timestamp, m, ttl); | ||
readStream.pipe(writeStream); | ||
writeStream.on('error', error); | ||
// pipe to the switchboard | ||
readStream.pipe(switchboard); | ||
}) | ||
// TODO: write any progress to redis | ||
.on('progress', function(e){}) | ||
// resolve the cache manifest entry | ||
.on('finish', function(){ | ||
if(!err) self.resolve(engineId, hash, timestamp, function(err){ | ||
if(err) switchboard.emit('error', err); | ||
.on('finish', function() { | ||
if (!err) self.resolve(hash, timestamp, function(err) { | ||
if (err) switchboard.emit('error', err); | ||
}); | ||
@@ -156,3 +199,6 @@ }); | ||
// callback with a snapshot | ||
if(typeof callback === 'function') return callback(null, {status: 'pending', created: timestamp}); | ||
if (typeof callback === 'function') return callback(null, { | ||
status: 'pending', | ||
created: timestamp | ||
}); | ||
@@ -159,0 +205,0 @@ } |
{ | ||
"name": "stillframe", | ||
"version": "0.0.3", | ||
"version": "0.1.0", | ||
"description": "A simple distributed url-to-whatever framework.", | ||
"main": "./lib/index.js", | ||
"scripts": { | ||
"test": "istanbul cover mocha", | ||
"test": "istanbul cover _mocha", | ||
"test-ci": "istanbul cover _mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | coveralls" | ||
@@ -20,4 +20,3 @@ }, | ||
"istanbul": "^0.3.20", | ||
"mocha": "^2.2.4", | ||
"stillframe-store-file": "^1.0.0" | ||
"mocha": "^2.2.4" | ||
}, | ||
@@ -24,0 +23,0 @@ "dependencies": { |
@@ -9,22 +9,27 @@ 'use strict'; | ||
// Stub Engine | ||
// ----------- | ||
function EchoEngine(config) { | ||
function StubEngine(config) { | ||
this.config = config || {}; | ||
}; | ||
} | ||
// run the generator | ||
// @return instance of stream.Readable | ||
EchoEngine.prototype.run = function run(request, options) { | ||
StubEngine.prototype.run = function run(request, options) { | ||
var s = new stream.PassThrough(); | ||
s.metadata = { | ||
type: 'application/json', | ||
extension: 'json' | ||
}; | ||
// DIRTY: this timeout exists for testing purposes | ||
setTimeout(function(){ | ||
// DIRTY: this timeout exists for testing purposes, which | ||
// really isn't a good idea, but works in a pinch | ||
setTimeout(function() { | ||
s.emit('metadata', { | ||
contentType: 'application/json' | ||
}); | ||
}, 10); | ||
setTimeout(function() { | ||
s.end(JSON.stringify(request)); | ||
}, 50); | ||
return s; | ||
@@ -35,27 +40,72 @@ }; | ||
// Stub Store | ||
// ---------- | ||
function StubStore(config) { | ||
this.config = config || {}; | ||
this.data = {}; | ||
} | ||
// fetch a file from cache | ||
// @return instance of stream.Readable | ||
StubStore.prototype.fetch = function fetch(hash, timestamp) { | ||
var self = this; | ||
var key = [hash, timestamp].join(':'); | ||
var s = new stream.PassThrough(); | ||
setTimeout(function() { | ||
s.emit('metadata', self.data[key].metadata ); | ||
s.emit('data', self.data[key].data ); | ||
s.emit('end'); | ||
}, 10); | ||
return s; | ||
}; | ||
// save a file to cache | ||
// @return instance of stream.Writable | ||
StubStore.prototype.save = function save(hash, timestamp, metadata, ttl) { | ||
var self = this; | ||
var key = [hash, timestamp].join(':'); | ||
self.data[key] = {metadata: {}, data: ''}; | ||
var s = new stream.Writable(); | ||
s._write = function(chunk, encoding, callback) { | ||
self.data[key].data += chunk; | ||
callback(); | ||
}; | ||
return s; | ||
}; | ||
var config = require('../config.test.js'); | ||
var Stillframe = require('../lib/index.js'); | ||
var redis = new (require('ioredis'))(config.redis); | ||
var redis = new(require('ioredis'))(config.redis); | ||
var assert = require('chai').assert; | ||
var FileStore = require('stillframe-store-file'); | ||
var tmp = __dirname + '/../tmp'; | ||
describe('Stillframe', function(){ | ||
describe('Stillframe', function() { | ||
before(clean); | ||
describe('-> resolve', function(){ | ||
describe('-> resolve', function() { | ||
var timestamp, stillframe; | ||
before(function(){ | ||
before(function() { | ||
timestamp = Date.now(); | ||
stillframe = new Stillframe(config, new FileStore({path: tmp}), {echo: new EchoEngine()}); | ||
stillframe = new Stillframe(config, new StubStore({ | ||
path: tmp | ||
}), { | ||
stub: new StubEngine() | ||
}); | ||
}); | ||
describe('new entry', function(){ | ||
it('should return null', function(done){ | ||
stillframe.resolve('echo', '804e772c9acf31c5d9321c491d0e1628ea04d985', timestamp, function(err, res){ | ||
describe('new entry', function() { | ||
it('should return null', function(done) { | ||
stillframe.resolve('804e772c9acf31c5d9321c491d0e1628ea04d985', timestamp, function(err, res) { | ||
assert.isNull(err); | ||
@@ -67,4 +117,4 @@ assert.isNull(res); | ||
it('should create a `complete` entry', function(done){ | ||
redis.hget([config.prefix, 'echo', '804e772c9acf31c5d9321c491d0e1628ea04d985'].join(':'), 'complete', function(err, res){ | ||
it('should create a `complete` entry', function(done) { | ||
redis.hget([config.prefix, '804e772c9acf31c5d9321c491d0e1628ea04d985'].join(':'), 'complete', function(err, res) { | ||
assert.isNull(err); | ||
@@ -76,4 +126,4 @@ assert.equal(res, timestamp); | ||
it('should not create a `pending` entry', function(done){ | ||
redis.hget([config.prefix, 'echo', '804e772c9acf31c5d9321c491d0e1628ea04d985'].join(':'), 'pending', function(err, res){ | ||
it('should not create a `pending` entry', function(done) { | ||
redis.hget([config.prefix, '804e772c9acf31c5d9321c491d0e1628ea04d985'].join(':'), 'pending', function(err, res) { | ||
assert.isNull(err); | ||
@@ -85,6 +135,16 @@ assert.isNull(res); | ||
it('should set an expiration for the entry', function(done){ | ||
getExpiration([config.prefix, 'echo', '804e772c9acf31c5d9321c491d0e1628ea04d985'].join(':'), function(err, res){ | ||
it('should set an expiration for the entry', function(done) { | ||
// DIRTY: make Date.now deterministic; this might be better solved by wrapping the test in a with statement | ||
var now = Date.now; | ||
Date.now = function() { | ||
return timestamp; | ||
}; | ||
getExpiration([config.prefix, '804e772c9acf31c5d9321c491d0e1628ea04d985'].join(':'), function(err, res) { | ||
assert.isNull(err); | ||
assert.equal(res, timestamp + config.ttl); | ||
// restore Date.now | ||
Date.now = now; | ||
done(err); | ||
@@ -95,13 +155,13 @@ }); | ||
describe('existing entry', function(){ | ||
before(function(done){ | ||
redis.hset([config.prefix, 'echo', '95578ef733b8c68839469854a0f05e4e683c0a16'].join(':'), 'complete', timestamp, done); | ||
describe('existing entry', function() { | ||
before(function(done) { | ||
redis.hset([config.prefix, '95578ef733b8c68839469854a0f05e4e683c0a16'].join(':'), 'complete', timestamp, done); | ||
}); | ||
before(function(done){ | ||
redis.hset([config.prefix, 'echo', '95578ef733b8c68839469854a0f05e4e683c0a16'].join(':'), 'pending', timestamp, done); | ||
before(function(done) { | ||
redis.hset([config.prefix, '95578ef733b8c68839469854a0f05e4e683c0a16'].join(':'), 'pending', timestamp, done); | ||
}); | ||
it('should return null for an existing key', function(done){ | ||
stillframe.resolve('echo', '95578ef733b8c68839469854a0f05e4e683c0a16', timestamp + 1000, function(err, res){ | ||
it('should return null for an existing key', function(done) { | ||
stillframe.resolve('95578ef733b8c68839469854a0f05e4e683c0a16', timestamp + 1000, function(err, res) { | ||
assert.isNull(err); | ||
@@ -113,4 +173,4 @@ assert.isNull(res); | ||
it('should update the `complete` entry', function(done){ | ||
redis.hget([config.prefix, 'echo', '95578ef733b8c68839469854a0f05e4e683c0a16'].join(':'), 'complete', function(err, res){ | ||
it('should update the `complete` entry', function(done) { | ||
redis.hget([config.prefix, '95578ef733b8c68839469854a0f05e4e683c0a16'].join(':'), 'complete', function(err, res) { | ||
assert.isNull(err); | ||
@@ -122,4 +182,4 @@ assert.equal(res, timestamp + 1000); | ||
it('should not update a `pending` entry', function(done){ | ||
redis.hget([config.prefix, 'echo', '95578ef733b8c68839469854a0f05e4e683c0a16'].join(':'), 'pending', function(err, res){ | ||
it('should not update a `pending` entry', function(done) { | ||
redis.hget([config.prefix, '95578ef733b8c68839469854a0f05e4e683c0a16'].join(':'), 'pending', function(err, res) { | ||
assert.isNull(err); | ||
@@ -131,4 +191,4 @@ assert.equal(res, timestamp); | ||
it('should update the expiration for the entry', function(done){ | ||
getExpiration([config.prefix, 'echo', '95578ef733b8c68839469854a0f05e4e683c0a16'].join(':'), function(err, res){ | ||
it('should update the expiration for the entry', function(done) { | ||
getExpiration([config.prefix, '95578ef733b8c68839469854a0f05e4e683c0a16'].join(':'), function(err, res) { | ||
assert.isNull(err); | ||
@@ -144,14 +204,18 @@ assert.equal(res, timestamp + 1000 + config.ttl); | ||
describe('-> lookup', function(){ | ||
describe('-> lookup', function() { | ||
var timestamp, ttl, stillframe; | ||
before(function(){ | ||
before(function() { | ||
timestamp = Date.now(); | ||
ttl = 1000*60*60; | ||
stillframe = new Stillframe(config, new FileStore({path: tmp}), {echo: new EchoEngine()}); | ||
ttl = 1000 * 60 * 60; | ||
stillframe = new Stillframe(config, new StubStore({ | ||
path: tmp | ||
}), { | ||
stub: new StubEngine() | ||
}); | ||
}); | ||
describe('new entry', function(){ | ||
it('should return null', function(done){ | ||
stillframe.lookup('echo', 'b6589fc6ab0dc82cf12099d1c2d40ab994e8410c', timestamp, ttl, function(err, res){ | ||
describe('new entry', function() { | ||
it('should return null', function(done) { | ||
stillframe.lookup('b6589fc6ab0dc82cf12099d1c2d40ab994e8410c', timestamp, ttl, function(err, res) { | ||
assert.isNull(err); | ||
@@ -163,4 +227,4 @@ assert.isNull(res); | ||
it('should create a `pending` entry', function(done){ | ||
redis.hget([config.prefix, 'echo', 'b6589fc6ab0dc82cf12099d1c2d40ab994e8410c'].join(':'), 'pending', function(err, res){ | ||
it('should create a `pending` entry', function(done) { | ||
redis.hget([config.prefix, 'b6589fc6ab0dc82cf12099d1c2d40ab994e8410c'].join(':'), 'pending', function(err, res) { | ||
assert.isNull(err); | ||
@@ -172,4 +236,4 @@ assert.equal(res, timestamp); | ||
it('should not create a `complete` entry', function(done){ | ||
redis.hget([config.prefix, 'echo', 'b6589fc6ab0dc82cf12099d1c2d40ab994e8410c'].join(':'), 'complete', function(err, res){ | ||
it('should not create a `complete` entry', function(done) { | ||
redis.hget([config.prefix, 'b6589fc6ab0dc82cf12099d1c2d40ab994e8410c'].join(':'), 'complete', function(err, res) { | ||
assert.isNull(err); | ||
@@ -181,4 +245,4 @@ assert.isNull(res); | ||
it('should set an expiration for the entry', function(done){ | ||
getExpiration([config.prefix, 'echo', 'b6589fc6ab0dc82cf12099d1c2d40ab994e8410c'].join(':'), function(err, res){ | ||
it('should set an expiration for the entry', function(done) { | ||
getExpiration([config.prefix, 'b6589fc6ab0dc82cf12099d1c2d40ab994e8410c'].join(':'), function(err, res) { | ||
assert.isNull(err); | ||
@@ -191,9 +255,9 @@ assert.equal(res, timestamp + config.ttl); | ||
describe('pending entry - inside timeout', function(){ | ||
before(function(done){ | ||
redis.hset([config.prefix, 'echo', '527d3f1d205d23678d303a0c52c98ec6e08b4e40'].join(':'), 'pending', timestamp, done); | ||
describe('pending entry - inside timeout', function() { | ||
before(function(done) { | ||
redis.hset([config.prefix, '527d3f1d205d23678d303a0c52c98ec6e08b4e40'].join(':'), 'pending', timestamp, done); | ||
}); | ||
it('should return the pending entry', function(done){ | ||
stillframe.lookup('echo', '527d3f1d205d23678d303a0c52c98ec6e08b4e40', timestamp + 1, ttl, function(err, res){ | ||
it('should return the pending entry', function(done) { | ||
stillframe.lookup('527d3f1d205d23678d303a0c52c98ec6e08b4e40', timestamp + 1, ttl, function(err, res) { | ||
assert.isNull(err); | ||
@@ -206,4 +270,4 @@ assert.isString(res); | ||
it('should not update the `pending` entry', function(done){ | ||
redis.hget([config.prefix, 'echo', '527d3f1d205d23678d303a0c52c98ec6e08b4e40'].join(':'), 'pending', function(err, res){ | ||
it('should not update the `pending` entry', function(done) { | ||
redis.hget([config.prefix, '527d3f1d205d23678d303a0c52c98ec6e08b4e40'].join(':'), 'pending', function(err, res) { | ||
assert.isNull(err); | ||
@@ -215,4 +279,4 @@ assert.equal(res, timestamp); | ||
it('should not update the `complete` entry', function(done){ | ||
redis.hget([config.prefix, 'echo', '527d3f1d205d23678d303a0c52c98ec6e08b4e40'].join(':'), 'complete', function(err, res){ | ||
it('should not update the `complete` entry', function(done) { | ||
redis.hget([config.prefix, '527d3f1d205d23678d303a0c52c98ec6e08b4e40'].join(':'), 'complete', function(err, res) { | ||
assert.isNull(err); | ||
@@ -224,4 +288,4 @@ assert.isNull(res); | ||
it('should not update the entry expiration', function(done){ | ||
getExpiration([config.prefix, 'echo', '527d3f1d205d23678d303a0c52c98ec6e08b4e40'].join(':'), function(err, res){ | ||
it('should not update the entry expiration', function(done) { | ||
getExpiration([config.prefix, '527d3f1d205d23678d303a0c52c98ec6e08b4e40'].join(':'), function(err, res) { | ||
assert.isNull(err); | ||
@@ -234,9 +298,9 @@ assert.isNull(res); | ||
describe('pending entry - past timeout', function(){ | ||
before(function(done){ | ||
redis.hset([config.prefix, 'echo', '76512cc7766948303ecbf3f9f498cbfe49a4f15e'].join(':'), 'pending', timestamp, done); | ||
describe('pending entry - past timeout', function() { | ||
before(function(done) { | ||
redis.hset([config.prefix, '76512cc7766948303ecbf3f9f498cbfe49a4f15e'].join(':'), 'pending', timestamp, done); | ||
}); | ||
it('should return null', function(done){ | ||
stillframe.lookup('echo', '76512cc7766948303ecbf3f9f498cbfe49a4f15e', timestamp + config.timeout + 1, ttl, function(err, res){ | ||
it('should return null', function(done) { | ||
stillframe.lookup('76512cc7766948303ecbf3f9f498cbfe49a4f15e', timestamp + config.timeout + 1, ttl, function(err, res) { | ||
assert.isNull(err); | ||
@@ -248,4 +312,4 @@ assert.isNull(res); | ||
it('should update the `pending` entry', function(done){ | ||
redis.hget([config.prefix, 'echo', '76512cc7766948303ecbf3f9f498cbfe49a4f15e'].join(':'), 'pending', function(err, res){ | ||
it('should update the `pending` entry', function(done) { | ||
redis.hget([config.prefix, '76512cc7766948303ecbf3f9f498cbfe49a4f15e'].join(':'), 'pending', function(err, res) { | ||
assert.isNull(err); | ||
@@ -257,4 +321,4 @@ assert.equal(res, timestamp + config.timeout + 1); | ||
it('should not update the `complete` entry', function(done){ | ||
redis.hget([config.prefix, 'echo', '76512cc7766948303ecbf3f9f498cbfe49a4f15e'].join(':'), 'complete', function(err, res){ | ||
it('should not update the `complete` entry', function(done) { | ||
redis.hget([config.prefix, '76512cc7766948303ecbf3f9f498cbfe49a4f15e'].join(':'), 'complete', function(err, res) { | ||
assert.isNull(err); | ||
@@ -266,4 +330,4 @@ assert.isNull(res); | ||
it('should update the entry expiration', function(done){ | ||
getExpiration([config.prefix, 'echo', '76512cc7766948303ecbf3f9f498cbfe49a4f15e'].join(':'), function(err, res){ | ||
it('should update the entry expiration', function(done) { | ||
getExpiration([config.prefix, '76512cc7766948303ecbf3f9f498cbfe49a4f15e'].join(':'), function(err, res) { | ||
assert.isNull(err); | ||
@@ -276,13 +340,13 @@ assert.equal(res, timestamp + config.timeout + 1 + config.ttl); | ||
describe('complete entry - inside ttl', function(){ | ||
before(function(done){ | ||
redis.hset([config.prefix, 'echo', '8c7925c217ce8a109c90e707085d2a7cb1f4b9b8'].join(':'), 'complete', timestamp, done); | ||
describe('complete entry - inside ttl', function() { | ||
before(function(done) { | ||
redis.hset([config.prefix, '8c7925c217ce8a109c90e707085d2a7cb1f4b9b8'].join(':'), 'complete', timestamp, done); | ||
}); | ||
before(function(done){ | ||
redis.hset([config.prefix, 'echo', '8c7925c217ce8a109c90e707085d2a7cb1f4b9b8'].join(':'), 'pending', timestamp, done); | ||
before(function(done) { | ||
redis.hset([config.prefix, '8c7925c217ce8a109c90e707085d2a7cb1f4b9b8'].join(':'), 'pending', timestamp, done); | ||
}); | ||
it('should return the complete entry', function(done){ | ||
stillframe.lookup('echo', '8c7925c217ce8a109c90e707085d2a7cb1f4b9b8', timestamp + 1, ttl, function(err, res){ | ||
it('should return the complete entry', function(done) { | ||
stillframe.lookup('8c7925c217ce8a109c90e707085d2a7cb1f4b9b8', timestamp + 1, ttl, function(err, res) { | ||
assert.isNull(err); | ||
@@ -295,4 +359,4 @@ assert.isString(res); | ||
it('should not update the `pending` entry', function(done){ | ||
redis.hget([config.prefix, 'echo', '8c7925c217ce8a109c90e707085d2a7cb1f4b9b8'].join(':'), 'pending', function(err, res){ | ||
it('should not update the `pending` entry', function(done) { | ||
redis.hget([config.prefix, '8c7925c217ce8a109c90e707085d2a7cb1f4b9b8'].join(':'), 'pending', function(err, res) { | ||
assert.isNull(err); | ||
@@ -304,4 +368,4 @@ assert.equal(res, timestamp); | ||
it('should not update the `complete` entry', function(done){ | ||
redis.hget([config.prefix, 'echo', '8c7925c217ce8a109c90e707085d2a7cb1f4b9b8'].join(':'), 'complete', function(err, res){ | ||
it('should not update the `complete` entry', function(done) { | ||
redis.hget([config.prefix, '8c7925c217ce8a109c90e707085d2a7cb1f4b9b8'].join(':'), 'complete', function(err, res) { | ||
assert.isNull(err); | ||
@@ -313,4 +377,4 @@ assert.equal(res, timestamp); | ||
it('should not update the entry expiration', function(done){ | ||
getExpiration([config.prefix, 'echo', '8c7925c217ce8a109c90e707085d2a7cb1f4b9b8'].join(':'), function(err, res){ | ||
it('should not update the entry expiration', function(done) { | ||
getExpiration([config.prefix, '8c7925c217ce8a109c90e707085d2a7cb1f4b9b8'].join(':'), function(err, res) { | ||
assert.isNull(err); | ||
@@ -323,13 +387,13 @@ assert.isNull(res); | ||
describe('complete entry - past ttl', function(){ | ||
before(function(done){ | ||
redis.hset([config.prefix, 'echo', '24440aa30f1dc8a7adbc57c35c648e6616179859'].join(':'), 'complete', timestamp, done); | ||
describe('complete entry - past ttl', function() { | ||
before(function(done) { | ||
redis.hset([config.prefix, '24440aa30f1dc8a7adbc57c35c648e6616179859'].join(':'), 'complete', timestamp, done); | ||
}); | ||
before(function(done){ | ||
redis.hset([config.prefix, 'echo', '24440aa30f1dc8a7adbc57c35c648e6616179859'].join(':'), 'pending', timestamp + ttl, done); | ||
before(function(done) { | ||
redis.hset([config.prefix, '24440aa30f1dc8a7adbc57c35c648e6616179859'].join(':'), 'pending', timestamp + ttl, done); | ||
}); | ||
it('should return `pending` entry', function(done){ | ||
stillframe.lookup('echo', '24440aa30f1dc8a7adbc57c35c648e6616179859', timestamp + ttl + 1, ttl, function(err, res){ | ||
it('should return `pending` entry', function(done) { | ||
stillframe.lookup('24440aa30f1dc8a7adbc57c35c648e6616179859', timestamp + ttl + 1, ttl, function(err, res) { | ||
assert.isNull(err); | ||
@@ -342,4 +406,4 @@ assert.isString(res); | ||
it('should not update the `pending` entry', function(done){ | ||
redis.hget([config.prefix, 'echo', '24440aa30f1dc8a7adbc57c35c648e6616179859'].join(':'), 'pending', function(err, res){ | ||
it('should not update the `pending` entry', function(done) { | ||
redis.hget([config.prefix, '24440aa30f1dc8a7adbc57c35c648e6616179859'].join(':'), 'pending', function(err, res) { | ||
assert.isNull(err); | ||
@@ -351,4 +415,4 @@ assert.equal(res, timestamp + ttl); | ||
it('should not update the `complete` entry', function(done){ | ||
redis.hget([config.prefix, 'echo', '24440aa30f1dc8a7adbc57c35c648e6616179859'].join(':'), 'complete', function(err, res){ | ||
it('should not update the `complete` entry', function(done) { | ||
redis.hget([config.prefix, '24440aa30f1dc8a7adbc57c35c648e6616179859'].join(':'), 'complete', function(err, res) { | ||
assert.isNull(err); | ||
@@ -360,4 +424,4 @@ assert.equal(res, timestamp); | ||
it('should not update the entry expiration', function(done){ | ||
getExpiration([config.prefix, 'echo', '24440aa30f1dc8a7adbc57c35c648e6616179859'].join(':'), function(err, res){ | ||
it('should not update the entry expiration', function(done) { | ||
getExpiration([config.prefix, '24440aa30f1dc8a7adbc57c35c648e6616179859'].join(':'), function(err, res) { | ||
assert.isNull(err); | ||
@@ -370,13 +434,13 @@ assert.isNull(res); | ||
describe('past complete ttl and pending timeout', function(){ | ||
before(function(done){ | ||
redis.hset([config.prefix, 'echo', 'b1245eaa50fe4ef9ec31a993a2e9b4dacfa27ab1'].join(':'), 'complete', timestamp, done); | ||
describe('past complete ttl and pending timeout', function() { | ||
before(function(done) { | ||
redis.hset([config.prefix, 'b1245eaa50fe4ef9ec31a993a2e9b4dacfa27ab1'].join(':'), 'complete', timestamp, done); | ||
}); | ||
before(function(done){ | ||
redis.hset([config.prefix, 'echo', 'b1245eaa50fe4ef9ec31a993a2e9b4dacfa27ab1'].join(':'), 'pending', timestamp, done); | ||
before(function(done) { | ||
redis.hset([config.prefix, 'b1245eaa50fe4ef9ec31a993a2e9b4dacfa27ab1'].join(':'), 'pending', timestamp, done); | ||
}); | ||
it('should return null', function(done){ | ||
stillframe.lookup('echo', 'b1245eaa50fe4ef9ec31a993a2e9b4dacfa27ab1', timestamp + ttl + 1, ttl, function(err, res){ | ||
it('should return null', function(done) { | ||
stillframe.lookup('b1245eaa50fe4ef9ec31a993a2e9b4dacfa27ab1', timestamp + ttl + 1, ttl, function(err, res) { | ||
assert.isNull(err); | ||
@@ -388,4 +452,4 @@ assert.isNull(res); | ||
it('should update the `pending` entry', function(done){ | ||
redis.hget([config.prefix, 'echo', 'b1245eaa50fe4ef9ec31a993a2e9b4dacfa27ab1'].join(':'), 'pending', function(err, res){ | ||
it('should update the `pending` entry', function(done) { | ||
redis.hget([config.prefix, 'b1245eaa50fe4ef9ec31a993a2e9b4dacfa27ab1'].join(':'), 'pending', function(err, res) { | ||
assert.isNull(err); | ||
@@ -397,4 +461,4 @@ assert.equal(res, timestamp + ttl + 1); | ||
it('should not update the `complete` entry', function(done){ | ||
redis.hget([config.prefix, 'echo', 'b1245eaa50fe4ef9ec31a993a2e9b4dacfa27ab1'].join(':'), 'complete', function(err, res){ | ||
it('should not update the `complete` entry', function(done) { | ||
redis.hget([config.prefix, 'b1245eaa50fe4ef9ec31a993a2e9b4dacfa27ab1'].join(':'), 'complete', function(err, res) { | ||
assert.isNull(err); | ||
@@ -406,4 +470,4 @@ assert.equal(res, timestamp); | ||
it('should update the entry expiration', function(done){ | ||
getExpiration([config.prefix, 'echo', 'b1245eaa50fe4ef9ec31a993a2e9b4dacfa27ab1'].join(':'), function(err, res){ | ||
it('should update the entry expiration', function(done) { | ||
getExpiration([config.prefix, 'b1245eaa50fe4ef9ec31a993a2e9b4dacfa27ab1'].join(':'), function(err, res) { | ||
assert.isNull(err); | ||
@@ -420,14 +484,20 @@ assert.equal(res, timestamp + ttl + 1 + config.ttl); | ||
describe('-> take', function(){ | ||
describe('-> take', function() { | ||
var timestamp, ttl, stillframe; | ||
before(function(){ | ||
before(function() { | ||
timestamp = Date.now(); | ||
ttl = 1000*60*60; | ||
stillframe = new Stillframe(config, new FileStore({path: tmp}), {echo: new EchoEngine()}); | ||
ttl = 1000 * 60 * 60; | ||
stillframe = new Stillframe(config, new StubStore({ | ||
path: tmp | ||
}), { | ||
stub: new StubEngine() | ||
}); | ||
}); | ||
describe('(callback)', function(done){ | ||
it('returns an error for a nonexistant engine', function(done){ | ||
stillframe.take('nonexistant', {url: 'http://www.example.com/callback'}, {}, ttl, function(err, snapshot){ | ||
describe('(callback)', function(done) { | ||
it('returns an error for a nonexistant engine', function(done) { | ||
stillframe.take('nonexistant', { | ||
url: 'http://www.example.com/callback' | ||
}, {}, ttl, function(err, snapshot) { | ||
assert.instanceOf(err, Error); | ||
@@ -438,10 +508,17 @@ done(); | ||
it('should return a new pending snapshot', function(done){ | ||
it('should return a new pending snapshot', function(done) { | ||
// DIRTY: make Date.now deterministic; this might be better solved by wrapping the test in a with statement | ||
var now = Date.now; | ||
Date.now = function(){ return timestamp; }; | ||
Date.now = function() { | ||
return timestamp; | ||
}; | ||
stillframe.take('echo', {url: 'http://www.example.com/callback'}, {}, ttl, function(err, snapshot){ | ||
stillframe.take('stub', { | ||
url: 'http://www.example.com/callback' | ||
}, {}, ttl, function(err, snapshot) { | ||
assert.isNull(err); | ||
assert.deepEqual(snapshot, {status: 'pending', created: timestamp}); | ||
assert.deepEqual(snapshot, { | ||
status: 'pending', | ||
created: timestamp | ||
}); | ||
@@ -455,7 +532,12 @@ // restore Date.now | ||
it('should return an existing pending snapshot', function(done){ | ||
// DIRTY: the echo engine has a 5ms timeout | ||
stillframe.take('echo', {url: 'http://www.example.com/callback'}, {}, ttl, function(err, snapshot){ | ||
it('should return an existing pending snapshot', function(done) { | ||
// DIRTY: the stub engine has a 50ms timeout | ||
stillframe.take('stub', { | ||
url: 'http://www.example.com/callback' | ||
}, {}, ttl, function(err, snapshot) { | ||
assert.isNull(err); | ||
assert.deepEqual(snapshot, {status: 'pending', created: timestamp}); | ||
assert.deepEqual(snapshot, { | ||
status: 'pending', | ||
created: timestamp | ||
}); | ||
done(); | ||
@@ -465,8 +547,13 @@ }); | ||
it('should return an complete snapshot', function(done){ | ||
// DIRTY: the echo engine has a 5ms timeout | ||
setTimeout(function(){ | ||
stillframe.take('echo', {url: 'http://www.example.com/callback'}, {}, ttl, function(err, snapshot){ | ||
it('should return an complete snapshot', function(done) { | ||
// DIRTY: the stub engine has a 50ms timeout | ||
setTimeout(function() { | ||
stillframe.take('stub', { | ||
url: 'http://www.example.com/callback' | ||
}, {}, ttl, function(err, snapshot) { | ||
assert.isNull(err); | ||
assert.deepEqual(snapshot, {status: 'complete', created: timestamp}); | ||
assert.deepEqual(snapshot, { | ||
status: 'complete', | ||
created: timestamp | ||
}); | ||
done(); | ||
@@ -479,60 +566,80 @@ }); | ||
describe('(stream)', function(done){ | ||
it('emits an error for a nonexistant engine', function(done){ | ||
stillframe.take('nonexistant', {url: 'http://www.example.com/stream'}, {}, ttl) | ||
.on('error', function(err){ | ||
assert.instanceOf(err, Error); | ||
done(); | ||
}); | ||
describe('(stream)', function(done) { | ||
it('emits an error for a nonexistant engine', function(done) { | ||
stillframe.take('nonexistant', { | ||
url: 'http://www.example.com/stream' | ||
}, {}, ttl) | ||
.on('error', function(err) { | ||
assert.instanceOf(err, Error); | ||
done(); | ||
}); | ||
}); | ||
it('should return the final result of a new pending snapshot', function(done){ | ||
it('should return the final result of a new pending snapshot', function(done) { | ||
// DIRTY: make Date.now deterministic; this might be better solved by wrapping the test in a with statement | ||
var now = Date.now; | ||
Date.now = function(){ return timestamp; }; | ||
Date.now = function() { | ||
return timestamp; | ||
}; | ||
var data = ''; | ||
stillframe.take('echo', {url: 'http://www.example.com/stream'}, {}, ttl) | ||
.on('error', done) | ||
.on('data', function(chunk){ data += chunk.toString(); }) | ||
.on('finish', function(){ | ||
assert.equal(data, '{"url":"http://www.example.com/stream"}'); | ||
stillframe.take('stub', { | ||
url: 'http://www.example.com/stream' | ||
}, {}, ttl) | ||
.on('error', done) | ||
.on('data', function(chunk) { | ||
data += chunk.toString(); | ||
}) | ||
.on('finish', function() { | ||
assert.equal(data, '{"url":"http://www.example.com/stream"}'); | ||
// restore Date.now | ||
Date.now = now; | ||
// restore Date.now | ||
Date.now = now; | ||
done(); | ||
}); | ||
done(); | ||
}); | ||
}); | ||
it('should return the final result of an existing pending snapshot', function(done){ | ||
it('should return the final result of an existing pending snapshot', function(done) { | ||
// DIRTY: make Date.now deterministic; this might be better solved by wrapping the test in a with statement | ||
var now = Date.now; | ||
Date.now = function(){ return timestamp; }; | ||
Date.now = function() { | ||
return timestamp; | ||
}; | ||
var data = ''; | ||
stillframe.take('echo', {url: 'http://www.example.com/stream2'}, {}, ttl, function(err, snapshot){ | ||
stillframe.take('echo', {url: 'http://www.example.com/stream2'}, ttl) | ||
.on('error', done) | ||
.on('data', function(chunk){ data += chunk.toString(); }) | ||
.on('finish', function(){ | ||
assert.equal(data, '{"url":"http://www.example.com/stream2"}'); | ||
stillframe.take('stub', { | ||
url: 'http://www.example.com/stream2' | ||
}, {}, ttl, function(err, snapshot) { | ||
stillframe.take('stub', { | ||
url: 'http://www.example.com/stream2' | ||
}, {}, ttl) | ||
.on('error', done) | ||
.on('data', function(chunk) { | ||
data += chunk.toString(); | ||
}) | ||
.on('finish', function() { | ||
assert.equal(data, '{"url":"http://www.example.com/stream2"}'); | ||
// restore Date.now | ||
Date.now = now; | ||
// restore Date.now | ||
Date.now = now; | ||
done(); | ||
}); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('should return the final result of a complete snapshot', function(done){ | ||
it('should return the final result of a complete snapshot', function(done) { | ||
var data = ''; | ||
stillframe.take('echo', {url: 'http://www.example.com/stream'}, {}, ttl) | ||
.on('error', done) | ||
.on('data', function(chunk){ data += chunk.toString(); }) | ||
.on('finish', function(){ | ||
assert.equal(data, '{"url":"http://www.example.com/stream"}'); | ||
done(); | ||
}); | ||
stillframe.take('stub', { | ||
url: 'http://www.example.com/stream' | ||
}, {}, ttl) | ||
.on('error', done) | ||
.on('data', function(chunk) { | ||
data += chunk.toString(); | ||
}) | ||
.on('finish', function() { | ||
assert.equal(data, '{"url":"http://www.example.com/stream"}'); | ||
done(); | ||
}); | ||
}); | ||
@@ -546,6 +653,6 @@ }); | ||
function deleteFolderRecursive(path) { | ||
if( fs.existsSync(path) ) { | ||
fs.readdirSync(path).forEach(function(file,index){ | ||
if (fs.existsSync(path)) { | ||
fs.readdirSync(path).forEach(function(file, index) { | ||
var curPath = path + '/' + file; | ||
if(fs.lstatSync(curPath).isDirectory()) { // recurse | ||
if (fs.lstatSync(curPath).isDirectory()) { // recurse | ||
deleteFolderRecursive(curPath); | ||
@@ -565,5 +672,5 @@ } else { // delete file | ||
function clean(callback){ | ||
function clean(callback) { | ||
deleteFolderRecursive(tmp); | ||
return redis.eval('local keys = redis.call(\'keys\', ARGV[1]) \n for i=1,#keys,5000 do \n redis.call(\'del\', unpack(keys, i, math.min(i+4999, #keys))) \n end \n return keys', 0, config.prefix, callback); | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
26497
4
684