| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 |
- import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals'
- import * as assert from 'assert'
- // 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 =
- '1234567890123456789012345678901234567890123456789012345678901234'
- let git: IGitCommandManager
- describe('ref-helper tests', () => {
- beforeEach(() => {
- git = {} as unknown as IGitCommandManager
- jest.clearAllMocks()
- })
- it('getCheckoutInfo requires git', async () => {
- const git = null as unknown as IGitCommandManager
- try {
- await refHelper.getCheckoutInfo(git, 'refs/heads/my/branch', commit)
- throw new Error('Should not reach here')
- } catch (err) {
- expect((err as any)?.message).toBe('Arg git cannot be empty')
- }
- })
- it('getCheckoutInfo requires ref or commit', async () => {
- try {
- await refHelper.getCheckoutInfo(git, '', '')
- throw new Error('Should not reach here')
- } catch (err) {
- expect((err as any)?.message).toBe(
- 'Args ref and commit cannot both be empty'
- )
- }
- })
- it('getCheckoutInfo sha only', async () => {
- const checkoutInfo = await refHelper.getCheckoutInfo(git, '', commit)
- expect(checkoutInfo.ref).toBe(commit)
- expect(checkoutInfo.startPoint).toBeFalsy()
- })
- it('getCheckoutInfo sha-256 only', async () => {
- const checkoutInfo = await refHelper.getCheckoutInfo(git, '', sha256Commit)
- expect(checkoutInfo.ref).toBe(sha256Commit)
- expect(checkoutInfo.startPoint).toBeFalsy()
- })
- it('getCheckoutInfo refs/heads/', async () => {
- const checkoutInfo = await refHelper.getCheckoutInfo(
- git,
- 'refs/heads/my/branch',
- commit
- )
- expect(checkoutInfo.ref).toBe('my/branch')
- expect(checkoutInfo.startPoint).toBe('refs/remotes/origin/my/branch')
- })
- it('getCheckoutInfo refs/pull/', async () => {
- const checkoutInfo = await refHelper.getCheckoutInfo(
- git,
- 'refs/pull/123/merge',
- commit
- )
- expect(checkoutInfo.ref).toBe('refs/remotes/pull/123/merge')
- expect(checkoutInfo.startPoint).toBeFalsy()
- })
- it('getCheckoutInfo refs/tags/', async () => {
- const checkoutInfo = await refHelper.getCheckoutInfo(
- git,
- 'refs/tags/my-tag',
- commit
- )
- expect(checkoutInfo.ref).toBe('refs/tags/my-tag')
- expect(checkoutInfo.startPoint).toBeFalsy()
- })
- it('getCheckoutInfo refs/', async () => {
- const checkoutInfo = await refHelper.getCheckoutInfo(
- git,
- 'refs/gh/queue/main/pr-123',
- commit
- )
- expect(checkoutInfo.ref).toBe(commit)
- expect(checkoutInfo.startPoint).toBeFalsy()
- })
- it('getCheckoutInfo refs/ without commit', async () => {
- const checkoutInfo = await refHelper.getCheckoutInfo(
- git,
- 'refs/non-standard-ref',
- ''
- )
- expect(checkoutInfo.ref).toBe('refs/non-standard-ref')
- expect(checkoutInfo.startPoint).toBeFalsy()
- })
- it('getCheckoutInfo unqualified branch only', async () => {
- git.branchExists = jest.fn(async (remote: boolean, pattern: string) => {
- return true
- })
- const checkoutInfo = await refHelper.getCheckoutInfo(git, 'my/branch', '')
- expect(checkoutInfo.ref).toBe('my/branch')
- expect(checkoutInfo.startPoint).toBe('refs/remotes/origin/my/branch')
- })
- it('getCheckoutInfo unqualified tag only', async () => {
- git.branchExists = jest.fn(async (remote: boolean, pattern: string) => {
- return false
- })
- git.tagExists = jest.fn(async (pattern: string) => {
- return true
- })
- const checkoutInfo = await refHelper.getCheckoutInfo(git, 'my-tag', '')
- expect(checkoutInfo.ref).toBe('refs/tags/my-tag')
- expect(checkoutInfo.startPoint).toBeFalsy()
- })
- it('getCheckoutInfo unqualified ref only, not a branch or tag', async () => {
- git.branchExists = jest.fn(async (remote: boolean, pattern: string) => {
- return false
- })
- git.tagExists = jest.fn(async (pattern: string) => {
- return false
- })
- try {
- await refHelper.getCheckoutInfo(git, 'my-ref', '')
- throw new Error('Should not reach here')
- } catch (err) {
- expect((err as any)?.message).toBe(
- "A branch or tag with the name 'my-ref' could not be found"
- )
- }
- })
- it('getRefSpec requires ref or commit', async () => {
- assert.throws(
- () => refHelper.getRefSpec('', ''),
- /Args ref and commit cannot both be empty/
- )
- })
- it('getRefSpec sha + refs/heads/', async () => {
- const refSpec = refHelper.getRefSpec('refs/heads/my/branch', commit)
- expect(refSpec.length).toBe(1)
- expect(refSpec[0]).toBe(`+${commit}:refs/remotes/origin/my/branch`)
- })
- it('getRefSpec sha + refs/pull/', async () => {
- const refSpec = refHelper.getRefSpec('refs/pull/123/merge', commit)
- expect(refSpec.length).toBe(1)
- expect(refSpec[0]).toBe(`+${commit}:refs/remotes/pull/123/merge`)
- })
- it('getRefSpec sha + refs/tags/', async () => {
- const refSpec = refHelper.getRefSpec('refs/tags/my-tag', commit)
- expect(refSpec.length).toBe(1)
- expect(refSpec[0]).toBe(`+refs/tags/my-tag:refs/tags/my-tag`)
- })
- it('getRefSpec sha + refs/tags/ with fetchTags', async () => {
- 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 () => {
- const refSpec = refHelper.getRefSpec('refs/heads/my/branch', commit, true)
- expect(refSpec.length).toBe(2)
- expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
- expect(refSpec[1]).toBe(`+${commit}:refs/remotes/origin/my/branch`)
- })
- it('getRefSpec sha only', async () => {
- const refSpec = refHelper.getRefSpec('', commit)
- expect(refSpec.length).toBe(1)
- expect(refSpec[0]).toBe(commit)
- })
- it('getRefSpec unqualified ref only', async () => {
- const refSpec = refHelper.getRefSpec('my-ref', '')
- expect(refSpec.length).toBe(2)
- expect(refSpec[0]).toBe('+refs/heads/my-ref*:refs/remotes/origin/my-ref*')
- expect(refSpec[1]).toBe('+refs/tags/my-ref*:refs/tags/my-ref*')
- })
- it('getRefSpec unqualified ref only with fetchTags', async () => {
- const refSpec = refHelper.getRefSpec('my-ref', '', true)
- expect(refSpec.length).toBe(2)
- expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
- expect(refSpec[1]).toBe('+refs/heads/my-ref*:refs/remotes/origin/my-ref*')
- })
- it('getRefSpec refs/heads/ only', async () => {
- const refSpec = refHelper.getRefSpec('refs/heads/my/branch', '')
- expect(refSpec.length).toBe(1)
- expect(refSpec[0]).toBe(
- '+refs/heads/my/branch:refs/remotes/origin/my/branch'
- )
- })
- it('getRefSpec refs/pull/ only', async () => {
- const refSpec = refHelper.getRefSpec('refs/pull/123/merge', '')
- expect(refSpec.length).toBe(1)
- expect(refSpec[0]).toBe('+refs/pull/123/merge:refs/remotes/pull/123/merge')
- })
- it('getRefSpec refs/tags/ only', async () => {
- const refSpec = refHelper.getRefSpec('refs/tags/my-tag', '')
- expect(refSpec.length).toBe(1)
- expect(refSpec[0]).toBe('+refs/tags/my-tag:refs/tags/my-tag')
- })
- it('getRefSpec refs/tags/ only with fetchTags', async () => {
- 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 () => {
- const refSpec = refHelper.getRefSpec('refs/heads/my/branch', '', true)
- expect(refSpec.length).toBe(2)
- expect(refSpec[0]).toBe('+refs/tags/*:refs/tags/*')
- expect(refSpec[1]).toBe(
- '+refs/heads/my/branch:refs/remotes/origin/my/branch'
- )
- })
- describe('checkCommitInfo', () => {
- const repositoryOwner = 'some-owner'
- const repositoryName = 'some-repo'
- const ref = 'refs/pull/123/merge'
- const sha1Head = '1111111111222222222233333333334444444444'
- const sha1Base = 'aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd'
- const sha256Head =
- '1111111111222222222233333333334444444444555555555566666666667777'
- const sha256Base =
- 'aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffffffffff0000'
- let repoGetSpy: jest.Mock<any>
- let originalEventName: string
- let originalPayload: unknown
- let originalRef: string
- let originalSha: string
- function setPullRequestContext(
- expectedHeadSha: string,
- expectedBaseSha: string,
- mergeCommit: string
- ): void {
- mockGithubContext.eventName = 'pull_request'
- mockGithubContext.ref = ref
- mockGithubContext.sha = mergeCommit
- mockGithubContext.payload = {
- action: 'synchronize',
- after: expectedHeadSha,
- number: 123,
- pull_request: {
- base: {
- sha: expectedBaseSha
- }
- },
- repository: {
- private: false
- }
- }
- }
- beforeEach(() => {
- originalEventName = mockGithubContext.eventName
- originalPayload = mockGithubContext.payload
- originalRef = mockGithubContext.ref
- originalSha = mockGithubContext.sha
- mockGithubContext.repo = {
- owner: repositoryOwner,
- repo: repositoryName
- }
- repoGetSpy = jest.fn(async () => ({}))
- mockGetOctokit.mockReturnValue({
- rest: {
- repos: {
- get: repoGetSpy
- }
- }
- } as any)
- })
- afterEach(() => {
- mockGithubContext.eventName = originalEventName
- mockGithubContext.payload = originalPayload
- mockGithubContext.ref = originalRef
- mockGithubContext.sha = originalSha
- jest.clearAllMocks()
- })
- it('returns early for SHA-1 merge commit', async () => {
- setPullRequestContext(sha1Head, sha1Base, commit)
- await refHelper.checkCommitInfo(
- 'token',
- `Merge ${sha1Head} into ${sha1Base}`,
- repositoryOwner,
- repositoryName,
- ref,
- commit
- )
- expect(mockGetOctokit).not.toHaveBeenCalled()
- expect(repoGetSpy).not.toHaveBeenCalled()
- })
- it('matches SHA-256 merge commit info', async () => {
- const actualHeadSha =
- '9999999999888888888877777777776666666666555555555544444444443333'
- setPullRequestContext(sha256Head, sha256Base, sha256Commit)
- await refHelper.checkCommitInfo(
- 'token',
- `Merge ${actualHeadSha} into ${sha256Base}`,
- repositoryOwner,
- repositoryName,
- ref,
- sha256Commit
- )
- expect(mockGetOctokit).toHaveBeenCalledWith(
- 'token',
- expect.objectContaining({
- userAgent: expect.stringContaining(
- `expected_head_sha=${sha256Head};actual_head_sha=${actualHeadSha}`
- )
- })
- )
- expect(repoGetSpy).toHaveBeenCalledWith({
- owner: repositoryOwner,
- repo: repositoryName
- })
- expect(mockDebug).toHaveBeenCalledWith(
- `Expected head sha ${sha256Head}; actual head sha ${actualHeadSha}`
- )
- expect(mockDebug).not.toHaveBeenCalledWith('Unexpected message format')
- })
- it('does not match 50-char hex as a valid merge', async () => {
- const invalidHeadSha =
- '99999999998888888888777777777766666666665555555555'
- setPullRequestContext(sha1Head, sha1Base, commit)
- await refHelper.checkCommitInfo(
- 'token',
- `Merge ${invalidHeadSha} into ${sha1Base}`,
- repositoryOwner,
- repositoryName,
- ref,
- commit
- )
- expect(mockGetOctokit).not.toHaveBeenCalled()
- expect(repoGetSpy).not.toHaveBeenCalled()
- expect(mockDebug).toHaveBeenCalledWith('Unexpected message format')
- })
- })
- })
|