more unit tests and corresponding refactoring (#174)

This commit is contained in:
eric sciple
2020-03-02 11:33:30 -05:00
committed by GitHub
parent 096e927750
commit f219062370
14 changed files with 1049 additions and 305 deletions

View File

@ -0,0 +1,200 @@
import * as core from '@actions/core'
import * as fs from 'fs'
import * as gitAuthHelper from '../lib/git-auth-helper'
import * as io from '@actions/io'
import * as path from 'path'
import {IGitCommandManager} from '../lib/git-command-manager'
import {IGitSourceSettings} from '../lib/git-source-settings'
const testWorkspace = path.join(__dirname, '_temp', 'git-auth-helper')
const originalRunnerTemp = process.env['RUNNER_TEMP']
let workspace: string
let gitConfigPath: string
let runnerTemp: string
let git: IGitCommandManager
let settings: IGitSourceSettings
describe('git-auth-helper tests', () => {
beforeAll(async () => {
// Clear test workspace
await io.rmRF(testWorkspace)
})
beforeEach(() => {
// Mock setSecret
jest.spyOn(core, 'setSecret').mockImplementation((secret: string) => {})
})
afterEach(() => {
// Unregister mocks
jest.restoreAllMocks()
})
afterAll(() => {
// Restore RUNNER_TEMP
delete process.env['RUNNER_TEMP']
if (originalRunnerTemp) {
process.env['RUNNER_TEMP'] = originalRunnerTemp
}
})
const configuresAuthHeader = 'configures auth header'
it(configuresAuthHeader, async () => {
// Arrange
await setup(configuresAuthHeader)
expect(settings.authToken).toBeTruthy() // sanity check
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
// Act
await authHelper.configureAuth()
// Assert config
const configContent = (await fs.promises.readFile(gitConfigPath)).toString()
const basicCredential = Buffer.from(
`x-access-token:${settings.authToken}`,
'utf8'
).toString('base64')
expect(
configContent.indexOf(
`http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}`
)
).toBeGreaterThanOrEqual(0)
})
const configuresAuthHeaderEvenWhenPersistCredentialsFalse =
'configures auth header even when persist credentials false'
it(configuresAuthHeaderEvenWhenPersistCredentialsFalse, async () => {
// Arrange
await setup(configuresAuthHeaderEvenWhenPersistCredentialsFalse)
expect(settings.authToken).toBeTruthy() // sanity check
settings.persistCredentials = false
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
// Act
await authHelper.configureAuth()
// Assert config
const configContent = (await fs.promises.readFile(gitConfigPath)).toString()
expect(
configContent.indexOf(
`http.https://github.com/.extraheader AUTHORIZATION`
)
).toBeGreaterThanOrEqual(0)
})
const registersBasicCredentialAsSecret =
'registers basic credential as secret'
it(registersBasicCredentialAsSecret, async () => {
// Arrange
await setup(registersBasicCredentialAsSecret)
expect(settings.authToken).toBeTruthy() // sanity check
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
// Act
await authHelper.configureAuth()
// Assert secret
const setSecretSpy = core.setSecret as jest.Mock<any, any>
expect(setSecretSpy).toHaveBeenCalledTimes(1)
const expectedSecret = Buffer.from(
`x-access-token:${settings.authToken}`,
'utf8'
).toString('base64')
expect(setSecretSpy).toHaveBeenCalledWith(expectedSecret)
})
const removesToken = 'removes token'
it(removesToken, async () => {
// Arrange
await setup(removesToken)
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
await authHelper.configureAuth()
let gitConfigContent = (
await fs.promises.readFile(gitConfigPath)
).toString()
expect(gitConfigContent.indexOf('http.')).toBeGreaterThanOrEqual(0) // sanity check
// Act
await authHelper.removeAuth()
// Assert git config
gitConfigContent = (await fs.promises.readFile(gitConfigPath)).toString()
expect(gitConfigContent.indexOf('http.')).toBeLessThan(0)
})
})
async function setup(testName: string): Promise<void> {
testName = testName.replace(/[^a-zA-Z0-9_]+/g, '-')
// Directories
workspace = path.join(testWorkspace, testName, 'workspace')
runnerTemp = path.join(testWorkspace, testName, 'runner-temp')
await fs.promises.mkdir(workspace, {recursive: true})
await fs.promises.mkdir(runnerTemp, {recursive: true})
process.env['RUNNER_TEMP'] = runnerTemp
// Create git config
gitConfigPath = path.join(workspace, '.git', 'config')
await fs.promises.mkdir(path.join(workspace, '.git'), {recursive: true})
await fs.promises.writeFile(path.join(workspace, '.git', 'config'), '')
git = {
branchDelete: jest.fn(),
branchExists: jest.fn(),
branchList: jest.fn(),
checkout: jest.fn(),
checkoutDetach: jest.fn(),
config: jest.fn(async (key: string, value: string) => {
await fs.promises.appendFile(gitConfigPath, `\n${key} ${value}`)
}),
configExists: jest.fn(
async (key: string): Promise<boolean> => {
const content = await fs.promises.readFile(gitConfigPath)
const lines = content
.toString()
.split('\n')
.filter(x => x)
return lines.some(x => x.startsWith(key))
}
),
fetch: jest.fn(),
getWorkingDirectory: jest.fn(() => workspace),
init: jest.fn(),
isDetached: jest.fn(),
lfsFetch: jest.fn(),
lfsInstall: jest.fn(),
log1: jest.fn(),
remoteAdd: jest.fn(),
setEnvironmentVariable: jest.fn(),
tagExists: jest.fn(),
tryClean: jest.fn(),
tryConfigUnset: jest.fn(
async (key: string): Promise<boolean> => {
let content = await fs.promises.readFile(gitConfigPath)
let lines = content
.toString()
.split('\n')
.filter(x => x)
.filter(x => !x.startsWith(key))
await fs.promises.writeFile(gitConfigPath, lines.join('\n'))
return true
}
),
tryDisableAutomaticGarbageCollection: jest.fn(),
tryGetFetchUrl: jest.fn(),
tryReset: jest.fn()
}
settings = {
authToken: 'some auth token',
clean: true,
commit: '',
fetchDepth: 1,
lfs: false,
persistCredentials: true,
ref: 'refs/heads/master',
repositoryName: 'my-repo',
repositoryOwner: 'my-org',
repositoryPath: ''
}
}

View File

@ -0,0 +1,382 @@
import * as core from '@actions/core'
import * as fs from 'fs'
import * as gitDirectoryHelper from '../lib/git-directory-helper'
import * as io from '@actions/io'
import * as path from 'path'
import {IGitCommandManager} from '../lib/git-command-manager'
const testWorkspace = path.join(__dirname, '_temp', 'git-directory-helper')
let repositoryPath: string
let repositoryUrl: string
let clean: boolean
let git: IGitCommandManager
describe('git-directory-helper tests', () => {
beforeAll(async () => {
// Clear test workspace
await io.rmRF(testWorkspace)
})
beforeEach(() => {
// Mock error/warning/info/debug
jest.spyOn(core, 'error').mockImplementation(jest.fn())
jest.spyOn(core, 'warning').mockImplementation(jest.fn())
jest.spyOn(core, 'info').mockImplementation(jest.fn())
jest.spyOn(core, 'debug').mockImplementation(jest.fn())
})
afterEach(() => {
// Unregister mocks
jest.restoreAllMocks()
})
const cleansWhenCleanTrue = 'cleans when clean true'
it(cleansWhenCleanTrue, async () => {
// Arrange
await setup(cleansWhenCleanTrue)
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
// Act
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
clean
)
// Assert
const files = await fs.promises.readdir(repositoryPath)
expect(files.sort()).toEqual(['.git', 'my-file'])
expect(git.tryClean).toHaveBeenCalled()
expect(git.tryReset).toHaveBeenCalled()
expect(core.warning).not.toHaveBeenCalled()
})
const checkoutDetachWhenNotDetached = 'checkout detach when not detached'
it(checkoutDetachWhenNotDetached, async () => {
// Arrange
await setup(checkoutDetachWhenNotDetached)
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
// Act
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
clean
)
// Assert
const files = await fs.promises.readdir(repositoryPath)
expect(files.sort()).toEqual(['.git', 'my-file'])
expect(git.checkoutDetach).toHaveBeenCalled()
})
const doesNotCheckoutDetachWhenNotAlreadyDetached =
'does not checkout detach when already detached'
it(doesNotCheckoutDetachWhenNotAlreadyDetached, async () => {
// Arrange
await setup(doesNotCheckoutDetachWhenNotAlreadyDetached)
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
const mockIsDetached = git.isDetached as jest.Mock<any, any>
mockIsDetached.mockImplementation(async () => {
return true
})
// Act
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
clean
)
// Assert
const files = await fs.promises.readdir(repositoryPath)
expect(files.sort()).toEqual(['.git', 'my-file'])
expect(git.checkoutDetach).not.toHaveBeenCalled()
})
const doesNotCleanWhenCleanFalse = 'does not clean when clean false'
it(doesNotCleanWhenCleanFalse, async () => {
// Arrange
await setup(doesNotCleanWhenCleanFalse)
clean = false
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
// Act
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
clean
)
// Assert
const files = await fs.promises.readdir(repositoryPath)
expect(files.sort()).toEqual(['.git', 'my-file'])
expect(git.isDetached).toHaveBeenCalled()
expect(git.branchList).toHaveBeenCalled()
expect(core.warning).not.toHaveBeenCalled()
expect(git.tryClean).not.toHaveBeenCalled()
expect(git.tryReset).not.toHaveBeenCalled()
})
const removesContentsWhenCleanFails = 'removes contents when clean fails'
it(removesContentsWhenCleanFails, async () => {
// Arrange
await setup(removesContentsWhenCleanFails)
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
let mockTryClean = git.tryClean as jest.Mock<any, any>
mockTryClean.mockImplementation(async () => {
return false
})
// Act
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
clean
)
// Assert
const files = await fs.promises.readdir(repositoryPath)
expect(files).toHaveLength(0)
expect(git.tryClean).toHaveBeenCalled()
expect(core.warning).toHaveBeenCalled()
expect(git.tryReset).not.toHaveBeenCalled()
})
const removesContentsWhenDifferentRepositoryUrl =
'removes contents when different repository url'
it(removesContentsWhenDifferentRepositoryUrl, async () => {
// Arrange
await setup(removesContentsWhenDifferentRepositoryUrl)
clean = false
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
const differentRepositoryUrl =
'https://github.com/my-different-org/my-different-repo'
// Act
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
differentRepositoryUrl,
clean
)
// Assert
const files = await fs.promises.readdir(repositoryPath)
expect(files).toHaveLength(0)
expect(core.warning).not.toHaveBeenCalled()
expect(git.isDetached).not.toHaveBeenCalled()
})
const removesContentsWhenNoGitDirectory =
'removes contents when no git directory'
it(removesContentsWhenNoGitDirectory, async () => {
// Arrange
await setup(removesContentsWhenNoGitDirectory)
clean = false
await io.rmRF(path.join(repositoryPath, '.git'))
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
// Act
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
clean
)
// Assert
const files = await fs.promises.readdir(repositoryPath)
expect(files).toHaveLength(0)
expect(core.warning).not.toHaveBeenCalled()
expect(git.isDetached).not.toHaveBeenCalled()
})
const removesContentsWhenResetFails = 'removes contents when reset fails'
it(removesContentsWhenResetFails, async () => {
// Arrange
await setup(removesContentsWhenResetFails)
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
let mockTryReset = git.tryReset as jest.Mock<any, any>
mockTryReset.mockImplementation(async () => {
return false
})
// Act
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
clean
)
// Assert
const files = await fs.promises.readdir(repositoryPath)
expect(files).toHaveLength(0)
expect(git.tryClean).toHaveBeenCalled()
expect(git.tryReset).toHaveBeenCalled()
expect(core.warning).toHaveBeenCalled()
})
const removesContentsWhenUndefinedGitCommandManager =
'removes contents when undefined git command manager'
it(removesContentsWhenUndefinedGitCommandManager, async () => {
// Arrange
await setup(removesContentsWhenUndefinedGitCommandManager)
clean = false
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
// Act
await gitDirectoryHelper.prepareExistingDirectory(
undefined,
repositoryPath,
repositoryUrl,
clean
)
// Assert
const files = await fs.promises.readdir(repositoryPath)
expect(files).toHaveLength(0)
expect(core.warning).not.toHaveBeenCalled()
})
const removesLocalBranches = 'removes local branches'
it(removesLocalBranches, async () => {
// Arrange
await setup(removesLocalBranches)
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
const mockBranchList = git.branchList as jest.Mock<any, any>
mockBranchList.mockImplementation(async (remote: boolean) => {
return remote ? [] : ['local-branch-1', 'local-branch-2']
})
// Act
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
clean
)
// Assert
const files = await fs.promises.readdir(repositoryPath)
expect(files.sort()).toEqual(['.git', 'my-file'])
expect(git.branchDelete).toHaveBeenCalledWith(false, 'local-branch-1')
expect(git.branchDelete).toHaveBeenCalledWith(false, 'local-branch-2')
})
const removesLockFiles = 'removes lock files'
it(removesLockFiles, async () => {
// Arrange
await setup(removesLockFiles)
clean = false
await fs.promises.writeFile(
path.join(repositoryPath, '.git', 'index.lock'),
''
)
await fs.promises.writeFile(
path.join(repositoryPath, '.git', 'shallow.lock'),
''
)
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
// Act
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
clean
)
// Assert
let files = await fs.promises.readdir(path.join(repositoryPath, '.git'))
expect(files).toHaveLength(0)
files = await fs.promises.readdir(repositoryPath)
expect(files.sort()).toEqual(['.git', 'my-file'])
expect(git.isDetached).toHaveBeenCalled()
expect(git.branchList).toHaveBeenCalled()
expect(core.warning).not.toHaveBeenCalled()
expect(git.tryClean).not.toHaveBeenCalled()
expect(git.tryReset).not.toHaveBeenCalled()
})
const removesRemoteBranches = 'removes local branches'
it(removesRemoteBranches, async () => {
// Arrange
await setup(removesRemoteBranches)
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
const mockBranchList = git.branchList as jest.Mock<any, any>
mockBranchList.mockImplementation(async (remote: boolean) => {
return remote ? ['remote-branch-1', 'remote-branch-2'] : []
})
// Act
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
clean
)
// Assert
const files = await fs.promises.readdir(repositoryPath)
expect(files.sort()).toEqual(['.git', 'my-file'])
expect(git.branchDelete).toHaveBeenCalledWith(true, 'remote-branch-1')
expect(git.branchDelete).toHaveBeenCalledWith(true, 'remote-branch-2')
})
})
async function setup(testName: string): Promise<void> {
testName = testName.replace(/[^a-zA-Z0-9_]+/g, '-')
// Repository directory
repositoryPath = path.join(testWorkspace, testName)
await fs.promises.mkdir(path.join(repositoryPath, '.git'), {recursive: true})
// Repository URL
repositoryUrl = 'https://github.com/my-org/my-repo'
// Clean
clean = true
// Git command manager
git = {
branchDelete: jest.fn(),
branchExists: jest.fn(),
branchList: jest.fn(async () => {
return []
}),
checkout: jest.fn(),
checkoutDetach: jest.fn(),
config: jest.fn(),
configExists: jest.fn(),
fetch: jest.fn(),
getWorkingDirectory: jest.fn(() => repositoryPath),
init: jest.fn(),
isDetached: jest.fn(),
lfsFetch: jest.fn(),
lfsInstall: jest.fn(),
log1: jest.fn(),
remoteAdd: jest.fn(),
setEnvironmentVariable: jest.fn(),
tagExists: jest.fn(),
tryClean: jest.fn(async () => {
return true
}),
tryConfigUnset: jest.fn(),
tryDisableAutomaticGarbageCollection: jest.fn(),
tryGetFetchUrl: jest.fn(async () => {
// Sanity check - this function shouldn't be called when the .git directory doesn't exist
await fs.promises.stat(path.join(repositoryPath, '.git'))
return repositoryUrl
}),
tryReset: jest.fn(async () => {
return true
})
}
}

View File

@ -4,7 +4,7 @@ import * as fsHelper from '../lib/fs-helper'
import * as github from '@actions/github'
import * as inputHelper from '../lib/input-helper'
import * as path from 'path'
import {ISourceSettings} from '../lib/git-source-provider'
import {IGitSourceSettings} from '../lib/git-source-settings'
const originalGitHubWorkspace = process.env['GITHUB_WORKSPACE']
const gitHubWorkspace = path.resolve('/checkout-tests/workspace')
@ -17,12 +17,18 @@ let originalContext = {...github.context}
describe('input-helper tests', () => {
beforeAll(() => {
// Mock @actions/core getInput()
// Mock getInput
jest.spyOn(core, 'getInput').mockImplementation((name: string) => {
return inputs[name]
})
// Mock @actions/github context
// Mock error/warning/info/debug
jest.spyOn(core, 'error').mockImplementation(jest.fn())
jest.spyOn(core, 'warning').mockImplementation(jest.fn())
jest.spyOn(core, 'info').mockImplementation(jest.fn())
jest.spyOn(core, 'debug').mockImplementation(jest.fn())
// Mock github context
jest.spyOn(github.context, 'repo', 'get').mockImplementation(() => {
return {
owner: 'some-owner',
@ -62,7 +68,7 @@ describe('input-helper tests', () => {
})
it('sets defaults', () => {
const settings: ISourceSettings = inputHelper.getInputs()
const settings: IGitSourceSettings = inputHelper.getInputs()
expect(settings).toBeTruthy()
expect(settings.authToken).toBeFalsy()
expect(settings.clean).toBe(true)
@ -80,7 +86,7 @@ describe('input-helper tests', () => {
let originalRef = github.context.ref
try {
github.context.ref = 'some-unqualified-ref'
const settings: ISourceSettings = inputHelper.getInputs()
const settings: IGitSourceSettings = inputHelper.getInputs()
expect(settings).toBeTruthy()
expect(settings.commit).toBe('1234567890123456789012345678901234567890')
expect(settings.ref).toBe('refs/heads/some-unqualified-ref')
@ -98,7 +104,7 @@ describe('input-helper tests', () => {
it('roots path', () => {
inputs.path = 'some-directory/some-subdirectory'
const settings: ISourceSettings = inputHelper.getInputs()
const settings: IGitSourceSettings = inputHelper.getInputs()
expect(settings.repositoryPath).toBe(
path.join(gitHubWorkspace, 'some-directory', 'some-subdirectory')
)
@ -106,21 +112,21 @@ describe('input-helper tests', () => {
it('sets correct default ref/sha for other repo', () => {
inputs.repository = 'some-owner/some-other-repo'
const settings: ISourceSettings = inputHelper.getInputs()
const settings: IGitSourceSettings = inputHelper.getInputs()
expect(settings.ref).toBe('refs/heads/master')
expect(settings.commit).toBeFalsy()
})
it('sets ref to empty when explicit sha', () => {
inputs.ref = '1111111111222222222233333333334444444444'
const settings: ISourceSettings = inputHelper.getInputs()
const settings: IGitSourceSettings = inputHelper.getInputs()
expect(settings.ref).toBeFalsy()
expect(settings.commit).toBe('1111111111222222222233333333334444444444')
})
it('sets sha to empty when explicit ref', () => {
inputs.ref = 'refs/heads/some-other-ref'
const settings: ISourceSettings = inputHelper.getInputs()
const settings: IGitSourceSettings = inputHelper.getInputs()
expect(settings.ref).toBe('refs/heads/some-other-ref')
expect(settings.commit).toBeFalsy()
})