diff options
Diffstat (limited to 'packages/server/src')
-rw-r--r-- | packages/server/src/helpers/iso/isoCrypto/getWebCrypto.test.ts | 138 | ||||
-rw-r--r-- | packages/server/src/helpers/iso/isoCrypto/getWebCrypto.ts | 9 |
2 files changed, 107 insertions, 40 deletions
diff --git a/packages/server/src/helpers/iso/isoCrypto/getWebCrypto.test.ts b/packages/server/src/helpers/iso/isoCrypto/getWebCrypto.test.ts index c372559..5a1ad92 100644 --- a/packages/server/src/helpers/iso/isoCrypto/getWebCrypto.test.ts +++ b/packages/server/src/helpers/iso/isoCrypto/getWebCrypto.test.ts @@ -1,75 +1,137 @@ -import { assertEquals } from 'https://deno.land/std@0.198.0/assert/mod.ts'; +import { assertEquals, assertRejects } from 'https://deno.land/std@0.198.0/assert/mod.ts'; import { returnsNext, stub } from 'https://deno.land/std@0.198.0/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(); +}); diff --git a/packages/server/src/helpers/iso/isoCrypto/getWebCrypto.ts b/packages/server/src/helpers/iso/isoCrypto/getWebCrypto.ts index 07ca8f8..fe86219 100644 --- a/packages/server/src/helpers/iso/isoCrypto/getWebCrypto.ts +++ b/packages/server/src/helpers/iso/isoCrypto/getWebCrypto.ts @@ -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; @@ -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); @@ -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; + }, }; |