index.test.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. "use strict";
  2. const promisify = require("util.promisify");
  3. const gensync = require("../");
  4. const TEST_ERROR = new Error("TEST_ERROR");
  5. const DID_ERROR = new Error("DID_ERROR");
  6. const doSuccess = gensync({
  7. sync: () => 42,
  8. async: () => Promise.resolve(42),
  9. });
  10. const doError = gensync({
  11. sync: () => {
  12. throw DID_ERROR;
  13. },
  14. async: () => Promise.reject(DID_ERROR),
  15. });
  16. function throwTestError() {
  17. throw TEST_ERROR;
  18. }
  19. async function expectResult(
  20. fn,
  21. arg,
  22. { error, value, expectSync = false, syncErrback = expectSync }
  23. ) {
  24. if (!expectSync) {
  25. expect(() => fn.sync(arg)).toThrow(TEST_ERROR);
  26. } else if (error) {
  27. expect(() => fn.sync(arg)).toThrow(error);
  28. } else {
  29. expect(fn.sync(arg)).toBe(value);
  30. }
  31. if (error) {
  32. await expect(fn.async(arg)).rejects.toBe(error);
  33. } else {
  34. await expect(fn.async(arg)).resolves.toBe(value);
  35. }
  36. await new Promise((resolve, reject) => {
  37. let sync = true;
  38. fn.errback(arg, (err, val) => {
  39. try {
  40. expect(err).toBe(error);
  41. expect(val).toBe(value);
  42. expect(sync).toBe(syncErrback);
  43. resolve();
  44. } catch (e) {
  45. reject(e);
  46. }
  47. });
  48. sync = false;
  49. });
  50. }
  51. describe("gensync({})", () => {
  52. describe("option validation", () => {
  53. test("disallow async and errback handler together", () => {
  54. try {
  55. gensync({
  56. sync: throwTestError,
  57. async: throwTestError,
  58. errback: throwTestError,
  59. });
  60. throwTestError();
  61. } catch (err) {
  62. expect(err.message).toMatch(
  63. /Expected one of either opts.async or opts.errback, but got _both_\./
  64. );
  65. expect(err.code).toBe("GENSYNC_OPTIONS_ERROR");
  66. }
  67. });
  68. test("disallow missing sync handler", () => {
  69. try {
  70. gensync({
  71. async: throwTestError,
  72. });
  73. throwTestError();
  74. } catch (err) {
  75. expect(err.message).toMatch(/Expected opts.sync to be a function./);
  76. expect(err.code).toBe("GENSYNC_OPTIONS_ERROR");
  77. }
  78. });
  79. test("errback callback required", () => {
  80. const fn = gensync({
  81. sync: throwTestError,
  82. async: throwTestError,
  83. });
  84. try {
  85. fn.errback();
  86. throwTestError();
  87. } catch (err) {
  88. expect(err.message).toMatch(/function called without callback/);
  89. expect(err.code).toBe("GENSYNC_ERRBACK_NO_CALLBACK");
  90. }
  91. });
  92. });
  93. describe("generator function metadata", () => {
  94. test("automatic naming", () => {
  95. expect(
  96. gensync({
  97. sync: function readFileSync() {},
  98. async: () => {},
  99. }).name
  100. ).toBe("readFile");
  101. expect(
  102. gensync({
  103. sync: function readFile() {},
  104. async: () => {},
  105. }).name
  106. ).toBe("readFile");
  107. expect(
  108. gensync({
  109. sync: function readFileAsync() {},
  110. async: () => {},
  111. }).name
  112. ).toBe("readFileAsync");
  113. expect(
  114. gensync({
  115. sync: () => {},
  116. async: function readFileSync() {},
  117. }).name
  118. ).toBe("readFileSync");
  119. expect(
  120. gensync({
  121. sync: () => {},
  122. async: function readFile() {},
  123. }).name
  124. ).toBe("readFile");
  125. expect(
  126. gensync({
  127. sync: () => {},
  128. async: function readFileAsync() {},
  129. }).name
  130. ).toBe("readFile");
  131. expect(
  132. gensync({
  133. sync: () => {},
  134. errback: function readFileSync() {},
  135. }).name
  136. ).toBe("readFileSync");
  137. expect(
  138. gensync({
  139. sync: () => {},
  140. errback: function readFile() {},
  141. }).name
  142. ).toBe("readFile");
  143. expect(
  144. gensync({
  145. sync: () => {},
  146. errback: function readFileAsync() {},
  147. }).name
  148. ).toBe("readFileAsync");
  149. });
  150. test("explicit naming", () => {
  151. expect(
  152. gensync({
  153. name: "readFile",
  154. sync: () => {},
  155. async: () => {},
  156. }).name
  157. ).toBe("readFile");
  158. });
  159. test("default arity", () => {
  160. expect(
  161. gensync({
  162. sync: function(a, b, c, d, e, f, g) {
  163. throwTestError();
  164. },
  165. async: throwTestError,
  166. }).length
  167. ).toBe(7);
  168. });
  169. test("explicit arity", () => {
  170. expect(
  171. gensync({
  172. arity: 3,
  173. sync: throwTestError,
  174. async: throwTestError,
  175. }).length
  176. ).toBe(3);
  177. });
  178. });
  179. describe("'sync' handler", async () => {
  180. test("success", async () => {
  181. const fn = gensync({
  182. sync: (...args) => JSON.stringify(args),
  183. });
  184. await expectResult(fn, 42, { value: "[42]", expectSync: true });
  185. });
  186. test("failure", async () => {
  187. const fn = gensync({
  188. sync: (...args) => {
  189. throw JSON.stringify(args);
  190. },
  191. });
  192. await expectResult(fn, 42, { error: "[42]", expectSync: true });
  193. });
  194. });
  195. describe("'async' handler", async () => {
  196. test("success", async () => {
  197. const fn = gensync({
  198. sync: throwTestError,
  199. async: (...args) => Promise.resolve(JSON.stringify(args)),
  200. });
  201. await expectResult(fn, 42, { value: "[42]" });
  202. });
  203. test("failure", async () => {
  204. const fn = gensync({
  205. sync: throwTestError,
  206. async: (...args) => Promise.reject(JSON.stringify(args)),
  207. });
  208. await expectResult(fn, 42, { error: "[42]" });
  209. });
  210. });
  211. describe("'errback' sync handler", async () => {
  212. test("success", async () => {
  213. const fn = gensync({
  214. sync: throwTestError,
  215. errback: (...args) => args.pop()(null, JSON.stringify(args)),
  216. });
  217. await expectResult(fn, 42, { value: "[42]", syncErrback: true });
  218. });
  219. test("failure", async () => {
  220. const fn = gensync({
  221. sync: throwTestError,
  222. errback: (...args) => args.pop()(JSON.stringify(args)),
  223. });
  224. await expectResult(fn, 42, { error: "[42]", syncErrback: true });
  225. });
  226. });
  227. describe("'errback' async handler", async () => {
  228. test("success", async () => {
  229. const fn = gensync({
  230. sync: throwTestError,
  231. errback: (...args) =>
  232. process.nextTick(() => args.pop()(null, JSON.stringify(args))),
  233. });
  234. await expectResult(fn, 42, { value: "[42]" });
  235. });
  236. test("failure", async () => {
  237. const fn = gensync({
  238. sync: throwTestError,
  239. errback: (...args) =>
  240. process.nextTick(() => args.pop()(JSON.stringify(args))),
  241. });
  242. await expectResult(fn, 42, { error: "[42]" });
  243. });
  244. });
  245. });
  246. describe("gensync(function* () {})", () => {
  247. test("sync throw before body", async () => {
  248. const fn = gensync(function*(arg = throwTestError()) {});
  249. await expectResult(fn, undefined, {
  250. error: TEST_ERROR,
  251. syncErrback: true,
  252. });
  253. });
  254. test("sync throw inside body", async () => {
  255. const fn = gensync(function*() {
  256. throwTestError();
  257. });
  258. await expectResult(fn, undefined, {
  259. error: TEST_ERROR,
  260. syncErrback: true,
  261. });
  262. });
  263. test("async throw inside body", async () => {
  264. const fn = gensync(function*() {
  265. const val = yield* doSuccess();
  266. throwTestError();
  267. });
  268. await expectResult(fn, undefined, {
  269. error: TEST_ERROR,
  270. });
  271. });
  272. test("error inside body", async () => {
  273. const fn = gensync(function*() {
  274. yield* doError();
  275. });
  276. await expectResult(fn, undefined, {
  277. error: DID_ERROR,
  278. expectSync: true,
  279. syncErrback: false,
  280. });
  281. });
  282. test("successful return value", async () => {
  283. const fn = gensync(function*() {
  284. const value = yield* doSuccess();
  285. expect(value).toBe(42);
  286. return 84;
  287. });
  288. await expectResult(fn, undefined, {
  289. value: 84,
  290. expectSync: true,
  291. syncErrback: false,
  292. });
  293. });
  294. test("successful final value", async () => {
  295. const fn = gensync(function*() {
  296. return 42;
  297. });
  298. await expectResult(fn, undefined, {
  299. value: 42,
  300. expectSync: true,
  301. });
  302. });
  303. test("yield unexpected object", async () => {
  304. const fn = gensync(function*() {
  305. yield {};
  306. });
  307. try {
  308. await fn.async();
  309. throwTestError();
  310. } catch (err) {
  311. expect(err.message).toMatch(
  312. /Got unexpected yielded value in gensync generator/
  313. );
  314. expect(err.code).toBe("GENSYNC_EXPECTED_START");
  315. }
  316. });
  317. test("yield suspend yield", async () => {
  318. const fn = gensync(function*() {
  319. yield Symbol.for("gensync:v1:start");
  320. // Should be "yield*" for no error.
  321. yield {};
  322. });
  323. try {
  324. await fn.async();
  325. throwTestError();
  326. } catch (err) {
  327. expect(err.message).toMatch(/Expected GENSYNC_SUSPEND, got {}/);
  328. expect(err.code).toBe("GENSYNC_EXPECTED_SUSPEND");
  329. }
  330. });
  331. test("yield suspend return", async () => {
  332. const fn = gensync(function*() {
  333. yield Symbol.for("gensync:v1:start");
  334. // Should be "yield*" for no error.
  335. return {};
  336. });
  337. try {
  338. await fn.async();
  339. throwTestError();
  340. } catch (err) {
  341. expect(err.message).toMatch(/Unexpected generator completion/);
  342. expect(err.code).toBe("GENSYNC_EXPECTED_SUSPEND");
  343. }
  344. });
  345. });
  346. describe("gensync.all()", () => {
  347. test("success", async () => {
  348. const fn = gensync(function*() {
  349. const result = yield* gensync.all([doSuccess(), doSuccess()]);
  350. expect(result).toEqual([42, 42]);
  351. });
  352. await expectResult(fn, undefined, {
  353. value: undefined,
  354. expectSync: true,
  355. syncErrback: false,
  356. });
  357. });
  358. test("error first", async () => {
  359. const fn = gensync(function*() {
  360. yield* gensync.all([doError(), doSuccess()]);
  361. });
  362. await expectResult(fn, undefined, {
  363. error: DID_ERROR,
  364. expectSync: true,
  365. syncErrback: false,
  366. });
  367. });
  368. test("error last", async () => {
  369. const fn = gensync(function*() {
  370. yield* gensync.all([doSuccess(), doError()]);
  371. });
  372. await expectResult(fn, undefined, {
  373. error: DID_ERROR,
  374. expectSync: true,
  375. syncErrback: false,
  376. });
  377. });
  378. test("empty list", async () => {
  379. const fn = gensync(function*() {
  380. yield* gensync.all([]);
  381. });
  382. await expectResult(fn, undefined, {
  383. value: undefined,
  384. expectSync: true,
  385. syncErrback: false,
  386. });
  387. });
  388. });
  389. describe("gensync.race()", () => {
  390. test("success", async () => {
  391. const fn = gensync(function*() {
  392. const result = yield* gensync.race([doSuccess(), doError()]);
  393. expect(result).toEqual(42);
  394. });
  395. await expectResult(fn, undefined, {
  396. value: undefined,
  397. expectSync: true,
  398. syncErrback: false,
  399. });
  400. });
  401. test("error", async () => {
  402. const fn = gensync(function*() {
  403. yield* gensync.race([doError(), doSuccess()]);
  404. });
  405. await expectResult(fn, undefined, {
  406. error: DID_ERROR,
  407. expectSync: true,
  408. syncErrback: false,
  409. });
  410. });
  411. });