diff --git a/src/client/tests/webapp/auto-verifier.test.ts b/src/client/tests/webapp/auto-verifier.test.ts index 35a3d61..9cca739 100644 --- a/src/client/tests/webapp/auto-verifier.test.ts +++ b/src/client/tests/webapp/auto-verifier.test.ts @@ -186,6 +186,10 @@ describe('fetchAndVerifyIfNeeded tests', () => { } } + // this function makes it easier to do concurrent request + // although its naming can make stuff a bit confusing depending on the situation + // a means the 1st function call for each func AND the promise of the 1st fetchAndVerifyIfNeeded + // b means the 2nd function call for each func AND the promise of the 2nd fetchAndVerifyIfNeeded function fetchAndVerifyIfNeededManually( av: AutoVerifier, nextPrimary: () => ManualPromise, @@ -209,8 +213,9 @@ describe('fetchAndVerifyIfNeeded tests', () => { resultPromise: av.fetchAndVerifyIfNeeded(lazyVerify), } - state.resultPromise.then((value: T | null) => { state.result = value; }); - state.resultPromise.catch((value: Error) => { console.log('caught', value); state.rejection = value; }); + state.resultPromise + .then((value: T | null) => { state.result = value; }) + .catch((value: Error) => { state.rejection = value; }); return state; } @@ -727,6 +732,64 @@ describe('fetchAndVerifyIfNeeded tests', () => { expect(verifyFunc).toHaveBeenCalledTimes(0); }); + test('primary null, then trusted with value, verifyFunc rejects - cache miss, return from server, verify failed', async () => { + const { + nextPrimary, nextTrusted, nextVerify, + primaryFunc, trustedFunc, verifyFunc + } = getManualsAndMocks(); + + const primary = nextPrimary(); + const trusted = nextTrusted(); + const verify = nextVerify(); + + const av = new AutoVerifier(primaryFunc, trustedFunc, verifyFunc); + const resultPromise = av.fetchAndVerifyIfNeeded(); + let result: BasicWE | null | undefined = undefined; + let rejection: unknown | undefined = undefined; + resultPromise + .then(value => { result = value; }) + .catch(value => { rejection = value; }); + + expect(av.primaryPromise).toBe(primary.promise); + expect(av.trustedPromise).toBe(trusted.promise); + expect(av.trustedStatus).toBe('fetching'); + expect(primaryFunc).toHaveBeenCalled(); + expect(trustedFunc).toHaveBeenCalled(); + + primary.resolve(null); + 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(); + + expect(verifyFunc).toHaveBeenCalledWith(null, new BasicWE(2)); + expect(av.primaryPromise).toBe(null); + expect(av.trustedPromise).toBe(trusted.promise); + expect(av.trustedStatus).toBe('verifying'); + + expect(result).toBeUndefined(); + expect(rejection).toBeUndefined(); + verify.reject(new Error('rejection')) + await disjoint(); + + expect(result).toBeUndefined(); + expect(rejection).toBeDefined(); + 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); + expect(verifyFunc).toHaveBeenCalledTimes(1); + }); + test('ab, a: primary with value, b: primary dedup, ab: trusted dedup with value', async () => { const { nextPrimary, nextTrusted, nextVerify, @@ -946,7 +1009,6 @@ describe('fetchAndVerifyIfNeeded tests', () => { 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); @@ -1169,7 +1231,7 @@ describe('fetchAndVerifyIfNeeded tests', () => { expect(av.trustedPromise).toBe(a.trusted.promise); expect(av.trustedStatus).toBe('verifying'); - a.trusted.reject(new Error('rejected')); + a.trusted.reject(new Error('rejection')); av.unverify() expect(av.primaryPromise).toBe(null); @@ -1392,6 +1454,67 @@ describe('fetchAndVerifyIfNeeded tests', () => { expect(trustedFunc).toHaveBeenCalledTimes(1); expect(verifyFunc).toHaveBeenCalledTimes(1); }); + + test('ab: a: primary with null, b: primary dedup, ab: verifyFunc false, ab: resolve with trusted', 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.reject(new Error('rejection')) + await disjoint(); + + expect(a.result).toBeUndefined(); + expect(b.result).toBeUndefined(); + expect(a.rejection).toBeDefined(); + expect(b.rejection).toBeDefined(); + expect(av.primaryPromise).toBe(null); + expect(av.trustedPromise).toBe(null); + expect(av.trustedStatus).toBe('none'); + + 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); + }); + // TODO: Why not have fetcher and a simple dedup wrapper to remove some complexity from this function? // This would likely make it possible to get rid of the "else" block in tryResolveTrustedPromise // Make sure it would work properly for retry functionality @@ -1400,7 +1523,6 @@ describe('fetchAndVerifyIfNeeded tests', () => { // Make sure to do try/catch errors/rejections, verification failures, unverifies in the middle // Fetching after verified doesn't re-fetch trusted // Unverify during verify w/ dedups - // verifyFunc returns false // verifyFunc rejects // not possible (and therefore not tested) list