fix(github): hide approve PR button on own PRs (#1017)

* fix(github): hide approve PR button on own PRs and reject self-approval server-side

The frontend's existing self-author check failed open when the current
GitHub user hadn't loaded yet — the PR panel didn't call useGitHubStatus,
so the green Approve button flashed on every PR (including your own)
during the brief window before /api/v1/github/status resolved.

Fail closed when the user is unknown, fetch the status from the panel,
and add a typed ErrSelfApprove guard in Service.SubmitReview so any path
that bypasses the visibility check gets a clean 422 instead of an opaque
upstream error.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(github): cover controller's self-approve 422 mapping

Address PR review feedback: add TestHttpSubmitReview_SelfApproveReturns422
to exercise the controller → service → 422 path end-to-end so a future
refactor can't silently reclassify ErrSelfApprove as a generic 500.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
João Salavisa
2026-05-22 17:10:38 +01:00
committed by GitHub
parent 1fb936fad5
commit b4a696a5e9
6 changed files with 289 additions and 5 deletions
+5 -1
View File
@@ -297,12 +297,16 @@ func (c *Controller) httpSubmitReview(ctx *gin.Context) {
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
return
}
validEvents := map[string]bool{"APPROVE": true, "COMMENT": true, "REQUEST_CHANGES": true}
validEvents := map[string]bool{reviewEventApprove: true, "COMMENT": true, "REQUEST_CHANGES": true}
if !validEvents[req.Event] {
ctx.JSON(http.StatusBadRequest, gin.H{"error": "event must be APPROVE, COMMENT, or REQUEST_CHANGES"})
return
}
if err := c.service.SubmitReview(ctx.Request.Context(), owner, repo, number, req.Event, req.Body); err != nil {
if errors.Is(err, ErrSelfApprove) {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()})
return
}
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}