mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
145 lines
6.0 KiB
TypeScript
145 lines
6.0 KiB
TypeScript
|
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();
|
||
|
});
|
||
|
});
|
||
|
});
|