remove ensureTrustedFuncReady, add ab primay null/dedup, trusted value/dedup
This commit is contained in:
parent
54195fdf14
commit
5326f067be
@ -105,21 +105,21 @@ describe('getChanges tests', () => {
|
||||
|
||||
describe('constructor/factory tests', () => {
|
||||
test('constructor', () => {
|
||||
const av = new AutoVerifier(jest.fn(), jest.fn(), jest.fn(), jest.fn());
|
||||
const av = new AutoVerifier(jest.fn(), jest.fn(), jest.fn());
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(null);
|
||||
expect(av.trustedStatus).toBe('none')
|
||||
});
|
||||
|
||||
test('createStandardListAutoVerifier', () => {
|
||||
const av = AutoVerifier.createStandardListAutoVerifier(jest.fn(), jest.fn(), jest.fn(), jest.fn());
|
||||
const av = AutoVerifier.createStandardListAutoVerifier(jest.fn(), jest.fn(), jest.fn());
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(null);
|
||||
expect(av.trustedStatus).toBe('none')
|
||||
});
|
||||
|
||||
test('createStandardSingleAutoVerifier', () => {
|
||||
const av = AutoVerifier.createStandardListAutoVerifier(jest.fn(), jest.fn(), jest.fn(), jest.fn());
|
||||
const av = AutoVerifier.createStandardListAutoVerifier(jest.fn(), jest.fn(), jest.fn());
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(null);
|
||||
expect(av.trustedStatus).toBe('none')
|
||||
@ -145,34 +145,28 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
function getManualsAndMocks(): {
|
||||
nextPrimary: () => ManualPromise<BasicWE | null>,
|
||||
nextTrusted: () => ManualPromise<BasicWE | null>,
|
||||
nextEnsureTrustedFuncReady: () => ManualPromise<void>,
|
||||
nextVerify: () => ManualPromise<boolean>,
|
||||
primaryFunc: jest.Mock<Promise<BasicWE | null>, []>,
|
||||
trustedFunc: jest.Mock<Promise<BasicWE | null>, []>,
|
||||
ensureTrustedFuncReadyFunc: jest.Mock<Promise<void>, []>,
|
||||
verifyFunc: jest.Mock<Promise<boolean>, [primaryResult: BasicWE | null, trustedResult: BasicWE | null]>,
|
||||
} {
|
||||
const manuals: {
|
||||
primary: ManualPromise<BasicWE | null>[],
|
||||
trusted: ManualPromise<BasicWE | null>[],
|
||||
ensureTrustedFuncReady: ManualPromise<void>[],
|
||||
verify: ManualPromise<boolean>[],
|
||||
} = {
|
||||
primary: [],
|
||||
trusted: [],
|
||||
ensureTrustedFuncReady: [],
|
||||
verify: [],
|
||||
};
|
||||
|
||||
const calls: {
|
||||
primary: number,
|
||||
trusted: number,
|
||||
ensureTrustedFuncReady: number,
|
||||
verify: number,
|
||||
} = {
|
||||
primary: 0,
|
||||
trusted: 0,
|
||||
ensureTrustedFuncReady: 0,
|
||||
verify: 0
|
||||
};
|
||||
|
||||
@ -185,11 +179,9 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
return {
|
||||
nextPrimary: () => getNext(manuals.primary),
|
||||
nextTrusted: () => getNext(manuals.trusted),
|
||||
nextEnsureTrustedFuncReady: () => getNext(manuals.ensureTrustedFuncReady),
|
||||
nextVerify: () => getNext(manuals.verify),
|
||||
primaryFunc: jest.fn(() => manuals.primary[calls.primary++]!.promise),
|
||||
trustedFunc: jest.fn(() => manuals.trusted[calls.trusted++]!.promise),
|
||||
ensureTrustedFuncReadyFunc: jest.fn(() => manuals.ensureTrustedFuncReady[calls.ensureTrustedFuncReady++]!.promise),
|
||||
verifyFunc: jest.fn((_primaryResult: BasicWE | null, _trustedResult: BasicWE | null) => manuals.verify[calls.verify++]!.promise),
|
||||
}
|
||||
}
|
||||
@ -198,21 +190,18 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
av: AutoVerifier<T>,
|
||||
nextPrimary: () => ManualPromise<T | null>,
|
||||
nextTrusted: () => ManualPromise<T | null>,
|
||||
nextEnsureTrustedFuncReady: () => ManualPromise<void>,
|
||||
nextVerify: () => ManualPromise<boolean>,
|
||||
lazyVerify: boolean = false,
|
||||
) {
|
||||
const state: {
|
||||
primary: ManualPromise<T | null>,
|
||||
trusted: ManualPromise<T | null>,
|
||||
ensureTrustedFuncReady: ManualPromise<void>,
|
||||
verify: ManualPromise<boolean>,
|
||||
result: T | null | undefined,
|
||||
rejection: Error | undefined,
|
||||
} = {
|
||||
primary: nextPrimary(),
|
||||
trusted: nextTrusted(),
|
||||
ensureTrustedFuncReady: nextEnsureTrustedFuncReady(),
|
||||
verify: nextVerify(),
|
||||
result: undefined,
|
||||
rejection: undefined,
|
||||
@ -231,16 +220,15 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
|
||||
test('primary null, then trusted with value - cache miss, return from server', async () => {
|
||||
const {
|
||||
nextPrimary, nextTrusted, nextEnsureTrustedFuncReady, nextVerify,
|
||||
primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc
|
||||
nextPrimary, nextTrusted, nextVerify,
|
||||
primaryFunc, trustedFunc, verifyFunc
|
||||
} = getManualsAndMocks();
|
||||
|
||||
const primary = nextPrimary();
|
||||
const trusted = nextTrusted();
|
||||
const ensureTrustedFuncReady = nextEnsureTrustedFuncReady();
|
||||
const verify = nextVerify();
|
||||
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc);
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, verifyFunc);
|
||||
const resultPromise = av.fetchAndVerifyIfNeeded();
|
||||
let result: BasicWE | null | undefined = undefined;
|
||||
resultPromise.then(value => { result = value; });
|
||||
@ -251,18 +239,9 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
expect(primaryFunc).toHaveBeenCalled();
|
||||
expect(trustedFunc).toHaveBeenCalled();
|
||||
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(0);
|
||||
primary.resolve(null);
|
||||
await disjoint();
|
||||
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalled();
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
|
||||
ensureTrustedFuncReady.resolve();
|
||||
await disjoint();
|
||||
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
@ -289,22 +268,20 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
|
||||
expect(primaryFunc).toHaveBeenCalledTimes(1);
|
||||
expect(trustedFunc).toHaveBeenCalledTimes(1);
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(1);
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('primary with value, then trusted with value - cache hit, verify with server', async () => {
|
||||
const {
|
||||
nextPrimary, nextTrusted, nextEnsureTrustedFuncReady, nextVerify,
|
||||
primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc
|
||||
nextPrimary, nextTrusted, nextVerify,
|
||||
primaryFunc, trustedFunc, verifyFunc
|
||||
} = getManualsAndMocks();
|
||||
|
||||
const primary = nextPrimary();
|
||||
const trusted = nextTrusted();
|
||||
const ensureTrustedFuncReady = nextEnsureTrustedFuncReady();
|
||||
const verify = nextVerify();
|
||||
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc);
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, verifyFunc);
|
||||
const resultPromise = av.fetchAndVerifyIfNeeded();
|
||||
let result: BasicWE | null | undefined = undefined;
|
||||
resultPromise.then(value => { result = value; });
|
||||
@ -316,23 +293,14 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
expect(trustedFunc).toHaveBeenCalled(); // TODO: Is this the source of the problem? - trustedFunc needs to wait to be called until ensureTrustedFuncReadyFunc is resolved
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(0);
|
||||
primary.resolve(new BasicWE(2));
|
||||
await disjoint();
|
||||
|
||||
expect(result).toEqual(new BasicWE(2));
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalled();
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
|
||||
ensureTrustedFuncReady.resolve();
|
||||
await disjoint();
|
||||
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(0);
|
||||
trusted.resolve(new BasicWE(2));
|
||||
await disjoint();
|
||||
@ -353,22 +321,20 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
|
||||
expect(primaryFunc).toHaveBeenCalledTimes(1);
|
||||
expect(trustedFunc).toHaveBeenCalledTimes(1);
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(1);
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('primary null, then trusted with null - cache miss, server confirms miss', async () => {
|
||||
const {
|
||||
nextPrimary, nextTrusted, nextEnsureTrustedFuncReady, nextVerify,
|
||||
primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc
|
||||
nextPrimary, nextTrusted, nextVerify,
|
||||
primaryFunc, trustedFunc, verifyFunc
|
||||
} = getManualsAndMocks();
|
||||
|
||||
const primary = nextPrimary();
|
||||
const trusted = nextTrusted();
|
||||
const ensureTrustedFuncReady = nextEnsureTrustedFuncReady();
|
||||
const verify = nextVerify();
|
||||
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc);
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, verifyFunc);
|
||||
const resultPromise = av.fetchAndVerifyIfNeeded();
|
||||
let result: BasicWE | null | undefined = undefined;
|
||||
resultPromise.then(value => { result = value; });
|
||||
@ -379,22 +345,13 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
expect(primaryFunc).toHaveBeenCalled();
|
||||
expect(trustedFunc).toHaveBeenCalled();
|
||||
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(0);
|
||||
primary.resolve(null);
|
||||
await disjoint();
|
||||
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalled();
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
|
||||
ensureTrustedFuncReady.resolve();
|
||||
await disjoint();
|
||||
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(0);
|
||||
trusted.resolve(null);
|
||||
await disjoint();
|
||||
@ -407,7 +364,7 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
expect(result).toBeUndefined();
|
||||
verify.resolve(true);
|
||||
await disjoint();
|
||||
|
||||
|
||||
expect(result).toBeNull();
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(trusted.promise);
|
||||
@ -417,22 +374,20 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
|
||||
expect(primaryFunc).toHaveBeenCalledTimes(1);
|
||||
expect(trustedFunc).toHaveBeenCalledTimes(1);
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(1);
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('primary with value, then trusted with null - cache hit, server deleted', async () => {
|
||||
const {
|
||||
nextPrimary, nextTrusted, nextEnsureTrustedFuncReady, nextVerify,
|
||||
primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc
|
||||
nextPrimary, nextTrusted, nextVerify,
|
||||
primaryFunc, trustedFunc, verifyFunc
|
||||
} = getManualsAndMocks();
|
||||
|
||||
const primary = nextPrimary();
|
||||
const trusted = nextTrusted();
|
||||
const ensureTrustedFuncReady = nextEnsureTrustedFuncReady();
|
||||
const verify = nextVerify();
|
||||
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc);
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, verifyFunc);
|
||||
const resultPromise = av.fetchAndVerifyIfNeeded();
|
||||
let result: BasicWE | null | undefined = undefined;
|
||||
resultPromise.then(value => { result = value; });
|
||||
@ -443,24 +398,15 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
expect(primaryFunc).toHaveBeenCalled();
|
||||
expect(trustedFunc).toHaveBeenCalled();
|
||||
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(0);
|
||||
expect(result).toBeUndefined();
|
||||
primary.resolve(new BasicWE(2));
|
||||
await disjoint();
|
||||
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalled();
|
||||
expect(result).toEqual(new BasicWE(2));
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
|
||||
ensureTrustedFuncReady.resolve();
|
||||
await disjoint();
|
||||
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(0);
|
||||
trusted.resolve(null);
|
||||
await disjoint();
|
||||
@ -481,22 +427,20 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
|
||||
expect(primaryFunc).toHaveBeenCalledTimes(1);
|
||||
expect(trustedFunc).toHaveBeenCalledTimes(1);
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(1);
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('primary with null, lazy - lazy cache miss, need to ping server', async () => {
|
||||
const {
|
||||
nextPrimary, nextTrusted, nextEnsureTrustedFuncReady, nextVerify,
|
||||
primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc
|
||||
nextPrimary, nextTrusted, nextVerify,
|
||||
primaryFunc, trustedFunc, verifyFunc
|
||||
} = getManualsAndMocks();
|
||||
|
||||
const primary = nextPrimary();
|
||||
const trusted = nextTrusted();
|
||||
const ensureTrustedFuncReady = nextEnsureTrustedFuncReady();
|
||||
const verify = nextVerify();
|
||||
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc);
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, verifyFunc);
|
||||
const resultPromise = av.fetchAndVerifyIfNeeded(true);
|
||||
let result: BasicWE | null | undefined = undefined;
|
||||
resultPromise.then(value => { result = value; });
|
||||
@ -507,23 +451,14 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
expect(primaryFunc).toHaveBeenCalled();
|
||||
|
||||
expect(trustedFunc).toHaveBeenCalledTimes(0);
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(0);
|
||||
primary.resolve(null);
|
||||
await disjoint();
|
||||
|
||||
expect(trustedFunc).toHaveBeenCalled();
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalled();
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying'); // notably, we will never publicly see it in 'fetching'
|
||||
|
||||
ensureTrustedFuncReady.resolve();
|
||||
await disjoint();
|
||||
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(0);
|
||||
trusted.resolve(new BasicWE(2));
|
||||
await disjoint();
|
||||
@ -535,7 +470,6 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
|
||||
expect(primaryFunc).toHaveBeenCalledTimes(1);
|
||||
expect(trustedFunc).toHaveBeenCalledTimes(1);
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(1);
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
@ -551,19 +485,18 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
|
||||
expect(primaryFunc).toHaveBeenCalledTimes(1);
|
||||
expect(trustedFunc).toHaveBeenCalledTimes(1);
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(1);
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('primary with value, lazy - lazy cache hit, no need to ping server', async () => {
|
||||
const {
|
||||
nextPrimary,
|
||||
primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc
|
||||
primaryFunc, trustedFunc, verifyFunc
|
||||
} = getManualsAndMocks();
|
||||
|
||||
const primary = nextPrimary();
|
||||
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc);
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, verifyFunc);
|
||||
const resultPromise = av.fetchAndVerifyIfNeeded(true);
|
||||
let result: BasicWE | null | undefined = undefined;
|
||||
resultPromise.then(value => { result = value; });
|
||||
@ -584,7 +517,6 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
|
||||
expect(primaryFunc).toHaveBeenCalledTimes(1);
|
||||
expect(trustedFunc).toHaveBeenCalledTimes(0);
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(0);
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
@ -592,13 +524,13 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
// expecting the promise to reject, the server to succeed, but the verify function to never be called
|
||||
const {
|
||||
nextPrimary, nextTrusted,
|
||||
primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc
|
||||
primaryFunc, trustedFunc, verifyFunc
|
||||
} = getManualsAndMocks();
|
||||
|
||||
const primary = nextPrimary();
|
||||
const trusted = nextTrusted();
|
||||
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc);
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, verifyFunc);
|
||||
const resultPromise = av.fetchAndVerifyIfNeeded();
|
||||
let rejection: Error | undefined = undefined;
|
||||
resultPromise.catch(value => { rejection = value; });
|
||||
@ -623,22 +555,20 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
|
||||
expect(primaryFunc).toHaveBeenCalledTimes(1);
|
||||
expect(trustedFunc).toHaveBeenCalledTimes(1); // notably, this server response will be thrown out
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(0);
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test('primary with null, trusted rejects - cache miss, failed to ping server', async () => {
|
||||
// expect the promise to reject, but the verify function to never be called
|
||||
const {
|
||||
nextPrimary, nextTrusted, nextEnsureTrustedFuncReady,
|
||||
primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc
|
||||
nextPrimary, nextTrusted,
|
||||
primaryFunc, trustedFunc, verifyFunc
|
||||
} = getManualsAndMocks();
|
||||
|
||||
const primary = nextPrimary();
|
||||
const trusted = nextTrusted();
|
||||
const ensureTrustedFuncReady = nextEnsureTrustedFuncReady();
|
||||
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc);
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, verifyFunc);
|
||||
const resultPromise = av.fetchAndVerifyIfNeeded();
|
||||
let rejection: Error | undefined = undefined;
|
||||
resultPromise.catch(value => { rejection = value; });
|
||||
@ -649,18 +579,9 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
expect(primaryFunc).toHaveBeenCalled();
|
||||
expect(trustedFunc).toHaveBeenCalled();
|
||||
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(0);
|
||||
primary.resolve(null);
|
||||
await disjoint();
|
||||
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalled();
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
|
||||
ensureTrustedFuncReady.resolve();
|
||||
await disjoint();
|
||||
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
@ -678,22 +599,20 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
|
||||
expect(primaryFunc).toHaveBeenCalledTimes(1);
|
||||
expect(trustedFunc).toHaveBeenCalledTimes(1); // notably, this server response will be thrown out
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(1);
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test('primary with value, trusted rejects - cache hit, failed to ping server', async () => {
|
||||
// expect the promise to reject, but the verify function to never be called
|
||||
const {
|
||||
nextPrimary, nextTrusted, nextEnsureTrustedFuncReady,
|
||||
primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc
|
||||
nextPrimary, nextTrusted,
|
||||
primaryFunc, trustedFunc, verifyFunc
|
||||
} = getManualsAndMocks();
|
||||
|
||||
const primary = nextPrimary();
|
||||
const trusted = nextTrusted();
|
||||
const ensureTrustedFuncReady = nextEnsureTrustedFuncReady();
|
||||
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc);
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, verifyFunc);
|
||||
const resultPromise = av.fetchAndVerifyIfNeeded();
|
||||
let result: BasicWE | null | undefined = undefined;
|
||||
resultPromise.then(value => { result = value; });
|
||||
@ -705,19 +624,10 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
expect(trustedFunc).toHaveBeenCalled();
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(0);
|
||||
primary.resolve(new BasicWE(2));
|
||||
await disjoint();
|
||||
|
||||
expect(result).toEqual(new BasicWE(2));
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalled();
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
|
||||
ensureTrustedFuncReady.resolve();
|
||||
await disjoint();
|
||||
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
@ -736,19 +646,18 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
|
||||
expect(primaryFunc).toHaveBeenCalledTimes(1);
|
||||
expect(trustedFunc).toHaveBeenCalledTimes(1); // notably, this server response will be thrown out
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(1);
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test('ab, a: primary with value, b: primary dedup, ab: trusted dedup with value', async () => {
|
||||
const {
|
||||
nextPrimary, nextTrusted, nextEnsureTrustedFuncReady, nextVerify,
|
||||
primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc
|
||||
nextPrimary, nextTrusted, nextVerify,
|
||||
primaryFunc, trustedFunc, verifyFunc
|
||||
} = getManualsAndMocks();
|
||||
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc);
|
||||
|
||||
const a = fetchAndVerifyIfNeededManually(av, nextPrimary, nextTrusted, nextEnsureTrustedFuncReady, nextVerify);
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, verifyFunc);
|
||||
|
||||
const a = fetchAndVerifyIfNeededManually(av, nextPrimary, nextTrusted, nextVerify);
|
||||
|
||||
expect(av.primaryPromise).toBe(a.primary.promise);
|
||||
expect(av.trustedPromise).toBe(a.trusted.promise);
|
||||
@ -756,7 +665,7 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
expect(primaryFunc).toHaveBeenCalled();
|
||||
expect(trustedFunc).toHaveBeenCalled();
|
||||
|
||||
const b = fetchAndVerifyIfNeededManually(av, nextPrimary, nextTrusted, nextEnsureTrustedFuncReady, nextVerify);
|
||||
const b = fetchAndVerifyIfNeededManually(av, nextPrimary, nextTrusted, nextVerify);
|
||||
|
||||
expect(av.primaryPromise).toBe(a.primary.promise); // doesn't change from a
|
||||
expect(av.trustedPromise).toBe(a.trusted.promise); // doesn't change from a
|
||||
@ -773,13 +682,6 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
expect(av.trustedPromise).toBe(a.trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
|
||||
a.ensureTrustedFuncReady.resolve();
|
||||
await disjoint();
|
||||
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(a.trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(0);
|
||||
a.trusted.resolve(new BasicWE(2));
|
||||
await disjoint();
|
||||
@ -791,7 +693,7 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
|
||||
a.verify.resolve(true);
|
||||
await disjoint();
|
||||
|
||||
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(a.trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verified');
|
||||
@ -801,10 +703,65 @@ describe('fetchAndVerifyIfNeeded tests', () => {
|
||||
// Even though there were two fetchAnd... calls, they were deduped such that each func was only called once
|
||||
expect(primaryFunc).toHaveBeenCalledTimes(1);
|
||||
expect(trustedFunc).toHaveBeenCalledTimes(1);
|
||||
expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(1);
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('ab, a: primary with null, b: primary dedup, ab: trusted dedup with value', async () => {
|
||||
const {
|
||||
nextPrimary, nextTrusted, nextVerify,
|
||||
primaryFunc, trustedFunc, verifyFunc
|
||||
} = getManualsAndMocks();
|
||||
|
||||
const av = new AutoVerifier(primaryFunc, trustedFunc, verifyFunc);
|
||||
|
||||
const a = fetchAndVerifyIfNeededManually(av, nextPrimary, nextTrusted, nextVerify);
|
||||
|
||||
expect(av.primaryPromise).toBe(a.primary.promise);
|
||||
expect(av.trustedPromise).toBe(a.trusted.promise);
|
||||
expect(av.trustedStatus).toBe('fetching');
|
||||
expect(primaryFunc).toHaveBeenCalled();
|
||||
expect(trustedFunc).toHaveBeenCalled();
|
||||
|
||||
const b = fetchAndVerifyIfNeededManually(av, nextPrimary, nextTrusted, nextVerify);
|
||||
|
||||
expect(av.primaryPromise).toBe(a.primary.promise); // doesn't change from a
|
||||
expect(av.trustedPromise).toBe(a.trusted.promise); // doesn't change from a
|
||||
expect(av.trustedStatus).toBe('fetching');
|
||||
|
||||
a.primary.resolve(null);
|
||||
await disjoint();
|
||||
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(a.trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(0);
|
||||
a.trusted.resolve(new BasicWE(2));
|
||||
await disjoint();
|
||||
|
||||
expect(verifyFunc).toHaveBeenCalledWith(null, new BasicWE(2));
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(a.trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verifying');
|
||||
|
||||
expect(a.result).toBeUndefined();
|
||||
expect(b.result).toBeUndefined();
|
||||
a.verify.resolve(true);
|
||||
await disjoint();
|
||||
|
||||
expect(a.result).toEqual(new BasicWE(2));
|
||||
expect(b.result).toEqual(new BasicWE(2));
|
||||
expect(av.primaryPromise).toBe(null);
|
||||
expect(av.trustedPromise).toBe(a.trusted.promise);
|
||||
expect(av.trustedStatus).toBe('verified');
|
||||
|
||||
await disjoint(); // sanity check
|
||||
|
||||
// Even though there were two fetchAnd... calls, they were deduped such that each func was only called once
|
||||
expect(primaryFunc).toHaveBeenCalledTimes(1);
|
||||
expect(trustedFunc).toHaveBeenCalledTimes(1);
|
||||
expect(verifyFunc).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
// Make sure to do try/catch errors/rejections, verification failures, unverifies in the middle
|
||||
// Multiple tryResolveTrustedPromises - multiple fetchAndVerifyIfNeeded at the same time
|
||||
// Trusted resolves *BEFORE* ensureTrustedFuncReady
|
||||
|
@ -26,6 +26,7 @@ export enum AutoVerifierChangesType {
|
||||
export class AutoVerifier<T> {
|
||||
public primaryPromise: Promise<T | null> | null = null;
|
||||
public trustedPromise: Promise<T | null> | null = null;
|
||||
public verifyPromise: Promise<boolean> | null = null;
|
||||
public trustedStatus: 'none' | 'fetching' | 'verifying' | 'verified' = 'none';
|
||||
|
||||
private verifierId: string;
|
||||
@ -39,7 +40,6 @@ export class AutoVerifier<T> {
|
||||
constructor(
|
||||
private primaryFunc: () => Promise<T | null>,
|
||||
private trustedFunc: () => Promise<T | null>,
|
||||
private ensureTrustedFuncReady: () => Promise<void>,
|
||||
private verifyFunc: (primaryResult: T | null, trustedResult: T | null) => Promise<boolean>,
|
||||
) {
|
||||
this.verifierId = uuid.v4().slice(undefined, 4);
|
||||
@ -123,13 +123,11 @@ export class AutoVerifier<T> {
|
||||
static createStandardListAutoVerifier<T extends WithEquals<T> & { id: string }>(
|
||||
primaryFunc: () => Promise<T[] | null>,
|
||||
trustedFunc: () => Promise<T[] | null>,
|
||||
ensureTrustedFuncReady: () => Promise<void>,
|
||||
changesFunc: (changesType: AutoVerifierChangesType, changes: Changes<T>) => Promise<boolean>,
|
||||
) {
|
||||
return new AutoVerifier<T[]>(
|
||||
primaryFunc,
|
||||
trustedFunc,
|
||||
ensureTrustedFuncReady,
|
||||
async (primaryResult: T[] | null, trustedResult: T[] | null) => {
|
||||
const changes = AutoVerifier.getChanges<T>(primaryResult, trustedResult);
|
||||
const changesType = AutoVerifier.getListChangesType<T>(primaryResult, trustedResult, changes);
|
||||
@ -141,7 +139,6 @@ export class AutoVerifier<T> {
|
||||
static createStandardSingleAutoVerifier<T extends WithEquals<T>>(
|
||||
primaryFunc: () => Promise<T | null>,
|
||||
trustedFunc: () => Promise<T | null>,
|
||||
ensureTrustedFuncReady: () => Promise<void>,
|
||||
changesFunc: (
|
||||
changesType: AutoVerifierChangesType,
|
||||
primaryResult: T | null,
|
||||
@ -151,7 +148,6 @@ export class AutoVerifier<T> {
|
||||
return new AutoVerifier<T>(
|
||||
primaryFunc,
|
||||
trustedFunc,
|
||||
ensureTrustedFuncReady,
|
||||
async (primaryResult: T | null, trustedResult: T | null) => {
|
||||
const changesType = AutoVerifier.getSingleChangesType<T>(primaryResult, trustedResult);
|
||||
return await changesFunc(changesType, primaryResult, trustedResult);
|
||||
@ -172,8 +168,14 @@ export class AutoVerifier<T> {
|
||||
console.warn('caught unverified trusted promise', e);
|
||||
});
|
||||
}
|
||||
if (this.verifyPromise) {
|
||||
this.verifyPromise.catch(e => {
|
||||
console.warn('caught unverified verify promise', e);
|
||||
});
|
||||
}
|
||||
this.primaryPromise = null;
|
||||
this.trustedPromise = null;
|
||||
this.verifyPromise = null;
|
||||
this.trustedStatus = 'none';
|
||||
}
|
||||
|
||||
@ -181,62 +183,39 @@ export class AutoVerifier<T> {
|
||||
// if the primary fetchable returns null but has not been verified yet, this will return the result of the trusted fetchable
|
||||
// if the trusted fetchable has not been used to verify the primary fetchable yet, this queries the trusted fetchable and calls verify
|
||||
// @param lazyVerify: set to true to only verify if primaryResult returns null (useful for fetching resources since they can never change)
|
||||
// @param debug: print debug messages. This is useful if you (unfortunately) think there is a bug in this
|
||||
async fetchAndVerifyIfNeeded(lazyVerify = false, debug = false): Promise<T | null> {
|
||||
async fetchAndVerifyIfNeeded(lazyVerify = false): Promise<T | null> {
|
||||
return await new Promise<T | null>(
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
async (resolve: (result: T | null) => void, reject: (error: Error) => void) => {
|
||||
let resolved = false;
|
||||
/* istanbul ignore next */
|
||||
const fetchId = debug && `v#${this.verifierId} f#${uuid.v4().slice(undefined, 4)}`;
|
||||
try {
|
||||
if (this.primaryPromise === null) {
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ': created primary promise');
|
||||
this.primaryPromise = this.primaryFunc();
|
||||
}
|
||||
const origPrimaryPromise = this.primaryPromise;
|
||||
|
||||
// pre-fetch the trusted result while we fetch the primary result
|
||||
if (!lazyVerify && this.trustedStatus === 'none') {
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ": created trusted promise, set to 'fetching'");
|
||||
this.trustedStatus = 'fetching';
|
||||
// TODO: The solution may be to merge the ensureTrustedFuncReady into the trustedPromise
|
||||
this.trustedPromise = this.trustedFunc();
|
||||
}
|
||||
|
||||
const primaryResult = await this.primaryPromise;
|
||||
if (this.primaryPromise === origPrimaryPromise) {
|
||||
// reset the primary promise so we create a new one next time
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ': reset the primary promise for next time');
|
||||
this.primaryPromise = null;
|
||||
}
|
||||
|
||||
if (primaryResult) {
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ': resolving with primary result');
|
||||
resolve(primaryResult);
|
||||
resolved = true;
|
||||
if (lazyVerify || this.trustedStatus === 'verified') {
|
||||
/* istanbul ignore next */
|
||||
if (debug)
|
||||
LOG.debug(fetchId + ': not waiting on trusted promise', {
|
||||
lazyVerify,
|
||||
trustedStatus: this.trustedStatus,
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ': waiting for trusted to resolve');
|
||||
}
|
||||
|
||||
if (this.trustedStatus === 'none') {
|
||||
// try to re-fetch the trusted result
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ": creating trusted result (since status is 'none'");
|
||||
this.trustedStatus = 'fetching';
|
||||
this.trustedPromise = this.trustedFunc();
|
||||
}
|
||||
@ -249,12 +228,6 @@ export class AutoVerifier<T> {
|
||||
// no one has started verifying the trusted yet
|
||||
|
||||
this.trustedStatus = 'verifying';
|
||||
// TODO: I want to remove the ensureTrustedFuncReady func in favor
|
||||
// of just including it yourself in the trustedFunc
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ': verifying... (awaiting trustedPromise)');
|
||||
await this.ensureTrustedFuncReady();
|
||||
// if (debug) LOG.debug(fetchId + ': verifying...');
|
||||
|
||||
// note: Promises that have already resolved will return the same value when awaited again :)
|
||||
const origTrustedPromise: Promise<T | null> | null = this.trustedPromise;
|
||||
@ -263,17 +236,11 @@ export class AutoVerifier<T> {
|
||||
if (this.trustedPromise !== origTrustedPromise) {
|
||||
// we've been invalidated while we were waiting for the trusted result!
|
||||
// TODO: This happens when a socket fetch is sent before the socket is connected to.
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ': unverified during fetch!', new Error());
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ': trustedPromise now: ', { trustedPromise: this.trustedPromise });
|
||||
console.warn(
|
||||
'RARE ALERT: we got unverified while trying to fetch a trusted promise for verification!',
|
||||
new Error(),
|
||||
);
|
||||
if (this.trustedPromise === null) {
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ': re-fetching since trustedPromise was null');
|
||||
this.trustedStatus = 'fetching';
|
||||
this.trustedPromise = this.trustedFunc();
|
||||
}
|
||||
@ -283,19 +250,16 @@ export class AutoVerifier<T> {
|
||||
|
||||
// make sure to verify BEFORE potentially resolving
|
||||
// this way the conflicts can be resolved before the result is returned
|
||||
const primaryUpToDate = await this.verifyFunc(primaryResult, trustedResult);
|
||||
this.verifyPromise = this.verifyFunc(primaryResult, trustedResult);
|
||||
const primaryUpToDate = await this.verifyPromise;
|
||||
|
||||
if (this.trustedPromise === origTrustedPromise) {
|
||||
if (trustedResult !== null && primaryUpToDate) {
|
||||
// we got a good trusted result and the primary data has been updated
|
||||
// to reflect the trusted data (or already reflects it).
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ': verified successfully.');
|
||||
this.trustedStatus = 'verified';
|
||||
} else {
|
||||
// we have to re-fetch the trusted promise again next fetch
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ': needs trusted promise re-fetched next time');
|
||||
this.trustedStatus = 'none';
|
||||
}
|
||||
} else {
|
||||
@ -307,8 +271,6 @@ export class AutoVerifier<T> {
|
||||
|
||||
if (!resolved) {
|
||||
// removed 01/09/2021 pretty sure should not be here... && trustedResult
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ': resolving with trusted result');
|
||||
resolve(trustedResult);
|
||||
resolved = true;
|
||||
}
|
||||
@ -317,32 +279,33 @@ export class AutoVerifier<T> {
|
||||
// await the same trusted promise and return its result if we didn't get a result
|
||||
// from the primary source.
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ': waiting on result of a different verifier...');
|
||||
|
||||
// note: Promises that have already resolved will return the same value when awaited again :)
|
||||
const origTrustedPromise: Promise<T | null> | null = this.trustedPromise;
|
||||
const trustedResult = await origTrustedPromise;
|
||||
|
||||
if (this.trustedPromise !== origTrustedPromise) {
|
||||
// we've been invalidated while we were waiting for the trusted result!
|
||||
/* istanbul ignore next */
|
||||
if (debug)
|
||||
LOG.debug(
|
||||
fetchId +
|
||||
': got unverified while waiting on the result of a different verifier!',
|
||||
new Error(),
|
||||
);
|
||||
console.warn(
|
||||
'ULTRA RARE ALERT: we got unverified while awaiting a trusted promise another path was verifying!',
|
||||
);
|
||||
console.warn(
|
||||
'ULTRA RARE ALERT: we got unverified while awaiting a trusted promise another path was verifying!',
|
||||
);
|
||||
await tryResolveTrustedPromise();
|
||||
return;
|
||||
}
|
||||
|
||||
const origVerifyPromise: Promise<boolean> | null = this.verifyPromise;
|
||||
await origVerifyPromise; // we don't care about the result, just that we wait for the verification to finish
|
||||
if (this.verifyPromise !== origVerifyPromise) {
|
||||
// we've been invalidated while we were waiting for the trusted result!
|
||||
console.warn(
|
||||
'ULTRA RARE ALERT: we got unverified while awaiting a verify promise another path was calling!',
|
||||
origVerifyPromise,
|
||||
this.verifyPromise,
|
||||
);
|
||||
await tryResolveTrustedPromise();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!resolved) {
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ': resolving with trusted result of different verifier');
|
||||
resolve(trustedResult);
|
||||
resolved = true;
|
||||
}
|
||||
@ -350,8 +313,6 @@ export class AutoVerifier<T> {
|
||||
} else {
|
||||
// we are all up to date, make sure to resolve if primaryResult is null
|
||||
if (!resolved) {
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ': no trusted promise, resolving with null');
|
||||
resolve(null);
|
||||
resolved = true;
|
||||
}
|
||||
@ -362,21 +323,13 @@ export class AutoVerifier<T> {
|
||||
if (!resolved) {
|
||||
this.trustedPromise = null; // suppress warnings
|
||||
this.primaryPromise = null; // suppress warnings
|
||||
this.verifyPromise = null; // suppress warnings
|
||||
this.unverify();
|
||||
/* istanbul ignore next */
|
||||
if (debug) LOG.debug(fetchId + ': error during fetch', e);
|
||||
// eslint-disable-next-line prefer-promise-reject-errors
|
||||
reject(e as Error);
|
||||
resolved = true;
|
||||
} else {
|
||||
this.unverify()
|
||||
/* istanbul ignore next */
|
||||
if (debug)
|
||||
LOG.debug(
|
||||
fetchId +
|
||||
': server request failed after returning cache value (or we already rejected)',
|
||||
e,
|
||||
);
|
||||
console.warn('server request failed after returning cache value (or when already rejected)', e);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user