diff --git a/src/client/tests/webapp/auto-verifier.test.ts b/src/client/tests/webapp/auto-verifier.test.ts index b65dc45..57137c9 100644 --- a/src/client/tests/webapp/auto-verifier.test.ts +++ b/src/client/tests/webapp/auto-verifier.test.ts @@ -150,7 +150,7 @@ describe('fetchAndVerifyIfNeeded tests', () => { primaryFunc: jest.Mock, []>, trustedFunc: jest.Mock, []>, ensureTrustedFuncReadyFunc: jest.Mock, []>, - verifyFunc: jest.Mock, []>, + verifyFunc: jest.Mock, [primaryResult: BasicWE | null, trustedResult: BasicWE | null]>, } { const manuals: { primary: ManualPromise[], @@ -190,7 +190,7 @@ describe('fetchAndVerifyIfNeeded tests', () => { 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(() => manuals.verify[calls.verify++]!.promise), + verifyFunc: jest.fn((_primaryResult: BasicWE | null, _trustedResult: BasicWE | null) => manuals.verify[calls.verify++]!.promise), } } function getGeneralMocks(): { @@ -221,8 +221,12 @@ describe('fetchAndVerifyIfNeeded tests', () => { async function disjoint() { await Util.sleep(0); } + + /* test('nothing before, primary null, then trusted with value', async () => { */ + /* expect(false).toBe(true); */ + /* }); */ + test('nothing before, primary with value, then trusted with value', async () => { - // TODO: Make xxxFunc mock functions that can be tested for if they were called const { nextPrimary, nextTrusted, nextEnsureTrustedFuncReady, nextVerify, primaryFunc, trustedFunc, ensureTrustedFuncReadyFunc, verifyFunc @@ -240,23 +244,27 @@ describe('fetchAndVerifyIfNeeded tests', () => { expect(av.trustedPromise).toBe(trusted.promise); expect(av.trustedStatus).toBe('fetching'); + expect(primaryFunc).toHaveBeenCalled(); + expect(trustedFunc).toHaveBeenCalled(); + expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(0); primary.resolve(new BasicWE(2)); await disjoint(); - expect(primaryFunc).toHaveBeenCalled(); expect(result).resolves.toEqual(new BasicWE(2)); expect(av.primaryPromise).toBe(null); expect(av.trustedPromise).toBe(trusted.promise); expect(av.trustedStatus).toBe('verifying'); + expect(ensureTrustedFuncReadyFunc).toHaveBeenCalled(); ensureTrustedFuncReady.resolve(); await disjoint(); - expect(ensureTrustedFuncReadyFunc).toHaveBeenCalled(); expect(av.primaryPromise).toBe(null); expect(av.trustedPromise).toBe(trusted.promise); expect(av.trustedStatus).toBe('verifying'); + expect(trustedFunc).toHaveBeenCalled(); + expect(verifyFunc).toHaveBeenCalledTimes(0); trusted.resolve(new BasicWE(2)); await disjoint(); @@ -265,10 +273,11 @@ describe('fetchAndVerifyIfNeeded tests', () => { expect(av.trustedPromise).toBe(trusted.promise); expect(av.trustedStatus).toBe('verifying'); + expect(verifyFunc).toHaveBeenCalled(); verify.resolve(true); await disjoint(); - expect(verifyFunc).toHaveBeenCalled(); + expect(verifyFunc).toHaveBeenCalledWith(new BasicWE(2), new BasicWE(2)); expect(av.primaryPromise).toBe(null); expect(av.trustedPromise).toBe(trusted.promise); expect(av.trustedStatus).toBe('verified'); @@ -278,5 +287,9 @@ describe('fetchAndVerifyIfNeeded tests', () => { expect(ensureTrustedFuncReadyFunc).toHaveBeenCalledTimes(1); expect(verifyFunc).toHaveBeenCalledTimes(1); }); + + /* test('nothing before, primary with value, then trusted with null', async () => { */ + /* expect(false).toBe(true); */ + /* }); */ // Make sure to do try/catch errors }); diff --git a/src/client/webapp/auto-verifier.ts b/src/client/webapp/auto-verifier.ts index 0271455..80eb9eb 100644 --- a/src/client/webapp/auto-verifier.ts +++ b/src/client/webapp/auto-verifier.ts @@ -187,9 +187,11 @@ export class AutoVerifier { // 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(); } @@ -197,6 +199,7 @@ export class AutoVerifier { // 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'; this.trustedPromise = this.trustedFunc(); @@ -205,15 +208,18 @@ export class AutoVerifier { 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, @@ -227,6 +233,7 @@ export class AutoVerifier { 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(); @@ -240,6 +247,7 @@ export class AutoVerifier { // no one has started verifying the trusted yet this.trustedStatus = 'verifying'; + /* istanbul ignore next */ if (debug) LOG.debug(fetchId + ': verifying... (awaiting trustedPromise)'); await this.ensureTrustedFuncReady(); // if (debug) LOG.debug(fetchId + ': verifying...'); @@ -251,11 +259,10 @@ export class AutoVerifier { 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()); - if (debug) - LOG.debug(fetchId + ': trustedPromise now: ', { - trustedPromise: this.trustedPromise, - }); + /* 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(), @@ -277,10 +284,12 @@ export class AutoVerifier { 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'; } @@ -293,6 +302,7 @@ export class AutoVerifier { 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; @@ -302,6 +312,7 @@ export class AutoVerifier { // 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 :) @@ -310,22 +321,23 @@ export class AutoVerifier { 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; } if (!resolved) { - if (debug) - LOG.debug(fetchId + ': resolving with trusted result of different verifier'); + /* istanbul ignore next */ + if (debug) LOG.debug(fetchId + ': resolving with trusted result of different verifier'); resolve(trustedResult); resolved = true; } @@ -333,6 +345,7 @@ export class AutoVerifier { } 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; @@ -343,11 +356,13 @@ export class AutoVerifier { } catch (e: unknown) { this.unverify(); if (!resolved) { + /* 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 { + /* istanbul ignore next */ if (debug) LOG.debug( fetchId +