339 lines
9.6 KiB
JavaScript
339 lines
9.6 KiB
JavaScript
|
'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'));
|
||
|
});
|