archive old auto-verifier tests
This commit is contained in:
parent
f3067fa9f1
commit
b49dfe3187
729
archive/auto-verifier.test.ts
Normal file
729
archive/auto-verifier.test.ts
Normal file
@ -0,0 +1,729 @@
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
import { AutoVerifier } from '../../webapp/auto-verifier';
|
||||
|
||||
function sleep(ms: number): Promise<void> {
|
||||
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
|
||||
let isPending = true;
|
||||
let isRejected = false;
|
||||
let isFulfilled = false;
|
||||
|
||||
// observe the promise, saving the fulfillment in a closure scope.
|
||||
const result = promise.then(
|
||||
v => {
|
||||
isFulfilled = true;
|
||||
isPending = false;
|
||||
return v;
|
||||
},
|
||||
e => {
|
||||
isRejected = true;
|
||||
isPending = false;
|
||||
throw e;
|
||||
},
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(result as any).isFulfilled = function () {
|
||||
return isFulfilled;
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(result as any).isPending = function () {
|
||||
return isPending;
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(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 () => primaryResult,
|
||||
async () => trustedResult,
|
||||
async (_primaryResult: T | null, _trustedResult: T | null) => true,
|
||||
);
|
||||
}
|
||||
|
||||
static createPromises<T>(
|
||||
primaryResult: T | null,
|
||||
trustedResult: T | null,
|
||||
primaryDelay: number,
|
||||
trustedDelay: number,
|
||||
verifyDelay: number,
|
||||
primaryFail = false,
|
||||
trustedFail = 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 }) => {
|
||||
/* */
|
||||
};
|
||||
const primaryPromise = new Promise<T | null>((resolve, reject) => {
|
||||
primaryResolve = resolve;
|
||||
});
|
||||
const trustedPromise = new Promise<T | null>((resolve, reject) => {
|
||||
trustedResolve = resolve;
|
||||
});
|
||||
const verifyPromise = new Promise<{ primaryResult: T | null; trustedResult: T | null }>(resolve => {
|
||||
verifyResolve = resolve;
|
||||
});
|
||||
const verifier = new AutoVerifier<T>(
|
||||
async () => {
|
||||
await sleep(primaryDelay);
|
||||
if (primaryFail) {
|
||||
primaryResolve(null);
|
||||
throw new Error('primary reject');
|
||||
} else {
|
||||
const 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 {
|
||||
const 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');
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const trustedResult = await trustedPromise;
|
||||
|
||||
expect(trustedResult).toBe(2);
|
||||
|
||||
expect(trustedPromise.isFulfilled()).toBe(true);
|
||||
expect(verifyPromise.isFulfilled()).toBe(false);
|
||||
expect(verifier.trustedStatus).toBe('verifying');
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const trustedResult = await trustedPromise; // note: this is already fulfilled
|
||||
expect(trustedResult).toBe(2);
|
||||
|
||||
expect(verifyPromise.isFulfilled()).toBe(false);
|
||||
expect(verifier.trustedStatus).toBe('verifying');
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const 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');
|
||||
|
||||
const 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
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const trustedResult = await trustedPromise;
|
||||
expect(trustedResult).toBe(3);
|
||||
|
||||
expect(trustedPromise.isFulfilled()).toBe(true);
|
||||
expect(verifyPromise.isFulfilled()).toBe(false);
|
||||
expect(verifier.trustedStatus).toBe('verifying');
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const trustedResult = await trustedPromise; // note: this is already fulfilled
|
||||
expect(trustedResult).toBe(3);
|
||||
|
||||
expect(verifyPromise.isFulfilled()).toBe(false);
|
||||
expect(verifier.trustedStatus).toBe('verifying');
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const trustedResult = await trustedPromise;
|
||||
expect(trustedResult).toBe(2);
|
||||
|
||||
expect(trustedPromise.isFulfilled()).toBe(true);
|
||||
expect(verifyPromise.isFulfilled()).toBe(false);
|
||||
expect(verifier.trustedStatus).toBe('verifying');
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const trustedResult = await trustedPromise; // note: this is already fulfilled
|
||||
expect(trustedResult).toBe(2);
|
||||
|
||||
expect(verifyPromise.isFulfilled()).toBe(false);
|
||||
expect(verifier.trustedStatus).toBe('verifying');
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const 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');
|
||||
|
||||
const 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');
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const 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
|
||||
|
||||
const 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 () => {
|
||||
const fetchResult = await verifier.fetchAndVerifyIfNeeded();
|
||||
expect(fetchResult).toBe(2);
|
||||
})(),
|
||||
(async () => {
|
||||
const 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 () => {
|
||||
const fetchResult = await verifier.fetchAndVerifyIfNeeded();
|
||||
expect(fetchResult).toBe(3);
|
||||
})(),
|
||||
(async () => {
|
||||
const 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 () => {
|
||||
const fetchPromise = verifier.fetchAndVerifyIfNeeded();
|
||||
const fetchResult = await fetchPromise;
|
||||
expect(fetchResult).toBe(null);
|
||||
})(),
|
||||
(async () => {
|
||||
const fetchPromise = verifier.fetchAndVerifyIfNeeded();
|
||||
await trustedPromise;
|
||||
verifier.unverify();
|
||||
const fetchResult = await fetchPromise;
|
||||
expect(fetchResult).toBe(null);
|
||||
})(),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: changesFunc
|
||||
// TODO: Standard autoverifiers?
|
@ -1,729 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
import { AutoVerifier } from '../../webapp/auto-verifier';
|
||||
|
||||
function sleep(ms: number): Promise<void> {
|
||||
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
|
||||
let isPending = true;
|
||||
let isRejected = false;
|
||||
let isFulfilled = false;
|
||||
|
||||
// observe the promise, saving the fulfillment in a closure scope.
|
||||
const result = promise.then(
|
||||
v => {
|
||||
isFulfilled = true;
|
||||
isPending = false;
|
||||
return v;
|
||||
},
|
||||
e => {
|
||||
isRejected = true;
|
||||
isPending = false;
|
||||
throw e;
|
||||
},
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(result as any).isFulfilled = function () {
|
||||
return isFulfilled;
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(result as any).isPending = function () {
|
||||
return isPending;
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(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 () => primaryResult,
|
||||
async () => trustedResult,
|
||||
async (_primaryResult: T | null, _trustedResult: T | null) => true,
|
||||
);
|
||||
}
|
||||
|
||||
static createPromises<T>(
|
||||
primaryResult: T | null,
|
||||
trustedResult: T | null,
|
||||
primaryDelay: number,
|
||||
trustedDelay: number,
|
||||
verifyDelay: number,
|
||||
primaryFail = false,
|
||||
trustedFail = 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 }) => {
|
||||
/* */
|
||||
};
|
||||
const primaryPromise = new Promise<T | null>((resolve, reject) => {
|
||||
primaryResolve = resolve;
|
||||
});
|
||||
const trustedPromise = new Promise<T | null>((resolve, reject) => {
|
||||
trustedResolve = resolve;
|
||||
});
|
||||
const verifyPromise = new Promise<{ primaryResult: T | null; trustedResult: T | null }>(resolve => {
|
||||
verifyResolve = resolve;
|
||||
});
|
||||
const verifier = new AutoVerifier<T>(
|
||||
async () => {
|
||||
await sleep(primaryDelay);
|
||||
if (primaryFail) {
|
||||
primaryResolve(null);
|
||||
throw new Error('primary reject');
|
||||
} else {
|
||||
const 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 {
|
||||
const 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');
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const trustedResult = await trustedPromise;
|
||||
|
||||
expect(trustedResult).toBe(2);
|
||||
|
||||
expect(trustedPromise.isFulfilled()).toBe(true);
|
||||
expect(verifyPromise.isFulfilled()).toBe(false);
|
||||
expect(verifier.trustedStatus).toBe('verifying');
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const trustedResult = await trustedPromise; // note: this is already fulfilled
|
||||
expect(trustedResult).toBe(2);
|
||||
|
||||
expect(verifyPromise.isFulfilled()).toBe(false);
|
||||
expect(verifier.trustedStatus).toBe('verifying');
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const 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');
|
||||
|
||||
const 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
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const trustedResult = await trustedPromise;
|
||||
expect(trustedResult).toBe(3);
|
||||
|
||||
expect(trustedPromise.isFulfilled()).toBe(true);
|
||||
expect(verifyPromise.isFulfilled()).toBe(false);
|
||||
expect(verifier.trustedStatus).toBe('verifying');
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const trustedResult = await trustedPromise; // note: this is already fulfilled
|
||||
expect(trustedResult).toBe(3);
|
||||
|
||||
expect(verifyPromise.isFulfilled()).toBe(false);
|
||||
expect(verifier.trustedStatus).toBe('verifying');
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const trustedResult = await trustedPromise;
|
||||
expect(trustedResult).toBe(2);
|
||||
|
||||
expect(trustedPromise.isFulfilled()).toBe(true);
|
||||
expect(verifyPromise.isFulfilled()).toBe(false);
|
||||
expect(verifier.trustedStatus).toBe('verifying');
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const trustedResult = await trustedPromise; // note: this is already fulfilled
|
||||
expect(trustedResult).toBe(2);
|
||||
|
||||
expect(verifyPromise.isFulfilled()).toBe(false);
|
||||
expect(verifier.trustedStatus).toBe('verifying');
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const 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');
|
||||
|
||||
const 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');
|
||||
|
||||
const 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');
|
||||
|
||||
const fetchPromise = makeQuerablePromise(verifier.fetchAndVerifyIfNeeded());
|
||||
|
||||
expect(verifier.trustedPromise !== null);
|
||||
expect(verifier.trustedStatus === 'fetching');
|
||||
|
||||
const 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');
|
||||
|
||||
const 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
|
||||
|
||||
const 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 () => {
|
||||
const fetchResult = await verifier.fetchAndVerifyIfNeeded();
|
||||
expect(fetchResult).toBe(2);
|
||||
})(),
|
||||
(async () => {
|
||||
const 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 () => {
|
||||
const fetchResult = await verifier.fetchAndVerifyIfNeeded();
|
||||
expect(fetchResult).toBe(3);
|
||||
})(),
|
||||
(async () => {
|
||||
const 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 () => {
|
||||
const fetchPromise = verifier.fetchAndVerifyIfNeeded();
|
||||
const fetchResult = await fetchPromise;
|
||||
expect(fetchResult).toBe(null);
|
||||
})(),
|
||||
(async () => {
|
||||
const fetchPromise = verifier.fetchAndVerifyIfNeeded();
|
||||
await trustedPromise;
|
||||
verifier.unverify();
|
||||
const fetchResult = await fetchPromise;
|
||||
expect(fetchResult).toBe(null);
|
||||
})(),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: changesFunc
|
||||
// TODO: Standard autoverifiers?
|
Loading…
Reference in New Issue
Block a user