1 | 'use strict';
|
2 |
|
3 | var EventEmitter = require('events').EventEmitter
|
4 | , assert = require('assert')
|
5 | , test = require('tap').test
|
6 | , cls = require('../context.js')
|
7 | ;
|
8 |
|
9 | var nextID = 1;
|
10 | function fresh(name, test) {
|
11 | assert.ok(!cls.getNamespace(name), "namespace " + name + " already exists");
|
12 |
|
13 |
|
14 | test.tearDown(function () {
|
15 | cls.destroyNamespace(name);
|
16 | assert.ok(!cls.getNamespace(name), "namespace " + name + " should no longer exist");
|
17 | });
|
18 | return cls.createNamespace(name);
|
19 | }
|
20 |
|
21 | function runInTransaction(name, fn) {
|
22 | var namespace = cls.getNamespace(name);
|
23 | assert(namespace, "namespaces " + name + " doesn't exist");
|
24 |
|
25 | var context = namespace.createContext();
|
26 | context.transaction = ++nextID;
|
27 | process.nextTick(namespace.bind(fn, context));
|
28 | }
|
29 |
|
30 | test("asynchronous state propagation", function (t) {
|
31 | t.plan(12);
|
32 |
|
33 | t.test("a. async transaction with setTimeout", function (t) {
|
34 | t.plan(2);
|
35 |
|
36 | var namespace = fresh('a', this);
|
37 |
|
38 | function handler() {
|
39 | t.ok(namespace.get('transaction'), "transaction should be visible");
|
40 | }
|
41 |
|
42 | t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
|
43 | runInTransaction('a', function () { setTimeout(handler, 100); });
|
44 | });
|
45 |
|
46 | t.test("b. async transaction with setInterval", function (t) {
|
47 | t.plan(4);
|
48 |
|
49 | var namespace = fresh('b', this)
|
50 | , count = 0
|
51 | , handle
|
52 | ;
|
53 |
|
54 | function handler() {
|
55 | count += 1;
|
56 | if (count > 2) clearInterval(handle);
|
57 | t.ok(namespace.get('transaction'), "transaction should be visible");
|
58 | }
|
59 |
|
60 | t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
|
61 | runInTransaction('b', function () { handle = setInterval(handler, 50); });
|
62 | });
|
63 |
|
64 | t.test("c. async transaction with process.nextTick", function (t) {
|
65 | t.plan(2);
|
66 |
|
67 | var namespace = fresh('c', this);
|
68 |
|
69 | function handler() {
|
70 | t.ok(namespace.get('transaction'), "transaction should be visible");
|
71 | }
|
72 |
|
73 | t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
|
74 | runInTransaction('c', function () { process.nextTick(handler); });
|
75 | });
|
76 |
|
77 | t.test("d. async transaction with EventEmitter.emit", function (t) {
|
78 | t.plan(2);
|
79 |
|
80 | var namespace = fresh('d', this)
|
81 | , ee = new EventEmitter()
|
82 | ;
|
83 |
|
84 | function handler() {
|
85 | t.ok(namespace.get('transaction'), "transaction should be visible");
|
86 | }
|
87 |
|
88 | t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
|
89 | runInTransaction('d', function () {
|
90 | ee.on('transaction', handler);
|
91 | ee.emit('transaction');
|
92 | });
|
93 | });
|
94 |
|
95 | t.test("e. two overlapping async transactions with setTimeout", function (t) {
|
96 | t.plan(6);
|
97 |
|
98 | var namespace = fresh('e', this)
|
99 | , first
|
100 | , second
|
101 | ;
|
102 |
|
103 | function handler(id) {
|
104 | t.ok(namespace.get('transaction'), "transaction should be visible");
|
105 | t.equal(namespace.get('transaction'), id, "transaction matches");
|
106 | }
|
107 |
|
108 | t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
|
109 | runInTransaction('e', function () {
|
110 | first = namespace.get('transaction');
|
111 | setTimeout(handler.bind(null, first), 100);
|
112 | });
|
113 |
|
114 | setTimeout(function () {
|
115 | runInTransaction('e', function () {
|
116 | second = namespace.get('transaction');
|
117 | t.notEqual(first, second, "different transaction IDs");
|
118 | setTimeout(handler.bind(null, second), 100);
|
119 | });
|
120 | }, 25);
|
121 | });
|
122 |
|
123 | t.test("f. two overlapping async transactions with setInterval", function (t) {
|
124 | t.plan(15);
|
125 |
|
126 | var namespace = fresh('f', this);
|
127 |
|
128 | function runInterval() {
|
129 | var count = 0
|
130 | , handle
|
131 | , id
|
132 | ;
|
133 |
|
134 | function handler() {
|
135 | count += 1;
|
136 | if (count > 2) clearInterval(handle);
|
137 | t.ok(namespace.get('transaction'), "transaction should be visible");
|
138 | t.equal(id, namespace.get('transaction'), "transaction ID should be immutable");
|
139 | }
|
140 |
|
141 | function run() {
|
142 | t.ok(namespace.get('transaction'), "transaction should have been created");
|
143 | id = namespace.get('transaction');
|
144 | handle = setInterval(handler, 50);
|
145 | }
|
146 |
|
147 | runInTransaction('f', run);
|
148 | }
|
149 |
|
150 | t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
|
151 | runInterval(); runInterval();
|
152 | });
|
153 |
|
154 | t.test("g. two overlapping async transactions with process.nextTick", function (t) {
|
155 | t.plan(6);
|
156 |
|
157 | var namespace = fresh('g', this)
|
158 | , first
|
159 | , second
|
160 | ;
|
161 |
|
162 | function handler(id) {
|
163 | var transaction = namespace.get('transaction');
|
164 | t.ok(transaction, "transaction should be visible");
|
165 | t.equal(transaction, id, "transaction matches");
|
166 | }
|
167 |
|
168 | t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
|
169 | runInTransaction('g', function () {
|
170 | first = namespace.get('transaction');
|
171 | process.nextTick(handler.bind(null, first));
|
172 | });
|
173 |
|
174 | process.nextTick(function () {
|
175 | runInTransaction('g', function () {
|
176 | second = namespace.get('transaction');
|
177 | t.notEqual(first, second, "different transaction IDs");
|
178 | process.nextTick(handler.bind(null, second));
|
179 | });
|
180 | });
|
181 | });
|
182 |
|
183 | t.test("h. two overlapping async runs with EventEmitter.prototype.emit", function (t) {
|
184 | t.plan(3);
|
185 |
|
186 | var namespace = fresh('h', this)
|
187 | , ee = new EventEmitter()
|
188 | ;
|
189 |
|
190 | function handler() {
|
191 | t.ok(namespace.get('transaction'), "transaction should be visible");
|
192 | }
|
193 |
|
194 | function lifecycle() {
|
195 | ee.once('transaction', process.nextTick.bind(process, handler));
|
196 | ee.emit('transaction');
|
197 | }
|
198 |
|
199 | t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
|
200 | runInTransaction('h', lifecycle);
|
201 | runInTransaction('h', lifecycle);
|
202 | });
|
203 |
|
204 | t.test("i. async transaction with an async sub-call with setTimeout", function (t) {
|
205 | t.plan(5);
|
206 |
|
207 | var namespace = fresh('i', this);
|
208 |
|
209 | function inner(callback) {
|
210 | setTimeout(function () {
|
211 | t.ok(namespace.get('transaction'), "transaction should (yep) still be visible");
|
212 | callback();
|
213 | }, 50);
|
214 | }
|
215 |
|
216 | function outer() {
|
217 | t.ok(namespace.get('transaction'), "transaction should be visible");
|
218 | setTimeout(function () {
|
219 | t.ok(namespace.get('transaction'), "transaction should still be visible");
|
220 | inner(function () {
|
221 | t.ok(namespace.get('transaction'), "transaction should even still be visible");
|
222 | });
|
223 | }, 50);
|
224 | }
|
225 |
|
226 | t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
|
227 | runInTransaction('i', setTimeout.bind(null, outer, 50));
|
228 | });
|
229 |
|
230 | t.test("j. async transaction with an async sub-call with setInterval", function (t) {
|
231 | t.plan(5);
|
232 |
|
233 | var namespace = fresh('j', this)
|
234 | , outerHandle
|
235 | , innerHandle
|
236 | ;
|
237 |
|
238 | function inner(callback) {
|
239 | innerHandle = setInterval(function () {
|
240 | clearInterval(innerHandle);
|
241 | t.ok(namespace.get('transaction'), "transaction should (yep) still be visible");
|
242 | callback();
|
243 | }, 50);
|
244 | }
|
245 |
|
246 | function outer() {
|
247 | t.ok(namespace.get('transaction'), "transaction should be visible");
|
248 | outerHandle = setInterval(function () {
|
249 | clearInterval(outerHandle);
|
250 | t.ok(namespace.get('transaction'), "transaction should still be visible");
|
251 | inner(function () {
|
252 | t.ok(namespace.get('transaction'), "transaction should even still be visible");
|
253 | });
|
254 | }, 50);
|
255 | }
|
256 |
|
257 | t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
|
258 | runInTransaction('j', outer);
|
259 | });
|
260 |
|
261 | t.test("k. async transaction with an async call with process.nextTick", function (t) {
|
262 | t.plan(5);
|
263 |
|
264 | var namespace = fresh('k', this);
|
265 |
|
266 | function inner(callback) {
|
267 | process.nextTick(function () {
|
268 | t.ok(namespace.get('transaction'), "transaction should (yep) still be visible");
|
269 | callback();
|
270 | });
|
271 | }
|
272 |
|
273 | function outer() {
|
274 | t.ok(namespace.get('transaction'), "transaction should be visible");
|
275 | process.nextTick(function () {
|
276 | t.ok(namespace.get('transaction'), "transaction should still be visible");
|
277 | inner(function () {
|
278 | t.ok(namespace.get('transaction'), "transaction should even still be visible");
|
279 | });
|
280 | });
|
281 | }
|
282 |
|
283 | t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
|
284 | runInTransaction('k', function () { process.nextTick(outer); });
|
285 | });
|
286 |
|
287 | t.test("l. async transaction with an async call with EventEmitter.emit", function (t) {
|
288 | t.plan(4);
|
289 |
|
290 | var namespace = fresh('l', this)
|
291 | , outer = new EventEmitter()
|
292 | , inner = new EventEmitter()
|
293 | ;
|
294 |
|
295 | inner.on('pong', function (callback) {
|
296 | t.ok(namespace.get('transaction'), "transaction should still be visible");
|
297 | callback();
|
298 | });
|
299 |
|
300 | function outerCallback() {
|
301 | t.ok(namespace.get('transaction'), "transaction should even still be visible");
|
302 | }
|
303 |
|
304 | outer.on('ping', function () {
|
305 | t.ok(namespace.get('transaction'), "transaction should be visible");
|
306 | inner.emit('pong', outerCallback);
|
307 | });
|
308 |
|
309 | t.notOk(namespace.get('transaction'), "transaction should not yet be visible");
|
310 | runInTransaction('l', outer.emit.bind(outer, 'ping'));
|
311 | });
|
312 | });
|