Spaces:
Configuration error
Configuration error
; | |
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, | |
}); | |
}); | |
}); | |