first commit

This commit is contained in:
Stefan Hacker
2026-04-03 09:38:48 +02:00
commit 37ad745546
47450 changed files with 3120798 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
'use strict';
var tap = require('tap')
, test = tap.test
, createNamespace = require('../context.js').createNamespace
;
test("asynchronously propagating state with local-context-domains", function (t) {
t.plan(2);
var namespace = createNamespace('namespace');
t.ok(process.namespaces.namespace, "namespace has been created");
namespace.run(function () {
namespace.set('test', 1337);
t.equal(namespace.get('test'), 1337, "namespace is working");
});
});
@@ -0,0 +1,23 @@
var test = require('tap').test
, cls = require('../context.js')
;
test("minimized test case that caused #6011 patch to fail", function (t) {
t.plan(3);
console.log('+');
// when the flaw was in the patch, commenting out this line would fix things:
process.nextTick(function () { console.log('!'); });
var n = cls.createNamespace("test");
t.ok(!n.get('state'), "state should not yet be visible");
n.run(function () {
n.set('state', true);
t.ok(n.get('state'), "state should be visible");
process.nextTick(function () {
t.ok(n.get('state'), "state should be visible");
});
});
});
+378
View File
@@ -0,0 +1,378 @@
'use strict';
var test = require('tap').test
, EventEmitter = require('events').EventEmitter
, cls = require('../context.js')
;
test("event emitters bound to CLS context", function (t) {
t.plan(13);
t.test("handler registered in context, emit out of context", function (t) {
t.plan(1);
var n = cls.createNamespace('in')
, ee = new EventEmitter()
;
n.run(function () {
n.set('value', 'hello');
n.bindEmitter(ee);
ee.on('event', function () {
t.equal(n.get('value'), 'hello', "value still set in EE.");
cls.destroyNamespace('in');
});
});
ee.emit('event');
});
t.test("once handler registered in context", function (t) {
t.plan(1);
var n = cls.createNamespace('inOnce')
, ee = new EventEmitter()
;
n.run(function () {
n.set('value', 'hello');
n.bindEmitter(ee);
ee.once('event', function () {
t.equal(n.get('value'), 'hello', "value still set in EE.");
cls.destroyNamespace('inOnce');
});
});
ee.emit('event');
});
t.test("handler registered out of context, emit in context", function (t) {
t.plan(1);
var n = cls.createNamespace('out')
, ee = new EventEmitter()
;
ee.on('event', function () {
t.equal(n.get('value'), 'hello', "value still set in EE.");
cls.destroyNamespace('out');
});
n.run(function () {
n.set('value', 'hello');
n.bindEmitter(ee);
ee.emit('event');
});
});
t.test("once handler registered out of context", function (t) {
t.plan(1);
var n = cls.createNamespace('outOnce')
, ee = new EventEmitter()
;
ee.once('event', function () {
t.equal(n.get('value'), 'hello', "value still set in EE.");
cls.destroyNamespace('outOnce');
});
n.run(function () {
n.set('value', 'hello');
n.bindEmitter(ee);
ee.emit('event');
});
});
t.test("handler registered out of context, emit out of context", function (t) {
t.plan(1);
var n = cls.createNamespace('out')
, ee = new EventEmitter()
;
ee.on('event', function () {
t.equal(n.get('value'), undefined, "no context.");
cls.destroyNamespace('out');
});
n.run(function () {
n.set('value', 'hello');
n.bindEmitter(ee);
});
ee.emit('event');
});
t.test("once handler registered out of context on Readable", function (t) {
var Readable = require('stream').Readable;
if (Readable) {
t.plan(12);
var n = cls.createNamespace('outOnceReadable')
, re = new Readable()
;
re._read = function () {};
t.ok(n.name, "namespace has a name");
t.equal(n.name, 'outOnceReadable', "namespace has a name");
re.once('data', function (data) {
t.equal(n.get('value'), 'hello', "value still set in EE");
t.equal(data, 'blah', "emit still works");
cls.destroyNamespace('outOnceReadable');
});
n.run(function () {
n.set('value', 'hello');
t.notOk(re.emit.__wrapped, "emit is not wrapped");
t.notOk(re.on.__wrapped, "on is not wrapped");
t.notOk(re.addListener.__wrapped, "addListener is not wrapped");
n.bindEmitter(re);
t.ok(re.emit.__wrapped, "emit is wrapped");
t.ok(re.on.__wrapped, "on is wrapped");
t.ok(re.addListener.__wrapped, "addListener is wrapped");
t.equal(typeof re._events.data, 'function', 'only the one data listener');
t.notOk(re._events.data['context@outOnceReadable'], "context isn't on listener");
re.emit('data', 'blah');
});
}
else {
t.comment("this test requires node 0.10+");
t.end();
}
});
t.test("emitter with newListener that removes handler", function (t) {
t.plan(3);
var n = cls.createNamespace('newListener')
, ee = new EventEmitter()
;
// add monkeypatching to ee
n.bindEmitter(ee);
function listen() {
ee.on('data', function (chunk) {
t.equal(chunk, 'chunk', 'listener still works');
});
}
ee.on('newListener', function handler(event) {
if (event !== 'data') return;
this.removeListener('newListener', handler);
t.notOk(this.listeners('newListener').length, 'newListener was removed');
process.nextTick(listen);
});
ee.on('drain', function (chunk) {
process.nextTick(function () {
ee.emit('data', chunk);
});
});
ee.on('data', function (chunk) {
t.equal(chunk, 'chunk', 'got data event');
cls.destroyNamespace('newListener');
});
ee.emit('drain', 'chunk');
});
t.test("handler registered in context on Readable", function (t) {
var Readable = require('stream').Readable;
if (Readable) {
t.plan(12);
var n = cls.createNamespace('outOnReadable')
, re = new Readable()
;
re._read = function () {};
t.ok(n.name, "namespace has a name");
t.equal(n.name, 'outOnReadable', "namespace has a name");
n.run(function () {
n.set('value', 'hello');
n.bindEmitter(re);
t.ok(re.emit.__wrapped, "emit is wrapped");
t.ok(re.on.__wrapped, "on is wrapped");
t.ok(re.addListener.__wrapped, "addListener is wrapped");
re.on('data', function (data) {
t.equal(n.get('value'), 'hello', "value still set in EE");
t.equal(data, 'blah', "emit still works");
cls.destroyNamespace('outOnReadable');
});
});
t.ok(re.emit.__wrapped, "emit is still wrapped");
t.ok(re.on.__wrapped, "on is still wrapped");
t.ok(re.addListener.__wrapped, "addListener is still wrapped");
t.equal(typeof re._events.data, 'function', 'only the one data listener');
t.ok(re._events.data['cls@contexts']['context@outOnReadable'],
"context is bound to listener");
re.emit('data', 'blah');
}
else {
t.comment("this test requires node 0.10+");
t.end();
}
});
t.test("handler added but used entirely out of context", function (t) {
t.plan(2);
var n = cls.createNamespace('none')
, ee = new EventEmitter()
;
n.run(function () {
n.set('value', 'hello');
n.bindEmitter(ee);
});
ee.on('event', function () {
t.ok(n, "n is set");
t.notOk(n.get('value'), "value shouldn't be visible");
cls.destroyNamespace('none');
});
ee.emit('event');
});
t.test("handler added but no listeners registered", function (t) {
t.plan(3);
var http = require('http')
, n = cls.createNamespace('no_listener')
;
// only fails on Node < 0.10
var server = http.createServer(function (req, res) {
n.bindEmitter(req);
t.doesNotThrow(function () {
req.emit('event');
});
res.writeHead(200, {"Content-Length" : 4});
res.end('WORD');
});
server.listen(8080);
http.get('http://localhost:8080/', function (res) {
t.equal(res.statusCode, 200, "request came back OK");
res.setEncoding('ascii');
res.on('data', function (body) {
t.equal(body, 'WORD');
server.close();
cls.destroyNamespace('no_listener');
});
});
});
t.test("listener with parameters added but not bound to context", function (t) {
t.plan(2);
var ee = new EventEmitter()
, n = cls.createNamespace('param_list')
;
function sent(value) {
t.equal(value, 3, "sent value is correct");
cls.destroyNamespace('param_list');
}
ee.on('send', sent);
n.bindEmitter(ee);
t.doesNotThrow(function () {
ee.emit('send', 3);
});
});
t.test("listener that throws doesn't leave removeListener wrapped", function (t) {
t.plan(4);
var ee = new EventEmitter()
, n = cls.createNamespace('kaboom')
;
n.bindEmitter(ee);
function kaboom() {
throw new Error('whoops');
}
n.run(function () {
ee.on('bad', kaboom);
t.throws(function () { ee.emit('bad'); });
t.equal(typeof ee.removeListener, 'function', 'removeListener is still there');
t.notOk(ee.removeListener.__wrapped, "removeListener got unwrapped");
t.equal(ee._events.bad, kaboom, "listener isn't still bound");
cls.destroyNamespace('kaboom');
});
});
t.test("emitter bound to multiple namespaces handles them correctly", function (t) {
t.plan(8);
var ee = new EventEmitter()
, ns1 = cls.createNamespace('1')
, ns2 = cls.createNamespace('2')
;
// emulate an incoming data emitter
setTimeout(function () {
ee.emit('data', 'hi');
}, 10);
t.doesNotThrow(function () { ns1.bindEmitter(ee); });
t.doesNotThrow(function () { ns2.bindEmitter(ee); });
ns1.run(function () {
ns2.run(function () {
ns1.set('name', 'tom1');
ns2.set('name', 'tom2');
t.doesNotThrow(function () { ns1.bindEmitter(ee); });
t.doesNotThrow(function () { ns2.bindEmitter(ee); });
ns1.run(function () {
process.nextTick(function () {
t.equal(ns1.get('name'), 'tom1', "ns1 value correct");
t.equal(ns2.get('name'), 'tom2', "ns2 value correct");
ns1.set('name', 'bob');
ns2.set('name', 'alice');
ee.on('data', function () {
t.equal(ns1.get('name'), 'bob', "ns1 value bound onto emitter");
t.equal(ns2.get('name'), 'alice', "ns2 value bound onto emitter");
});
});
});
});
});
});
});
+45
View File
@@ -0,0 +1,45 @@
'use strict';
// stdlib
var tap = require('tap');
var test = tap.test;
var EventEmitter = require('events').EventEmitter;
// module under test
var context = require('../context.js');
// multiple contexts in use
var tracer = context.createNamespace('tracer');
function Trace(harvester) {
this.harvester = harvester;
}
Trace.prototype.runHandler = function (callback) {
var wrapped = tracer.bind(function () {
callback();
this.harvester.emit('finished', tracer.get('transaction'));
}.bind(this));
wrapped();
};
test("simple tracer built on contexts", function (t) {
t.plan(6);
var harvester = new EventEmitter();
var trace = new Trace(harvester);
harvester.on('finished', function (transaction) {
t.ok(transaction, "transaction should have been passed in");
t.equal(transaction.status, 'ok', "transaction should have finished OK");
t.equal(Object.keys(process.namespaces).length, 1, "Should only have one namespace.");
});
trace.runHandler(function inScope() {
t.ok(tracer.active, "tracer should have an active context");
tracer.set('transaction', {status : 'ok'});
t.ok(tracer.get('transaction'), "can retrieve newly-set value");
t.equal(tracer.get('transaction').status, 'ok', "value should be correct");
});
});
+82
View File
@@ -0,0 +1,82 @@
'use strict';
var tap = require('tap')
, semver = require('semver')
, test = tap.test
, createNamespace = require('../context.js').createNamespace
;
var crypto;
try { crypto = require('crypto'); }
catch (err) {}
if (crypto) {
test("continuation-local state with crypto.randomBytes", function (t) {
t.plan(1);
var namespace = createNamespace('namespace');
namespace.run(function () {
namespace.set('test', 0xabad1dea);
t.test("randomBytes", function (t) {
namespace.run(function () {
namespace.set('test', 42);
crypto.randomBytes(100, function (err) {
if (err) throw err;
t.equal(namespace.get('test'), 42, "mutated state was preserved");
t.end();
});
});
});
});
});
test("continuation-local state with crypto.pseudoRandomBytes", function (t) {
t.plan(1);
var namespace = createNamespace('namespace');
namespace.run(function () {
namespace.set('test', 0xabad1dea);
t.test("pseudoRandomBytes", function (t) {
namespace.run(function () {
namespace.set('test', 42);
crypto.pseudoRandomBytes(100, function (err) {
if (err) throw err;
t.equal(namespace.get('test'), 42, "mutated state was preserved");
t.end();
});
});
});
});
});
test("continuation-local state with crypto.pbkdf2", function (t) {
t.plan(1);
var namespace = createNamespace('namespace');
namespace.run(function () {
namespace.set('test', 0xabad1dea);
t.test("pbkdf2", function (t) {
namespace.run(function () {
namespace.set('test', 42);
// this API changed after 0.10.0, and errors if digest is missing after v6
if (semver.gte(process.version, "0.12.0")) {
crypto.pbkdf2("s3cr3tz", "451243", 10, 40, "sha512", function (err) {
if (err) throw err;
t.equal(namespace.get('test'), 42, "mutated state was preserved");
t.end();
});
} else {
crypto.pbkdf2("s3cr3tz", "451243", 10, 40, function (err) {
if (err) throw err;
t.equal(namespace.get('test'), 42, "mutated state was preserved");
t.end();
});
}
});
});
});
});
}
+206
View File
@@ -0,0 +1,206 @@
'use strict';
var dns = require('dns')
, tap = require('tap')
, test = tap.test
, createNamespace = require('../context.js').createNamespace
;
test("continuation-local state with MakeCallback and DNS module", function (t) {
t.plan(11);
var namespace = createNamespace('dns');
namespace.run(function () {
namespace.set('test', 0xabad1dea);
t.test("dns.lookup", function (t) {
namespace.run(function () {
namespace.set('test', 808);
t.equal(namespace.get('test'), 808, "state has been mutated");
dns.lookup('www.newrelic.com', 4, function (err, addresses) {
t.notOk(err, "lookup succeeded");
t.ok(addresses.length > 0, "some results were found");
t.equal(namespace.get('test'), 808,
"mutated state has persisted to dns.lookup's callback");
t.end();
});
});
});
t.test("dns.resolve", function (t) {
namespace.run(function () {
namespace.set('test', 909);
t.equal(namespace.get('test'), 909, "state has been mutated");
dns.resolve('newrelic.com', 'NS', function (err, addresses) {
t.notOk(err, "lookup succeeded");
t.ok(addresses.length > 0, "some results were found");
t.equal(namespace.get('test'), 909,
"mutated state has persisted to dns.resolve's callback");
t.end();
});
});
});
t.test("dns.resolve4", function (t) {
namespace.run(function () {
namespace.set('test', 303);
t.equal(namespace.get('test'), 303, "state has been mutated");
dns.resolve4('www.newrelic.com', function (err, addresses) {
t.notOk(err, "lookup succeeded");
t.ok(addresses.length > 0, "some results were found");
t.equal(namespace.get('test'), 303,
"mutated state has persisted to dns.resolve4's callback");
t.end();
});
});
});
t.test("dns.resolve6", function (t) {
namespace.run(function () {
namespace.set('test', 101);
t.equal(namespace.get('test'), 101, "state has been mutated");
dns.resolve6('google.com', function (err, addresses) {
t.notOk(err, "lookup succeeded");
t.ok(addresses.length > 0, "some results were found");
t.equal(namespace.get('test'), 101,
"mutated state has persisted to dns.resolve6's callback");
t.end();
});
});
});
t.test("dns.resolveCname", function (t) {
namespace.run(function () {
namespace.set('test', 212);
t.equal(namespace.get('test'), 212, "state has been mutated");
dns.resolveCname('mail.newrelic.com', function (err, addresses) {
t.notOk(err, "lookup succeeded");
t.ok(addresses.length > 0, "some results were found");
t.equal(namespace.get('test'), 212,
"mutated state has persisted to dns.resolveCname's callback");
t.end();
});
});
});
t.test("dns.resolveMx", function (t) {
namespace.run(function () {
namespace.set('test', 707);
t.equal(namespace.get('test'), 707, "state has been mutated");
dns.resolveMx('newrelic.com', function (err, addresses) {
t.notOk(err, "lookup succeeded");
t.ok(addresses.length > 0, "some results were found");
t.equal(namespace.get('test'), 707,
"mutated state has persisted to dns.resolveMx's callback");
t.end();
});
});
});
t.test("dns.resolveNs", function (t) {
namespace.run(function () {
namespace.set('test', 717);
t.equal(namespace.get('test'), 717, "state has been mutated");
dns.resolveNs('newrelic.com', function (err, addresses) {
t.notOk(err, "lookup succeeded");
t.ok(addresses.length > 0, "some results were found");
t.equal(namespace.get('test'), 717,
"mutated state has persisted to dns.resolveNs's callback");
t.end();
});
});
});
t.test("dns.resolveTxt", function (t) {
namespace.run(function () {
namespace.set('test', 2020);
t.equal(namespace.get('test'), 2020, "state has been mutated");
dns.resolveTxt('newrelic.com', function (err, addresses) {
t.notOk(err, "lookup succeeded");
t.ok(addresses.length > 0, "some results were found");
t.equal(namespace.get('test'), 2020,
"mutated state has persisted to dns.resolveTxt's callback");
t.end();
});
});
});
t.test("dns.resolveSrv", function (t) {
namespace.run(function () {
namespace.set('test', 9000);
t.equal(namespace.get('test'), 9000, "state has been mutated");
dns.resolveSrv('_xmpp-server._tcp.google.com', function (err, addresses) {
t.notOk(err, "lookup succeeded");
t.ok(addresses.length > 0, "some results were found");
t.equal(namespace.get('test'), 9000,
"mutated state has persisted to dns.resolveSrv's callback");
t.end();
});
});
});
t.test("dns.resolveNaptr", function (t) {
// dns.resolveNaptr only in Node > 0.9.x
if (!dns.resolveNaptr) return t.end();
namespace.run(function () {
namespace.set('test', 'Polysix');
t.equal(namespace.get('test'), 'Polysix', "state has been mutated");
dns.resolveNaptr('columbia.edu', function (err, addresses) {
t.notOk(err, "lookup succeeded");
t.ok(addresses.length > 0, "some results were found");
t.equal(namespace.get('test'), 'Polysix',
"mutated state has persisted to dns.resolveNaptr's callback");
t.end();
});
});
});
t.test("dns.reverse", function (t) {
namespace.run(function () {
namespace.set('test', 1000);
t.equal(namespace.get('test'), 1000, "state has been mutated");
dns.reverse('204.93.223.144', function (err, addresses) {
t.notOk(err, "lookup succeeded");
t.ok(addresses.length > 0, "some results were found");
t.equal(namespace.get('test'), 1000,
"mutated state has persisted to dns.reverse's callback");
t.end();
});
});
});
});
});
+144
View File
@@ -0,0 +1,144 @@
'use strict';
var test = require('tap').test
, cls = require('../context.js')
, domain = require('domain')
;
test("continuation-local storage glue with a throw in the continuation chain",
function (t) {
var namespace = cls.createNamespace('test');
namespace.run(function () {
var d = domain.create();
namespace.set('outer', true);
namespace.bindEmitter(d);
d.on('error', function (blerg) {
t.equal(blerg.message, "explicitly nonlocal exit", "got the expected exception");
t.ok(namespace.get('outer'), "outer context is still active");
t.notOk(namespace.get('inner'), "inner context should have been exited by throw");
t.equal(namespace._set.length, 1, "should be back to outer state");
cls.destroyNamespace('test');
t.end();
});
// tap is only trying to help
process.nextTick(d.bind(function () {
t.ok(namespace.get('outer'), "outer mutation worked");
t.notOk(namespace.get('inner'), "inner mutation hasn't happened yet");
namespace.run(function () {
namespace.set('inner', true);
throw new Error("explicitly nonlocal exit");
});
}));
});
});
test("synchronous throw attaches the context", function (t) {
t.plan(3);
var namespace = cls.createNamespace('cls@synchronous');
namespace.run(function () {
namespace.set('value', 'transaction clear');
try {
namespace.run(function () {
namespace.set('value', 'transaction set');
throw new Error('cls@synchronous explosion');
});
}
catch (e) {
t.ok(namespace.fromException(e), "context was attached to error");
t.equal(namespace.fromException(e)['value'], 'transaction set',
"found the inner value");
}
t.equal(namespace.get('value'), 'transaction clear', "everything was reset");
});
cls.destroyNamespace('cls@synchronous');
});
test("synchronous throw checks if error exists", function (t) {
t.plan(2);
var namespace = cls.createNamespace('cls@synchronous-null-error');
namespace.run(function () {
namespace.set('value', 'transaction clear');
try {
namespace.run(function () {
namespace.set('value', 'transaction set');
throw null;
});
}
catch (e) {
// as we had a null error, cls couldn't set the new inner value
t.equal(namespace.get('value'), 'transaction clear', 'from outer value');
}
t.equal(namespace.get('value'), 'transaction clear', "everything was reset");
});
cls.destroyNamespace('cls@synchronous-null-error');
});
test("throw in process.nextTick attaches the context", function (t) {
t.plan(3);
var namespace = cls.createNamespace('cls@nexttick');
var d = domain.create();
namespace.bindEmitter(d);
d.on('error', function (e) {
t.ok(namespace.fromException(e), "context was attached to error");
t.equal(namespace.fromException(e)['value'], 'transaction set',
"found the inner value");
cls.destroyNamespace('cls@nexttick');
});
namespace.run(function () {
namespace.set('value', 'transaction clear');
// tap is only trying to help
process.nextTick(d.bind(function () {
namespace.run(function () {
namespace.set('value', 'transaction set');
throw new Error("cls@nexttick explosion");
});
}));
t.equal(namespace.get('value'), 'transaction clear', "everything was reset");
});
});
test("throw in setTimeout attaches the context", function (t) {
t.plan(3);
var namespace = cls.createNamespace('cls@setTimeout');
var d = domain.create();
namespace.bindEmitter(d);
d.on('error', function (e) {
t.ok(namespace.fromException(e), "context was attached to error");
t.equal(namespace.fromException(e)['value'], 'transaction set',
"found the inner value");
cls.destroyNamespace('cls@setTimeout');
});
namespace.run(function () {
namespace.set('value', 'transaction clear');
// tap is only trying to help
setTimeout(d.bind(function () {
namespace.run(function () {
namespace.set('value', 'transaction set');
throw new Error("cls@setTimeout explosion");
});
}));
t.equal(namespace.get('value'), 'transaction clear', "everything was reset");
});
});
+892
View File
@@ -0,0 +1,892 @@
'use strict';
var createNamespace = require('../context.js').createNamespace
, fs = require('fs')
, path = require('path')
, exec = require('child_process').exec
, tap = require('tap')
, test = tap.test
;
// CONSTANTS
var FILENAME = '__testfile'
, DIRNAME = '__TESTDIR'
, LINKNAME = '__testlink'
, HARDLINKNAME = '__testhardlink'
;
function createFile(assert) {
var contents = new Buffer("UHOH")
, file = fs.openSync(FILENAME, 'w')
, written = fs.writeSync(file, contents, 0, contents.length, 0)
;
assert.equals(written, contents.length, "whole buffer was written");
var rc = fs.closeSync(file);
// need this here to avoid dealing with umask complications
fs.chmodSync(FILENAME, '0666');
return rc;
}
function deleteFile() { return fs.unlinkSync(FILENAME); }
function createLink(assert) {
createFile(assert);
fs.symlinkSync(FILENAME, LINKNAME);
if (fs.lchmodSync) {
// This function only exists on BSD systems (like OSX)
fs.lchmodSync(LINKNAME, '0777');
}
}
function deleteLink() {
fs.unlinkSync(LINKNAME);
return deleteFile();
}
function createDirectory(assert) {
fs.mkdirSync(DIRNAME);
assert.ok(fs.existsSync(DIRNAME), "directory was created");
}
function deleteDirectory() { return fs.rmdirSync(DIRNAME); }
function mapIds(username, groupname, callback) {
if (!callback) throw new Error("mapIds requires callback");
if (!username) return callback(new Error("mapIds requires username"));
if (!groupname) return callback(new Error("mapIds requires groupname"));
exec('id -u ' + username, function (error, stdout, stderr) {
if (error) return callback(error);
if (stderr) return callback(new Error(stderr));
var uid = +stdout;
exec('id -g ' + groupname, function (error, stdout, stderr) {
if (error) return callback(error);
if (stderr) return callback(new Error(stderr));
var gid = +stdout;
callback(null, uid, gid);
});
});
}
test("continuation-local state with MakeCallback and fs module", function (t) {
t.plan(33);
var namespace = createNamespace('fs');
namespace.run(function () {
namespace.set('test', 0xabad1dea);
t.test("fs.rename", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'rename');
t.equal(namespace.get('test'), 'rename', "state has been mutated");
fs.rename(FILENAME, '__renamed', function (error) {
t.notOk(error, "renaming shouldn't error");
t.equal(namespace.get('test'), 'rename',
"mutated state has persisted to fs.rename's callback");
fs.unlinkSync('__renamed');
t.end();
});
});
});
t.test("fs.truncate", function (t) {
// truncate -> ftruncate in Node > 0.8.x
if (!fs.ftruncate) return t.end();
createFile(t);
namespace.run(function () {
namespace.set('test', 'truncate');
t.equal(namespace.get('test'), 'truncate', "state has been mutated");
fs.truncate(FILENAME, 0, function (error) {
t.notOk(error, "truncation shouldn't error");
var stats = fs.statSync(FILENAME);
t.equal(stats.size, 0, "file has been truncated");
t.equal(namespace.get('test'), 'truncate',
"mutated state has persisted to fs.truncate's callback");
deleteFile();
t.end();
});
});
});
t.test("fs.ftruncate", function (t) {
createFile(t);
// truncate -> ftruncate in Node > 0.8.x
var truncate = fs.ftruncate ? fs.ftruncate : fs.truncate;
namespace.run(function () {
namespace.set('test', 'ftruncate');
t.equal(namespace.get('test'), 'ftruncate', "state has been mutated");
var file = fs.openSync(FILENAME, 'w');
truncate(file, 0, function (error) {
t.notOk(error, "truncation shouldn't error");
fs.closeSync(file);
var stats = fs.statSync(FILENAME);
t.equal(stats.size, 0, "file has been truncated");
t.equal(namespace.get('test'), 'ftruncate',
"mutated state has persisted to fs.ftruncate's callback");
deleteFile();
t.end();
});
});
});
t.test("fs.chown", function (t) {
createFile(t);
mapIds('daemon', 'daemon', function (error, uid, gid) {
t.notOk(error, "looking up uid & gid shouldn't error");
t.ok(uid, "uid for daemon was found");
t.ok(gid, "gid for daemon was found");
namespace.run(function () {
namespace.set('test', 'chown');
t.equal(namespace.get('test'), 'chown', "state has been mutated");
fs.chown(FILENAME, uid, gid, function (error) {
t.ok(error, "changing ownership will error for non-root users");
t.equal(namespace.get('test'), 'chown',
"mutated state has persisted to fs.chown's callback");
deleteFile();
t.end();
});
});
});
});
t.test("fs.fchown", function (t) {
createFile(t);
mapIds('daemon', 'daemon', function (error, uid, gid) {
t.notOk(error, "looking up uid & gid shouldn't error");
t.ok(uid, "uid for daemon was found");
t.ok(gid, "gid for daemon was found");
namespace.run(function () {
namespace.set('test', 'fchown');
t.equal(namespace.get('test'), 'fchown', "state has been mutated");
var file = fs.openSync(FILENAME, 'w');
fs.fchown(file, uid, gid, function (error) {
t.ok(error, "changing ownership will error for non-root users");
t.equal(namespace.get('test'), 'fchown',
"mutated state has persisted to fs.fchown's callback");
fs.closeSync(file);
deleteFile();
t.end();
});
});
});
});
t.test("fs.lchown", function (t) {
if (!fs.lchown) return t.end();
createLink(t);
mapIds('daemon', 'daemon', function (error, uid, gid) {
t.notOk(error, "looking up uid & gid shouldn't error");
t.ok(uid, "uid for daemon was found");
t.ok(gid, "gid for daemon was found");
namespace.run(function () {
namespace.set('test', 'lchown');
t.equal(namespace.get('test'), 'lchown', "state has been mutated");
fs.lchown(LINKNAME, uid, gid, function (error) {
t.ok(error, "changing ownership will error for non-root users");
t.equal(namespace.get('test'), 'lchown',
"mutated state has persisted to fs.lchown's callback");
deleteLink();
t.end();
});
});
});
});
t.test("fs.chmod", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'chmod');
t.equal(namespace.get('test'), 'chmod', "state has been mutated");
fs.chmod(FILENAME, '0700', function (error) {
t.notOk(error, "changing mode shouldn't error");
t.equal(namespace.get('test'), 'chmod',
"mutated state has persisted to fs.chmod's callback");
var stats = fs.statSync(FILENAME);
t.equal(stats.mode.toString(8), '100700', "extra access bits are stripped");
deleteFile();
t.end();
});
});
});
t.test("fs.fchmod", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'fchmod');
t.equal(namespace.get('test'), 'fchmod', "state has been mutated");
var file = fs.openSync(FILENAME, 'w+');
fs.fchmod(file, '0700', function (error) {
t.notOk(error, "changing mode shouldn't error");
t.equal(namespace.get('test'), 'fchmod',
"mutated state has persisted to fs.fchmod's callback");
fs.closeSync(file);
var stats = fs.statSync(FILENAME);
t.equal(stats.mode.toString(8), '100700', "extra access bits are stripped");
deleteFile();
t.end();
});
});
});
t.test("fs.lchmod", function (t) {
if (!fs.lchmod) return t.end();
createLink(t);
namespace.run(function () {
namespace.set('test', 'lchmod');
t.equal(namespace.get('test'), 'lchmod', "state has been mutated");
fs.lchmod(LINKNAME, '0700', function (error) {
t.notOk(error, "changing mode shouldn't error");
t.equal(namespace.get('test'), 'lchmod',
"mutated state has persisted to fs.lchmod's callback");
var stats = fs.lstatSync(LINKNAME);
t.equal(stats.mode.toString(8), '120700', "extra access bits are stripped");
deleteLink();
t.end();
});
});
});
t.test("fs.stat", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'stat');
t.equal(namespace.get('test'), 'stat', "state has been mutated");
fs.stat(FILENAME, function (error, stats) {
t.notOk(error, "reading stats shouldn't error");
t.equal(namespace.get('test'), 'stat',
"mutated state has persisted to fs.stat's callback");
t.equal(stats.mode.toString(8), '100666', "permissions should be as created");
deleteFile();
t.end();
});
});
});
t.test("fs.fstat", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'fstat');
t.equal(namespace.get('test'), 'fstat', "state has been mutated");
var file = fs.openSync(FILENAME, 'r');
fs.fstat(file, function (error, stats) {
t.notOk(error, "reading stats shouldn't error");
t.equal(namespace.get('test'), 'fstat',
"mutated state has persisted to fs.fstat's callback");
t.equal(stats.mode.toString(8), '100666', "permissions should be as created");
fs.closeSync(file);
deleteFile();
t.end();
});
});
});
t.test("fs.lstat", function (t) {
createLink(t);
namespace.run(function () {
namespace.set('test', 'lstat');
t.equal(namespace.get('test'), 'lstat', "state has been mutated");
fs.lstat(LINKNAME, function (error, stats) {
t.notOk(error, "reading stats shouldn't error");
t.equal(namespace.get('test'), 'lstat',
"mutated state has persisted to fs.lstat's callback");
t.equal(
stats.mode.toString(8),
'120777',
"permissions should be as created"
);
deleteLink();
t.end();
});
});
});
t.test("fs.link", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'link');
t.equal(namespace.get('test'), 'link', "state has been mutated");
fs.link(FILENAME, HARDLINKNAME, function (error) {
t.notOk(error, "creating a link shouldn't error");
t.equal(namespace.get('test'), 'link',
"mutated state has persisted to fs.link's callback");
var orig = fs.statSync(FILENAME)
, linked = fs.statSync(HARDLINKNAME)
;
t.equal(orig.ino, linked.ino, "entries should point to same file");
t.notOk(fs.unlinkSync(HARDLINKNAME), "link has been removed");
deleteFile();
t.end();
});
});
});
t.test("fs.symlink", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'symlink');
t.equal(namespace.get('test'), 'symlink', "state has been mutated");
fs.symlink(FILENAME, LINKNAME, function (error) {
t.notOk(error, "creating a symlink shouldn't error");
t.equal(namespace.get('test'), 'symlink',
"mutated state has persisted to fs.symlink's callback");
var pointed = fs.readlinkSync(LINKNAME);
t.equal(pointed, FILENAME, "symlink points back to original file");
t.notOk(fs.unlinkSync(LINKNAME), "symlink has been removed");
deleteFile();
t.end();
});
});
});
t.test("fs.readlink", function (t) {
createLink(t);
namespace.run(function () {
namespace.set('test', 'readlink');
t.equal(namespace.get('test'), 'readlink', "state has been mutated");
fs.readlink(LINKNAME, function (error, pointed) {
t.notOk(error, "reading symlink shouldn't error");
t.equal(namespace.get('test'), 'readlink',
"mutated state has persisted to fs.readlink's callback");
t.equal(pointed, FILENAME, "symlink points back to original file");
deleteLink();
t.end();
});
});
});
t.test("fs.unlink", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'unlink');
t.equal(namespace.get('test'), 'unlink', "state has been mutated");
fs.unlink(FILENAME, function (error) {
t.notOk(error, "deleting file shouldn't error");
t.equal(namespace.get('test'), 'unlink',
"mutated state has persisted to fs.unlink's callback");
t.notOk(fs.exists(FILENAME), "file should be gone");
t.end();
});
});
});
t.test("fs.realpath", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'realpath');
t.equal(namespace.get('test'), 'realpath', "state has been mutated");
fs.realpath(FILENAME, function (error, real) {
t.notOk(error, "deleting file shouldn't error");
t.equal(namespace.get('test'), 'realpath',
"mutated state has persisted to fs.realpath's callback");
t.equal(real, path.resolve(FILENAME), "no funny business with the real path");
deleteFile();
t.end();
});
});
});
t.test("fs.mkdir", function (t) {
namespace.run(function () {
namespace.set('test', 'mkdir');
t.equal(namespace.get('test'), 'mkdir', "state has been mutated");
fs.mkdir(DIRNAME, function (error) {
t.notOk(error, "creating directory shouldn't error");
t.equal(namespace.get('test'), 'mkdir',
"mutated state has persisted to fs.mkdir's callback");
t.ok(fs.existsSync(DIRNAME), "directory was created");
fs.rmdirSync(DIRNAME);
t.end();
});
});
});
t.test("fs.rmdir", function (t) {
createDirectory(t);
namespace.run(function () {
namespace.set('test', 'rmdir');
t.equal(namespace.get('test'), 'rmdir', "state has been mutated");
fs.rmdir(DIRNAME, function (error) {
t.notOk(error, "deleting directory shouldn't error");
t.equal(namespace.get('test'), 'rmdir',
"mutated state has persisted to fs.rmdir's callback");
t.notOk(fs.existsSync(DIRNAME), "directory was removed");
t.end();
});
});
});
t.test("fs.readdir", function (t) {
createDirectory(t);
var file1 = fs.openSync(path.join(DIRNAME, 'file1'), 'w');
fs.writeSync(file1, 'one');
fs.closeSync(file1);
var file2 = fs.openSync(path.join(DIRNAME, 'file2'), 'w');
fs.writeSync(file2, 'two');
fs.closeSync(file2);
var file3 = fs.openSync(path.join(DIRNAME, 'file3'), 'w');
fs.writeSync(file3, 'three');
fs.closeSync(file3);
namespace.run(function () {
namespace.set('test', 'readdir');
t.equal(namespace.get('test'), 'readdir', "state has been mutated");
fs.readdir(DIRNAME, function (error, contents) {
t.notOk(error, "reading directory shouldn't error");
t.equal(namespace.get('test'), 'readdir',
"mutated state has persisted to fs.readdir's callback");
t.equal(contents.length, 3, "3 files were found");
fs.unlinkSync(path.join(DIRNAME, 'file1'));
fs.unlinkSync(path.join(DIRNAME, 'file2'));
fs.unlinkSync(path.join(DIRNAME, 'file3'));
deleteDirectory();
t.end();
});
});
});
t.test("fs.watch", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'watch');
t.equal(namespace.get('test'), 'watch', "state has been mutated");
var watcher = fs.watch(FILENAME,
{persistent : false, interval : 200},
function (event) {
t.equal(namespace.get('test'), 'watch',
"mutated state has persisted to fs.watch's callback");
t.equal(event, 'change', "file was changed");
watcher.close();
process.nextTick(function cleanup() {
deleteFile();
t.end();
});
});
setTimeout(function poke() {
fs.writeFileSync(FILENAME, 'still a test');
}, 20);
});
});
t.test("fs.utimes", function (t) {
createFile(t);
/* utimes(2) takes seconds since the epoch, and Date() deals with
* milliseconds. I just want a date some time in the past.
*/
var PASTIME = new Date(Math.floor((Date.now() - 31337) / 1000) * 1000);
namespace.run(function () {
namespace.set('test', 'utimes');
t.equal(namespace.get('test'), 'utimes', "state has been mutated");
var before = fs.statSync(FILENAME);
t.ok(before.atime, "access time of newly-created file set");
t.ok(before.mtime, "modification time of newly-created file set");
fs.utimes(FILENAME, PASTIME, PASTIME, function (error) {
t.notOk(error, "setting utimes shouldn't error");
t.equal(namespace.get('test'), 'utimes',
"mutated state has persisted to fs.utimes's callback");
var after = fs.statSync(FILENAME);
t.deepEqual(after.atime, PASTIME, "access time of newly-created file is reset");
t.deepEqual(after.mtime, PASTIME, "mod time of newly-created file is reset");
t.notDeepEqual(before.atime, after.atime, "access time changed");
t.notDeepEqual(before.mtime, after.mtime, "mod time changed");
deleteFile();
t.end();
});
});
});
t.test("fs.futimes", function (t) {
createFile(t);
/* futimes(2) takes seconds since the epoch, and Date() deals with
* milliseconds. I just want a date some time in the past.
*/
var PASTIME = new Date(Math.floor((Date.now() - 0xb33fd) / 1000) * 1000);
namespace.run(function () {
namespace.set('test', 'futimes');
t.equal(namespace.get('test'), 'futimes', "state has been mutated");
var before = fs.statSync(FILENAME);
t.ok(before.atime, "access time of newly-created file set");
t.ok(before.mtime, "modification time of newly-created file set");
var file = fs.openSync(FILENAME, "w+");
fs.futimes(file, PASTIME, PASTIME, function (error) {
t.notOk(error, "setting futimes shouldn't error");
fs.closeSync(file);
t.equal(namespace.get('test'), 'futimes',
"mutated state has persisted to fs.futimes's callback");
var after = fs.statSync(FILENAME);
t.deepEqual(after.atime, PASTIME, "access time of newly-created file is reset");
t.deepEqual(after.mtime, PASTIME, "mod time of newly-created file is reset");
t.notDeepEqual(before.atime, after.atime, "access time changed");
t.notDeepEqual(before.mtime, after.mtime, "mod time changed");
deleteFile();
t.end();
});
});
});
t.test("fs.fsync", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'fsync');
t.equal(namespace.get('test'), 'fsync', "state has been mutated");
var file = fs.openSync(FILENAME, 'w+');
fs.fsync(file, function (error) {
t.notOk(error, "syncing the file shouldn't error");
t.equal(namespace.get('test'), 'fsync',
"mutated state has persisted to fs.fsync's callback");
fs.closeSync(file);
deleteFile();
t.end();
});
});
});
t.test("fs.open", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'open');
t.equal(namespace.get('test'), 'open', "state has been mutated");
fs.open(FILENAME, 'r', function (error, file) {
t.notOk(error, "opening the file shouldn't error");
t.equal(namespace.get('test'), 'open',
"mutated state has persisted to fs.open's callback");
var contents = new Buffer(4);
fs.readSync(file, contents, 0, 4, 0);
t.equal(contents.toString(), 'UHOH', "contents are still available");
fs.closeSync(file);
deleteFile();
t.end();
});
});
});
t.test("fs.close", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'close');
t.equal(namespace.get('test'), 'close', "state has been mutated");
var file = fs.openSync(FILENAME, 'r');
fs.close(file, function (error) {
t.notOk(error, "closing the file shouldn't error");
t.equal(namespace.get('test'), 'close',
"mutated state has persisted to fs.close's callback");
deleteFile();
t.end();
});
});
});
t.test("fs.read", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'read');
t.equal(namespace.get('test'), 'read', "state has been mutated");
var file = fs.openSync(FILENAME, 'r')
, contents = new Buffer(4)
;
fs.read(file, contents, 0, 4, 0, function (error) {
t.notOk(error, "reading from the file shouldn't error");
t.equal(namespace.get('test'), 'read',
"mutated state has persisted to fs.read's callback");
t.equal(contents.toString(), 'UHOH', "contents are still available");
fs.closeSync(file);
deleteFile();
t.end();
});
});
});
t.test("fs.write", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'write');
t.equal(namespace.get('test'), 'write', "state has been mutated");
var file = fs.openSync(FILENAME, 'w')
, contents = new Buffer('yeap')
;
fs.write(file, contents, 0, 4, 0, function (error) {
t.notOk(error, "writing to the file shouldn't error");
t.equal(namespace.get('test'), 'write',
"mutated state has persisted to fs.write's callback");
fs.closeSync(file);
var readback = fs.readFileSync(FILENAME);
t.equal(readback.toString(), 'yeap', "contents are still available");
deleteFile();
t.end();
});
});
});
t.test("fs.readFile", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'readFile');
t.equal(namespace.get('test'), 'readFile', "state has been mutated");
fs.readFile(FILENAME, function (error, contents) {
t.notOk(error, "reading from the file shouldn't error");
t.equal(namespace.get('test'), 'readFile',
"mutated state has persisted to fs.readFile's callback");
t.equal(contents.toString(), 'UHOH', "contents are still available");
deleteFile();
t.end();
});
});
});
t.test("fs.writeFile", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'writeFile');
t.equal(namespace.get('test'), 'writeFile', "state has been mutated");
fs.writeFile(FILENAME, 'woopwoop', function (error) {
t.notOk(error, "rewriting the file shouldn't error");
t.equal(namespace.get('test'), 'writeFile',
"mutated state has persisted to fs.writeFile's callback");
var readback = fs.readFileSync(FILENAME);
t.equal(readback.toString(), 'woopwoop', "rewritten contents are available");
deleteFile();
t.end();
});
});
});
t.test("fs.appendFile", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'appendFile');
t.equal(namespace.get('test'), 'appendFile', "state has been mutated");
fs.appendFile(FILENAME, '/jk', function (error) {
t.notOk(error, "appending to the file shouldn't error");
t.equal(namespace.get('test'), 'appendFile',
"mutated state has persisted to fs.appendFile's callback");
var readback = fs.readFileSync(FILENAME);
t.equal(readback.toString(), 'UHOH/jk',
"appended contents are still available");
deleteFile();
t.end();
});
});
});
t.test("fs.exists", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'exists');
t.equal(namespace.get('test'), 'exists', "state has been mutated");
fs.exists(FILENAME, function (yep) {
t.equal(namespace.get('test'), 'exists',
"mutated state has persisted to fs.exists's callback");
t.ok(yep, "precreated file does indeed exist.");
fs.exists('NOPENOWAY', function (nope) {
t.equal(namespace.get('test'), 'exists',
"mutated state continues to persist to fs.exists's second callback");
t.notOk(nope, "nonexistent file doesn't exist.");
deleteFile();
t.end();
});
});
});
});
t.test("fs.watchFile", function (t) {
createFile(t);
namespace.run(function () {
namespace.set('test', 'watchFile');
t.equal(namespace.get('test'), 'watchFile', "state has been mutated");
var options = {
persistent: true,
interval: 20
};
fs.watchFile(FILENAME, options, function (before, after) {
t.equal(namespace.get('test'), 'watchFile',
"mutated state has persisted to fs.watchFile's callback");
t.ok(before.ino, "file has an entry");
t.equal(before.ino, after.ino, "file is at the same location");
fs.unwatchFile(FILENAME);
process.nextTick(function () {
deleteFile();
t.end();
});
});
setTimeout(function poke() {
fs.appendFileSync(FILENAME, 'still a test');
}, 20);
});
});
});
});
@@ -0,0 +1,58 @@
'use strict';
var cls = require('../context.js')
, test = require('tap').test
;
function cleanNamespace(name){
if (cls.getNamespace(name)) cls.destroyNamespace(name);
return cls.createNamespace(name);
}
test("interleaved contexts", function (t) {
t.plan(3);
t.test("interleaving with run", function (t) {
t.plan(2);
var ns = cleanNamespace('test');
var ctx = ns.createContext();
ns.enter(ctx);
ns.run(function () {
t.equal(ns._set.length, 2, "2 contexts in the active set");
t.doesNotThrow(function () { ns.exit(ctx); });
});
});
t.test("entering and exiting staggered", function (t) {
t.plan(4);
var ns = cleanNamespace('test');
var ctx1 = ns.createContext();
var ctx2 = ns.createContext();
t.doesNotThrow(function () { ns.enter(ctx1); });
t.doesNotThrow(function () { ns.enter(ctx2); });
t.doesNotThrow(function () { ns.exit(ctx1); });
t.doesNotThrow(function () { ns.exit(ctx2); });
});
t.test("creating, entering and exiting staggered", function (t) {
t.plan(4);
var ns = cleanNamespace('test');
var ctx1 = ns.createContext();
t.doesNotThrow(function () { ns.enter(ctx1); });
var ctx2 = ns.createContext();
t.doesNotThrow(function () { ns.enter(ctx2); });
t.doesNotThrow(function () { ns.exit(ctx1); });
t.doesNotThrow(function () { ns.exit(ctx2); });
});
});
+55
View File
@@ -0,0 +1,55 @@
'use strict';
var test = require('tap').test;
if (!process.addAsyncListener) {
test("overwriting startup.processNextTick", function (t) {
t.plan(2);
t.doesNotThrow(function () { require('../context.js'); });
t.ok(process.nextTick.__wrapped, "should wrap process.nextTick()");
});
test("overwriting domain helpers", function (t) {
// domain helpers were only in 0.10.x
if (!(process._nextDomainTick && process._tickDomainCallback)) {
return t.end();
}
t.plan(2);
t.ok(process._nextDomainTick.__wrapped,
"should wrap process._nextDomainTick()");
t.ok(process._tickDomainCallback.__wrapped,
"should wrap process._tickDomainCallback()");
});
test("overwriting timers", function (t) {
t.plan(2);
var timers = require('timers');
t.ok(timers.setTimeout.__wrapped, "should wrap setTimeout()");
t.ok(timers.setInterval.__wrapped, "should wrap setInterval()");
/* It would be nice to test that monkeypatching preserves the status quo
* ante, but assert thinks setTimeout !== global.setTimeout (why?) and both of
* those are a wrapper around NativeModule.require("timers").setTimeout,
* presumably to try to prevent the kind of "fun" I'm having here.
*/
});
test("overwriting setImmediate", function (t) {
// setTimeout's a johnny-come-lately
if (!global.setImmediate) return t.end();
t.plan(1);
t.ok(require('timers').setImmediate.__wrapped, "should wrap setImmediate()");
/* It would be nice to test that monkeypatching preserves the status quo
* ante, but assert thinks setTimeout !== global.setTimeout (why?) and both of
* those are a wrapper around NativeModule.require("timers").setTimeout,
* presumably to try to prevent the kind of "fun" I'm having here.
*/
});
}
+29
View File
@@ -0,0 +1,29 @@
'use strict';
var tap = require('tap');
var test = tap.test;
var context = require('../context.js');
test("namespace management", function (t) {
t.plan(8);
t.throws(function () { context.createNamespace(); }, "name is required");
var namespace = context.createNamespace('test');
t.ok(namespace, "namespace is returned upon creation");
t.equal(context.getNamespace('test'), namespace, "namespace lookup works");
t.doesNotThrow(function () { context.reset(); }, "allows resetting namespaces");
t.equal(Object.keys(process.namespaces).length, 0, "namespaces have been reset");
namespace = context.createNamespace('another');
t.ok(process.namespaces.another, "namespace is available from global");
t.doesNotThrow(function () { context.destroyNamespace('another'); },
"destroying works");
t.notOk(process.namespaces.another, "namespace has been removed");
});
+73
View File
@@ -0,0 +1,73 @@
'use strict';
var tap = require('tap');
var test = tap.test;
var cls = require('../context.js');
test("nested contexts on a single namespace", function (t) {
t.plan(7);
var namespace = cls.createNamespace("namespace");
namespace.run(function () {
namespace.set("value", 1);
t.equal(namespace.get("value"), 1,
"namespaces have associated data even without contexts.");
namespace.run(function () {
t.equal(namespace.get("value"), 1, "lookup will check enclosing context");
namespace.set("value", 2);
t.equal(namespace.get("value"), 2, "setting works on top-level context");
namespace.run(function () {
t.equal(namespace.get("value"), 2, "lookup will check enclosing context");
namespace.set("value", 3);
t.equal(namespace.get("value"), 3, "setting works on nested context");
});
t.equal(namespace.get("value"), 2,
"should revert to value set in top-level context");
});
t.equal(namespace.get("value"), 1, "namespace retains its outermost value.");
});
});
test("the example from the docs", function (t) {
var writer = cls.createNamespace('writer');
writer.run(function () {
writer.set('value', 0);
t.equal(writer.get('value'), 0, "outer hasn't been entered yet");
function requestHandler() {
writer.run(function (outer) {
t.equal(writer.active, outer, "writer.active == outer");
writer.set('value', 1);
t.equal(writer.get('value'), 1, "writer.active == outer");
t.equal(outer.value, 1, "outer is active");
process.nextTick(function () {
t.equal(writer.active, outer, "writer.active == outer");
t.equal(writer.get('value'), 1, "inner has been entered");
writer.run(function (inner) {
t.equal(writer.active, inner, "writer.active == inner");
writer.set('value', 2);
t.equal(outer.value, 1, "outer is unchanged");
t.equal(inner.value, 2, "inner is active");
t.equal(writer.get('value'), 2, "writer.active == inner");
});
});
});
setTimeout(function () {
t.equal(writer.get('value'), 0, "writer.active == global");
t.end();
}, 100);
}
requestHandler();
});
});
+45
View File
@@ -0,0 +1,45 @@
'use strict';
var net = require('net')
, tap = require('tap')
, test = tap.test
, createNamespace = require('../context').createNamespace
;
test("continuation-local state with net connection", function (t) {
t.plan(4);
var namespace = createNamespace('net');
namespace.run(function () {
namespace.set('test', 0xabad1dea);
var server;
namespace.run(function () {
namespace.set('test', 0x1337);
server = net.createServer(function (socket) {
t.equal(namespace.get('test'), 0x1337, "state has been mutated");
socket.on("data", function () {
t.equal(namespace.get('test'), 0x1337, "state is still preserved");
server.close();
socket.end("GoodBye");
});
});
server.listen(function () {
var address = server.address();
namespace.run(function () {
namespace.set("test", "MONKEY");
var client = net.connect(address.port, function () {
t.equal(namespace.get("test"), "MONKEY",
"state preserved for client connection");
client.write("Hello");
client.on("data", function () {
t.equal(namespace.get("test"), "MONKEY", "state preserved for client data");
t.end();
});
});
});
});
});
});
});
+121
View File
@@ -0,0 +1,121 @@
'use strict';
var tap = require('tap')
, test = tap.test
, createNamespace = require('../context.js').createNamespace
;
test("continuation-local state with promises", function (t) {
t.plan(4);
var namespace = createNamespace('namespace');
namespace.run(function () {
namespace.set('test', 0xabad1dea);
t.test("chained promises", function (t) {
if (!global.Promise) return t.end();
namespace.run(function () {
namespace.set('test', 31337);
t.equal(namespace.get('test'), 31337, "state has been mutated");
Promise.resolve()
.then(function () {
t.equal(namespace.get('test'), 31337,
"mutated state has persisted to first continuation");
})
.then(function () {
t.equal(namespace.get('test'), 31337,
"mutated state has persisted to second continuation");
})
.then(function () {
t.equal(namespace.get('test'), 31337,
"mutated state has persisted to third continuation");
t.end();
});
});
});
t.test("chained unwrapped promises", function (t) {
if (!global.Promise) return t.end();
namespace.run(function () {
namespace.set('test', 999);
t.equal(namespace.get('test'), 999, "state has been mutated");
Promise.resolve()
.then(function () {
t.equal(namespace.get('test'), 999,
"mutated state has persisted to first continuation");
return Promise.resolve();
})
.then(function () {
t.equal(namespace.get('test'), 999,
"mutated state has persisted to second continuation");
return Promise.resolve();
})
.then(function () {
t.equal(namespace.get('test'), 999,
"mutated state has persisted to third continuation");
t.end();
});
});
});
t.test("nested promises", function (t) {
if (!global.Promise) return t.end();
namespace.run(function () {
namespace.set('test', 54321);
t.equal(namespace.get('test'), 54321, "state has been mutated");
Promise.resolve()
.then(function () {
t.equal(namespace.get('test'), 54321,
"mutated state has persisted to first continuation");
Promise.resolve()
.then(function () {
t.equal(namespace.get('test'), 54321,
"mutated state has persisted to second continuation");
Promise.resolve()
.then(function () {
t.equal(namespace.get('test'), 54321,
"mutated state has persisted to third continuation");
t.end();
});
});
});
});
});
t.test("forked continuations", function (t) {
if (!global.Promise) return t.end();
namespace.run(function () {
namespace.set('test', 10101);
t.equal(namespace.get('test'), 10101, "state has been mutated");
var promise = Promise.resolve();
promise
.then(function () {
t.equal(namespace.get('test'), 10101,
"mutated state has persisted to first continuation");
});
promise
.then(function () {
t.equal(namespace.get('test'), 10101,
"mutated state has persisted to second continuation");
});
promise
.then(function () {
t.equal(namespace.get('test'), 10101,
"mutated state has persisted to third continuation");
t.end();
});
});
});
});
});
+17
View File
@@ -0,0 +1,17 @@
'use strict';
// I love when a tap.plan() comes together
console.log('1..1');
process.on('uncaughtException', function (err) {
if (err.message === 'oops') {
console.log("ok got expected message: %s", err.message);
}
else {
throw err;
}
});
var cls = require('../context.js');
var ns = cls.createNamespace('x');
ns.run(function () { throw new Error('oops'); });
+40
View File
@@ -0,0 +1,40 @@
'use strict';
// stdlib
var tap = require('tap');
var test = tap.test;
var EventEmitter = require('events').EventEmitter;
// module under test
var context = require('../context.js');
// multiple contexts in use
var tracer = context.createNamespace('tracer');
test("simple tracer built on contexts", function (t) {
t.plan(7);
var harvester = new EventEmitter();
harvester.on('finished', function (transaction) {
t.ok(transaction, "transaction should have been passed in");
t.equal(transaction.status, 'ok', "transaction should have finished OK");
t.equal(Object.keys(process.namespaces).length, 1, "Should only have one namespace.");
});
var returnValue = {};
var returnedValue = tracer.runAndReturn(function(context) {
t.ok(tracer.active, "tracer should have an active context");
tracer.set('transaction', {status : 'ok'});
t.ok(tracer.get('transaction'), "can retrieve newly-set value");
t.equal(tracer.get('transaction').status, 'ok', "value should be correct");
harvester.emit('finished', context.transaction);
return returnValue;
});
t.equal(returnedValue, returnValue, "method should pass through return value of function run in scope");
});
+42
View File
@@ -0,0 +1,42 @@
'use strict';
// stdlib
var tap = require('tap');
var test = tap.test;
var EventEmitter = require('events').EventEmitter;
// module under test
var context = require('../context.js');
// multiple contexts in use
var tracer = context.createNamespace('tracer');
function Trace(harvester) {
this.harvester = harvester;
}
Trace.prototype.runHandler = function (handler) {
var trace = tracer.run(handler);
this.harvester.emit('finished', trace.transaction);
};
test("simple tracer built on contexts", function (t) {
t.plan(6);
var harvester = new EventEmitter();
var trace = new Trace(harvester);
harvester.on('finished', function (transaction) {
t.ok(transaction, "transaction should have been passed in");
t.equal(transaction.status, 'ok', "transaction should have finished OK");
t.equal(Object.keys(process.namespaces).length, 1, "Should only have one namespace.");
});
trace.runHandler(function inScope() {
t.ok(tracer.active, "tracer should have an active context");
tracer.set('transaction', {status : 'ok'});
t.ok(tracer.get('transaction'), "can retrieve newly-set value");
t.equal(tracer.get('transaction').status, 'ok', "value should be correct");
});
});
+76
View File
@@ -0,0 +1,76 @@
'use strict';
var tap = require('tap')
, test = tap.test
, createNamespace = require('../context.js').createNamespace
;
test("continuation-local state with timers", function (t) {
t.plan(4);
var namespace = createNamespace('namespace');
namespace.run(function () {
namespace.set('test', 0xabad1dea);
t.test("process.nextTick", function (t) {
namespace.run(function () {
namespace.set('test', 31337);
t.equal(namespace.get('test'), 31337, "state has been mutated");
process.nextTick(function () {
t.equal(namespace.get('test'), 31337,
"mutated state has persisted to process.nextTick's callback");
t.end();
});
});
});
t.test("setImmediate", function (t) {
// setImmediate only in Node > 0.9.x
if (!global.setImmediate) return t.end();
namespace.run(function () {
namespace.set('test', 999);
t.equal(namespace.get('test'), 999, "state has been mutated");
setImmediate(function () {
t.equal(namespace.get('test'), 999,
"mutated state has persisted to setImmediate's callback");
t.end();
});
});
});
t.test("setTimeout", function (t) {
namespace.run(function () {
namespace.set('test', 54321);
t.equal(namespace.get('test'), 54321, "state has been mutated");
setTimeout(function () {
t.equal(namespace.get('test'), 54321,
"mutated state has persisted to setTimeout's callback");
t.end();
});
});
});
t.test("setInterval", function (t) {
namespace.run(function () {
namespace.set('test', 10101);
t.equal(namespace.get('test'), 10101,
"continuation-local state has been mutated");
var ref = setInterval(function () {
t.equal(namespace.get('test'), 10101,
"mutated state has persisted to setInterval's callback");
clearInterval(ref);
t.end();
}, 20);
});
});
});
});
+338
View File
@@ -0,0 +1,338 @@
'use strict';
var EventEmitter = require('events').EventEmitter
, assert = require('assert')
, test = require('tap').test
, cls = require('../context.js')
;
var nextID = 1;
function fresh(name) {
assert.ok(!cls.getNamespace(name), "namespace " + name + " already exists");
return cls.createNamespace(name);
}
function destroy(name) {
return function destroyer(t) {
cls.destroyNamespace(name);
assert.ok(!cls.getNamespace(name), "namespace '" + name + "' should no longer exist");
t.end();
};
}
function runInTransaction(name, fn) {
var namespace = cls.getNamespace(name);
assert(namespace, "namespaces " + name + " doesn't exist");
var context = namespace.createContext();
context.transaction = ++nextID;
process.nextTick(namespace.bind(fn, context));
}
test("asynchronous state propagation", function (t) {
t.plan(24);
t.test("a. async transaction with setTimeout", function (t) {
t.plan(2);
var namespace = fresh('a', this);
function handler() {
t.ok(namespace.get('transaction'), "transaction should be visible");
}
t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
runInTransaction('a', function () { setTimeout(handler, 100); });
});
t.test("a. cleanup", destroy('a'));
t.test("b. async transaction with setInterval", function (t) {
t.plan(4);
var namespace = fresh('b', this)
, count = 0
, handle
;
function handler() {
count += 1;
if (count > 2) clearInterval(handle);
t.ok(namespace.get('transaction'), "transaction should be visible");
}
t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
runInTransaction('b', function () { handle = setInterval(handler, 50); });
});
t.test("b. cleanup", destroy('b'));
t.test("c. async transaction with process.nextTick", function (t) {
t.plan(2);
var namespace = fresh('c', this);
function handler() {
t.ok(namespace.get('transaction'), "transaction should be visible");
}
t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
runInTransaction('c', function () { process.nextTick(handler); });
});
t.test("c. cleanup", destroy('c'));
t.test("d. async transaction with EventEmitter.emit", function (t) {
t.plan(2);
var namespace = fresh('d', this)
, ee = new EventEmitter()
;
function handler() {
t.ok(namespace.get('transaction'), "transaction should be visible");
}
t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
runInTransaction('d', function () {
ee.on('transaction', handler);
ee.emit('transaction');
});
});
t.test("d. cleanup", destroy('d'));
t.test("e. two overlapping async transactions with setTimeout", function (t) {
t.plan(6);
var namespace = fresh('e', this)
, first
, second
;
function handler(id) {
t.ok(namespace.get('transaction'), "transaction should be visible");
t.equal(namespace.get('transaction'), id, "transaction matches");
}
t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
runInTransaction('e', function () {
first = namespace.get('transaction');
setTimeout(handler.bind(null, first), 100);
});
setTimeout(function () {
runInTransaction('e', function () {
second = namespace.get('transaction');
t.notEqual(first, second, "different transaction IDs");
setTimeout(handler.bind(null, second), 100);
});
}, 25);
});
t.test("e. cleanup", destroy('e'));
t.test("f. two overlapping async transactions with setInterval", function (t) {
t.plan(15);
var namespace = fresh('f', this);
function runInterval() {
var count = 0
, handle
, id
;
function handler() {
count += 1;
if (count > 2) clearInterval(handle);
t.ok(namespace.get('transaction'), "transaction should be visible");
t.equal(id, namespace.get('transaction'), "transaction ID should be immutable");
}
function run() {
t.ok(namespace.get('transaction'), "transaction should have been created");
id = namespace.get('transaction');
handle = setInterval(handler, 50);
}
runInTransaction('f', run);
}
t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
runInterval(); runInterval();
});
t.test("f. cleanup", destroy('f'));
t.test("g. two overlapping async transactions with process.nextTick", function (t) {
t.plan(6);
var namespace = fresh('g', this)
, first
, second
;
function handler(id) {
var transaction = namespace.get('transaction');
t.ok(transaction, "transaction should be visible");
t.equal(transaction, id, "transaction matches");
}
t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
runInTransaction('g', function () {
first = namespace.get('transaction');
process.nextTick(handler.bind(null, first));
});
process.nextTick(function () {
runInTransaction('g', function () {
second = namespace.get('transaction');
t.notEqual(first, second, "different transaction IDs");
process.nextTick(handler.bind(null, second));
});
});
});
t.test("g. cleanup", destroy('g'));
t.test("h. two overlapping async runs with EventEmitter.prototype.emit", function (t) {
t.plan(3);
var namespace = fresh('h', this)
, ee = new EventEmitter()
;
function handler() {
t.ok(namespace.get('transaction'), "transaction should be visible");
}
function lifecycle() {
ee.once('transaction', process.nextTick.bind(process, handler));
ee.emit('transaction');
}
t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
runInTransaction('h', lifecycle);
runInTransaction('h', lifecycle);
});
t.test("h. cleanup", destroy('h'));
t.test("i. async transaction with an async sub-call with setTimeout", function (t) {
t.plan(5);
var namespace = fresh('i', this);
function inner(callback) {
setTimeout(function () {
t.ok(namespace.get('transaction'), "transaction should (yep) still be visible");
callback();
}, 50);
}
function outer() {
t.ok(namespace.get('transaction'), "transaction should be visible");
setTimeout(function () {
t.ok(namespace.get('transaction'), "transaction should still be visible");
inner(function () {
t.ok(namespace.get('transaction'), "transaction should even still be visible");
});
}, 50);
}
t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
runInTransaction('i', setTimeout.bind(null, outer, 50));
});
t.test("i. cleanup", destroy('i'));
t.test("j. async transaction with an async sub-call with setInterval", function (t) {
t.plan(5);
var namespace = fresh('j', this)
, outerHandle
, innerHandle
;
function inner(callback) {
innerHandle = setInterval(function () {
clearInterval(innerHandle);
t.ok(namespace.get('transaction'), "transaction should (yep) still be visible");
callback();
}, 50);
}
function outer() {
t.ok(namespace.get('transaction'), "transaction should be visible");
outerHandle = setInterval(function () {
clearInterval(outerHandle);
t.ok(namespace.get('transaction'), "transaction should still be visible");
inner(function () {
t.ok(namespace.get('transaction'), "transaction should even still be visible");
});
}, 50);
}
t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
runInTransaction('j', outer);
});
t.test("j. cleanup", destroy('j'));
t.test("k. async transaction with an async call with process.nextTick", function (t) {
t.plan(5);
var namespace = fresh('k', this);
function inner(callback) {
process.nextTick(function () {
t.ok(namespace.get('transaction'), "transaction should (yep) still be visible");
callback();
});
}
function outer() {
t.ok(namespace.get('transaction'), "transaction should be visible");
process.nextTick(function () {
t.ok(namespace.get('transaction'), "transaction should still be visible");
inner(function () {
t.ok(namespace.get('transaction'), "transaction should even still be visible");
});
});
}
t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
runInTransaction('k', function () { process.nextTick(outer); });
});
t.test("k. cleanup", destroy('k'));
t.test("l. async transaction with an async call with EventEmitter.emit", function (t) {
t.plan(4);
var namespace = fresh('l', this)
, outer = new EventEmitter()
, inner = new EventEmitter()
;
inner.on('pong', function (callback) {
t.ok(namespace.get('transaction'), "transaction should still be visible");
callback();
});
function outerCallback() {
t.ok(namespace.get('transaction'), "transaction should even still be visible");
}
outer.on('ping', function () {
t.ok(namespace.get('transaction'), "transaction should be visible");
inner.emit('pong', outerCallback);
});
t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
runInTransaction('l', outer.emit.bind(outer, 'ping'));
});
t.test("l. cleanup", destroy('l'));
});
+28
View File
@@ -0,0 +1,28 @@
'use strict';
var tap = require('tap')
, test = tap.test
, createNamespace = require('../context.js').createNamespace
;
var zlib = require('zlib');
test("continuation-local state with zlib", function (t) {
t.plan(1);
var namespace = createNamespace('namespace');
namespace.run(function () {
namespace.set('test', 0xabad1dea);
t.test("deflate", function (t) {
namespace.run(function () {
namespace.set('test', 42);
zlib.deflate(new Buffer("Goodbye World"), function (err) {
if (err) throw err;
t.equal(namespace.get('test'), 42, "mutated state was preserved");
t.end();
});
});
});
});
});