144 lines
4.7 KiB
TypeScript
144 lines
4.7 KiB
TypeScript
import { describe, expect, test } from 'bun:test'
|
|
import { mkdtemp, writeFile } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
|
|
import {
|
|
eventContext,
|
|
monitorPRReviewGuidance,
|
|
reviewGuidanceCheckName,
|
|
reviewGuidanceChecks
|
|
} from '../src/index'
|
|
|
|
function response(body: unknown, init: ResponseInit = {}) {
|
|
return new Response(JSON.stringify(body), {
|
|
headers: { 'content-type': 'application/json' },
|
|
...init
|
|
})
|
|
}
|
|
|
|
async function writeEvent(event: unknown) {
|
|
const dir = await mkdtemp(join(tmpdir(), 'open-pencil-pr-guidance-'))
|
|
const path = join(dir, 'event.json')
|
|
await writeFile(path, JSON.stringify(event), 'utf8')
|
|
return path
|
|
}
|
|
|
|
describe('reviewGuidanceCheckName', () => {
|
|
test('accepts legacy PR Hygiene errors from CodeRabbit tables', () => {
|
|
expect(reviewGuidanceCheckName('| PR Hygiene: Template | ❌ Error | Missing template |')).toBe(
|
|
'PR Hygiene: Template'
|
|
)
|
|
})
|
|
|
|
test('accepts softer PR Description check names', () => {
|
|
expect(reviewGuidanceCheckName('| PR Description: Context | Error | Add validation |')).toBe(
|
|
'PR Description: Context'
|
|
)
|
|
})
|
|
|
|
test('ignores unrelated or passing checks', () => {
|
|
expect(reviewGuidanceCheckName('| Security | ❌ Error | Something else |')).toBeNull()
|
|
expect(reviewGuidanceCheckName('| PR Description: Context | ✅ Pass | Fine |')).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('reviewGuidanceChecks', () => {
|
|
test('extracts only relevant failed checks', () => {
|
|
const checks = reviewGuidanceChecks(`
|
|
| Check name | Status | Explanation |
|
|
|---|---|---|
|
|
| PR Description: Template | ❌ Error | Missing template |
|
|
| Security | ❌ Error | Not relevant |
|
|
| PR Readability: English | Error | Needs English |
|
|
`)
|
|
|
|
expect(checks).toEqual(['PR Description: Template', 'PR Readability: English'])
|
|
})
|
|
})
|
|
|
|
describe('eventContext', () => {
|
|
test('reads pull request review events', () => {
|
|
expect(
|
|
eventContext({
|
|
pull_request: { number: 42 },
|
|
review: { state: 'changes_requested', body: 'review body' }
|
|
})
|
|
).toEqual({ issueNumber: 42, shouldInspect: true, text: 'changes_requested\nreview body' })
|
|
})
|
|
|
|
test('reads pull request issue comments', () => {
|
|
expect(
|
|
eventContext({
|
|
issue: { number: 42, pull_request: {} },
|
|
comment: { body: 'comment body' }
|
|
})
|
|
).toEqual({ issueNumber: 42, shouldInspect: true, text: 'comment body' })
|
|
})
|
|
})
|
|
|
|
describe('monitorPRReviewGuidance', () => {
|
|
test('logs a maintainer note and only reads PR data', async () => {
|
|
const eventPath = await writeEvent({
|
|
sender: { login: 'coderabbitai[bot]' },
|
|
issue: { number: 294, pull_request: {} },
|
|
comment: {
|
|
body: '| Check name | Status | Explanation |\n|---|---|---|\n| PR Description: Template | ❌ Error | Missing template |'
|
|
}
|
|
})
|
|
const requests: string[] = []
|
|
const messages: string[] = []
|
|
|
|
await monitorPRReviewGuidance({
|
|
env: {
|
|
GITHUB_API_URL: 'https://example.test',
|
|
GITHUB_EVENT_PATH: eventPath,
|
|
GITHUB_REPOSITORY: 'open-pencil/open-pencil',
|
|
GITHUB_REPOSITORY_OWNER: 'open-pencil',
|
|
GITHUB_TOKEN: 'token'
|
|
},
|
|
fetchImpl: (async (input, init) => {
|
|
requests.push(`${init?.method ?? 'GET'} ${String(input)}`)
|
|
return response({
|
|
author_association: 'CONTRIBUTOR',
|
|
state: 'open',
|
|
title: 'feat(dev-install): add script',
|
|
user: { login: 'joeycumines' }
|
|
})
|
|
}) satisfies typeof fetch,
|
|
log: (message) => messages.push(message)
|
|
})
|
|
|
|
expect(requests).toEqual(['GET https://example.test/repos/open-pencil/open-pencil/pulls/294'])
|
|
expect(messages.join('\n')).toContain('No automatic label, comment, or close was applied')
|
|
})
|
|
|
|
test('ignores non-CodeRabbit comments', async () => {
|
|
const eventPath = await writeEvent({
|
|
sender: { login: 'contributor' },
|
|
issue: { number: 294, pull_request: {} },
|
|
comment: { body: '| PR Description: Template | ❌ Error | Missing template |' }
|
|
})
|
|
const requests: string[] = []
|
|
const messages: string[] = []
|
|
|
|
await monitorPRReviewGuidance({
|
|
env: {
|
|
GITHUB_API_URL: 'https://example.test',
|
|
GITHUB_EVENT_PATH: eventPath,
|
|
GITHUB_REPOSITORY: 'open-pencil/open-pencil',
|
|
GITHUB_REPOSITORY_OWNER: 'open-pencil',
|
|
GITHUB_TOKEN: 'token'
|
|
},
|
|
fetchImpl: (async (input, init) => {
|
|
requests.push(`${init?.method ?? 'GET'} ${String(input)}`)
|
|
return response({})
|
|
}) satisfies typeof fetch,
|
|
log: (message) => messages.push(message)
|
|
})
|
|
|
|
expect(requests).toEqual([])
|
|
expect(messages).toEqual(['No action needed: event sender is contributor, not CodeRabbit.'])
|
|
})
|
|
})
|