gristlabs_grist-core/test/common/AsyncCreate.ts

145 lines
6.0 KiB
TypeScript
Raw Normal View History

import {AsyncCreate, asyncOnce, mapGetOrSet} from 'app/common/AsyncCreate';
import {assert} from 'chai';
import * as sinon from 'sinon';
describe('AsyncCreate', function() {
it('should call create func on first use and after failure', async function() {
const createFunc = sinon.stub();
const cp = new AsyncCreate(createFunc);
sinon.assert.notCalled(createFunc);
const value = {hello: 'world'};
createFunc.returns(Promise.resolve(value));
// Check that .get() calls the createFunc and returns the expected value.
assert.strictEqual(await cp.get(), value);
sinon.assert.calledOnce(createFunc);
createFunc.resetHistory();
// Subsequent calls return the cached value.
assert.strictEqual(await cp.get(), value);
sinon.assert.notCalled(createFunc);
// After clearing, .get() calls createFunc again. We'll make this one fail.
cp.clear();
createFunc.returns(Promise.reject(new Error('fake-error1')));
await assert.isRejected(cp.get(), /fake-error1/);
sinon.assert.calledOnce(createFunc);
createFunc.resetHistory();
// After failure, subsequent calls try again.
createFunc.returns(Promise.reject(new Error('fake-error2')));
await assert.isRejected(cp.get(), /fake-error2/);
sinon.assert.calledOnce(createFunc);
createFunc.resetHistory();
// While a createFunc() is pending we do NOT call it again.
createFunc.returns(Promise.reject(new Error('fake-error3')));
await Promise.all([
assert.isRejected(cp.get(), /fake-error3/),
assert.isRejected(cp.get(), /fake-error3/),
]);
sinon.assert.calledOnce(createFunc); // Called just once here.
createFunc.resetHistory();
});
it('asyncOnce should call func once and after failure', async function() {
const createFunc = sinon.stub();
let onceFunc = asyncOnce(createFunc);
sinon.assert.notCalled(createFunc);
const value = {hello: 'world'};
createFunc.returns(Promise.resolve(value));
// Check that .get() calls the createFunc and returns the expected value.
assert.strictEqual(await onceFunc(), value);
sinon.assert.calledOnce(createFunc);
createFunc.resetHistory();
// Subsequent calls return the cached value.
assert.strictEqual(await onceFunc(), value);
sinon.assert.notCalled(createFunc);
// Create a new onceFunc. We'll make this one fail.
onceFunc = asyncOnce(createFunc);
createFunc.returns(Promise.reject(new Error('fake-error1')));
await assert.isRejected(onceFunc(), /fake-error1/);
sinon.assert.calledOnce(createFunc);
createFunc.resetHistory();
// After failure, subsequent calls try again.
createFunc.returns(Promise.reject(new Error('fake-error2')));
await assert.isRejected(onceFunc(), /fake-error2/);
sinon.assert.calledOnce(createFunc);
createFunc.resetHistory();
// While a createFunc() is pending we do NOT call it again.
createFunc.returns(Promise.reject(new Error('fake-error3')));
await Promise.all([
assert.isRejected(onceFunc(), /fake-error3/),
assert.isRejected(onceFunc(), /fake-error3/),
]);
sinon.assert.calledOnce(createFunc); // Called just once here.
createFunc.resetHistory();
});
describe("mapGetOrSet", function() {
it('should call create func on first use and after failure', async function() {
const createFunc = sinon.stub();
const amap = new Map<string, any>();
createFunc.callsFake(async (key: string) => ({myKey: key.toUpperCase()}));
// Check that mapGetOrSet() calls the createFunc and returns the expected value.
assert.deepEqual(await mapGetOrSet(amap, "foo", createFunc), {myKey: "FOO"});
assert.deepEqual(await mapGetOrSet(amap, "bar", createFunc), {myKey: "BAR"});
sinon.assert.calledTwice(createFunc);
createFunc.resetHistory();
// Subsequent calls return the cached value.
assert.deepEqual(await mapGetOrSet(amap, "foo", createFunc), {myKey: "FOO"});
assert.deepEqual(await mapGetOrSet(amap, "bar", createFunc), {myKey: "BAR"});
sinon.assert.notCalled(createFunc);
// Calls to plain .get() also return the cached value.
assert.deepEqual(await amap.get("foo"), {myKey: "FOO"});
assert.deepEqual(await amap.get("bar"), {myKey: "BAR"});
sinon.assert.notCalled(createFunc);
// After clearing, .get() returns undefined. (The usual Map behavior.)
amap.delete("foo");
assert.strictEqual(await amap.get("foo"), undefined);
// After clearing, mapGetOrSet() calls createFunc again. We'll make this one fail.
createFunc.callsFake((key: string) => Promise.reject(new Error('fake-error1-' + key)));
await assert.isRejected(mapGetOrSet(amap, "foo", createFunc), /fake-error1-foo/);
assert.strictEqual(await amap.get("foo"), undefined);
sinon.assert.calledOnce(createFunc);
createFunc.resetHistory();
// Other keys should be unaffected.
assert.deepEqual(await mapGetOrSet(amap, "bar", createFunc), {myKey: "BAR"});
assert.deepEqual(await amap.get("bar"), {myKey: "BAR"});
sinon.assert.notCalled(createFunc);
// After failure, subsequent calls try again.
createFunc.callsFake((key: string) => Promise.reject(new Error('fake-error2-' + key)));
await assert.isRejected(mapGetOrSet(amap, "foo", createFunc), /fake-error2-foo/);
sinon.assert.calledOnce(createFunc);
createFunc.resetHistory();
// While a createFunc() is pending we do NOT call it again.
createFunc.callsFake((key: string) => Promise.reject(new Error('fake-error3-' + key)));
amap.delete("bar");
await Promise.all([
assert.isRejected(mapGetOrSet(amap, "foo", createFunc), /fake-error3-foo/),
assert.isRejected(mapGetOrSet(amap, "bar", createFunc), /fake-error3-bar/),
assert.isRejected(mapGetOrSet(amap, "foo", createFunc), /fake-error3-foo/),
assert.isRejected(mapGetOrSet(amap, "bar", createFunc), /fake-error3-bar/),
]);
sinon.assert.calledTwice(createFunc); // Called just twice, once for each value.
createFunc.resetHistory();
});
});
});