Skip to content

Commit

Permalink
✨ feat(core): Add triggerCacheCreation function
Browse files Browse the repository at this point in the history
  • Loading branch information
duckception committed Oct 26, 2023
1 parent 7471108 commit a66ae73
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
37 changes: 37 additions & 0 deletions packages/core/src/utils/triggerCacheCreation.ts
Original file line number Diff line number Diff line change
@@ -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<ReturnType<typeof getUniqueWalletSetupFunctions>>,
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<void> = createCacheForWalletSetupFunction(metamaskPath, cachePath, setupFunction)
cacheCreationPromises.push(createCachePromise)
}

return cacheCreationPromises
}
180 changes: 180 additions & 0 deletions packages/core/test/utils/triggerCacheCreation.test.ts
Original file line number Diff line number Diff line change
@@ -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<string, { fileName: string; setupFunction: WalletSetupFunction }>()

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')
})
})
})
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit a66ae73

Please sign in to comment.