Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions __tests__/issueCommentTest/meow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,22 @@ describe('/meow', () => {
)
})

it('names a status-less response as a redirect', async () => {
// Response.error() is the only status 0 fetch can resolve with
vi.spyOn(globalThis, 'fetch').mockResolvedValue(Response.error())
const warning = vi.spyOn(core, 'warning').mockImplementation(() => {})

await handleIssueComment(contextFor('/meow'))

expect(warning).toHaveBeenCalledWith(expect.stringContaining('cat api responded with a redirect'))
expect(createComment).toHaveBeenCalledWith(
expect.anything(),
expect.anything(),
1,
'The cat API is unavailable right now.',
)
})

it('degrades to a note on a network error', async () => {
server.use(http.get(catApi, () => HttpResponse.error()))
vi.spyOn(core, 'warning').mockImplementation(() => {})
Expand Down
13 changes: 12 additions & 1 deletion __tests__/issueCommentTest/trigger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { setupServer } from 'msw/node'
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'

import { handleIssueComment } from '../../src/issueComment/handleIssueComment'
import { okToTestOnPullRequest, retest } from '../../src/issueComment/trigger'
import { approvePendingRuns, okToTestOnPullRequest, retest } from '../../src/issueComment/trigger'
import { newOctokit } from '../../src/utils/octokit'
import issueCommentEvent from '../fixtures/issues/issueCommentEvent.json'
import prOpenedEvent from '../fixtures/pullReq/pullReqOpenedEvent.json'
import * as utils from '../testUtils'
Expand Down Expand Up @@ -807,6 +808,16 @@ describe('/ok-to-test', () => {
expect(setFailed).toHaveBeenCalledWith(expect.stringContaining('could not approve run 8 (CI)'))
})

it('a non-Error approve rejection carries no status and fails with its string form', async () => {
server.use(serveRuns(pendingRuns))
const octokit = newOctokit('some-token')
vi.spyOn(octokit.actions, 'approveWorkflowRun').mockRejectedValue('nope')

await expect(approvePendingRuns(octokit, new utils.MockContext(prCommentEvent('/ok-to-test')), 1, headSha))
.rejects
.toThrow('could not approve run 8 (CI): nope')
})

it('on an issue comments that it only applies to pull requests', async () => {
const reply = new utils.ObserveRequest()
const event = structuredClone(issueCommentEvent)
Expand Down
57 changes: 56 additions & 1 deletion __tests__/plugins/tide.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { http } from 'msw'
import { setupServer } from 'msw/node'
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'

import { fetchMergeability, tideOnCheckSuite, tideOnComment, tideOnPullRequest, tideOnReview, tryMergePullRequest, unknownRetryDelaysMs } from '../../src/plugins/tide'
import { fetchMergeability, loadTide, mergeOnce, tideOnCheckSuite, tideOnComment, tideOnPullRequest, tideOnReview, tryMergePullRequest, unknownRetryDelaysMs } from '../../src/plugins/tide'
import { resolveTide } from '../../src/utils/config'
import { newOctokit } from '../../src/utils/octokit'
import { resetOwnersCaches } from '../../src/utils/owners'
import * as sleepModule from '../../src/utils/sleep'
import labelFileContents from '../fixtures/labels/labelFileContentsResp.json'
import checkSuiteCompletedEvent from '../fixtures/pullReq/checkSuiteCompletedEvent.json'
Expand Down Expand Up @@ -114,6 +115,16 @@ describe('fetchMergeability', () => {
expect(info).toHaveBeenCalledWith('mergeability of pr #1 is still unknown after 3 retries')
})

it('a state computed on the read after the last wait is returned without the give-up line', async () => {
const gets = servePull(pull([], unknown), pull([], unknown), pull([], unknown), pull([], { mergeable_state: 'behind' }))
const info = vi.spyOn(core, 'info')

await expect(fetchMergeability(octokit, context, 1)).resolves.toMatchObject({ state: 'behind', mergeable: true })
expect(gets).toHaveLength(unknownRetryDelaysMs.length + 1)
expect(sleep.mock.calls.map(call => call[0])).toEqual(unknownRetryDelaysMs)
expect(info).not.toHaveBeenCalled()
})

it('treats a null mergeable as unknown even when the state says otherwise', async () => {
const gets = servePull(pull([], { mergeable: null, mergeable_state: 'clean' }), pull([]))

Expand All @@ -128,6 +139,37 @@ describe('fetchMergeability', () => {
expect(gets).toHaveLength(1)
expect(sleep).not.toHaveBeenCalled()
})

it('reads a pull request without a draft field as not draft', async () => {
const { draft, ...noDraft } = pull([])
expect(draft).toBe(false)
servePull(noDraft)

await expect(fetchMergeability(octokit, context, 1)).resolves.toMatchObject({ draft: false })
})
})

describe('mergeOnce', () => {
it('reports a non-Error rejection by its string form, without a status', async () => {
vi.spyOn(octokit.pulls, 'merge').mockRejectedValue('nope')

await expect(mergeOnce(octokit, context, 1, tide, 'headsha')).resolves.toEqual({ result: 'failed', message: 'nope', status: undefined })
})
})

describe('loadTide', () => {
beforeEach(() => {
resetOwnersCaches()
server.use(...utils.noOrgOrRepoConfigExcept())
})

it('without a base in scope, reads the OWNERS files of the default branch', async () => {
const tree = new utils.ObserveRequest()
server.use(utils.defaultBranchTree(['OWNERS'], tree))

await expect(loadTide(octokit, context)).resolves.toMatchObject({ labels: ['lgtm', 'approved'] })
await expect(tree.called()).resolves.toBe('called')
})
})

describe('tryMergePullRequest', () => {
Expand Down Expand Up @@ -437,6 +479,19 @@ describe('tryMergePullRequest', () => {
expect(error).toHaveBeenCalledWith(expect.stringContaining('Resource not accessible by integration'))
})

it('a compare response without a files list is read as no workflow files behind', async () => {
servePull(forkPull())
observeMerge(403, forbidden)
const diff = serveDiff([], ['src/a.go'])
server.use(http.get(`${repo}/compare/headsha...master`, utils.mockResponse(200, { status: 'identical' }, diff.compare)))
const error = vi.spyOn(core, 'error').mockImplementation(() => {})

await expect(tryMergePullRequest(octokit, context, 1, tide)).resolves.toBe('failed')
await expect(diff.compare.called()).resolves.toBe('called')
await expect(diff.comment.notCalled()).resolves.toBe('not called')
expect(error).toHaveBeenCalledWith(expect.stringContaining('Resource not accessible by integration'))
})

it('a fork 403 with no workflow file anywhere is still a failure', async () => {
servePull(forkPull())
observeMerge(403, forbidden)
Expand Down
Loading