Make webhook tests independent with isolated PRs and retry on merge timing issues
Some checks failed
CI / test (pull_request) Has been cancelled
Some checks failed
CI / test (pull_request) Has been cancelled
Each test now creates its own PR and cleans up after itself. Tests no longer depend on shared state or ordering, making them resilient to failures. Added retry logic to mergePR for Gitea's 404/405 responses while computing mergeability. Used crypto.randomUUID() for unique branch names instead of Date.now() to eliminate collision risk.
This commit is contained in:
parent
238df92888
commit
a71bf2d492
|
|
@ -228,10 +228,30 @@ export async function commentOnPR(repo: string, prNumber: number, body: string,
|
|||
}
|
||||
|
||||
export async function mergePR(repo: string, prNumber: number, user: User) {
|
||||
return giteaFetch(`/repos/${repo}/pulls/${prNumber}/merge`, user, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ Do: "merge", merge_message_field: "Test merge" }),
|
||||
})
|
||||
// Gitea returns 404/405 while computing mergeability after PR creation
|
||||
const path = `/repos/${repo}/pulls/${prNumber}/merge`
|
||||
|
||||
for (let attempt = 0; attempt < 10; attempt++) {
|
||||
const response = await fetch(`${giteaUrl}/api/v1${path}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `token ${user.token}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ Do: "merge" }),
|
||||
})
|
||||
|
||||
if (response.ok) return
|
||||
if (response.status === 404 || response.status === 405) {
|
||||
await Bun.sleep(1000)
|
||||
continue
|
||||
}
|
||||
|
||||
const body = await response.text().catch(() => "")
|
||||
throw new Error(`Gitea ${response.status}: POST ${path}\n${body}`)
|
||||
}
|
||||
|
||||
throw new Error(`Gitea merge failed after 10 retries for ${path}`)
|
||||
}
|
||||
|
||||
export async function createReview(repo: string, prNumber: number, user: User, filePath: string) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ setDefaultTimeout(30_000)
|
|||
import { ensure } from "@workshop/shared/utils"
|
||||
import {
|
||||
type User,
|
||||
type TestPR,
|
||||
setupWebhooks,
|
||||
expectPullRequestWebhook,
|
||||
expectIssueCommentWebhook,
|
||||
|
|
@ -15,6 +14,10 @@ import {
|
|||
mergePR,
|
||||
} from "./helpers"
|
||||
|
||||
function testBranch(label: string) {
|
||||
return `test-${crypto.randomUUID().slice(0, 8)}-${label}`
|
||||
}
|
||||
|
||||
function getEnv(name: string): string {
|
||||
const value = process.env[name]
|
||||
ensure(value, `Missing env var: ${name}. See .env.example`)
|
||||
|
|
@ -37,20 +40,19 @@ afterAll(() => teardown())
|
|||
// --- Tests for Corey's repo (Spike opens PR, Corey comments/reviews/merges) ---
|
||||
|
||||
describe(`${coreyRepo}`, () => {
|
||||
let pr: TestPR
|
||||
const branch = `test-${Date.now()}-corey`
|
||||
|
||||
test("opening a PR sends pull_request webhook with correct author and repo", async () => {
|
||||
pr = await expectPullRequestWebhook({ action: "opened", branch }, async (webhook) => {
|
||||
const result = await openTestPR(coreyRepo, spike, branch)
|
||||
const branch = testBranch("open")
|
||||
await expectPullRequestWebhook({ action: "opened", branch }, async (webhook) => {
|
||||
await openTestPR(coreyRepo, spike, branch)
|
||||
const payload = await webhook
|
||||
expect(payload.pull_request.user.login).toBe(spike.username)
|
||||
expect(payload.repository.full_name).toBe(coreyRepo)
|
||||
return result
|
||||
})
|
||||
})
|
||||
|
||||
test("commenting sends issue_comment webhook with correct user", async () => {
|
||||
const branch = testBranch("comment")
|
||||
const pr = await openTestPR(coreyRepo, spike, branch)
|
||||
await expectIssueCommentWebhook({ action: "created", username: corey.username, prNumber: pr.number }, async (webhook) => {
|
||||
await commentOnPR(coreyRepo, pr.number, "Looks good!", corey)
|
||||
const payload = await webhook
|
||||
|
|
@ -59,6 +61,8 @@ describe(`${coreyRepo}`, () => {
|
|||
})
|
||||
|
||||
test("review with line comment sends pull_request_comment webhook", async () => {
|
||||
const branch = testBranch("review")
|
||||
const pr = await openTestPR(coreyRepo, spike, branch)
|
||||
await expectPullRequestCommentWebhook({ action: "reviewed", branch }, async (webhook) => {
|
||||
await createReview(coreyRepo, pr.number, corey, pr.filename)
|
||||
const payload = await webhook
|
||||
|
|
@ -68,6 +72,8 @@ describe(`${coreyRepo}`, () => {
|
|||
})
|
||||
|
||||
test("merging sends pull_request closed webhook", async () => {
|
||||
const branch = testBranch("merge")
|
||||
const pr = await openTestPR(coreyRepo, spike, branch)
|
||||
await expectPullRequestWebhook({ action: "closed", branch }, async (webhook) => {
|
||||
await mergePR(coreyRepo, pr.number, corey)
|
||||
const payload = await webhook
|
||||
|
|
@ -79,20 +85,19 @@ describe(`${coreyRepo}`, () => {
|
|||
// --- Tests for Spike's repo (Corey opens PR, Spike comments/reviews/merges) ---
|
||||
|
||||
describe(`${spikeRepo}`, () => {
|
||||
let pr: TestPR
|
||||
const branch = `test-${Date.now()}-spike`
|
||||
|
||||
test("opening a PR sends pull_request webhook with correct author and repo", async () => {
|
||||
pr = await expectPullRequestWebhook({ action: "opened", branch }, async (webhook) => {
|
||||
const result = await openTestPR(spikeRepo, corey, branch)
|
||||
const branch = testBranch("open")
|
||||
await expectPullRequestWebhook({ action: "opened", branch }, async (webhook) => {
|
||||
await openTestPR(spikeRepo, corey, branch)
|
||||
const payload = await webhook
|
||||
expect(payload.pull_request.user.login).toBe(corey.username)
|
||||
expect(payload.repository.full_name).toBe(spikeRepo)
|
||||
return result
|
||||
})
|
||||
})
|
||||
|
||||
test("commenting sends issue_comment webhook with correct user", async () => {
|
||||
const branch = testBranch("comment")
|
||||
const pr = await openTestPR(spikeRepo, corey, branch)
|
||||
await expectIssueCommentWebhook({ action: "created", username: spike.username, prNumber: pr.number }, async (webhook) => {
|
||||
await commentOnPR(spikeRepo, pr.number, "On it!", spike)
|
||||
const payload = await webhook
|
||||
|
|
@ -101,6 +106,8 @@ describe(`${spikeRepo}`, () => {
|
|||
})
|
||||
|
||||
test("review with line comment sends pull_request_comment webhook", async () => {
|
||||
const branch = testBranch("review")
|
||||
const pr = await openTestPR(spikeRepo, corey, branch)
|
||||
await expectPullRequestCommentWebhook({ action: "reviewed", branch }, async (webhook) => {
|
||||
await createReview(spikeRepo, pr.number, spike, pr.filename)
|
||||
const payload = await webhook
|
||||
|
|
@ -110,6 +117,8 @@ describe(`${spikeRepo}`, () => {
|
|||
})
|
||||
|
||||
test("merging sends pull_request closed webhook", async () => {
|
||||
const branch = testBranch("merge")
|
||||
const pr = await openTestPR(spikeRepo, corey, branch)
|
||||
await expectPullRequestWebhook({ action: "closed", branch }, async (webhook) => {
|
||||
await mergePR(spikeRepo, pr.number, spike)
|
||||
const payload = await webhook
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user