mirror of
https://github.com/actions/checkout.git
synced 2025-03-31 05:20:06 +02:00

This commit adds tests to verify the behavior of the gitDirectoryHelper.prepareExistingDirectory() function when the submodule status is either true or false. The test cleanWhenSubmoduleStatusIsFalse verifies that the function will clean the directory when the submodule status is false. The test sets up a mock implementation of git.submoduleStatus to always return false, writes a file to the repository, and then calls gitDirectoryHelper.prepareExistingDirectory(). The test verifies that the directory is cleaned and that git.tryClean() is called. The test doesNotCleanWhenSubmoduleStatusIsTrue verifies that the function will not clean the directory when the submodule status is true. The test sets up a mock implementation of git.submoduleStatus to always return true, writes a file to the repository, and then calls gitDirectoryHelper.prepareExistingDirectory(). The test verifies that the directory is not cleaned, that the file and .git folder are present, and that git.tryClean() is called. These tests ensure that the function behaves as expected based on the submodule status.
503 lines
14 KiB
TypeScript
503 lines
14 KiB
TypeScript
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 ref: string
|
|
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,
|
|
ref
|
|
)
|
|
|
|
// 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,
|
|
ref
|
|
)
|
|
|
|
// 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,
|
|
ref
|
|
)
|
|
|
|
// 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,
|
|
ref
|
|
)
|
|
|
|
// 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,
|
|
ref
|
|
)
|
|
|
|
// 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,
|
|
ref
|
|
)
|
|
|
|
// 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,
|
|
ref
|
|
)
|
|
|
|
// 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,
|
|
ref
|
|
)
|
|
|
|
// 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,
|
|
ref
|
|
)
|
|
|
|
// 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,
|
|
ref
|
|
)
|
|
|
|
// 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 cleanWhenSubmoduleStatusIsFalse =
|
|
'cleans when submodule status is false'
|
|
|
|
it(cleanWhenSubmoduleStatusIsFalse, async () => {
|
|
// Arrange
|
|
await setup(cleanWhenSubmoduleStatusIsFalse)
|
|
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
|
|
|
//mock bad submodule
|
|
|
|
const submoduleStatus = git.submoduleStatus as jest.Mock<any, any>
|
|
submoduleStatus.mockImplementation(async (remote: boolean) => {
|
|
return false
|
|
})
|
|
|
|
// Act
|
|
await gitDirectoryHelper.prepareExistingDirectory(
|
|
git,
|
|
repositoryPath,
|
|
repositoryUrl,
|
|
clean,
|
|
ref
|
|
)
|
|
|
|
// Assert
|
|
const files = await fs.promises.readdir(repositoryPath)
|
|
expect(files).toHaveLength(0)
|
|
expect(git.tryClean).toHaveBeenCalled()
|
|
})
|
|
|
|
const doesNotCleanWhenSubmoduleStatusIsTrue =
|
|
'does not clean when submodule status is true'
|
|
|
|
it(doesNotCleanWhenSubmoduleStatusIsTrue, async () => {
|
|
// Arrange
|
|
await setup(doesNotCleanWhenSubmoduleStatusIsTrue)
|
|
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
|
|
|
const submoduleStatus = git.submoduleStatus as jest.Mock<any, any>
|
|
submoduleStatus.mockImplementation(async (remote: boolean) => {
|
|
return true
|
|
})
|
|
|
|
// Act
|
|
await gitDirectoryHelper.prepareExistingDirectory(
|
|
git,
|
|
repositoryPath,
|
|
repositoryUrl,
|
|
clean,
|
|
ref
|
|
)
|
|
|
|
// Assert
|
|
|
|
const files = await fs.promises.readdir(repositoryPath)
|
|
expect(files.sort()).toEqual(['.git', 'my-file'])
|
|
expect(git.tryClean).toHaveBeenCalled()
|
|
})
|
|
|
|
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,
|
|
ref
|
|
)
|
|
|
|
// 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 removesAncestorRemoteBranch = 'removes ancestor remote branch'
|
|
it(removesAncestorRemoteBranch, async () => {
|
|
// Arrange
|
|
await setup(removesAncestorRemoteBranch)
|
|
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 ? ['origin/remote-branch-1', 'origin/remote-branch-2'] : []
|
|
})
|
|
ref = 'remote-branch-1/conflict'
|
|
|
|
// Act
|
|
await gitDirectoryHelper.prepareExistingDirectory(
|
|
git,
|
|
repositoryPath,
|
|
repositoryUrl,
|
|
clean,
|
|
ref
|
|
)
|
|
|
|
// Assert
|
|
const files = await fs.promises.readdir(repositoryPath)
|
|
expect(files.sort()).toEqual(['.git', 'my-file'])
|
|
expect(git.branchDelete).toHaveBeenCalledTimes(1)
|
|
expect(git.branchDelete).toHaveBeenCalledWith(
|
|
true,
|
|
'origin/remote-branch-1'
|
|
)
|
|
})
|
|
|
|
const removesDescendantRemoteBranches = 'removes descendant remote branch'
|
|
it(removesDescendantRemoteBranches, async () => {
|
|
// Arrange
|
|
await setup(removesDescendantRemoteBranches)
|
|
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
|
|
? ['origin/remote-branch-1/conflict', 'origin/remote-branch-2']
|
|
: []
|
|
})
|
|
ref = 'remote-branch-1'
|
|
|
|
// Act
|
|
await gitDirectoryHelper.prepareExistingDirectory(
|
|
git,
|
|
repositoryPath,
|
|
repositoryUrl,
|
|
clean,
|
|
ref
|
|
)
|
|
|
|
// Assert
|
|
const files = await fs.promises.readdir(repositoryPath)
|
|
expect(files.sort()).toEqual(['.git', 'my-file'])
|
|
expect(git.branchDelete).toHaveBeenCalledTimes(1)
|
|
expect(git.branchDelete).toHaveBeenCalledWith(
|
|
true,
|
|
'origin/remote-branch-1/conflict'
|
|
)
|
|
})
|
|
})
|
|
|
|
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
|
|
|
|
// Ref
|
|
ref = ''
|
|
|
|
// 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(),
|
|
getDefaultBranch: jest.fn(),
|
|
getWorkingDirectory: jest.fn(() => repositoryPath),
|
|
init: jest.fn(),
|
|
isDetached: jest.fn(),
|
|
lfsFetch: jest.fn(),
|
|
lfsInstall: jest.fn(),
|
|
log1: jest.fn(),
|
|
remoteAdd: jest.fn(),
|
|
removeEnvironmentVariable: jest.fn(),
|
|
revParse: jest.fn(),
|
|
setEnvironmentVariable: jest.fn(),
|
|
shaExists: jest.fn(),
|
|
submoduleForeach: jest.fn(),
|
|
submoduleSync: jest.fn(),
|
|
submoduleUpdate: jest.fn(),
|
|
submoduleStatus: jest.fn(async () => {
|
|
return true
|
|
}),
|
|
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
|
|
})
|
|
}
|
|
}
|