From a66ae7383fa3625d8b7556873ad43cf0610635bb Mon Sep 17 00:00:00 2001 From: Daniel Izdebski Date: Thu, 26 Oct 2023 19:16:48 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(core):=20Add=20`triggerCacheCr?= =?UTF-8?q?eation`=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/package.json | 1 + .../core/src/utils/triggerCacheCreation.ts | 37 ++++ .../test/utils/triggerCacheCreation.test.ts | 180 ++++++++++++++++++ pnpm-lock.yaml | 3 + 4 files changed, 221 insertions(+) create mode 100644 packages/core/src/utils/triggerCacheCreation.ts create mode 100644 packages/core/test/utils/triggerCacheCreation.test.ts diff --git a/packages/core/package.json b/packages/core/package.json index 27ac74973..3ee2fd513 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -33,6 +33,7 @@ "axios": "^1.4.0", "esbuild": "^0.19.5", "fs-extra": "^11.1.1", + "metamask": "workspace:*", "unzipper": "^0.10.14", "zod": "^3.22.4" }, diff --git a/packages/core/src/utils/triggerCacheCreation.ts b/packages/core/src/utils/triggerCacheCreation.ts new file mode 100644 index 000000000..535443180 --- /dev/null +++ b/packages/core/src/utils/triggerCacheCreation.ts @@ -0,0 +1,37 @@ +import path from 'node:path' +import fs from 'fs-extra' +import { prepareExtension } from 'metamask' +import { ensureCacheDirExists } from '../ensureCacheDirExists' +import { createCacheForWalletSetupFunction } from './createCacheForWalletSetupFunction' +import { getUniqueWalletSetupFunctions } from './getUniqueWalletSetupFunctions' + +export async function triggerCacheCreation( + setupFunctions: Awaited>, + force: boolean +) { + const cacheDirPath = ensureCacheDirExists() + const metamaskPath = await prepareExtension() + + const cacheCreationPromises = [] + + for (const [funcHash, { fileName, setupFunction }] of setupFunctions) { + const cachePath = path.join(cacheDirPath, funcHash) + if (await fs.exists(cachePath)) { + if (!force) { + console.log(`Cache already exists for ${funcHash}. Skipping...`) + continue + } + + console.log(`Cache already exists for ${funcHash} but force flag is set. Deleting cache...`) + await fs.remove(cachePath) + } + + console.log(`Triggering cache creation for: ${funcHash} (${fileName})`) + + // We're not inferring the return type here to make sure we don't accidentally await the function. + const createCachePromise: Promise = createCacheForWalletSetupFunction(metamaskPath, cachePath, setupFunction) + cacheCreationPromises.push(createCachePromise) + } + + return cacheCreationPromises +} diff --git a/packages/core/test/utils/triggerCacheCreation.test.ts b/packages/core/test/utils/triggerCacheCreation.test.ts new file mode 100644 index 000000000..b2015f843 --- /dev/null +++ b/packages/core/test/utils/triggerCacheCreation.test.ts @@ -0,0 +1,180 @@ +import { fs, vol } from 'memfs' +import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +import path from 'node:path' +import fsExtra from 'fs-extra' +import * as Metamask from 'metamask' +import type { WalletSetupFunction } from '../../src/defineWalletSetup' +import * as EnsureCacheDirExists from '../../src/ensureCacheDirExists' +import * as CreateCacheForWalletSetupFunction from '../../src/utils/createCacheForWalletSetupFunction' +import { triggerCacheCreation } from '../../src/utils/triggerCacheCreation' + +const ROOT_DIR = '/tmp' +const EXTENSION_PATH = path.join(ROOT_DIR, 'extension') + +vi.mock('fs-extra', async () => { + return { + default: { + ...fs.promises, + exists: async (path: string) => { + return vol.existsSync(path) + }, + remove: async (path: string) => { + vol.rmdirSync(path) + } + } + } +}) + +vi.mock('metamask', async () => { + return { + prepareExtension: vi.fn(async () => EXTENSION_PATH) + } +}) + +vi.mock('../../src/ensureCacheDirExists', async () => { + return { + ensureCacheDirExists: vi.fn(() => ROOT_DIR) + } +}) + +vi.mock('../../src/utils/createCacheForWalletSetupFunction', async () => { + return { + createCacheForWalletSetupFunction: vi.fn(async () => { + return 'Resolved Quack! 🦆' + }) + } +}) + +describe('triggerCacheCreation', () => { + const createCacheForWalletSetupFunctionSpy = vi.spyOn( + CreateCacheForWalletSetupFunction, + 'createCacheForWalletSetupFunction' + ) + + const testSetupFunction = vi.fn() + + function prepareSetupFunctions(hashes: string[]) { + const setupFunctions = new Map() + + for (const hash of hashes) { + setupFunctions.set(hash, { fileName: path.join(ROOT_DIR, hash), setupFunction: testSetupFunction }) + } + + return setupFunctions + } + + function expectCreateCacheForWalletSetupFunction(n: number, contextCacheDirName: string) { + expect(createCacheForWalletSetupFunctionSpy).toHaveBeenNthCalledWith( + n, + EXTENSION_PATH, + path.join(ROOT_DIR, contextCacheDirName), + testSetupFunction + ) + } + + afterAll(() => { + vi.resetAllMocks() + }) + + beforeEach(() => { + vol.mkdirSync(ROOT_DIR) + }) + + afterEach(() => { + vi.clearAllMocks() + vol.reset() // Clear the in-memory file system after each test + }) + + it('calls ensureCacheDirExists', async () => { + const ensureCacheDirExistsSpy = vi.spyOn(EnsureCacheDirExists, 'ensureCacheDirExists') + + const setupFunctions = prepareSetupFunctions(['hash1', 'hash2']) + await triggerCacheCreation(setupFunctions, false) + + expect(ensureCacheDirExistsSpy).toHaveBeenCalledOnce() + }) + + it('calls metamask.prepareExtension', async () => { + const prepareExtensionSpy = vi.spyOn(Metamask, 'prepareExtension') + + const setupFunctions = prepareSetupFunctions(['hash1', 'hash2']) + await triggerCacheCreation(setupFunctions, false) + + expect(prepareExtensionSpy).toHaveBeenCalledOnce() + }) + + it('calls createCacheForWalletSetupFunction with correct arguments', async () => { + const setupFunctions = prepareSetupFunctions(['hash1', 'hash2']) + await triggerCacheCreation(setupFunctions, false) + + expect(createCacheForWalletSetupFunctionSpy).toHaveBeenCalledTimes(2) + expectCreateCacheForWalletSetupFunction(1, 'hash1') + expectCreateCacheForWalletSetupFunction(2, 'hash2') + }) + + it('checks if cache already exists for each entry', async () => { + const existsSpy = vi.spyOn(fsExtra, 'exists') + + const setupFunctions = prepareSetupFunctions(['hash1', 'hash2']) + await triggerCacheCreation(setupFunctions, false) + + expect(existsSpy).toHaveBeenCalledTimes(2) + expect(existsSpy).toHaveBeenNthCalledWith(1, path.join(ROOT_DIR, 'hash1')) + expect(existsSpy).toHaveBeenNthCalledWith(2, path.join(ROOT_DIR, 'hash2')) + }) + + it('returns an array of createCacheForWalletSetupFunction promises', async () => { + const setupFunctions = prepareSetupFunctions(['hash1', 'hash2']) + const promises = await triggerCacheCreation(setupFunctions, false) + + expect(promises).toHaveLength(2) + expect(promises[0]).toBeInstanceOf(Promise) + expect(promises[1]).toBeInstanceOf(Promise) + }) + + describe('when force flag is false', () => { + it('ignores setup function for which cache already exists', async () => { + const setupFunctions = prepareSetupFunctions(['hash1', 'hash2', 'hash3']) + + // Creating cache for 2nd setup function. + fs.mkdirSync(path.join(ROOT_DIR, 'hash2')) + + const promises = await triggerCacheCreation(setupFunctions, false) + + expect(promises).toHaveLength(2) + expect(createCacheForWalletSetupFunctionSpy).toHaveBeenCalledTimes(2) + expectCreateCacheForWalletSetupFunction(1, 'hash1') + expectCreateCacheForWalletSetupFunction(2, 'hash3') + }) + }) + + describe('when force flag is true', () => { + it('removes cache if it already exists for given setup function', async () => { + const setupFunctions = prepareSetupFunctions(['hash1', 'hash2', 'hash3']) + + // Creating cache for 2nd setup function. + const pathToExistingCache = path.join(ROOT_DIR, 'hash2') + fs.mkdirSync(pathToExistingCache) + + await triggerCacheCreation(setupFunctions, true) + + expect(fs.existsSync(pathToExistingCache)).toBe(false) + }) + + it('calls createCacheForWalletSetupFunction for setup functions that were previously cached', async () => { + const setupFunctions = prepareSetupFunctions(['hash1', 'hash2', 'hash3']) + + // Creating cache for 2nd setup function. + fs.mkdirSync(path.join(ROOT_DIR, 'hash2')) + + const promises = await triggerCacheCreation(setupFunctions, true) + + expect(promises).toHaveLength(3) + expect(createCacheForWalletSetupFunctionSpy).toHaveBeenCalledTimes(3) + expectCreateCacheForWalletSetupFunction(1, 'hash1') + expectCreateCacheForWalletSetupFunction(2, 'hash2') + expectCreateCacheForWalletSetupFunction(3, 'hash3') + }) + }) +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee6fbd99e..122dee5fb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -58,6 +58,9 @@ importers: fs-extra: specifier: ^11.1.1 version: 11.1.1 + metamask: + specifier: workspace:* + version: link:../../wallets/metamask playwright-core: specifier: 1.39.0 version: 1.39.0