Files
pnpm/packages/package-store/test/createImportPackage.spec.ts
2021-02-15 05:06:10 +02:00

148 lines
5.3 KiB
TypeScript

import path from 'path'
const fsMock = { promises: {} as any } as any // eslint-disable-line
jest.mock('fs', () => {
const { access, constants, promises } = jest.requireActual('fs')
fsMock.constants = constants
fsMock.promises.mkdir = promises.mkdir
fsMock.promises.readdir = promises.readdir
fsMock.access = access
return fsMock
})
jest.mock('path-temp', () => (dir: string) => path.join(dir, '_tmp'))
jest.mock('rename-overwrite', () => jest.fn())
// eslint-disable-next-line
import createImportPackage from '@pnpm/package-store/lib/storeController/createImportPackage'
test('packageImportMethod=auto: clone files by default', async () => {
const importPackage = createImportPackage('auto')
fsMock.promises.copyFile = jest.fn()
fsMock.promises.rename = jest.fn()
expect(await importPackage('project/package', {
filesMap: {
'index.js': 'hash2',
'package.json': 'hash1',
},
force: false,
fromStore: false,
})).toBe('clone')
expect(fsMock.promises.copyFile).toBeCalledWith(
path.join('hash1'),
path.join('project', '_tmp', 'package.json'),
fsMock.constants.COPYFILE_FICLONE_FORCE
)
expect(fsMock.promises.copyFile).toBeCalledWith(
path.join('hash2'),
path.join('project', '_tmp', 'index.js'),
fsMock.constants.COPYFILE_FICLONE_FORCE
)
})
test('packageImportMethod=auto: link files if cloning fails', async () => {
const importPackage = createImportPackage('auto')
fsMock.promises.copyFile = jest.fn(() => {
throw new Error('This file system does not support cloning')
})
fsMock.promises.link = jest.fn()
fsMock.promises.rename = jest.fn()
expect(await importPackage('project/package', {
filesMap: {
'index.js': 'hash2',
'package.json': 'hash1',
},
force: false,
fromStore: false,
})).toBe('hardlink')
expect(fsMock.promises.link).toBeCalledWith(path.join('hash1'), path.join('project', '_tmp', 'package.json'))
expect(fsMock.promises.link).toBeCalledWith(path.join('hash2'), path.join('project', '_tmp', 'index.js'))
expect(fsMock.promises.copyFile).toBeCalled()
fsMock.promises.copyFile.mockClear()
// The copy function will not be called again
expect(await importPackage('project2/package', {
filesMap: {
'index.js': 'hash2',
'package.json': 'hash1',
},
force: false,
fromStore: false,
})).toBe('hardlink')
expect(fsMock.promises.copyFile).not.toBeCalled()
expect(fsMock.promises.link).toBeCalledWith(path.join('hash1'), path.join('project2', '_tmp', 'package.json'))
expect(fsMock.promises.link).toBeCalledWith(path.join('hash2'), path.join('project2', '_tmp', 'index.js'))
})
test('packageImportMethod=auto: link files if cloning fails and even hard linking fails but not with EXDEV error', async () => {
const importPackage = createImportPackage('auto')
fsMock.promises.copyFile = jest.fn(() => {
throw new Error('This file system does not support cloning')
})
let linkFirstCall = true
fsMock.promises.link = jest.fn(() => {
if (linkFirstCall) {
linkFirstCall = false
throw new Error()
}
})
fsMock.promises.rename = jest.fn()
expect(await importPackage('project/package', {
filesMap: {
'index.js': 'hash2',
},
force: false,
fromStore: false,
})).toBe('hardlink')
expect(fsMock.promises.link).toBeCalledWith(path.join('hash2'), path.join('project', '_tmp', 'index.js'))
expect(fsMock.promises.link).toBeCalledTimes(2)
expect(fsMock.promises.copyFile).toBeCalledTimes(1)
})
test('packageImportMethod=auto: chooses copying if cloning and hard linking is not possible', async () => {
const importPackage = createImportPackage('auto')
fsMock.promises.copyFile = jest.fn((src: string, dest: string, flags?: number) => {
if (flags === fsMock.constants.COPYFILE_FICLONE_FORCE) {
throw new Error('This file system does not support cloning')
}
})
fsMock.promises.link = jest.fn(() => {
throw new Error('EXDEV: cross-device link not permitted')
})
fsMock.promises.rename = jest.fn()
expect(await importPackage('project/package', {
filesMap: {
'index.js': 'hash2',
},
force: false,
fromStore: false,
})).toBe('copy')
expect(fsMock.promises.copyFile).toBeCalledWith(path.join('hash2'), path.join('project', '_tmp', 'index.js'))
expect(fsMock.promises.copyFile).toBeCalledTimes(2)
})
test('packageImportMethod=hardlink: fall back to copying if hardlinking fails', async () => {
const importPackage = createImportPackage('hardlink')
fsMock.promises.link = jest.fn((src: string, dest: string) => {
if (dest.endsWith('license')) {
const err = new Error('')
err['code'] = 'EEXIST'
throw err
}
throw new Error('This file system does not support hard linking')
})
fsMock.promises.copyFile = jest.fn()
expect(await importPackage('project/package', {
filesMap: {
'index.js': 'hash2',
'package.json': 'hash1',
license: 'hash3',
},
force: false,
fromStore: false,
})).toBe('hardlink')
expect(fsMock.promises.link).toBeCalledTimes(3)
expect(fsMock.promises.copyFile).toBeCalledTimes(2) // One time the target already exists, so it won't be copied
expect(fsMock.promises.copyFile).toBeCalledWith(path.join('hash1'), path.join('project', '_tmp', 'package.json'))
expect(fsMock.promises.copyFile).toBeCalledWith(path.join('hash2'), path.join('project', '_tmp', 'index.js'))
})