fix: labels are missing in the pull request payload removing a label

When ReplaceIssueLabels calls issue.LoadLabels it was a noop because
issue.isLabelsLoaded is still set to true because of the call  to
issue.LoadLabels that was done at the beginning of the function.

(cherry picked from commit c801838690)
This commit is contained in:
Earl Warren 2024-11-06 15:36:02 +01:00 committed by forgejo-backport-action
parent 09a35a7cb8
commit eda6b436dc
2 changed files with 133 additions and 56 deletions

View file

@ -496,6 +496,7 @@ func ReplaceIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer
}
}
issue.isLabelsLoaded = false
issue.Labels = nil
if err = issue.LoadLabels(ctx); err != nil {
return err

View file

@ -84,7 +84,23 @@ jobs:
baseGitRepo.Close()
}()
// prepare the pull request
// prepare the repository labels
labelStr := "/api/v1/repos/user2/repo-pull-request/labels"
labelsCount := 2
labels := make([]*api.Label, labelsCount)
for i := 0; i < labelsCount; i++ {
color := "abcdef"
req := NewRequestWithJSON(t, "POST", labelStr, &api.CreateLabelOption{
Name: fmt.Sprintf("label%d", i),
Color: color,
}).AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusCreated)
labels[i] = new(api.Label)
DecodeJSON(t, resp, &labels[i])
assert.Equal(t, color, labels[i].Color)
}
// create the pull request
testEditFileToNewBranch(t, session, "user2", "repo-pull-request", "main", "wip-something", "README.md", "Hello, world 1")
testPullCreate(t, session, "user2", "repo-pull-request", true, "main", "wip-something", "Commit status PR")
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: baseRepo.ID})
@ -94,24 +110,15 @@ jobs:
issueURL := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%s", "user2", "repo-pull-request", fmt.Sprintf("%d", pr.Issue.Index))
// prepare the labels
labelStr := "/api/v1/repos/user2/repo-pull-request/labels"
req := NewRequestWithJSON(t, "POST", labelStr, &api.CreateLabelOption{
Name: "mylabel",
Color: "abcdef",
Description: "description mylabel",
}).AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusCreated)
label := new(api.Label)
DecodeJSON(t, resp, &label)
labelURL := fmt.Sprintf("%s/labels", issueURL)
// prepare the milestone
milestoneStr := "/api/v1/repos/user2/repo-pull-request/milestones"
req = NewRequestWithJSON(t, "POST", milestoneStr, &api.CreateMilestoneOption{
req := NewRequestWithJSON(t, "POST", milestoneStr, &api.CreateMilestoneOption{
Title: "mymilestone",
State: "open",
}).AddTokenAuth(token)
resp = MakeRequest(t, req, http.StatusCreated)
resp := MakeRequest(t, req, http.StatusCreated)
milestone := new(api.Milestone)
DecodeJSON(t, resp, &milestone)
@ -128,47 +135,97 @@ jobs:
return false
}
count := 0
assertActionRun := func(t *testing.T, sha, onType string, action api.HookIssueAction, actionRun *actions_model.ActionRun) {
assert.Equal(t, fmt.Sprintf("%s.yml", onType), actionRun.WorkflowID)
assert.Equal(t, sha, actionRun.CommitSHA)
assert.Equal(t, actions_module.GithubEventPullRequest, actionRun.TriggerEvent)
event, err := actionRun.GetPullRequestEventPayload()
require.NoError(t, err)
assert.Equal(t, action, event.Action)
}
type assertType func(t *testing.T, sha, onType string, action api.HookIssueAction, actionRuns []*actions_model.ActionRun)
assertActionRuns := func(t *testing.T, sha, onType string, action api.HookIssueAction, actionRuns []*actions_model.ActionRun) {
require.Len(t, actionRuns, 1)
assertActionRun(t, sha, onType, action, actionRuns[0])
}
for _, testCase := range []struct {
onType string
jobID string
doSomething func()
actionRunCount int
action api.HookIssueAction
hasLabel bool
assert assertType
}{
{
onType: "opened",
doSomething: func() {},
actionRunCount: 1,
action: api.HookIssueOpened,
assert: assertActionRuns,
},
{
onType: "synchronize",
doSomething: func() {
testEditFile(t, session, "user2", "repo-pull-request", "wip-something", "README.md", "Hello, world 2")
},
actionRunCount: 1,
action: api.HookIssueSynchronized,
assert: assertActionRuns,
},
{
onType: "labeled",
doSomething: func() {
req := NewRequestWithJSON(t, "POST", labelURL, &api.IssueLabelsOption{
Labels: []any{label.ID},
Labels: []any{labels[0].ID, labels[1].ID},
}).AddTokenAuth(token)
MakeRequest(t, req, http.StatusOK)
},
actionRunCount: 2,
action: api.HookIssueLabelUpdated,
hasLabel: true,
assert: func(t *testing.T, sha, onType string, action api.HookIssueAction, actionRuns []*actions_model.ActionRun) {
assertActionRun(t, sha, onType, api.HookIssueLabelUpdated, actionRuns[0])
assertActionRun(t, sha, onType, api.HookIssueLabelUpdated, actionRuns[1])
},
},
{
onType: "unlabeled",
doSomething: func() {
req := NewRequestWithJSON(t, "PUT", labelURL, &api.IssueLabelsOption{
Labels: []any{},
Labels: []any{labels[0].ID},
}).AddTokenAuth(token)
MakeRequest(t, req, http.StatusOK)
},
actionRunCount: 3,
action: api.HookIssueLabelCleared,
hasLabel: true,
assert: func(t *testing.T, sha, onType string, action api.HookIssueAction, actionRuns []*actions_model.ActionRun) {
foundPayloadWithLabels := false
knownLabels := []string{"label0", "label1"}
for _, actionRun := range actionRuns {
assert.Equal(t, sha, actionRun.CommitSHA)
assert.Equal(t, actions_module.GithubEventPullRequest, actionRun.TriggerEvent)
event, err := actionRun.GetPullRequestEventPayload()
require.NoError(t, err)
switch event.Action {
case api.HookIssueLabelUpdated:
assert.Equal(t, "labeled.yml", actionRun.WorkflowID)
assert.Equal(t, "label0", event.Label.Name)
require.Len(t, event.PullRequest.Labels, 1)
assert.Contains(t, "label0", event.PullRequest.Labels[0].Name)
case api.HookIssueLabelCleared:
assert.Equal(t, "unlabeled.yml", actionRun.WorkflowID)
assert.Contains(t, knownLabels, event.Label.Name)
if len(event.PullRequest.Labels) > 0 {
foundPayloadWithLabels = true
assert.Contains(t, knownLabels, event.PullRequest.Labels[0].Name)
}
default:
require.Fail(t, fmt.Sprintf("unexpected action '%s'", event.Action))
}
}
assert.True(t, foundPayloadWithLabels, "expected at least one clear label payload with non empty labels")
},
},
{
onType: "assigned",
@ -178,7 +235,9 @@ jobs:
}).AddTokenAuth(token)
MakeRequest(t, req, http.StatusCreated)
},
actionRunCount: 1,
action: api.HookIssueAssigned,
assert: assertActionRuns,
},
{
onType: "unassigned",
@ -188,7 +247,9 @@ jobs:
}).AddTokenAuth(token)
MakeRequest(t, req, http.StatusCreated)
},
actionRunCount: 1,
action: api.HookIssueUnassigned,
assert: assertActionRuns,
},
{
onType: "milestoned",
@ -198,7 +259,9 @@ jobs:
}).AddTokenAuth(token)
MakeRequest(t, req, http.StatusCreated)
},
actionRunCount: 1,
action: api.HookIssueMilestoned,
assert: assertActionRuns,
},
{
onType: "demilestoned",
@ -209,7 +272,9 @@ jobs:
}).AddTokenAuth(token)
MakeRequest(t, req, http.StatusCreated)
},
actionRunCount: 1,
action: api.HookIssueDemilestoned,
assert: assertActionRuns,
},
{
onType: "closed",
@ -219,7 +284,9 @@ jobs:
err = issue_service.ChangeStatus(db.DefaultContext, pr.Issue, user2, sha, true)
require.NoError(t, err)
},
actionRunCount: 1,
action: api.HookIssueClosed,
assert: assertActionRuns,
},
{
onType: "reopened",
@ -229,43 +296,52 @@ jobs:
err = issue_service.ChangeStatus(db.DefaultContext, pr.Issue, user2, sha, false)
require.NoError(t, err)
},
actionRunCount: 1,
action: api.HookIssueReOpened,
assert: assertActionRuns,
},
} {
t.Run(testCase.onType, func(t *testing.T) {
defer func() {
// cleanup leftovers, start from scratch
_, err = db.DeleteByBean(db.DefaultContext, actions_model.ActionRun{RepoID: baseRepo.ID})
require.NoError(t, err)
_, err = db.DeleteByBean(db.DefaultContext, actions_model.ActionRunJob{RepoID: baseRepo.ID})
require.NoError(t, err)
}()
// trigger the onType event
testCase.doSomething()
count++
count := testCase.actionRunCount
context := fmt.Sprintf("%[1]s / %[1]s (pull_request)", testCase.onType)
// wait for a new ActionRun to be created
assert.Eventually(t, func() bool {
return count == unittest.GetCount(t, &actions_model.ActionRun{RepoID: baseRepo.ID})
var actionRuns []*actions_model.ActionRun
// wait for ActionRun(s) to be created
require.Eventually(t, func() bool {
actionRuns = make([]*actions_model.ActionRun, 0)
require.NoError(t, db.GetEngine(db.DefaultContext).Where("repo_id=?", baseRepo.ID).Find(&actionRuns))
return assert.Len(t, actionRuns, count)
}, 30*time.Second, 1*time.Second)
// verify the expected ActionRun was created
// verify the expected ActionRuns were created
sha, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
require.NoError(t, err)
actionRun := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{RepoID: baseRepo.ID, WorkflowID: fmt.Sprintf("%s.yml", testCase.onType)})
assert.Equal(t, sha, actionRun.CommitSHA)
assert.Equal(t, actions_module.GithubEventPullRequest, actionRun.TriggerEvent)
event, err := actionRun.GetPullRequestEventPayload()
if testCase.hasLabel {
assert.NotNil(t, event.Label)
}
require.NoError(t, err)
assert.Equal(t, testCase.action, event.Action)
// verify the expected ActionRunJob was created and is StatusWaiting
job := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{JobID: testCase.onType, CommitSHA: sha})
assert.Equal(t, actions_model.StatusWaiting, job.Status)
// verify the commit status changes to CommitStatusSuccess when the job changes to StatusSuccess
assert.True(t, checkCommitStatus(sha, context, api.CommitStatusPending))
for _, actionRun := range actionRuns {
// verify the expected ActionRunJob was created and is StatusWaiting
job := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{RunID: actionRun.ID, CommitSHA: sha})
assert.Equal(t, actions_model.StatusWaiting, job.Status)
// change the state of the job to success
job.Status = actions_model.StatusSuccess
actions_service.CreateCommitStatus(db.DefaultContext, job)
}
// verify the commit status changed to CommitStatusSuccess because the job(s) changed to StatusSuccess
assert.True(t, checkCommitStatus(sha, context, api.CommitStatusSuccess))
testCase.assert(t, sha, testCase.onType, testCase.action, actionRuns)
})
}
})