Skip to content
Closed
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
69 changes: 69 additions & 0 deletions __tests__/bundle/labelSyncAllowlist.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { FakeGithub } from './fakeGithub'
import { Buffer } from 'node:buffer'
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest'

import labelFileContents from '../fixtures/labels/labelFileContentsResp.json'
import { start } from './fakeGithub'
import { configReads, helpersFor, repo, token } from './helpers'
import { runBundle } from './runBundle'

vi.setConfig({ testTimeout: 30_000 })

const labelsRead = `GET ${repo}/labels?per_page=100`

function yamlFile(text: string) {
const file = structuredClone(labelFileContents)
file.content = Buffer.from(text).toString('base64')
return file
}

// the catalog's verbatim arm (labelCatalog.ts `prefixed('', value)`), driven through dist/index.js: the `/label`
// allowlist is the `labels.labels` section and its values are created as-is, never as `labels/<value>`.
// labelSyncCatalog.test.ts only composes prefixed sections (kind, a custom key) and needs-* labels
describe('dist/index.js label-sync creates the /label allowlist verbatim', () => {
let gh: FakeGithub
const { expectRequests } = helpersFor(() => gh)

beforeAll(async () => {
gh = await start()
})
afterEach(() => gh.reset())
afterAll(() => gh.close())

function runLabelSync() {
return runBundle({ eventName: 'workflow_dispatch', payload: {}, inputs: { ...token, jobs: 'label-sync' }, apiUrl: gh.url })
}

it('a labels.labels section yields the bare names, with color and description carried over', async () => {
const config = yamlFile([
'labels:',
' labels:',
' - documentation',
' - name: tide/merge-method-squash',
' color: FEF2C0',
' description: Squash when merging',
'',
].join('\n'))
gh.route('GET', '/repos/Codertocat/.project/contents/prow.yaml', { status: 200, body: config })
gh.route('GET', `${repo}/labels`, { status: 200, body: [] })
gh.route('POST', `${repo}/labels`, { status: 201, body: {} })

const result = await runLabelSync()

expect(result.status, result.stdout).toBe(0)
expect(result.errors).toEqual([])
const posts = gh.requestsMatching('POST', /\/labels$/)
const created = posts.map(p => (p.body as { name: string }).name)
expect(created).toContain('documentation')
expect(created).toContain('tide/merge-method-squash')
expect(created.filter(name => name.startsWith('labels/'))).toEqual([])
expect(posts.find(p => (p.body as { name: string }).name === 'documentation')!.body).toEqual({ name: 'documentation' })
expect(posts.find(p => (p.body as { name: string }).name === 'tide/merge-method-squash')!.body).toEqual({
name: 'tide/merge-method-squash',
color: 'fef2c0',
description: 'Squash when merging',
})
expect(result.stdout).toContain(`label-sync: created ${created.length} [${created.join(', ')}]`)
expectRequests([...configReads({ org: '.project' }), labelsRead], created.map(() => `POST ${repo}/labels`))
})
})
66 changes: 66 additions & 0 deletions __tests__/bundle/lgtmJobNoLabel.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { FakeGithub } from './fakeGithub'
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest'

import pullReqOpenedEvent from '../fixtures/pullReq/pullReqOpenedEvent.json'
import { start } from './fakeGithub'
import { helpersFor, ownersProbe, ownersReads, repo, token } from './helpers'
import { runBundle } from './runBundle'

vi.setConfig({ testTimeout: 30_000 })

// the lgtm job's no-op arm (onPrLgtm.ts: `currentLabels.includes('lgtm')` false), driven through dist/index.js:
// a new push to a pull request that does not carry lgtm reads the labels and removes nothing. bundle.test.ts
// only drives the arm where the label is present and gets removed
describe('dist/index.js pull_request lgtm job on a pull request without the lgtm label', () => {
let gh: FakeGithub
const { expectRequests, routeOwners } = helpersFor(() => gh)

beforeAll(async () => {
gh = await start()
})
afterEach(() => gh.reset())
afterAll(() => gh.close())

function synchronize(eventName: string) {
return runBundle({
eventName,
payload: { ...pullReqOpenedEvent, action: 'synchronize' },
inputs: { ...token, jobs: 'lgtm' },
apiUrl: gh.url,
})
}

for (const eventName of ['pull_request', 'pull_request_target']) {
it(`${eventName}: reads the labels and issues no DELETE when lgtm is absent`, async () => {
routeOwners({}, ['src/file1.txt'])
gh.route('GET', `${repo}/issues/1`, { status: 200, body: { labels: [{ name: 'kind/bug' }, { name: 'approved' }] } })

const result = await synchronize(eventName)

expect(result.status, result.stdout).toBe(0)
expect(result.errors).toEqual([])
expect(result.stdout).toContain('remove-lgtm: found labels for issue kind/bug,approved')
expect(gh.requestsMatching('DELETE', /./)).toEqual([])
// owners-label reads the (OWNERS-less) base tree, approve probes the default branch, then the lgtm job
// reads the labels and stops there
expectRequests([], [
...ownersReads,
ownersProbe,
`GET ${repo}/issues/1`,
])
})
}

it('pull_request: an unlabeled pull request is read and left alone', async () => {
routeOwners({}, ['src/file1.txt'])
gh.route('GET', `${repo}/issues/1`, { status: 200, body: { labels: [] } })

const result = await synchronize('pull_request')

expect(result.status, result.stdout).toBe(0)
expect(result.errors).toEqual([])
expect(result.stdout).toContain('remove-lgtm: found labels for issue ')
expect(gh.requestsMatching('DELETE', /./)).toEqual([])
expectRequests([], [...ownersReads, ownersProbe, `GET ${repo}/issues/1`])
})
})
Loading