Skip to content

Commit

Permalink
fix: binary file copy (#440)
Browse files Browse the repository at this point in the history
  • Loading branch information
scolladon authored Feb 1, 2023
1 parent 3b2a36c commit b0e363a
Show file tree
Hide file tree
Showing 9 changed files with 342 additions and 108 deletions.
199 changes: 199 additions & 0 deletions __tests__/unit/lib/utils/childProcessUtils.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
'use strict'
const {
EOLRegex,
getStreamContent,
linify,
treatPathSep,
sanitizePath,
} = require('../../../../src/utils/childProcessUtils')
const { EventEmitter, Readable } = require('stream')
const { sep } = require('path')

describe('childProcessUtils', () => {
describe('getStreamContent', () => {
describe.each([Buffer.from('text'), 'text'])(
'when called with stream of %o',
content => {
it('returns Buffer', async () => {
// Arrange
const stream = new EventEmitter()
stream.stdout = new Readable({
read() {
this.push(content)
this.push(null)
stream.emit('close')
},
})

// Act
const result = await getStreamContent(stream)

// Assert
expect(result).toEqual(Buffer.from('text'))
})
}
)

describe('when stream emit error', () => {
it('throws the error', async () => {
// Arrange
expect.assertions(1)
const stream = new EventEmitter()
stream.stdout = new Readable({
read() {
this.push(null)
stream.emit('error')
},
})

// Act
try {
await getStreamContent(stream)

// Assert
} catch (error) {
expect(error).toBeDefined()
}
})
})
})
describe('linify', () => {
describe('when called with lines', () => {
it('yield content line by line', async () => {
// Arrange
const input = 'multiline\ntext'
const stream = new Readable.from(input)

// Act
const lines = []
for await (const line of linify(stream)) {
lines.push(line)
}

// Assert
expect(lines).toEqual(expect.arrayContaining(input.split('\n')))
})
})

describe('when stream has no content in stdout', () => {
it('returns no lines', async () => {
// Arrange
const stream = new Readable({
read() {
this.push(null)
},
})

// Act
const lines = []
for await (const line of linify(stream)) {
lines.push(line)
}

// Assert
expect(lines).toEqual([])
})
})
})
describe('treatPathSep', () => {
it(`replace / by ${sep}`, () => {
// Arrange
const input = 'test///test//test/test'

// Act
const result = treatPathSep(input)

// Assert
expect(result).toBe(`test${sep}test${sep}test${sep}test`)
})

it(`replace \\ by ${sep}`, () => {
// Arrange
const input = 'test\\\\\\test\\\\test\\test'

// Act
const result = treatPathSep(input)

// Assert
expect(result).toBe(`test${sep}test${sep}test${sep}test`)
})
})
describe('sanitizePath', () => {
describe.each([undefined, null])('when called with %s', val => {
it(`returns ${val}`, () => {
// Act
const result = sanitizePath(val)

// Assert
expect(result).toEqual(val)
})
})

it(`returns path with '${sep}' separator`, () => {
// Arrange
const input = 'test\\test/test'

// Act
const result = sanitizePath(input)

// Assert
expect(result).toBe(`test${sep}test${sep}test`)
})

it(`normalize path`, () => {
// Arrange
const input = 'test/test\\../test'

// Act
const result = sanitizePath(input)

// Assert
expect(result).toBe(`test${sep}test`)
})
})
describe('EOLRegex', () => {
it('matches CR LF', () => {
// Arrange
const input = 'test\r\ntest'

// Act
const matches = EOLRegex.test(input)

// Assert
expect(matches).toBe(true)
})

it('matches LF', () => {
// Arrange
const input = 'testtest\n'

// Act
const matches = EOLRegex.test(input)

// Assert
expect(matches).toBe(true)
})

it('does not matches CR only', () => {
// Arrange
const input = 'test\rtest'

// Act
const matches = EOLRegex.test(input)

// Assert
expect(matches).toBe(false)
})

it('does not matches any string ', () => {
// Arrange
const input = 'test,test'

// Act
const matches = EOLRegex.test(input)

// Assert
expect(matches).toBe(false)
})
})
})
2 changes: 1 addition & 1 deletion __tests__/unit/lib/utils/fileGitDiff.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const child_process = require('child_process')

const TEST_PATH = 'path/to/file'

describe(`test if fileGitDiff`, () => {
describe(`fileGitDiff`, () => {
beforeEach(() => {
child_process.__setOutput([])
child_process.__setError(false)
Expand Down
68 changes: 42 additions & 26 deletions __tests__/unit/lib/utils/fsHelper.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,13 @@ jest.mock('../../../../src/utils/childProcessUtils', () => {

return {
...originalModule,
getStreamContent: jest.fn(),
treatPathSep: jest.fn(),
getStreamContent: jest.fn(() => Promise.resolve(Buffer.from(''))),
treatPathSep: jest.fn(() => ''),
}
})

let work
beforeEach(() => {
//jest.clearAllMocks()
work = {
config: {
output: '',
Expand Down Expand Up @@ -78,6 +77,13 @@ describe('readPathFromGit', () => {
['windows', 'force-app\\main\\default\\classes\\myClass.cls'],
['unix', 'force-app/main/default/classes/myClass.cls'],
])('when path is %s format', (_, path) => {
beforeEach(() => {
// Arrange
getStreamContent.mockImplementation(() =>
Promise.resolve(Buffer.from(''))
)
})

it('should use "config.to" and "normalized path" to get git history', async () => {
// Act
await readPathFromGit(path, work.config)
Expand All @@ -97,7 +103,6 @@ describe('readPathFromGit', () => {
describe('copyFile', () => {
describe('when file is already copied', () => {
it('should not copy file', async () => {
// Arrange
await copyFiles(work.config, 'source/file', 'output/file')
jest.resetAllMocks()

Expand All @@ -113,9 +118,6 @@ describe('copyFile', () => {

describe('when source location is empty', () => {
it('should not copy file', async () => {
// Arrange
getStreamContent.mockImplementation(() => '')

// Act
await copyFiles(work.config, 'source/doNotCopy', 'output/doNotCopy')

Expand All @@ -131,10 +133,12 @@ describe('copyFile', () => {
it('should copy the folder', async () => {
// Arrange
treatPathSep.mockImplementationOnce(() => 'output/copyDir/copyFile')
getStreamContent.mockImplementationOnce(
() => 'tree HEAD:folder\n\ncopyFile'
getStreamContent.mockImplementationOnce(() =>
Promise.resolve(Buffer.from('tree HEAD:folder\n\ncopyFile'))
)
getStreamContent.mockImplementation(() =>
Promise.resolve(Buffer.from('content'))
)
getStreamContent.mockImplementation(() => 'content')

// Act
await copyFiles(work.config, 'source/copyDir', 'output/copyDir')
Expand All @@ -143,6 +147,10 @@ describe('copyFile', () => {
expect(spawn).toBeCalledTimes(2)
expect(getStreamContent).toBeCalledTimes(2)
expect(outputFile).toBeCalledTimes(1)
expect(outputFile).toHaveBeenCalledWith(
'output/copyDir/copyFile',
Buffer.from('content')
)
expect(treatPathSep).toBeCalledTimes(1)
})
})
Expand All @@ -151,7 +159,9 @@ describe('copyFile', () => {
expect.assertions(4)
// Arrange
const fatalError = 'fatal: not a git repository'
getStreamContent.mockImplementation(() => 'fatal: not a git repository')
getStreamContent.mockImplementation(() =>
Promise.resolve(Buffer.from('fatal: not a git repository'))
)

// Act
try {
Expand All @@ -169,7 +179,9 @@ describe('copyFile', () => {
describe('when content is a file', () => {
beforeEach(async () => {
// Arrange
getStreamContent.mockImplementation(() => 'content')
getStreamContent.mockImplementation(() =>
Promise.resolve(Buffer.from('content'))
)
treatPathSep.mockImplementationOnce(() => 'output/copyFile')
})
it('should copy the file', async () => {
Expand All @@ -180,6 +192,10 @@ describe('copyFile', () => {
expect(spawn).toBeCalled()
expect(getStreamContent).toBeCalled()
expect(outputFile).toBeCalledTimes(1)
expect(outputFile).toHaveBeenCalledWith(
'output/copyFile',
Buffer.from('content')
)
expect(treatPathSep).toBeCalledTimes(1)
})
})
Expand All @@ -193,7 +209,7 @@ describe('readDir', () => {
beforeEach(() => {
// Arrange
getStreamContent.mockImplementation(() =>
Promise.resolve([`tree HEAD:${dir}`, '', file].join(EOL))
Promise.resolve(Buffer.from([`tree HEAD:${dir}`, '', file].join(EOL)))
)
})
it('should return the file', async () => {
Expand Down Expand Up @@ -279,7 +295,9 @@ describe('scan', () => {
describe('when getStreamContent returns nothing', () => {
beforeEach(() => {
// Arrange
getStreamContent.mockImplementation(() => Promise.resolve(''))
getStreamContent.mockImplementation(() =>
Promise.resolve(Buffer.from(''))
)
})
it('should return nothing', async () => {
// Arrange
Expand All @@ -297,7 +315,7 @@ describe('scan', () => {
beforeEach(() => {
// Arrange
getStreamContent.mockImplementation(() =>
Promise.resolve([`tree HEAD:${dir}`, '', file].join(EOL))
Promise.resolve(Buffer.from([`tree HEAD:${dir}`, '', file].join(EOL)))
)
})
it('should return a file', async () => {
Expand All @@ -315,13 +333,11 @@ describe('scan', () => {
const subDir = 'subDir/'
it('should return nothing', async () => {
// Arrange
getStreamContent.mockImplementation(() =>
Promise.resolve(
Promise.resolve([`tree HEAD:${dir}`, '', subDir].join(EOL))
)
getStreamContent.mockImplementationOnce(() =>
Promise.resolve(Buffer.from([`tree HEAD:${dir}`, '', subDir].join(EOL)))
)
getStreamContent.mockImplementation(() =>
Promise.resolve([`tree HEAD:${dir}${subDir}`].join(EOL))
Promise.resolve(Buffer.from([`tree HEAD:${dir}${subDir}`].join(EOL)))
)
const g = scan('dir', work)

Expand All @@ -339,12 +355,12 @@ describe('scan', () => {
beforeEach(() => {
// Arrange
getStreamContent.mockImplementationOnce(() =>
Promise.resolve(
Promise.resolve([`tree HEAD:${dir}`, '', subDir].join(EOL))
)
Promise.resolve(Buffer.from([`tree HEAD:${dir}`, '', subDir].join(EOL)))
)
getStreamContent.mockImplementation(() =>
Promise.resolve([`tree HEAD:${dir}${subDir}`, '', subFile].join(EOL))
Promise.resolve(
Buffer.from([`tree HEAD:${dir}${subDir}`, '', subFile].join(EOL))
)
)
})
it('should return a file', async () => {
Expand All @@ -365,7 +381,7 @@ describe('scanExtension', () => {
beforeEach(() => {
// Arrange
getStreamContent.mockImplementation(() =>
Promise.resolve([`tree HEAD:${dir}`, '', file].join(EOL))
Promise.resolve(Buffer.from([`tree HEAD:${dir}`, '', file].join(EOL)))
)
})
it('should return', async () => {
Expand All @@ -385,7 +401,7 @@ describe('scanExtension', () => {
beforeEach(() => {
// Arrange
getStreamContent.mockImplementation(() =>
Promise.resolve([`tree HEAD:${dir}`, '', file].join(EOL))
Promise.resolve(Buffer.from([`tree HEAD:${dir}`, '', file].join(EOL)))
)
})
it('should return a file', async () => {
Expand Down
Loading

0 comments on commit b0e363a

Please sign in to comment.