699 lines
24 KiB
TypeScript
699 lines
24 KiB
TypeScript
import { AutoVerifier } from '../../webapp/auto-verifier';
|
|
|
|
async function sleep(ms: number) {
|
|
if (ms === 0) return;
|
|
return new Promise((resolve) => { setTimeout(resolve, ms); });
|
|
}
|
|
|
|
// Disjoint the current async function to allow other promises to be handled before the next lines are run
|
|
async function disjoint() {
|
|
await sleep(0);
|
|
}
|
|
|
|
type QueryablePromise<T> = Promise<T> & { isPending: () => boolean, isRejected: () => boolean, isFulfilled: () => boolean };
|
|
|
|
/**
|
|
* This function allow you to modify a JS Promise by adding some status properties.
|
|
* Based on: http://stackoverflow.com/questions/21485545/is-there-a-way-to-tell-if-an-es6-promise-is-fulfilled-rejected-resolved
|
|
* But modified according to the specs of promises : https://promisesaplus.com/
|
|
*/
|
|
function makeQuerablePromise<T>(promise: Promise<T>) {
|
|
// Set initial state
|
|
var isPending = true;
|
|
var isRejected = false;
|
|
var isFulfilled = false;
|
|
|
|
// Observe the promise, saving the fulfillment in a closure scope.
|
|
var result = promise.then(
|
|
function(v) {
|
|
isFulfilled = true;
|
|
isPending = false;
|
|
return v;
|
|
},
|
|
function(e) {
|
|
isRejected = true;
|
|
isPending = false;
|
|
throw e;
|
|
}
|
|
);
|
|
|
|
(result as any).isFulfilled = function() { return isFulfilled; };
|
|
(result as any).isPending = function() { return isPending; };
|
|
(result as any).isRejected = function() { return isRejected; };
|
|
return result as QueryablePromise<T>;
|
|
}
|
|
|
|
class AutoVerifierTest {
|
|
static createSimple<T>(primaryResult: T, trustedResult: T): AutoVerifier<T> {
|
|
return new AutoVerifier<T>(
|
|
async () => { return primaryResult; },
|
|
async () => { return trustedResult; },
|
|
async (primaryResult: T | null, trustedResult: T | null) => {}
|
|
);
|
|
}
|
|
|
|
static createPromises<T>(primaryResult: T | null, trustedResult: T | null, primaryDelay: number, trustedDelay: number, verifyDelay: number, primaryFail: boolean = false, trustedFail: boolean = false): {
|
|
primaryPromise: QueryablePromise<T | null>, // resolves BEFORE the function returns
|
|
trustedPromise: QueryablePromise<T | null>, // resolves BEFORE the function returns
|
|
verifyPromise: QueryablePromise<{ primaryResult: T | null, trustedResult: T | null }>, // resolves AFTER function returns
|
|
verifier: AutoVerifier<T>
|
|
} {
|
|
let primaryResolve = (value: T | null) => {};
|
|
let trustedResolve = (value: T | null) => {};
|
|
let verifyResolve = (value: { primaryResult: T | null, trustedResult: T | null }) => {};
|
|
let primaryPromise = new Promise<T | null>((resolve, reject) => { primaryResolve = resolve; });
|
|
let trustedPromise = new Promise<T | null>((resolve, reject) => { trustedResolve = resolve; });
|
|
let verifyPromise = new Promise<{ primaryResult: T | null, trustedResult: T | null }>((resolve) => { verifyResolve = resolve; });
|
|
let verifier = new AutoVerifier<T>(
|
|
async () => {
|
|
await sleep(primaryDelay);
|
|
if (primaryFail) {
|
|
primaryResolve(null);
|
|
throw new Error('primary reject');
|
|
} else {
|
|
let result = primaryResult;
|
|
primaryResolve(result);
|
|
await disjoint(); // if you need to know what this does you will not be happy :)
|
|
return result;
|
|
}
|
|
},
|
|
async () => {
|
|
await sleep(trustedDelay);
|
|
if (trustedFail) {
|
|
trustedResolve(null);
|
|
throw new Error('trusted reject');
|
|
} else {
|
|
let result = trustedResult;
|
|
trustedResolve(result);
|
|
await disjoint();
|
|
return result;
|
|
}
|
|
},
|
|
async (primaryResult: T | null, trustedResult: T | null) => {
|
|
await sleep(verifyDelay);
|
|
if (primaryResult !== null && trustedResult !== null && primaryResult != trustedResult) verifier.unverify(); // auto-unverify if not equal
|
|
(async () => {
|
|
await disjoint();
|
|
verifyResolve({ primaryResult, trustedResult });
|
|
})();
|
|
}
|
|
);
|
|
return {
|
|
primaryPromise: makeQuerablePromise(primaryPromise),
|
|
trustedPromise: makeQuerablePromise(trustedPromise),
|
|
verifyPromise: makeQuerablePromise(verifyPromise),
|
|
verifier
|
|
};
|
|
}
|
|
}
|
|
|
|
describe('basic functionality', () => {
|
|
test('basic functionality', async () => {
|
|
const verifier = AutoVerifierTest.createSimple(2, 2);
|
|
expect(verifier.trustedPromise === null);
|
|
expect(verifier.trustedStatus === 'none');
|
|
let result = await verifier.fetchAndVerifyIfNeeded();
|
|
expect(result).toBe(2);
|
|
});
|
|
});
|
|
|
|
describe('promise order tests', () => {
|
|
test('normal verification', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(2, 2, 35, 65, 15);
|
|
|
|
expect(primaryPromise.isFulfilled()).toBe(false);
|
|
expect(trustedPromise.isFulfilled()).toBe(false);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
|
|
let fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
|
|
|
expect(verifier.trustedPromise !== null);
|
|
expect(verifier.trustedStatus === 'fetching');
|
|
|
|
let primaryResult = await primaryPromise;
|
|
expect(primaryResult).toBe(2);
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(false);
|
|
expect(primaryPromise.isFulfilled()).toBe(true);
|
|
expect(trustedPromise.isFulfilled()).toBe(false); // since 35ms < 65ms
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('fetching');
|
|
|
|
await fetchPromise;
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(true);
|
|
expect(trustedPromise.isFulfilled()).toBe(false); // since 35ms < 65ms
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('verifying');
|
|
|
|
let trustedResult = await trustedPromise;
|
|
|
|
expect(trustedResult).toBe(2);
|
|
|
|
expect(trustedPromise.isFulfilled()).toBe(true);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('verifying');
|
|
|
|
let verification = await verifyPromise;
|
|
|
|
expect(verification.primaryResult).toBe(2);
|
|
expect(verification.trustedResult).toBe(2);
|
|
|
|
expect(verifyPromise.isFulfilled()).toBe(true);
|
|
expect(verifier.trustedStatus).toBe('verified');
|
|
});
|
|
|
|
test('odd-ordered verification', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(2, 2, 65, 35, 15);
|
|
|
|
expect(primaryPromise.isFulfilled()).toBe(false);
|
|
expect(trustedPromise.isFulfilled()).toBe(false);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
|
|
let fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
|
|
|
expect(verifier.trustedPromise !== null);
|
|
expect(verifier.trustedStatus === 'fetching');
|
|
|
|
let primaryResult = await primaryPromise;
|
|
expect(primaryResult).toBe(2);
|
|
|
|
expect(primaryPromise.isFulfilled()).toBe(true);
|
|
expect(trustedPromise.isFulfilled()).toBe(true); // since 65ms > 35ms
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('fetching');
|
|
|
|
await fetchPromise;
|
|
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('verifying');
|
|
|
|
let trustedResult = await trustedPromise; // note: this is already fulfilled
|
|
expect(trustedResult).toBe(2);
|
|
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('verifying');
|
|
|
|
let verification = await verifyPromise;
|
|
|
|
expect(verification.primaryResult).toBe(2);
|
|
expect(verification.trustedResult).toBe(2);
|
|
|
|
expect(verifyPromise.isFulfilled()).toBe(true);
|
|
expect(verifier.trustedStatus).toBe('verified');
|
|
});
|
|
|
|
test('normal trusted override', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(null, 3, 35, 65, 15);
|
|
|
|
expect(primaryPromise.isFulfilled()).toBe(false);
|
|
expect(trustedPromise.isFulfilled()).toBe(false);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
|
|
let fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
|
|
|
expect(verifier.trustedPromise !== null);
|
|
expect(verifier.trustedStatus === 'fetching');
|
|
|
|
let primaryResult = await primaryPromise;
|
|
expect(primaryResult).toBe(null);
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(false);
|
|
expect(primaryPromise.isFulfilled()).toBe(true);
|
|
expect(trustedPromise.isFulfilled()).toBe(false); // since 35ms < 65ms
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('fetching');
|
|
|
|
let trustedResult = await trustedPromise;
|
|
expect(trustedResult).toBe(3);
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(false);
|
|
expect(trustedPromise.isFulfilled()).toBe(true);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('verifying');
|
|
|
|
let verification = await verifyPromise;
|
|
|
|
expect(verification.primaryResult).toBe(null);
|
|
expect(verification.trustedResult).toBe(3);
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(true); // since verifyPromise is called AFTER
|
|
expect(verifyPromise.isFulfilled()).toBe(true);
|
|
expect(verifier.trustedStatus).toBe('verified'); // does not get auto unverified
|
|
|
|
let result = await fetchPromise;
|
|
expect(result).toBe(3);
|
|
});
|
|
|
|
|
|
test('normal invalidation', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(2, 3, 35, 65, 15);
|
|
|
|
expect(primaryPromise.isFulfilled()).toBe(false);
|
|
expect(trustedPromise.isFulfilled()).toBe(false);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
|
|
let fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
|
|
|
expect(verifier.trustedPromise !== null);
|
|
expect(verifier.trustedStatus === 'fetching');
|
|
|
|
let primaryResult = await primaryPromise;
|
|
expect(primaryResult).toBe(2);
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(false);
|
|
expect(primaryPromise.isFulfilled()).toBe(true);
|
|
expect(trustedPromise.isFulfilled()).toBe(false); // since 35ms < 65ms
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('fetching');
|
|
|
|
await fetchPromise;
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(true);
|
|
expect(trustedPromise.isFulfilled()).toBe(false); // since 35ms < 65ms
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('verifying');
|
|
|
|
let trustedResult = await trustedPromise;
|
|
expect(trustedResult).toBe(3);
|
|
|
|
expect(trustedPromise.isFulfilled()).toBe(true);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('verifying');
|
|
|
|
let verification = await verifyPromise;
|
|
|
|
expect(verification.primaryResult).toBe(2);
|
|
expect(verification.trustedResult).toBe(3);
|
|
|
|
expect(verifyPromise.isFulfilled()).toBe(true);
|
|
expect(verifier.trustedStatus).toBe('none'); // gets auto unverified
|
|
});
|
|
|
|
test('odd-ordered invalidation', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(2, 3, 65, 35, 15);
|
|
|
|
expect(primaryPromise.isFulfilled()).toBe(false);
|
|
expect(trustedPromise.isFulfilled()).toBe(false);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
|
|
let fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
|
|
|
expect(verifier.trustedPromise !== null);
|
|
expect(verifier.trustedStatus === 'fetching');
|
|
|
|
let primaryResult = await primaryPromise;
|
|
expect(primaryResult).toBe(2);
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(false);
|
|
expect(primaryPromise.isFulfilled()).toBe(true);
|
|
expect(trustedPromise.isFulfilled()).toBe(true); // since 65ms > 35ms
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('fetching');
|
|
|
|
await fetchPromise;
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(true);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('verifying');
|
|
|
|
let trustedResult = await trustedPromise; // note: this is already fulfilled
|
|
expect(trustedResult).toBe(3);
|
|
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('verifying');
|
|
|
|
let verification = await verifyPromise;
|
|
|
|
expect(verification.primaryResult).toBe(2);
|
|
expect(verification.trustedResult).toBe(3);
|
|
|
|
expect(verifyPromise.isFulfilled()).toBe(true);
|
|
expect(verifier.trustedStatus).toBe('none'); // gets auto unverified
|
|
});
|
|
|
|
|
|
test('normal unverification during primary func', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(2, 2, 35, 65, 15);
|
|
|
|
expect(primaryPromise.isFulfilled()).toBe(false);
|
|
expect(trustedPromise.isFulfilled()).toBe(false);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
|
|
let fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
|
|
|
expect(verifier.trustedPromise !== null);
|
|
expect(verifier.trustedStatus === 'fetching');
|
|
|
|
let primaryResult = await primaryPromise;
|
|
expect(primaryResult).toBe(2);
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(false);
|
|
expect(primaryPromise.isFulfilled()).toBe(true);
|
|
expect(trustedPromise.isFulfilled()).toBe(false); // since 35ms < 65ms
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('fetching');
|
|
|
|
verifier.unverify(); // keep in mind this is still during the primary func since this is before we await the fetchPromise
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
|
|
await fetchPromise;
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(true);
|
|
expect(trustedPromise.isFulfilled()).toBe(false); // since 35ms < 65ms
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('verifying');
|
|
|
|
let trustedResult = await trustedPromise;
|
|
expect(trustedResult).toBe(2);
|
|
|
|
expect(trustedPromise.isFulfilled()).toBe(true);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('verifying');
|
|
|
|
let verification = await verifyPromise;
|
|
|
|
expect(verification.primaryResult).toBe(2);
|
|
expect(verification.trustedResult).toBe(2);
|
|
|
|
expect(verifyPromise.isFulfilled()).toBe(true);
|
|
expect(verifier.trustedStatus).toBe('verified');
|
|
});
|
|
|
|
test('odd-ordered unverification during primary func', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(2, 2, 65, 35, 15);
|
|
|
|
expect(primaryPromise.isFulfilled()).toBe(false);
|
|
expect(trustedPromise.isFulfilled()).toBe(false);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
|
|
let fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
|
|
|
expect(verifier.trustedPromise !== null);
|
|
expect(verifier.trustedStatus === 'fetching');
|
|
|
|
let primaryResult = await primaryPromise;
|
|
expect(primaryResult).toBe(2);
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(false);
|
|
expect(primaryPromise.isFulfilled()).toBe(true);
|
|
expect(trustedPromise.isFulfilled()).toBe(true); // since 65ms > 35ms
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('fetching');
|
|
|
|
verifier.unverify();
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
|
|
await fetchPromise;
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(true);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('verifying');
|
|
|
|
let trustedResult = await trustedPromise; // note: this is already fulfilled
|
|
expect(trustedResult).toBe(2);
|
|
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('verifying');
|
|
|
|
let verification = await verifyPromise;
|
|
|
|
expect(verification.primaryResult).toBe(2);
|
|
expect(verification.trustedResult).toBe(2);
|
|
|
|
expect(verifyPromise.isFulfilled()).toBe(true);
|
|
expect(verifier.trustedStatus).toBe('verified');
|
|
});
|
|
|
|
test('normal unverification during trusted func', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(2, 2, 35, 65, 15);
|
|
|
|
expect(primaryPromise.isFulfilled()).toBe(false);
|
|
expect(trustedPromise.isFulfilled()).toBe(false);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
|
|
let fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
|
|
|
expect(verifier.trustedPromise !== null);
|
|
expect(verifier.trustedStatus === 'fetching');
|
|
|
|
let primaryResult = await primaryPromise;
|
|
expect(primaryResult).toBe(2);
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(false);
|
|
expect(primaryPromise.isFulfilled()).toBe(true);
|
|
expect(trustedPromise.isFulfilled()).toBe(false); // since 35ms < 65ms
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('fetching');
|
|
|
|
await fetchPromise;
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(true);
|
|
expect(trustedPromise.isFulfilled()).toBe(false); // since 35ms < 65ms
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('verifying');
|
|
|
|
verifier.unverify();
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
|
|
let trustedResult = await trustedPromise;
|
|
expect(trustedResult).toBe(2);
|
|
|
|
expect(trustedPromise.isFulfilled()).toBe(true);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
|
|
// verification will not happen since the verifier.trustedPromise is null and we already returned
|
|
});
|
|
|
|
test('normal unverification during primary func with trusted override', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(null, 2, 35, 65, 15);
|
|
|
|
expect(primaryPromise.isFulfilled()).toBe(false);
|
|
expect(trustedPromise.isFulfilled()).toBe(false);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
|
|
let fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
|
|
|
expect(verifier.trustedPromise !== null);
|
|
expect(verifier.trustedStatus === 'fetching');
|
|
|
|
let primaryResult = await primaryPromise;
|
|
expect(primaryResult).toBe(null);
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(false);
|
|
expect(primaryPromise.isFulfilled()).toBe(true);
|
|
expect(trustedPromise.isFulfilled()).toBe(false); // since 35ms < 65ms
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('fetching');
|
|
|
|
verifier.unverify(); // keep in mind this is still during the primary func since this is before we await the fetchPromise
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
|
|
let trustedResult = await trustedPromise;
|
|
expect(trustedResult).toBe(2);
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(false);
|
|
expect(trustedPromise.isFulfilled()).toBe(true);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('verifying');
|
|
|
|
let verification = await verifyPromise;
|
|
expect(verification.primaryResult).toBe(null);
|
|
expect(verification.trustedResult).toBe(2);
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(true); // since verifyPromise occurs AFTER it returns
|
|
expect(verifyPromise.isFulfilled()).toBe(true);
|
|
expect(verifier.trustedStatus).toBe('verified');
|
|
|
|
let fetchResult = await fetchPromise;
|
|
expect(fetchResult).toBe(2);
|
|
expect(fetchPromise.isFulfilled()).toBe(true);
|
|
});
|
|
|
|
test('normal unverification during trusted func with trusted override', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(null, 2, 35, 65, 15);
|
|
|
|
expect(primaryPromise.isFulfilled()).toBe(false);
|
|
expect(trustedPromise.isFulfilled()).toBe(false);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
|
|
let fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
|
|
|
expect(verifier.trustedPromise !== null);
|
|
expect(verifier.trustedStatus === 'fetching');
|
|
|
|
let primaryResult = await primaryPromise;
|
|
expect(primaryResult).toBe(null);
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(false);
|
|
expect(primaryPromise.isFulfilled()).toBe(true);
|
|
expect(trustedPromise.isFulfilled()).toBe(false); // since 35ms < 65ms
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
expect(verifier.trustedStatus).toBe('fetching');
|
|
|
|
let trustedResult = await trustedPromise;
|
|
expect(trustedResult).toBe(2);
|
|
|
|
verifier.unverify(); // keep in mind this is still during the trusted func since this is before we await the fetchPromise
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
|
|
expect(fetchPromise.isFulfilled()).toBe(false);
|
|
expect(trustedPromise.isFulfilled()).toBe(true);
|
|
expect(verifyPromise.isFulfilled()).toBe(false);
|
|
|
|
// this will resolve with null since we got unverified during the fetch
|
|
|
|
let fetchResult = await fetchPromise;
|
|
expect(fetchResult).toBe(null);
|
|
expect(fetchPromise.isFulfilled()).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('error tests', () => {
|
|
test('primary error', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(2, 2, 35, 65, 15, true, false);
|
|
|
|
expect.assertions(3);
|
|
try {
|
|
await verifier.fetchAndVerifyIfNeeded();
|
|
} catch (e) {
|
|
expect(e).toBeDefined();
|
|
}
|
|
await primaryPromise;
|
|
await trustedPromise;
|
|
await disjoint();
|
|
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
});
|
|
|
|
test('trusted error thrown out', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(2, 2, 35, 65, 15, false, true);
|
|
|
|
await verifier.fetchAndVerifyIfNeeded();
|
|
await primaryPromise;
|
|
await trustedPromise;
|
|
await disjoint();
|
|
|
|
await disjoint(); // :D since trustedPromise happens BEFORE
|
|
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
});
|
|
|
|
test('trusted error considered since null primary result', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(null, 2, 35, 65, 15, false, true);
|
|
|
|
expect.assertions(3);
|
|
try {
|
|
await verifier.fetchAndVerifyIfNeeded();
|
|
} catch (e) {
|
|
expect(e).toBeDefined();
|
|
}
|
|
await primaryPromise;
|
|
await trustedPromise;
|
|
await disjoint();
|
|
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
});
|
|
|
|
test('both error only one exception', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(2, 2, 35, 65, 15, true, true);
|
|
|
|
expect.assertions(3);
|
|
try {
|
|
await verifier.fetchAndVerifyIfNeeded();
|
|
} catch (e) {
|
|
expect(e).toBeDefined();
|
|
}
|
|
await primaryPromise;
|
|
await trustedPromise;
|
|
await disjoint();
|
|
|
|
expect(verifier.trustedPromise).toBe(null);
|
|
expect(verifier.trustedStatus).toBe('none');
|
|
});
|
|
});
|
|
|
|
describe('deduplication', () => {
|
|
test('primary deduplication', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(2, 3, 35, 65, 15);
|
|
|
|
await Promise.all([
|
|
(async () => {
|
|
let fetchResult = await verifier.fetchAndVerifyIfNeeded();
|
|
expect(fetchResult).toBe(2);
|
|
})(),
|
|
(async () => {
|
|
let fetchResult = await verifier.fetchAndVerifyIfNeeded();
|
|
expect(fetchResult).toBe(2);
|
|
})()
|
|
]);
|
|
});
|
|
|
|
test('trusted deduplication', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(null, 3, 35, 65, 15);
|
|
|
|
await Promise.all([
|
|
(async () => {
|
|
let fetchResult = await verifier.fetchAndVerifyIfNeeded();
|
|
expect(fetchResult).toBe(3);
|
|
})(),
|
|
(async () => {
|
|
let fetchResult = await verifier.fetchAndVerifyIfNeeded();
|
|
expect(fetchResult).toBe(3);
|
|
})()
|
|
]);
|
|
|
|
// Doesn't really test it but it gets the code coverage B)
|
|
});
|
|
|
|
test('trusted deduplication with unverification', async () => {
|
|
const { primaryPromise, trustedPromise, verifyPromise, verifier } = AutoVerifierTest.createPromises(null, 3, 35, 65, 15);
|
|
|
|
await Promise.all([
|
|
(async () => {
|
|
let fetchPromise = verifier.fetchAndVerifyIfNeeded();
|
|
let fetchResult = await fetchPromise;
|
|
expect(fetchResult).toBe(null);
|
|
})(),
|
|
(async () => {
|
|
let fetchPromise = verifier.fetchAndVerifyIfNeeded();
|
|
await trustedPromise;
|
|
verifier.unverify();
|
|
let fetchResult = await fetchPromise;
|
|
expect(fetchResult).toBe(null);
|
|
})()
|
|
]);
|
|
});
|
|
});
|
|
|
|
// TODO: changesFunc
|
|
// TODO: Standard autoverifiers?
|