tracer-scenarios.tap.js 9.6 KB

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