diff options
Diffstat (limited to '__test__')
| -rw-r--r-- | __test__/git-auth-helper.test.ts | 75 | ||||
| -rw-r--r-- | __test__/git-command-manager.test.ts | 105 | ||||
| -rw-r--r-- | __test__/git-directory-helper.test.ts | 60 | ||||
| -rw-r--r-- | __test__/git-version.test.ts | 5 | ||||
| -rw-r--r-- | __test__/github-api-helper.test.ts | 44 | ||||
| -rw-r--r-- | __test__/input-helper.test.ts | 119 | ||||
| -rw-r--r-- | __test__/ref-helper.test.ts | 92 | ||||
| -rw-r--r-- | __test__/retry-helper.test.ts | 33 | ||||
| -rw-r--r-- | __test__/unsafe-pr-checkout-helper.test.ts | 56 | ||||
| -rw-r--r-- | __test__/url-helper.test.ts | 3 |
10 files changed, 378 insertions, 214 deletions
diff --git a/__test__/git-auth-helper.test.ts b/__test__/git-auth-helper.test.ts index 3c4f049..2c963b9 100644 --- a/__test__/git-auth-helper.test.ts +++ b/__test__/git-auth-helper.test.ts @@ -1,12 +1,46 @@ -import * as core from '@actions/core' +import { + jest, + describe, + it, + expect, + beforeAll, + beforeEach, + afterEach, + afterAll +} from '@jest/globals' import * as fs from 'fs' -import * as gitAuthHelper from '../lib/git-auth-helper' import * as io from '@actions/io' import * as os from 'os' import * as path from 'path' -import * as stateHelper from '../lib/state-helper' -import {IGitCommandManager} from '../lib/git-command-manager' -import {IGitSourceSettings} from '../lib/git-source-settings' +import {fileURLToPath} from 'url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +// Mock @actions/core before loading git-auth-helper +jest.unstable_mockModule('@actions/core', () => ({ + setSecret: jest.fn(), + error: jest.fn(), + warning: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + setFailed: jest.fn() +})) + +// Mock state-helper +jest.unstable_mockModule('../src/state-helper.js', () => ({ + setSshKeyPath: jest.fn(), + setSshKnownHostsPath: jest.fn(), + IsPost: false, + RepositoryPath: '' +})) + +// Dynamic imports after mocking +const core = await import('@actions/core') +const gitAuthHelper = await import('../src/git-auth-helper.js') +type IGitCommandManager = + import('../src/git-command-manager.js').IGitCommandManager +type IGitSourceSettings = + import('../src/git-source-settings.js').IGitSourceSettings const isWindows = process.platform === 'win32' const testWorkspace = path.join(__dirname, '_temp', 'git-auth-helper') @@ -32,25 +66,12 @@ describe('git-auth-helper tests', () => { }) beforeEach(() => { - // Mock setSecret - jest.spyOn(core, 'setSecret').mockImplementation((secret: string) => {}) - - // 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 state helper - jest.spyOn(stateHelper, 'setSshKeyPath').mockImplementation(jest.fn()) - jest - .spyOn(stateHelper, 'setSshKnownHostsPath') - .mockImplementation(jest.fn()) + jest.clearAllMocks() }) afterEach(() => { // Unregister mocks - jest.restoreAllMocks() + jest.clearAllMocks() // Restore HOME if (originalHome) { @@ -229,7 +250,7 @@ describe('git-auth-helper tests', () => { await authHelper.configureAuth() // Assert secret - const setSecretSpy = core.setSecret as jest.Mock<any, any> + const setSecretSpy = core.setSecret as jest.Mock<any> expect(setSecretSpy).toHaveBeenCalledTimes(1) const expectedSecret = Buffer.from( `x-access-token:${settings.authToken}`, @@ -529,7 +550,7 @@ describe('git-auth-helper tests', () => { settings.sshKey = '' const authHelper = gitAuthHelper.createAuthHelper(git, settings) await authHelper.configureAuth() - const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any> + const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any> mockSubmoduleForeach.mockClear() // reset calls // Act @@ -562,7 +583,7 @@ describe('git-auth-helper tests', () => { settings.persistCredentials = false const authHelper = gitAuthHelper.createAuthHelper(git, settings) await authHelper.configureAuth() - const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any> + const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any> mockSubmoduleForeach.mockClear() // reset calls // Act @@ -588,7 +609,7 @@ describe('git-auth-helper tests', () => { settings.sshKey = '' const authHelper = gitAuthHelper.createAuthHelper(git, settings) await authHelper.configureAuth() - const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any> + const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any> mockSubmoduleForeach.mockClear() // reset calls // Act @@ -627,7 +648,7 @@ describe('git-auth-helper tests', () => { ) const authHelper = gitAuthHelper.createAuthHelper(git, settings) await authHelper.configureAuth() - const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any> + const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any> mockSubmoduleForeach.mockClear() // reset calls // Act @@ -809,7 +830,7 @@ describe('git-auth-helper tests', () => { // Mock getSubmoduleConfigPaths to return our fake submodules (for both configure and remove) const mockGetSubmoduleConfigPaths = - git.getSubmoduleConfigPaths as jest.Mock<any, any> + git.getSubmoduleConfigPaths as jest.Mock<any> mockGetSubmoduleConfigPaths.mockResolvedValue([ submodule1ConfigPath, submodule2ConfigPath @@ -1147,7 +1168,7 @@ async function setup(testName: string): Promise<void> { ), tryReset: jest.fn(), version: jest.fn() - } + } as unknown as IGitCommandManager & {env: {[key: string]: string}} settings = { authToken: 'some auth token', diff --git a/__test__/git-command-manager.test.ts b/__test__/git-command-manager.test.ts index 47a0f8f..e669c0f 100644 --- a/__test__/git-command-manager.test.ts +++ b/__test__/git-command-manager.test.ts @@ -1,26 +1,51 @@ -import * as exec from '@actions/exec' -import * as fshelper from '../lib/fs-helper' -import * as commandManager from '../lib/git-command-manager' - -let git: commandManager.IGitCommandManager -let mockExec = jest.fn() +import { + jest, + describe, + it, + expect, + beforeAll, + beforeEach, + afterEach, + afterAll +} from '@jest/globals' + +// Mock @actions/exec +const mockExec = jest.fn() +jest.unstable_mockModule('@actions/exec', () => ({ + exec: mockExec +})) + +// Mock fs-helper +const mockFileExistsSync = jest.fn() +const mockDirectoryExistsSync = jest.fn() +jest.unstable_mockModule('../src/fs-helper.js', () => ({ + fileExistsSync: mockFileExistsSync, + directoryExistsSync: mockDirectoryExistsSync +})) + +// Dynamic imports after mocking +const commandManager = await import('../src/git-command-manager.js') +type IGitCommandManager = + import('../src/git-command-manager.js').IGitCommandManager + +let git: IGitCommandManager describe('git-auth-helper tests', () => { beforeAll(async () => {}) beforeEach(async () => { - jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn()) - jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn()) + mockFileExistsSync.mockReset() + mockDirectoryExistsSync.mockReset() }) afterEach(() => { - jest.restoreAllMocks() + jest.clearAllMocks() }) afterAll(() => {}) it('branch list matches', async () => { - mockExec.mockImplementation((path, args, options) => { + mockExec.mockImplementation((path: any, args: any, options: any) => { console.log(args, options.listeners.stdout) if (args.includes('version')) { @@ -36,7 +61,7 @@ describe('git-auth-helper tests', () => { return 1 }) - jest.spyOn(exec, 'exec').mockImplementation(mockExec) + // exec.exec is already mockExec const workingDirectory = 'test' const lfs = false const doSparseCheckout = false @@ -53,7 +78,7 @@ describe('git-auth-helper tests', () => { }) it('ambiguous ref name output is captured', async () => { - mockExec.mockImplementation((path, args, options) => { + mockExec.mockImplementation((path: any, args: any, options: any) => { console.log(args, options.listeners.stdout) if (args.includes('version')) { @@ -72,7 +97,7 @@ describe('git-auth-helper tests', () => { return 1 }) - jest.spyOn(exec, 'exec').mockImplementation(mockExec) + // exec.exec is already mockExec const workingDirectory = 'test' const lfs = false const doSparseCheckout = false @@ -91,9 +116,9 @@ describe('git-auth-helper tests', () => { describe('Test fetchDepth and fetchTags options', () => { beforeEach(async () => { - jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn()) - jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn()) - mockExec.mockImplementation((path, args, options) => { + mockFileExistsSync.mockReset() + mockDirectoryExistsSync.mockReset() + mockExec.mockImplementation((path: any, args: any, options: any) => { console.log(args, options.listeners.stdout) if (args.includes('version')) { @@ -105,11 +130,11 @@ describe('Test fetchDepth and fetchTags options', () => { }) afterEach(() => { - jest.restoreAllMocks() + jest.clearAllMocks() }) it('should call execGit with the correct arguments when fetchDepth is 0', async () => { - jest.spyOn(exec, 'exec').mockImplementation(mockExec) + // exec.exec is already mockExec const workingDirectory = 'test' const lfs = false const doSparseCheckout = false @@ -146,7 +171,7 @@ describe('Test fetchDepth and fetchTags options', () => { }) it('should call execGit with the correct arguments when fetchDepth is 0 and refSpec includes tags', async () => { - jest.spyOn(exec, 'exec').mockImplementation(mockExec) + // exec.exec is already mockExec const workingDirectory = 'test' const lfs = false @@ -184,7 +209,7 @@ describe('Test fetchDepth and fetchTags options', () => { }) it('should call execGit with the correct arguments when fetchDepth is 1', async () => { - jest.spyOn(exec, 'exec').mockImplementation(mockExec) + // exec.exec is already mockExec const workingDirectory = 'test' const lfs = false @@ -222,7 +247,7 @@ describe('Test fetchDepth and fetchTags options', () => { }) it('should call execGit with the correct arguments when fetchDepth is 1 and refSpec includes tags', async () => { - jest.spyOn(exec, 'exec').mockImplementation(mockExec) + // exec.exec is already mockExec const workingDirectory = 'test' const lfs = false @@ -261,7 +286,7 @@ describe('Test fetchDepth and fetchTags options', () => { }) it('should call execGit with the correct arguments when showProgress is true', async () => { - jest.spyOn(exec, 'exec').mockImplementation(mockExec) + // exec.exec is already mockExec const workingDirectory = 'test' const lfs = false @@ -299,7 +324,7 @@ describe('Test fetchDepth and fetchTags options', () => { }) it('should call execGit with the correct arguments when fetchDepth is 42 and showProgress is true', async () => { - jest.spyOn(exec, 'exec').mockImplementation(mockExec) + // exec.exec is already mockExec const workingDirectory = 'test' const lfs = false @@ -339,7 +364,7 @@ describe('Test fetchDepth and fetchTags options', () => { }) it('should call execGit with the correct arguments when showProgress is true and refSpec includes tags', async () => { - jest.spyOn(exec, 'exec').mockImplementation(mockExec) + // exec.exec is already mockExec const workingDirectory = 'test' const lfs = false @@ -380,23 +405,23 @@ describe('Test fetchDepth and fetchTags options', () => { describe('repository initialization object format', () => { beforeEach(async () => { - jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn()) - jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn()) + mockFileExistsSync.mockReset() + mockDirectoryExistsSync.mockReset() }) afterEach(() => { - jest.restoreAllMocks() + jest.clearAllMocks() }) it('initializes SHA-256 repositories with the matching object format', async () => { - mockExec.mockImplementation((path, args, options) => { + mockExec.mockImplementation((path: any, args: any, options: any) => { if (args.includes('version')) { options.listeners.stdout(Buffer.from('git version 2.50.1')) } return 0 }) - jest.spyOn(exec, 'exec').mockImplementation(mockExec) + // exec.exec is already mockExec git = await commandManager.createCommandManager('test', false, false) @@ -410,14 +435,14 @@ describe('repository initialization object format', () => { }) it('initializes SHA-1 repositories with existing default arguments', async () => { - mockExec.mockImplementation((path, args, options) => { + mockExec.mockImplementation((path: any, args: any, options: any) => { if (args.includes('version')) { options.listeners.stdout(Buffer.from('git version 2.50.1')) } return 0 }) - jest.spyOn(exec, 'exec').mockImplementation(mockExec) + // exec.exec is already mockExec git = await commandManager.createCommandManager('test', false, false) @@ -433,12 +458,12 @@ describe('repository initialization object format', () => { describe('git user-agent with orchestration ID', () => { beforeEach(async () => { - jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn()) - jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn()) + mockFileExistsSync.mockReset() + mockDirectoryExistsSync.mockReset() }) afterEach(() => { - jest.restoreAllMocks() + jest.clearAllMocks() // Clean up environment variable to prevent test pollution delete process.env['ACTIONS_ORCHESTRATION_ID'] }) @@ -448,7 +473,7 @@ describe('git user-agent with orchestration ID', () => { process.env['ACTIONS_ORCHESTRATION_ID'] = orchId let capturedEnv: any = null - mockExec.mockImplementation((path, args, options) => { + mockExec.mockImplementation((path: any, args: any, options: any) => { if (args.includes('version')) { options.listeners.stdout(Buffer.from('2.18')) } @@ -456,7 +481,7 @@ describe('git user-agent with orchestration ID', () => { capturedEnv = options.env return 0 }) - jest.spyOn(exec, 'exec').mockImplementation(mockExec) + // exec.exec is already mockExec const workingDirectory = 'test' const lfs = false @@ -483,7 +508,7 @@ describe('git user-agent with orchestration ID', () => { process.env['ACTIONS_ORCHESTRATION_ID'] = orchId let capturedEnv: any = null - mockExec.mockImplementation((path, args, options) => { + mockExec.mockImplementation((path: any, args: any, options: any) => { if (args.includes('version')) { options.listeners.stdout(Buffer.from('2.18')) } @@ -491,7 +516,7 @@ describe('git user-agent with orchestration ID', () => { capturedEnv = options.env return 0 }) - jest.spyOn(exec, 'exec').mockImplementation(mockExec) + // exec.exec is already mockExec const workingDirectory = 'test' const lfs = false @@ -517,7 +542,7 @@ describe('git user-agent with orchestration ID', () => { delete process.env['ACTIONS_ORCHESTRATION_ID'] let capturedEnv: any = null - mockExec.mockImplementation((path, args, options) => { + mockExec.mockImplementation((path: any, args: any, options: any) => { if (args.includes('version')) { options.listeners.stdout(Buffer.from('2.18')) } @@ -525,7 +550,7 @@ describe('git user-agent with orchestration ID', () => { capturedEnv = options.env return 0 }) - jest.spyOn(exec, 'exec').mockImplementation(mockExec) + // exec.exec is already mockExec const workingDirectory = 'test' const lfs = false diff --git a/__test__/git-directory-helper.test.ts b/__test__/git-directory-helper.test.ts index de79dc8..4dd5fda 100644 --- a/__test__/git-directory-helper.test.ts +++ b/__test__/git-directory-helper.test.ts @@ -1,9 +1,36 @@ -import * as core from '@actions/core' +import { + jest, + describe, + it, + expect, + beforeAll, + beforeEach, + afterEach +} from '@jest/globals' 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' +import {fileURLToPath} from 'url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +// Mock @actions/core before loading git-directory-helper +jest.unstable_mockModule('@actions/core', () => ({ + error: jest.fn(), + warning: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + setFailed: jest.fn(), + startGroup: jest.fn(), + endGroup: jest.fn() +})) + +// Dynamic imports after mocking +const core = await import('@actions/core') +const gitDirectoryHelper = await import('../src/git-directory-helper.js') + +type IGitCommandManager = + import('../src/git-command-manager.js').IGitCommandManager const testWorkspace = path.join(__dirname, '_temp', 'git-directory-helper') let repositoryPath: string @@ -19,16 +46,11 @@ describe('git-directory-helper tests', () => { }) 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()) + jest.clearAllMocks() }) afterEach(() => { - // Unregister mocks - jest.restoreAllMocks() + jest.clearAllMocks() }) const cleansWhenCleanTrue = 'cleans when clean true' @@ -81,7 +103,7 @@ describe('git-directory-helper tests', () => { // Arrange await setup(doesNotCheckoutDetachWhenNotAlreadyDetached) await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') - const mockIsDetached = git.isDetached as jest.Mock<any, any> + const mockIsDetached = git.isDetached as jest.Mock<any> mockIsDetached.mockImplementation(async () => { return true }) @@ -132,7 +154,7 @@ describe('git-directory-helper tests', () => { // Arrange await setup(removesContentsWhenCleanFails) await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') - let mockTryClean = git.tryClean as jest.Mock<any, any> + let mockTryClean = git.tryClean as jest.Mock<any> mockTryClean.mockImplementation(async () => { return false }) @@ -210,7 +232,7 @@ describe('git-directory-helper tests', () => { // Arrange await setup(removesContentsWhenResetFails) await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') - let mockTryReset = git.tryReset as jest.Mock<any, any> + let mockTryReset = git.tryReset as jest.Mock<any> mockTryReset.mockImplementation(async () => { return false }) @@ -260,7 +282,7 @@ describe('git-directory-helper tests', () => { // Arrange await setup(removesLocalBranches) await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') - const mockBranchList = git.branchList as jest.Mock<any, any> + const mockBranchList = git.branchList as jest.Mock<any> mockBranchList.mockImplementation(async (remote: boolean) => { return remote ? [] : ['local-branch-1', 'local-branch-2'] }) @@ -291,7 +313,7 @@ describe('git-directory-helper tests', () => { //mock bad submodule - const submoduleStatus = git.submoduleStatus as jest.Mock<any, any> + const submoduleStatus = git.submoduleStatus as jest.Mock<any> submoduleStatus.mockImplementation(async (remote: boolean) => { return false }) @@ -319,7 +341,7 @@ describe('git-directory-helper tests', () => { await setup(doesNotCleanWhenSubmoduleStatusIsTrue) await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') - const submoduleStatus = git.submoduleStatus as jest.Mock<any, any> + const submoduleStatus = git.submoduleStatus as jest.Mock<any> submoduleStatus.mockImplementation(async (remote: boolean) => { return true }) @@ -381,7 +403,7 @@ describe('git-directory-helper tests', () => { // Arrange await setup(removesAncestorRemoteBranch) await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') - const mockBranchList = git.branchList as jest.Mock<any, any> + const mockBranchList = git.branchList as jest.Mock<any> mockBranchList.mockImplementation(async (remote: boolean) => { return remote ? ['origin/remote-branch-1', 'origin/remote-branch-2'] : [] }) @@ -411,7 +433,7 @@ describe('git-directory-helper tests', () => { // Arrange await setup(removesDescendantRemoteBranches) await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') - const mockBranchList = git.branchList as jest.Mock<any, any> + const mockBranchList = git.branchList as jest.Mock<any> mockBranchList.mockImplementation(async (remote: boolean) => { return remote ? ['origin/remote-branch-1/conflict', 'origin/remote-branch-2'] @@ -507,5 +529,5 @@ async function setup(testName: string): Promise<void> { return true }), version: jest.fn() - } + } as unknown as IGitCommandManager } diff --git a/__test__/git-version.test.ts b/__test__/git-version.test.ts index 27f702e..740d46e 100644 --- a/__test__/git-version.test.ts +++ b/__test__/git-version.test.ts @@ -1,5 +1,6 @@ -import {GitVersion} from '../src/git-version' -import {MinimumGitSparseCheckoutVersion} from '../src/git-command-manager' +import {describe, it, expect} from '@jest/globals' +import {GitVersion} from '../src/git-version.js' +import {MinimumGitSparseCheckoutVersion} from '../src/git-command-manager.js' describe('git-version tests', () => { it('basics', async () => { diff --git a/__test__/github-api-helper.test.ts b/__test__/github-api-helper.test.ts index 6319e20..3470f20 100644 --- a/__test__/github-api-helper.test.ts +++ b/__test__/github-api-helper.test.ts @@ -1,11 +1,25 @@ -import * as core from '@actions/core' -import * as github from '@actions/github' -import * as githubApiHelper from '../lib/github-api-helper' +import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals' + +// Mock @actions/core +const mockDebug = jest.fn() +jest.unstable_mockModule('@actions/core', () => ({ + debug: mockDebug, + info: jest.fn(), + warning: jest.fn(), + error: jest.fn() +})) + +// Mock @actions/github +const mockGetOctokit = jest.fn() +jest.unstable_mockModule('@actions/github', () => ({ + getOctokit: mockGetOctokit +})) + +// Dynamic imports after mocking +const githubApiHelper = await import('../src/github-api-helper.js') describe('github-api-helper object format', () => { - let getOctokitSpy: jest.SpyInstance - let debugSpy: jest.SpyInstance - let request: jest.Mock + let request: jest.Mock<any> function mockHashAlgorithmApi(hashAlgorithm: string): void { request = jest.fn(async () => ({ @@ -13,17 +27,18 @@ describe('github-api-helper object format', () => { hash_algorithm: hashAlgorithm } })) - getOctokitSpy = jest.spyOn(github, 'getOctokit').mockReturnValue({ + mockGetOctokit.mockReturnValue({ request } as any) } beforeEach(() => { - debugSpy = jest.spyOn(core, 'debug').mockImplementation(jest.fn()) + mockDebug.mockClear() + mockGetOctokit.mockClear() }) afterEach(() => { - jest.restoreAllMocks() + jest.clearAllMocks() }) it('detects SHA-256 from the repository hash algorithm endpoint', async () => { @@ -33,7 +48,7 @@ describe('github-api-helper object format', () => { githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo') ).resolves.toEqual({format: 'sha256', succeeded: true}) - expect(getOctokitSpy).toHaveBeenCalledWith( + expect(mockGetOctokit).toHaveBeenCalledWith( 'token', expect.objectContaining({baseUrl: 'https://api.github.com'}) ) @@ -54,7 +69,6 @@ describe('github-api-helper object format', () => { it('detects object format from an existing commit without API calls', async () => { const commitSha = '9422233ca7ee1b17f1e905d0e141faf0c401556c41cdc6acd71c6bd685da2e92' - getOctokitSpy = jest.spyOn(github, 'getOctokit') await expect( githubApiHelper.tryGetRepositoryObjectFormat( @@ -66,7 +80,7 @@ describe('github-api-helper object format', () => { ) ).resolves.toEqual({format: 'sha256', succeeded: true}) - expect(getOctokitSpy).not.toHaveBeenCalled() + expect(mockGetOctokit).not.toHaveBeenCalled() }) it('returns unsuccessful when the hash algorithm endpoint value is not recognized', async () => { @@ -75,7 +89,7 @@ describe('github-api-helper object format', () => { await expect( githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo') ).resolves.toEqual({format: '', succeeded: false}) - expect(debugSpy).toHaveBeenCalledWith( + expect(mockDebug).toHaveBeenCalledWith( 'Unable to determine repository object format from hash-algorithm endpoint' ) }) @@ -84,14 +98,14 @@ describe('github-api-helper object format', () => { request = jest.fn(async () => { throw new Error('not found') }) - jest.spyOn(github, 'getOctokit').mockReturnValue({ + mockGetOctokit.mockReturnValue({ request } as any) await expect( githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo') ).resolves.toEqual({format: '', succeeded: false}) - expect(debugSpy).toHaveBeenCalledWith( + expect(mockDebug).toHaveBeenCalledWith( 'Unable to determine repository object format from hash-algorithm endpoint: not found' ) }) diff --git a/__test__/input-helper.test.ts b/__test__/input-helper.test.ts index 25b6d18..b2f9074 100644 --- a/__test__/input-helper.test.ts +++ b/__test__/input-helper.test.ts @@ -1,10 +1,13 @@ -import * as core from '@actions/core' -import * as fsHelper from '../lib/fs-helper' -import * as github from '@actions/github' -import * as inputHelper from '../lib/input-helper' +import { + jest, + describe, + it, + expect, + beforeAll, + beforeEach, + afterAll +} from '@jest/globals' import * as path from 'path' -import * as workflowContextHelper from '../lib/workflow-context-helper' -import {IGitSourceSettings} from '../lib/git-source-settings' const originalGitHubWorkspace = process.env['GITHUB_WORKSPACE'] const gitHubWorkspace = path.resolve('/checkout-tests/workspace') @@ -12,42 +15,58 @@ const gitHubWorkspace = path.resolve('/checkout-tests/workspace') // Inputs for mock @actions/core let inputs = {} as any -// Shallow clone original @actions/github context -let originalContext = {...github.context} +// Mutable mock github context +const mockGithubContext: any = { + ref: 'refs/heads/some-ref', + sha: '1234567890123456789012345678901234567890', + repo: {owner: 'some-owner', repo: 'some-repo'}, + eventName: '', + payload: {} +} + +// Mock @actions/core before loading input-helper +jest.unstable_mockModule('@actions/core', () => ({ + getInput: jest.fn((name: string) => inputs[name]), + getBooleanInput: jest.fn((name: string) => inputs[name]), + getMultilineInput: jest.fn((name: string) => + inputs[name] ? String(inputs[name]).split('\n').filter(Boolean) : [] + ), + error: jest.fn(), + warning: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + setFailed: jest.fn(), + setOutput: jest.fn(), + setSecret: jest.fn() +})) + +// Mock @actions/github before loading input-helper +jest.unstable_mockModule('@actions/github', () => ({ + context: mockGithubContext, + getOctokit: jest.fn() +})) + +// Mock fs-helper +const mockDirectoryExistsSync = jest.fn((p: string) => p === gitHubWorkspace) +jest.unstable_mockModule('../src/fs-helper.js', () => ({ + directoryExistsSync: mockDirectoryExistsSync, + fileExistsSync: jest.fn() +})) + +// Mock workflow-context-helper +const mockGetOrganizationId = jest.fn(async () => 123456) +jest.unstable_mockModule('../src/workflow-context-helper.js', () => ({ + getOrganizationId: mockGetOrganizationId +})) + +// Dynamic imports after mocking +const core = await import('@actions/core') +const inputHelper = await import('../src/input-helper.js') +type IGitSourceSettings = + import('../src/git-source-settings.js').IGitSourceSettings describe('input-helper tests', () => { beforeAll(() => { - // Mock getInput - jest.spyOn(core, 'getInput').mockImplementation((name: string) => { - return inputs[name] - }) - - // 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', - repo: 'some-repo' - } - }) - github.context.ref = 'refs/heads/some-ref' - github.context.sha = '1234567890123456789012345678901234567890' - - // Mock ./fs-helper directoryExistsSync() - jest - .spyOn(fsHelper, 'directoryExistsSync') - .mockImplementation((path: string) => path == gitHubWorkspace) - - // Mock ./workflowContextHelper getOrganizationId() - jest - .spyOn(workflowContextHelper, 'getOrganizationId') - .mockImplementation(() => Promise.resolve(123456)) - // GitHub workspace process.env['GITHUB_WORKSPACE'] = gitHubWorkspace }) @@ -55,6 +74,15 @@ describe('input-helper tests', () => { beforeEach(() => { // Reset inputs inputs = {} + jest.clearAllMocks() + // Re-apply default mocks + ;(core.getInput as jest.Mock<any>).mockImplementation( + (name: string) => inputs[name] + ) + mockDirectoryExistsSync.mockImplementation( + (p: string) => p === gitHubWorkspace + ) + mockGetOrganizationId.mockResolvedValue(123456) }) afterAll(() => { @@ -65,11 +93,8 @@ describe('input-helper tests', () => { } // Restore @actions/github context - github.context.ref = originalContext.ref - github.context.sha = originalContext.sha - - // Restore - jest.restoreAllMocks() + mockGithubContext.ref = 'refs/heads/some-ref' + mockGithubContext.sha = '1234567890123456789012345678901234567890' }) it('sets defaults', async () => { @@ -95,15 +120,15 @@ describe('input-helper tests', () => { }) it('qualifies ref', async () => { - let originalRef = github.context.ref + let originalRef = mockGithubContext.ref try { - github.context.ref = 'some-unqualified-ref' + mockGithubContext.ref = 'some-unqualified-ref' const settings: IGitSourceSettings = await inputHelper.getInputs() expect(settings).toBeTruthy() expect(settings.commit).toBe('1234567890123456789012345678901234567890') expect(settings.ref).toBe('refs/heads/some-unqualified-ref') } finally { - github.context.ref = originalRef + mockGithubContext.ref = originalRef } }) diff --git a/__test__/ref-helper.test.ts b/__test__/ref-helper.test.ts index b1578f2..961bc99 100644 --- a/__test__/ref-helper.test.ts +++ b/__test__/ref-helper.test.ts @@ -1,8 +1,36 @@ +import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals' import * as assert from 'assert' -import * as core from '@actions/core' -import * as github from '@actions/github' -import * as refHelper from '../lib/ref-helper' -import {IGitCommandManager} from '../lib/git-command-manager' + +// Mutable mock github context +const mockGithubContext: any = { + eventName: '', + payload: {}, + repo: {owner: 'some-owner', repo: 'some-repo'}, + ref: '', + sha: '' +} + +// Mock @actions/core +const mockDebug = jest.fn() +jest.unstable_mockModule('@actions/core', () => ({ + debug: mockDebug, + info: jest.fn(), + warning: jest.fn(), + error: jest.fn(), + setFailed: jest.fn() +})) + +// Mock @actions/github +const mockGetOctokit = jest.fn() +jest.unstable_mockModule('@actions/github', () => ({ + context: mockGithubContext, + getOctokit: mockGetOctokit +})) + +// Dynamic imports after mocking +const refHelper = await import('../src/ref-helper.js') +type IGitCommandManager = + import('../src/git-command-manager.js').IGitCommandManager const commit = '1234567890123456789012345678901234567890' const sha256Commit = @@ -12,6 +40,7 @@ let git: IGitCommandManager describe('ref-helper tests', () => { beforeEach(() => { git = {} as unknown as IGitCommandManager + jest.clearAllMocks() }) it('getCheckoutInfo requires git', async () => { @@ -166,14 +195,12 @@ describe('ref-helper tests', () => { }) it('getRefSpec sha + refs/tags/ with fetchTags', async () => { - // When fetchTags is true, only include tags wildcard (specific tag is redundant) const refSpec = refHelper.getRefSpec('refs/tags/my-tag', commit, true) expect(refSpec.length).toBe(1) expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*') }) it('getRefSpec sha + refs/heads/ with fetchTags', async () => { - // When fetchTags is true, include both the branch refspec and tags wildcard const refSpec = refHelper.getRefSpec('refs/heads/my/branch', commit, true) expect(refSpec.length).toBe(2) expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*') @@ -194,7 +221,6 @@ describe('ref-helper tests', () => { }) it('getRefSpec unqualified ref only with fetchTags', async () => { - // When fetchTags is true, skip specific tag pattern since wildcard covers all const refSpec = refHelper.getRefSpec('my-ref', '', true) expect(refSpec.length).toBe(2) expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*') @@ -222,14 +248,12 @@ describe('ref-helper tests', () => { }) it('getRefSpec refs/tags/ only with fetchTags', async () => { - // When fetchTags is true, only include tags wildcard (specific tag is redundant) const refSpec = refHelper.getRefSpec('refs/tags/my-tag', '', true) expect(refSpec.length).toBe(1) expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*') }) it('getRefSpec refs/heads/ only with fetchTags', async () => { - // When fetchTags is true, include both the branch refspec and tags wildcard const refSpec = refHelper.getRefSpec('refs/heads/my/branch', '', true) expect(refSpec.length).toBe(2) expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*') @@ -248,9 +272,7 @@ describe('ref-helper tests', () => { '1111111111222222222233333333334444444444555555555566666666667777' const sha256Base = 'aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffffffffff0000' - let debugSpy: jest.SpyInstance - let getOctokitSpy: jest.SpyInstance - let repoGetSpy: jest.Mock + let repoGetSpy: jest.Mock<any> let originalEventName: string let originalPayload: unknown let originalRef: string @@ -261,10 +283,10 @@ describe('ref-helper tests', () => { expectedBaseSha: string, mergeCommit: string ): void { - ;(github.context as any).eventName = 'pull_request' - github.context.ref = ref - github.context.sha = mergeCommit - ;(github.context as any).payload = { + mockGithubContext.eventName = 'pull_request' + mockGithubContext.ref = ref + mockGithubContext.sha = mergeCommit + mockGithubContext.payload = { action: 'synchronize', after: expectedHeadSha, number: 123, @@ -280,18 +302,18 @@ describe('ref-helper tests', () => { } beforeEach(() => { - originalEventName = github.context.eventName - originalPayload = github.context.payload - originalRef = github.context.ref - originalSha = github.context.sha + originalEventName = mockGithubContext.eventName + originalPayload = mockGithubContext.payload + originalRef = mockGithubContext.ref + originalSha = mockGithubContext.sha - jest.spyOn(github.context, 'repo', 'get').mockReturnValue({ + mockGithubContext.repo = { owner: repositoryOwner, repo: repositoryName - }) - debugSpy = jest.spyOn(core, 'debug').mockImplementation(jest.fn()) + } + repoGetSpy = jest.fn(async () => ({})) - getOctokitSpy = jest.spyOn(github, 'getOctokit').mockReturnValue({ + mockGetOctokit.mockReturnValue({ rest: { repos: { get: repoGetSpy @@ -301,11 +323,11 @@ describe('ref-helper tests', () => { }) afterEach(() => { - ;(github.context as any).eventName = originalEventName - ;(github.context as any).payload = originalPayload - github.context.ref = originalRef - github.context.sha = originalSha - jest.restoreAllMocks() + mockGithubContext.eventName = originalEventName + mockGithubContext.payload = originalPayload + mockGithubContext.ref = originalRef + mockGithubContext.sha = originalSha + jest.clearAllMocks() }) it('returns early for SHA-1 merge commit', async () => { @@ -320,7 +342,7 @@ describe('ref-helper tests', () => { commit ) - expect(getOctokitSpy).not.toHaveBeenCalled() + expect(mockGetOctokit).not.toHaveBeenCalled() expect(repoGetSpy).not.toHaveBeenCalled() }) @@ -338,7 +360,7 @@ describe('ref-helper tests', () => { sha256Commit ) - expect(getOctokitSpy).toHaveBeenCalledWith( + expect(mockGetOctokit).toHaveBeenCalledWith( 'token', expect.objectContaining({ userAgent: expect.stringContaining( @@ -350,10 +372,10 @@ describe('ref-helper tests', () => { owner: repositoryOwner, repo: repositoryName }) - expect(debugSpy).toHaveBeenCalledWith( + expect(mockDebug).toHaveBeenCalledWith( `Expected head sha ${sha256Head}; actual head sha ${actualHeadSha}` ) - expect(debugSpy).not.toHaveBeenCalledWith('Unexpected message format') + expect(mockDebug).not.toHaveBeenCalledWith('Unexpected message format') }) it('does not match 50-char hex as a valid merge', async () => { @@ -370,9 +392,9 @@ describe('ref-helper tests', () => { commit ) - expect(getOctokitSpy).not.toHaveBeenCalled() + expect(mockGetOctokit).not.toHaveBeenCalled() expect(repoGetSpy).not.toHaveBeenCalled() - expect(debugSpy).toHaveBeenCalledWith('Unexpected message format') + expect(mockDebug).toHaveBeenCalledWith('Unexpected message format') }) }) }) diff --git a/__test__/retry-helper.test.ts b/__test__/retry-helper.test.ts index a5d3f79..2cd5026 100644 --- a/__test__/retry-helper.test.ts +++ b/__test__/retry-helper.test.ts @@ -1,16 +1,32 @@ -import * as core from '@actions/core' -import {RetryHelper} from '../lib/retry-helper' +import { + jest, + describe, + it, + expect, + beforeAll, + beforeEach, + afterAll +} from '@jest/globals' + +let info: string[] = [] + +// Mock @actions/core before loading retry-helper +jest.unstable_mockModule('@actions/core', () => ({ + info: jest.fn((message: string) => { + info.push(message) + }), + debug: jest.fn(), + warning: jest.fn(), + error: jest.fn() +})) + +// Dynamic imports after mocking +const {RetryHelper} = await import('../src/retry-helper.js') -let info: string[] let retryHelper: any describe('retry-helper tests', () => { beforeAll(() => { - // Mock @actions/core info() - jest.spyOn(core, 'info').mockImplementation((message: string) => { - info.push(message) - }) - retryHelper = new RetryHelper(3, 0, 0) }) @@ -20,7 +36,6 @@ describe('retry-helper tests', () => { }) afterAll(() => { - // Restore jest.restoreAllMocks() }) diff --git a/__test__/unsafe-pr-checkout-helper.test.ts b/__test__/unsafe-pr-checkout-helper.test.ts index 9efa246..fbd854e 100644 --- a/__test__/unsafe-pr-checkout-helper.test.ts +++ b/__test__/unsafe-pr-checkout-helper.test.ts @@ -1,10 +1,12 @@ -import * as github from '@actions/github' -import {assertSafePrCheckout} from '../lib/unsafe-pr-checkout-helper' - -// Shallow clone original @actions/github context -const originalContext = {...github.context} -const originalEventName = github.context.eventName -const originalPayload = github.context.payload +import { + jest, + describe, + it, + expect, + beforeAll, + afterEach, + afterAll +} from '@jest/globals' const BASE_REPO_ID = 100 const FORK_REPO_ID = 200 @@ -15,9 +17,30 @@ const WORKFLOW_RUN_HEAD_COMMIT_SHA = '4444444444444444444444444444444444444444' const BASE_QUALIFIED_REPO = 'some-owner/some-repo' const FORK_QUALIFIED_REPO = 'another-repo/fork' +// Mutable mock context +const mockContext: any = { + eventName: '', + payload: {}, + repo: {owner: 'some-owner', repo: 'some-repo'}, + ref: '', + sha: '' +} + +jest.unstable_mockModule('@actions/github', () => ({ + context: mockContext +})) + +// Dynamic imports after mocking +const {assertSafePrCheckout} = await import( + '../src/unsafe-pr-checkout-helper.js' +) + +const originalEventName = mockContext.eventName +const originalPayload = mockContext.payload + function setContext(eventName: string, payload: object): void { - ;(github.context as {eventName: string}).eventName = eventName - ;(github.context as {payload: object}).payload = payload + mockContext.eventName = eventName + mockContext.payload = payload } function forkPullRequestTargetPayload(): object { @@ -59,22 +82,17 @@ function forkWorkflowRunPayload(): object { describe('unsafe-pr-checkout-helper', () => { beforeAll(() => { - jest.spyOn(github.context, 'repo', 'get').mockReturnValue({ - owner: 'some-owner', - repo: 'some-repo' - }) + mockContext.repo = {owner: 'some-owner', repo: 'some-repo'} }) afterEach(() => { - ;(github.context as {eventName: string}).eventName = originalEventName - ;(github.context as {payload: object}).payload = originalPayload + mockContext.eventName = originalEventName + mockContext.payload = originalPayload }) afterAll(() => { - ;(github.context as {eventName: string}).eventName = - originalContext.eventName - ;(github.context as {payload: object}).payload = originalContext.payload - jest.restoreAllMocks() + mockContext.eventName = originalEventName + mockContext.payload = originalPayload }) it('allows pull_request events untouched', () => { diff --git a/__test__/url-helper.test.ts b/__test__/url-helper.test.ts index 57cb28f..cbe7bc2 100644 --- a/__test__/url-helper.test.ts +++ b/__test__/url-helper.test.ts @@ -1,4 +1,5 @@ -import * as urlHelper from '../src/url-helper' +import {jest, describe, it, expect, beforeEach, afterAll} from '@jest/globals' +import * as urlHelper from '../src/url-helper.js' describe('getServerUrl tests', () => { it('basics', async () => { |
