mirror of
https://github.com/go-gitea/gitea.git
synced 2026-11-02 13:44:16 +09:00
Runs triggered by pull request reviews took their ref and commit from the review's `commit_id` instead of the pull request. They now use the pull request head like other pull request events, and their commit status is set on the commit that ran.
131 lines
5.7 KiB
Go
131 lines
5.7 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package actions
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
actions_model "gitea.dev/models/actions"
|
|
issues_model "gitea.dev/models/issues"
|
|
repo_model "gitea.dev/models/repo"
|
|
"gitea.dev/models/unittest"
|
|
user_model "gitea.dev/models/user"
|
|
actions_module "gitea.dev/modules/actions"
|
|
"gitea.dev/modules/actions/jobparser"
|
|
webhook_module "gitea.dev/modules/webhook"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestIfNeedApproval(t *testing.T) {
|
|
alwaysWrite := func(_ context.Context, _ *repo_model.Repository, _ *user_model.User) (bool, error) {
|
|
return true, nil
|
|
}
|
|
neverWrite := func(_ context.Context, _ *repo_model.Repository, _ *user_model.User) (bool, error) {
|
|
return false, nil
|
|
}
|
|
hasMerged := func(_ context.Context, _, _ int64) (bool, error) { return true, nil }
|
|
noMerged := func(_ context.Context, _, _ int64) (bool, error) { return false, nil }
|
|
errPerm := errors.New("perm error")
|
|
errMerge := errors.New("merge error")
|
|
|
|
forkRun := &actions_model.ActionRun{IsForkPullRequest: true, TriggerEvent: actions_module.GithubEventPullRequest}
|
|
nonForkRun := &actions_model.ActionRun{IsForkPullRequest: false, TriggerEvent: actions_module.GithubEventPullRequest}
|
|
prTargetRun := &actions_model.ActionRun{IsForkPullRequest: true, TriggerEvent: actions_module.GithubEventPullRequestTarget}
|
|
|
|
repo := &repo_model.Repository{ID: 1}
|
|
normalUser := &user_model.User{ID: 10}
|
|
restrictedUser := &user_model.User{ID: 11, IsRestricted: true}
|
|
|
|
for _, tt := range []struct {
|
|
name string
|
|
run *actions_model.ActionRun
|
|
user *user_model.User
|
|
canWrite func(context.Context, *repo_model.Repository, *user_model.User) (bool, error)
|
|
hasMerged func(context.Context, int64, int64) (bool, error)
|
|
check assert.BoolAssertionFunc
|
|
}{
|
|
{"not a fork PR never needs approval", nonForkRun, normalUser, alwaysWrite, hasMerged, assert.False},
|
|
{"pull_request_target never needs approval even when fork", prTargetRun, normalUser, alwaysWrite, hasMerged, assert.False},
|
|
{"restricted user always needs approval", forkRun, restrictedUser, alwaysWrite, hasMerged, assert.True},
|
|
{"fork PR with write permission does not need approval", forkRun, normalUser, alwaysWrite, noMerged, assert.False},
|
|
{"fork PR with merged PR but no write permission does not need approval", forkRun, normalUser, neverWrite, hasMerged, assert.False},
|
|
{"fork PR with no write and no merged PR needs approval", forkRun, normalUser, neverWrite, noMerged, assert.True},
|
|
} {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
need, err := ifNeedApprovalWith(t.Context(), tt.run, repo, tt.user, tt.canWrite, tt.hasMerged)
|
|
require.NoError(t, err)
|
|
tt.check(t, need)
|
|
})
|
|
}
|
|
|
|
t.Run("canWriteActions error is propagated", func(t *testing.T) {
|
|
failWrite := func(_ context.Context, _ *repo_model.Repository, _ *user_model.User) (bool, error) {
|
|
return false, errPerm
|
|
}
|
|
_, err := ifNeedApprovalWith(t.Context(), forkRun, repo, normalUser, failWrite, noMerged)
|
|
require.ErrorIs(t, err, errPerm)
|
|
})
|
|
|
|
t.Run("hasMergedPR error is propagated", func(t *testing.T) {
|
|
failMerge := func(_ context.Context, _, _ int64) (bool, error) { return false, errMerge }
|
|
_, err := ifNeedApprovalWith(t.Context(), forkRun, repo, normalUser, neverWrite, failMerge)
|
|
require.ErrorIs(t, err, errMerge)
|
|
})
|
|
|
|
t.Run("restricted user skips permission check entirely", func(t *testing.T) {
|
|
// The perm and merge functions must not be called for a restricted user.
|
|
called := false
|
|
trackWrite := func(_ context.Context, _ *repo_model.Repository, _ *user_model.User) (bool, error) {
|
|
called = true
|
|
return true, nil
|
|
}
|
|
need, err := ifNeedApprovalWith(t.Context(), forkRun, repo, restrictedUser, trackWrite, noMerged)
|
|
require.NoError(t, err)
|
|
assert.True(t, need)
|
|
assert.False(t, called, "permission check must not run for restricted user")
|
|
})
|
|
}
|
|
|
|
func TestGetApprovalUsersAddsForkPullRequestAuthorUnlessDefaultBranchWorkflow(t *testing.T) {
|
|
require.NoError(t, unittest.PrepareTestDatabase())
|
|
|
|
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
|
|
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
|
|
|
approvalUsers, err := getApprovalUsers(t.Context(), ¬ifyInput{Doer: doer, PullRequest: pr, Event: webhook_module.HookEventPullRequest}, true)
|
|
require.NoError(t, err)
|
|
require.Len(t, approvalUsers, 2)
|
|
assert.Equal(t, []int64{doer.ID, pr.Issue.PosterID}, []int64{approvalUsers[0].ID, approvalUsers[1].ID})
|
|
|
|
approvalUsers, err = getApprovalUsers(t.Context(), ¬ifyInput{Doer: doer, PullRequest: pr, Event: webhook_module.HookEventIssueComment}, true)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, []*user_model.User{doer}, approvalUsers)
|
|
}
|
|
|
|
func TestFilteredWorkflowCommitStatusForForkPullRequest(t *testing.T) {
|
|
input := new(notifyInput).WithPullRequest(&issues_model.PullRequest{
|
|
Flow: issues_model.PullRequestFlowGithub,
|
|
BaseRepoID: 1,
|
|
HeadRepoID: 2,
|
|
Index: 1,
|
|
})
|
|
|
|
assert.True(t, isForkPullRequestInput(input))
|
|
assert.Equal(t, "refs/pull/1/head", input.Ref.String())
|
|
assert.False(t, shouldCreateSkippedCommitStatusForFilteredWorkflow(input, &actions_module.DetectedWorkflow{
|
|
TriggerEvent: &jobparser.Event{Name: actions_module.GithubEventPullRequest},
|
|
}))
|
|
assert.True(t, shouldCreateSkippedCommitStatusForFilteredWorkflow(input, &actions_module.DetectedWorkflow{
|
|
TriggerEvent: &jobparser.Event{Name: actions_module.GithubEventPullRequestTarget},
|
|
}))
|
|
|
|
assert.True(t, shouldCreateSkippedCommitStatusForFilteredWorkflow(newNotifyInput(&repo_model.Repository{ID: 1}, &user_model.User{ID: 2}, actions_module.GithubEventPullRequest), &actions_module.DetectedWorkflow{
|
|
TriggerEvent: &jobparser.Event{Name: actions_module.GithubEventPullRequest},
|
|
}))
|
|
}
|