diff --git a/src/client/tests/webapp/auto-verifier.test.ts b/src/client/tests/webapp/auto-verifier.test.ts index cede5dc..83ef042 100644 --- a/src/client/tests/webapp/auto-verifier.test.ts +++ b/src/client/tests/webapp/auto-verifier.test.ts @@ -635,7 +635,6 @@ describe('fetchAndVerifyIfNeeded tests', () => { expect(av.trustedStatus).toBe('verifying'); expect(rejection).toBeUndefined(); - jest.spyOn(console, 'warn').mockImplementationOnce(() => {}); // suppress the warning trusted.reject(new Error()); await disjoint(); @@ -652,6 +651,64 @@ describe('fetchAndVerifyIfNeeded tests', () => { 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 + } = getManualsAndMocks(); + + const primary = nextPrimary(); + const trusted = nextTrusted(); + const ensureTrustedFuncReady = nextEnsureTrustedFuncReady(); + + const av = new AutoVerifier(primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc); + const resultPromise = av.fetchAndVerifyIfNeeded(); + let result: BasicWE | null | undefined = undefined; + resultPromise.then(value => { result = value; }); + + expect(av.primaryPromise).toBe(primary.promise); + expect(av.trustedPromise).toBe(trusted.promise); + expect(av.trustedStatus).toBe('fetching'); + expect(primaryFunc).toHaveBeenCalled(); + 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'); + + // suppress the warning about trusted rejecting after primary hit + const cwSpy = jest.spyOn(console, 'warn').mockImplementationOnce(() => {}); + trusted.reject(new Error()); + await disjoint(); + + expect(cwSpy).toHaveBeenCalled(); + expect(av.primaryPromise).toBe(null); + expect(av.trustedPromise).toBe(null); + expect(av.trustedStatus).toBe('none'); + + await disjoint(); // sanity check + + expect(primaryFunc).toHaveBeenCalledTimes(1); + expect(trustedFunc).toHaveBeenCalledTimes(1); // notably, this server response will be thrown out + expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(1); + expect(verifyFunc).toHaveBeenCalledTimes(0); + }); + // Make sure to do try/catch errors/rejections, verification failures, unverifies in the middle // Multiple tryResolveTrustedPromises // Multiple fetchAndVerifyIfNeeded at the same time diff --git a/src/client/webapp/auto-verifier.ts b/src/client/webapp/auto-verifier.ts index f96b26c..07e2062 100644 --- a/src/client/webapp/auto-verifier.ts +++ b/src/client/webapp/auto-verifier.ts @@ -357,14 +357,17 @@ export class AutoVerifier { }; await tryResolveTrustedPromise(); } catch (e: unknown) { - this.unverify(); if (!resolved) { + this.trustedPromise = null; // suppress warnings + this.primaryPromise = 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(