Skip to content

Commit

Permalink
Add more test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
MasterKale committed Oct 27, 2023
1 parent 2d81cd5 commit 8dc0d13
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 40 deletions.
138 changes: 100 additions & 38 deletions packages/server/src/helpers/iso/isoCrypto/getWebCrypto.test.ts
Original file line number Diff line number Diff line change
@@ -1,75 +1,137 @@
import { assertEquals } from 'https://deno.land/[email protected]/assert/mod.ts';
import { assertEquals, assertRejects } from 'https://deno.land/[email protected]/assert/mod.ts';
import { returnsNext, stub } from 'https://deno.land/[email protected]/testing/mock.ts';

import { _getWebCryptoInternals, getWebCrypto } from './getWebCrypto.ts';
import { _getWebCryptoInternals, getWebCrypto, MissingWebCrypto } from './getWebCrypto.ts';

Deno.test('Should return globalThis.crypto when present', async () => {
// Back up globalThis.crypto
const originalCrypto = globalThis.crypto;

// Overwrite globalThis.crypto
const newCrypto = {};
Object.defineProperty(globalThis, 'crypto', { value: newCrypto, writable: true });
Deno.test('should return globalThis.crypto when present', async () => {
// Pretend globalThis.crypto exists
const newGlobalThisCrypto = {};
const mockGlobalThisCrypto = stub(
_getWebCryptoInternals,
'stubThisGlobalThisCrypto',
// @ts-ignore: globalThis.crypto
returnsNext([newGlobalThisCrypto]),
);

const returnedCrypto = await getWebCrypto();

assertEquals(returnedCrypto, newCrypto);
assertEquals(returnedCrypto, newGlobalThisCrypto);

// Restore globalThis.crypto
Object.defineProperty(globalThis, 'crypto', { value: originalCrypto, writable: true });
mockGlobalThisCrypto.restore();
});

Deno.test('Should return node:crypto.webcrypto when globalThis.crypto is missing', async () => {
Deno.test('should return node:crypto.webcrypto when globalThis.crypto is missing', async () => {
// Pretend globalThis.crypto doesn't exist
const mockGlobalThisCrypto = stub(
_getWebCryptoInternals,
'stubThisGlobalThisCrypto',
// @ts-ignore: globalThis.crypto
returnsNext([undefined]),
);

// Mock out just enough of the 'node:crypto' module
const fakeNodeCrypto = { webcrypto: {} };
const mockDecodeClientData = stub(
const mockImportNodeCrypto = stub(
_getWebCryptoInternals,
'stubThisImportNodeCrypto',
// @ts-ignore: Pretending to return something from Node
// @ts-ignore: node:crypto
returnsNext([fakeNodeCrypto]),
);

// Back up globalThis.crypto
const originalCrypto = globalThis.crypto;

// Overwrite globalThis.crypto
const newCrypto = undefined;
Object.defineProperty(globalThis, 'crypto', { value: newCrypto, writable: true });

const returnedCrypto = await getWebCrypto();

assertEquals(returnedCrypto, fakeNodeCrypto.webcrypto);

// Restore globalThis.crypto
Object.defineProperty(globalThis, 'crypto', { value: originalCrypto, writable: true });
mockDecodeClientData.restore();
mockGlobalThisCrypto.restore();
mockImportNodeCrypto.restore();
});

Deno.test(
'Should return globalThis.crypto when present, while node:crypto is present but missing webcrypto',
'should return globalThis.crypto when present, while node:crypto.webcrypto is present',
async () => {
// Pretend globalThis.crypto exists
const fakeGlobalThisCrypto = {};
const mockGlobalThisCrypto = stub(
_getWebCryptoInternals,
'stubThisGlobalThisCrypto',
// @ts-ignore: globalThis.crypto
returnsNext([fakeGlobalThisCrypto]),
);

// Mock out just enough of the 'node:crypto' module, but like we're in Node v14
const fakeNodeCrypto = { webcrypto: undefined };
const mockDecodeClientData = stub(
const fakeNodeCrypto = { webcrypto: {} };
const mockImportNodeCrypto = stub(
_getWebCryptoInternals,
'stubThisImportNodeCrypto',
// @ts-ignore: Pretending to return something from Node
// @ts-ignore: node:crypto
returnsNext([fakeNodeCrypto]),
);

// Back up globalThis.crypto
const originalCrypto = globalThis.crypto;
const returnedCrypto = await getWebCrypto();

assertEquals(returnedCrypto, fakeGlobalThisCrypto);

mockGlobalThisCrypto.restore();
mockImportNodeCrypto.restore();
},
);

Deno.test(
'should return globalThis.crypto when present, while node:crypto is present but missing webcrypto',
async () => {
// Pretend globalThis.crypto exists
const fakeGlobalThisCrypto = {};
const mockGlobalThisCrypto = stub(
_getWebCryptoInternals,
'stubThisGlobalThisCrypto',
// @ts-ignore: globalThis.crypto
returnsNext([fakeGlobalThisCrypto]),
);

// Overwrite globalThis.crypto
const fakeGlobalCrypto = {};
Object.defineProperty(globalThis, 'crypto', { value: fakeGlobalCrypto, writable: true });
// Mock out just enough of the 'node:crypto' module, but like we're in Node v14
const fakeNodeCrypto = { webcrypto: undefined };
const mockImportNodeCrypto = stub(
_getWebCryptoInternals,
'stubThisImportNodeCrypto',
// @ts-ignore: node:crypto
returnsNext([fakeNodeCrypto]),
);

const returnedCrypto = await getWebCrypto();

assertEquals(returnedCrypto, fakeGlobalCrypto);
assertEquals(returnedCrypto, fakeGlobalThisCrypto);

// Restore globalThis.crypto
Object.defineProperty(globalThis, 'crypto', { value: originalCrypto, writable: true });
mockDecodeClientData.restore();
mockGlobalThisCrypto.restore();
mockImportNodeCrypto.restore();
},
);

Deno.test('should raise MissingWebCrypto error when nothing is available', async () => {
// Clear whatever version of crypto might have been set
_getWebCryptoInternals.setCachedCrypto(undefined);

// Pretend globalThis.crypto doesn't exist
const mockGlobalThisCrypto = stub(
_getWebCryptoInternals,
'stubThisGlobalThisCrypto',
// @ts-ignore: globalThis.crypto
returnsNext([undefined]),
);

// Pretend node:crypto doesn't exist
const mockImportNodeCrypto = stub(
_getWebCryptoInternals,
'stubThisImportNodeCrypto',
// @ts-ignore: node:crypto
returnsNext([undefined]),
);

await assertRejects(
() => getWebCrypto(),
MissingWebCrypto,
'Crypto API could not be located',
);

mockGlobalThisCrypto.restore();
mockImportNodeCrypto.restore();
});
9 changes: 7 additions & 2 deletions packages/server/src/helpers/iso/isoCrypto/getWebCrypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export async function getWebCrypto(): Promise<Crypto> {
* Naively attempt to access Crypto as a global object, which popular alternative run-times
* support.
*/
const _crypto = globalThis.crypto;
const _crypto = _getWebCryptoInternals.stubThisGlobalThisCrypto();

if (_crypto) {
webCrypto = _crypto;
Expand All @@ -40,7 +40,7 @@ export async function getWebCrypto(): Promise<Crypto> {
throw new MissingWebCrypto();
}

class MissingWebCrypto extends Error {
export class MissingWebCrypto extends Error {
constructor() {
const message = 'An instance of the Crypto API could not be located';
super(message);
Expand All @@ -52,4 +52,9 @@ class MissingWebCrypto extends Error {
export const _getWebCryptoInternals = {
// dnt-shim-ignore
stubThisImportNodeCrypto: () => import('node:crypto'),
stubThisGlobalThisCrypto: () => globalThis.crypto,
// Make it possible to reset the `webCrypto` at the top of the file
setCachedCrypto: (newCrypto: Crypto | undefined) => {
webCrypto = newCrypto;
},
};

0 comments on commit 8dc0d13

Please sign in to comment.