index.test.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  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){ throwTestError(); },
  163. async: throwTestError,
  164. }).length
  165. ).toBe(7);
  166. });
  167. test("explicit arity", () => {
  168. expect(
  169. gensync({
  170. arity: 3,
  171. sync: throwTestError,
  172. async: throwTestError,
  173. }).length
  174. ).toBe(3);
  175. });
  176. });
  177. describe("'sync' handler", async () => {
  178. test("success", async () => {
  179. const fn = gensync({
  180. sync: (...args) => JSON.stringify(args),
  181. });
  182. await expectResult(fn, 42, { value: "[42]", expectSync: true });
  183. });
  184. test("failure", async () => {
  185. const fn = gensync({
  186. sync: (...args) => {
  187. throw JSON.stringify(args);
  188. },
  189. });
  190. await expectResult(fn, 42, { error: "[42]", expectSync: true });
  191. });
  192. });
  193. describe("'async' handler", async () => {
  194. test("success", async () => {
  195. const fn = gensync({
  196. sync: throwTestError,
  197. async: (...args) => Promise.resolve(JSON.stringify(args)),
  198. });
  199. await expectResult(fn, 42, { value: "[42]" });
  200. });
  201. test("failure", async () => {
  202. const fn = gensync({
  203. sync: throwTestError,
  204. async: (...args) => Promise.reject(JSON.stringify(args)),
  205. });
  206. await expectResult(fn, 42, { error: "[42]" });
  207. });
  208. });
  209. describe("'errback' sync handler", async () => {
  210. test("success", async () => {
  211. const fn = gensync({
  212. sync: throwTestError,
  213. errback: (...args) => args.pop()(null, JSON.stringify(args)),
  214. });
  215. await expectResult(fn, 42, { value: "[42]", syncErrback: true });
  216. });
  217. test("failure", async () => {
  218. const fn = gensync({
  219. sync: throwTestError,
  220. errback: (...args) => args.pop()(JSON.stringify(args)),
  221. });
  222. await expectResult(fn, 42, { error: "[42]", syncErrback: true });
  223. });
  224. });
  225. describe("'errback' async handler", async () => {
  226. test("success", async () => {
  227. const fn = gensync({
  228. sync: throwTestError,
  229. errback: (...args) =>
  230. process.nextTick(() => args.pop()(null, JSON.stringify(args))),
  231. });
  232. await expectResult(fn, 42, { value: "[42]" });
  233. });
  234. test("failure", async () => {
  235. const fn = gensync({
  236. sync: throwTestError,
  237. errback: (...args) =>
  238. process.nextTick(() => args.pop()(JSON.stringify(args))),
  239. });
  240. await expectResult(fn, 42, { error: "[42]" });
  241. });
  242. });
  243. });
  244. describe("gensync(function* () {})", () => {
  245. test("sync throw before body", async () => {
  246. const fn = gensync(function*(arg = throwTestError()) {});
  247. await expectResult(fn, undefined, {
  248. error: TEST_ERROR,
  249. syncErrback: true,
  250. });
  251. });
  252. test("sync throw inside body", async () => {
  253. const fn = gensync(function*() {
  254. throwTestError();
  255. });
  256. await expectResult(fn, undefined, {
  257. error: TEST_ERROR,
  258. syncErrback: true,
  259. });
  260. });
  261. test("async throw inside body", async () => {
  262. const fn = gensync(function*() {
  263. const val = yield* doSuccess();
  264. throwTestError();
  265. });
  266. await expectResult(fn, undefined, {
  267. error: TEST_ERROR,
  268. });
  269. });
  270. test("error inside body", async () => {
  271. const fn = gensync(function*() {
  272. yield* doError();
  273. });
  274. await expectResult(fn, undefined, {
  275. error: DID_ERROR,
  276. expectSync: true,
  277. syncErrback: false,
  278. });
  279. });
  280. test("successful return value", async () => {
  281. const fn = gensync(function*() {
  282. const value = yield* doSuccess();
  283. expect(value).toBe(42);
  284. return 84;
  285. });
  286. await expectResult(fn, undefined, {
  287. value: 84,
  288. expectSync: true,
  289. syncErrback: false,
  290. });
  291. });
  292. test("successful final value", async () => {
  293. const fn = gensync(function*() {
  294. return 42;
  295. });
  296. await expectResult(fn, undefined, {
  297. value: 42,
  298. expectSync: true,
  299. });
  300. });
  301. test("yield unexpected object", async () => {
  302. const fn = gensync(function*() {
  303. yield {};
  304. });
  305. try {
  306. await fn.async();
  307. throwTestError();
  308. } catch (err) {
  309. expect(err.message).toMatch(
  310. /Got unexpected yielded value in gensync generator/
  311. );
  312. expect(err.code).toBe("GENSYNC_EXPECTED_START");
  313. }
  314. });
  315. test("yield suspend yield", async () => {
  316. const fn = gensync(function*() {
  317. yield Symbol.for("gensync:v1:start");
  318. // Should be "yield*" for no error.
  319. yield {};
  320. });
  321. try {
  322. await fn.async();
  323. throwTestError();
  324. } catch (err) {
  325. expect(err.message).toMatch(/Expected GENSYNC_SUSPEND, got {}/);
  326. expect(err.code).toBe("GENSYNC_EXPECTED_SUSPEND");
  327. }
  328. });
  329. test("yield suspend return", async () => {
  330. const fn = gensync(function*() {
  331. yield Symbol.for("gensync:v1:start");
  332. // Should be "yield*" for no error.
  333. return {};
  334. });
  335. try {
  336. await fn.async();
  337. throwTestError();
  338. } catch (err) {
  339. expect(err.message).toMatch(/Unexpected generator completion/);
  340. expect(err.code).toBe("GENSYNC_EXPECTED_SUSPEND");
  341. }
  342. });
  343. });
  344. describe("gensync.all()", () => {
  345. test("success", async () => {
  346. const fn = gensync(function*() {
  347. const result = yield* gensync.all([doSuccess(), doSuccess()]);
  348. expect(result).toEqual([42, 42]);
  349. });
  350. await expectResult(fn, undefined, {
  351. value: undefined,
  352. expectSync: true,
  353. syncErrback: false,
  354. });
  355. });
  356. test("error first", async () => {
  357. const fn = gensync(function*() {
  358. yield* gensync.all([doError(), doSuccess()]);
  359. });
  360. await expectResult(fn, undefined, {
  361. error: DID_ERROR,
  362. expectSync: true,
  363. syncErrback: false,
  364. });
  365. });
  366. test("error last", async () => {
  367. const fn = gensync(function*() {
  368. yield* gensync.all([doSuccess(), doError()]);
  369. });
  370. await expectResult(fn, undefined, {
  371. error: DID_ERROR,
  372. expectSync: true,
  373. syncErrback: false,
  374. });
  375. });
  376. });
  377. describe("gensync.race()", () => {
  378. test("success", async () => {
  379. const fn = gensync(function*() {
  380. const result = yield* gensync.race([doSuccess(), doError()]);
  381. expect(result).toEqual(42);
  382. });
  383. await expectResult(fn, undefined, {
  384. value: undefined,
  385. expectSync: true,
  386. syncErrback: false,
  387. });
  388. });
  389. test("error", async () => {
  390. const fn = gensync(function*() {
  391. yield* gensync.race([doError(), doSuccess()]);
  392. });
  393. await expectResult(fn, undefined, {
  394. error: DID_ERROR,
  395. expectSync: true,
  396. syncErrback: false,
  397. });
  398. });
  399. });