123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- "use strict";
- const promisify = require("util.promisify");
- const gensync = require("../");
- const TEST_ERROR = new Error("TEST_ERROR");
- const DID_ERROR = new Error("DID_ERROR");
- const doSuccess = gensync({
- sync: () => 42,
- async: () => Promise.resolve(42),
- });
- const doError = gensync({
- sync: () => {
- throw DID_ERROR;
- },
- async: () => Promise.reject(DID_ERROR),
- });
- function throwTestError() {
- throw TEST_ERROR;
- }
- async function expectResult(
- fn,
- arg,
- { error, value, expectSync = false, syncErrback = expectSync }
- ) {
- if (!expectSync) {
- expect(() => fn.sync(arg)).toThrow(TEST_ERROR);
- } else if (error) {
- expect(() => fn.sync(arg)).toThrow(error);
- } else {
- expect(fn.sync(arg)).toBe(value);
- }
- if (error) {
- await expect(fn.async(arg)).rejects.toBe(error);
- } else {
- await expect(fn.async(arg)).resolves.toBe(value);
- }
- await new Promise((resolve, reject) => {
- let sync = true;
- fn.errback(arg, (err, val) => {
- try {
- expect(err).toBe(error);
- expect(val).toBe(value);
- expect(sync).toBe(syncErrback);
- resolve();
- } catch (e) {
- reject(e);
- }
- });
- sync = false;
- });
- }
- describe("gensync({})", () => {
- describe("option validation", () => {
- test("disallow async and errback handler together", () => {
- try {
- gensync({
- sync: throwTestError,
- async: throwTestError,
- errback: throwTestError,
- });
- throwTestError();
- } catch (err) {
- expect(err.message).toMatch(
- /Expected one of either opts.async or opts.errback, but got _both_\./
- );
- expect(err.code).toBe("GENSYNC_OPTIONS_ERROR");
- }
- });
- test("disallow missing sync handler", () => {
- try {
- gensync({
- async: throwTestError,
- });
- throwTestError();
- } catch (err) {
- expect(err.message).toMatch(/Expected opts.sync to be a function./);
- expect(err.code).toBe("GENSYNC_OPTIONS_ERROR");
- }
- });
- test("errback callback required", () => {
- const fn = gensync({
- sync: throwTestError,
- async: throwTestError,
- });
- try {
- fn.errback();
- throwTestError();
- } catch (err) {
- expect(err.message).toMatch(/function called without callback/);
- expect(err.code).toBe("GENSYNC_ERRBACK_NO_CALLBACK");
- }
- });
- });
- describe("generator function metadata", () => {
- test("automatic naming", () => {
- expect(
- gensync({
- sync: function readFileSync() {},
- async: () => {},
- }).name
- ).toBe("readFile");
- expect(
- gensync({
- sync: function readFile() {},
- async: () => {},
- }).name
- ).toBe("readFile");
- expect(
- gensync({
- sync: function readFileAsync() {},
- async: () => {},
- }).name
- ).toBe("readFileAsync");
- expect(
- gensync({
- sync: () => {},
- async: function readFileSync() {},
- }).name
- ).toBe("readFileSync");
- expect(
- gensync({
- sync: () => {},
- async: function readFile() {},
- }).name
- ).toBe("readFile");
- expect(
- gensync({
- sync: () => {},
- async: function readFileAsync() {},
- }).name
- ).toBe("readFile");
- expect(
- gensync({
- sync: () => {},
- errback: function readFileSync() {},
- }).name
- ).toBe("readFileSync");
- expect(
- gensync({
- sync: () => {},
- errback: function readFile() {},
- }).name
- ).toBe("readFile");
- expect(
- gensync({
- sync: () => {},
- errback: function readFileAsync() {},
- }).name
- ).toBe("readFileAsync");
- });
- test("explicit naming", () => {
- expect(
- gensync({
- name: "readFile",
- sync: () => {},
- async: () => {},
- }).name
- ).toBe("readFile");
- });
- test("default arity", () => {
- expect(
- gensync({
- sync: function(a, b, c, d, e, f, g) {
- throwTestError();
- },
- async: throwTestError,
- }).length
- ).toBe(7);
- });
- test("explicit arity", () => {
- expect(
- gensync({
- arity: 3,
- sync: throwTestError,
- async: throwTestError,
- }).length
- ).toBe(3);
- });
- });
- describe("'sync' handler", async () => {
- test("success", async () => {
- const fn = gensync({
- sync: (...args) => JSON.stringify(args),
- });
- await expectResult(fn, 42, { value: "[42]", expectSync: true });
- });
- test("failure", async () => {
- const fn = gensync({
- sync: (...args) => {
- throw JSON.stringify(args);
- },
- });
- await expectResult(fn, 42, { error: "[42]", expectSync: true });
- });
- });
- describe("'async' handler", async () => {
- test("success", async () => {
- const fn = gensync({
- sync: throwTestError,
- async: (...args) => Promise.resolve(JSON.stringify(args)),
- });
- await expectResult(fn, 42, { value: "[42]" });
- });
- test("failure", async () => {
- const fn = gensync({
- sync: throwTestError,
- async: (...args) => Promise.reject(JSON.stringify(args)),
- });
- await expectResult(fn, 42, { error: "[42]" });
- });
- });
- describe("'errback' sync handler", async () => {
- test("success", async () => {
- const fn = gensync({
- sync: throwTestError,
- errback: (...args) => args.pop()(null, JSON.stringify(args)),
- });
- await expectResult(fn, 42, { value: "[42]", syncErrback: true });
- });
- test("failure", async () => {
- const fn = gensync({
- sync: throwTestError,
- errback: (...args) => args.pop()(JSON.stringify(args)),
- });
- await expectResult(fn, 42, { error: "[42]", syncErrback: true });
- });
- });
- describe("'errback' async handler", async () => {
- test("success", async () => {
- const fn = gensync({
- sync: throwTestError,
- errback: (...args) =>
- process.nextTick(() => args.pop()(null, JSON.stringify(args))),
- });
- await expectResult(fn, 42, { value: "[42]" });
- });
- test("failure", async () => {
- const fn = gensync({
- sync: throwTestError,
- errback: (...args) =>
- process.nextTick(() => args.pop()(JSON.stringify(args))),
- });
- await expectResult(fn, 42, { error: "[42]" });
- });
- });
- });
- describe("gensync(function* () {})", () => {
- test("sync throw before body", async () => {
- const fn = gensync(function*(arg = throwTestError()) {});
- await expectResult(fn, undefined, {
- error: TEST_ERROR,
- syncErrback: true,
- });
- });
- test("sync throw inside body", async () => {
- const fn = gensync(function*() {
- throwTestError();
- });
- await expectResult(fn, undefined, {
- error: TEST_ERROR,
- syncErrback: true,
- });
- });
- test("async throw inside body", async () => {
- const fn = gensync(function*() {
- const val = yield* doSuccess();
- throwTestError();
- });
- await expectResult(fn, undefined, {
- error: TEST_ERROR,
- });
- });
- test("error inside body", async () => {
- const fn = gensync(function*() {
- yield* doError();
- });
- await expectResult(fn, undefined, {
- error: DID_ERROR,
- expectSync: true,
- syncErrback: false,
- });
- });
- test("successful return value", async () => {
- const fn = gensync(function*() {
- const value = yield* doSuccess();
- expect(value).toBe(42);
- return 84;
- });
- await expectResult(fn, undefined, {
- value: 84,
- expectSync: true,
- syncErrback: false,
- });
- });
- test("successful final value", async () => {
- const fn = gensync(function*() {
- return 42;
- });
- await expectResult(fn, undefined, {
- value: 42,
- expectSync: true,
- });
- });
- test("yield unexpected object", async () => {
- const fn = gensync(function*() {
- yield {};
- });
- try {
- await fn.async();
- throwTestError();
- } catch (err) {
- expect(err.message).toMatch(
- /Got unexpected yielded value in gensync generator/
- );
- expect(err.code).toBe("GENSYNC_EXPECTED_START");
- }
- });
- test("yield suspend yield", async () => {
- const fn = gensync(function*() {
- yield Symbol.for("gensync:v1:start");
- // Should be "yield*" for no error.
- yield {};
- });
- try {
- await fn.async();
- throwTestError();
- } catch (err) {
- expect(err.message).toMatch(/Expected GENSYNC_SUSPEND, got {}/);
- expect(err.code).toBe("GENSYNC_EXPECTED_SUSPEND");
- }
- });
- test("yield suspend return", async () => {
- const fn = gensync(function*() {
- yield Symbol.for("gensync:v1:start");
- // Should be "yield*" for no error.
- return {};
- });
- try {
- await fn.async();
- throwTestError();
- } catch (err) {
- expect(err.message).toMatch(/Unexpected generator completion/);
- expect(err.code).toBe("GENSYNC_EXPECTED_SUSPEND");
- }
- });
- });
- describe("gensync.all()", () => {
- test("success", async () => {
- const fn = gensync(function*() {
- const result = yield* gensync.all([doSuccess(), doSuccess()]);
- expect(result).toEqual([42, 42]);
- });
- await expectResult(fn, undefined, {
- value: undefined,
- expectSync: true,
- syncErrback: false,
- });
- });
- test("error first", async () => {
- const fn = gensync(function*() {
- yield* gensync.all([doError(), doSuccess()]);
- });
- await expectResult(fn, undefined, {
- error: DID_ERROR,
- expectSync: true,
- syncErrback: false,
- });
- });
- test("error last", async () => {
- const fn = gensync(function*() {
- yield* gensync.all([doSuccess(), doError()]);
- });
- await expectResult(fn, undefined, {
- error: DID_ERROR,
- expectSync: true,
- syncErrback: false,
- });
- });
- test("empty list", async () => {
- const fn = gensync(function*() {
- yield* gensync.all([]);
- });
- await expectResult(fn, undefined, {
- value: undefined,
- expectSync: true,
- syncErrback: false,
- });
- });
- });
- describe("gensync.race()", () => {
- test("success", async () => {
- const fn = gensync(function*() {
- const result = yield* gensync.race([doSuccess(), doError()]);
- expect(result).toEqual(42);
- });
- await expectResult(fn, undefined, {
- value: undefined,
- expectSync: true,
- syncErrback: false,
- });
- });
- test("error", async () => {
- const fn = gensync(function*() {
- yield* gensync.race([doError(), doSuccess()]);
- });
- await expectResult(fn, undefined, {
- error: DID_ERROR,
- expectSync: true,
- syncErrback: false,
- });
- });
- });
|