ab primary null/dedup, trusted rejects

This commit is contained in:
Michael Peters 2022-10-13 22:46:05 -07:00
parent 1d53aa46fa
commit eaf24a574f
2 changed files with 64 additions and 5 deletions

View File

@ -199,17 +199,18 @@ describe('fetchAndVerifyIfNeeded tests', () => {
verify: ManualPromise<boolean>,
result: T | null | undefined,
rejection: Error | undefined,
resultPromise: Promise<T | null>,
} = {
primary: nextPrimary(),
trusted: nextTrusted(),
verify: nextVerify(),
result: undefined,
rejection: undefined,
resultPromise: av.fetchAndVerifyIfNeeded(lazyVerify),
}
const resultPromise = av.fetchAndVerifyIfNeeded(lazyVerify);
resultPromise.then((value) => { state.result = value; });
resultPromise.catch((value) => { state.rejection = value; });
state.resultPromise.then((value: T | null) => { state.result = value; });
state.resultPromise.catch((value: Error) => { console.log('caught', value); state.rejection = value; });
return state;
}
@ -588,7 +589,7 @@ describe('fetchAndVerifyIfNeeded tests', () => {
expect(av.trustedStatus).toBe('verifying');
expect(rejection).toBeUndefined();
trusted.reject(new Error());
trusted.reject(new Error('rejection'));
await disjoint();
expect(rejection).toBeDefined();
@ -635,7 +636,7 @@ describe('fetchAndVerifyIfNeeded tests', () => {
// suppress the warning about trusted rejecting after primary hit
const cwSpy = jest.spyOn(console, 'warn').mockImplementationOnce(() => {}).mockReset(); // suppress the warning
trusted.reject(new Error());
trusted.reject(new Error('rejection'));
await disjoint();
expect(cwSpy).toHaveBeenCalledTimes(1);
@ -763,6 +764,60 @@ describe('fetchAndVerifyIfNeeded tests', () => {
expect(trustedFunc).toHaveBeenCalledTimes(1);
expect(verifyFunc).toHaveBeenCalledTimes(1);
});
test('ab, a: primary with null, b: primary dedup, ab: trusted rejects', async () => {
const {
nextPrimary, nextTrusted,
primaryFunc, trustedFunc, verifyFunc
} = getManualsAndMocks();
const av = new AutoVerifier(primaryFunc, trustedFunc, verifyFunc);
const primary = nextPrimary();
const trusted = nextTrusted();
const resultPromise1 = av.fetchAndVerifyIfNeeded();
let rejection1: Error | undefined = undefined;
resultPromise1.catch(value => { rejection1 = value });
expect(av.primaryPromise).toBe(primary.promise);
expect(av.trustedPromise).toBe(trusted.promise);
expect(av.trustedStatus).toBe('fetching');
expect(primaryFunc).toHaveBeenCalled();
expect(trustedFunc).toHaveBeenCalled();
const resultPromise2 = av.fetchAndVerifyIfNeeded();
let rejection2: Error | undefined = undefined;
resultPromise2.catch(value => { rejection2 = value });
expect(av.primaryPromise).toBe(primary.promise); // doesn't change
expect(av.trustedPromise).toBe(trusted.promise); // doesn't change
expect(av.trustedStatus).toBe('fetching');
primary.resolve(null);
await disjoint();
expect(av.primaryPromise).toBe(null);
expect(av.trustedPromise).toBe(trusted.promise);
expect(av.trustedStatus).toBe('verifying');
trusted.reject(new Error('rejection'));
await disjoint();
expect(av.primaryPromise).toBe(null);
expect(av.trustedPromise).toBe(null);
expect(av.trustedStatus).toBe('none');
expect(rejection1).toBeDefined();
expect(rejection2).toBeDefined();
expect(rejection1).toBe(rejection2);
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(0);
});
test('a: primary with null, b: primary re-fetch null, a: trusted with value, b: dedup', async () => {
const {

View File

@ -297,9 +297,13 @@ export class AutoVerifier<T> {
trustedResult = await origTrustedPromise;
} catch (e: unknown) {
if (this.trustedPromise === origTrustedPromise) {
// TODO: decide if can unverify here rather than in the "catch-all"
// maybe this will let us get rid of the catch all?
throw e;
} else {
console.warn('trusted promise from another path rejected after unverify', e);
// TODO: only retry if there is another trusted promise available...
// OR always reject?
await tryResolveTrustedPromise();
return;
}