mirror of https://github.com/go-gitea/gitea.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
337 lines
10 KiB
337 lines
10 KiB
// Copyright 2023 The Gitea Authors. All rights reserved. |
|
// SPDX-License-Identifier: MIT |
|
|
|
package actions |
|
|
|
import ( |
|
"testing" |
|
|
|
"code.gitea.io/gitea/modules/git" |
|
api "code.gitea.io/gitea/modules/structs" |
|
webhook_module "code.gitea.io/gitea/modules/webhook" |
|
|
|
"github.com/stretchr/testify/assert" |
|
) |
|
|
|
func TestDetectMatched(t *testing.T) { |
|
testCases := []struct { |
|
desc string |
|
commit *git.Commit |
|
triggedEvent webhook_module.HookEventType |
|
payload api.Payloader |
|
yamlOn string |
|
expected bool |
|
}{ |
|
{ |
|
desc: "HookEventCreate(create) matches GithubEventCreate(create)", |
|
triggedEvent: webhook_module.HookEventCreate, |
|
payload: nil, |
|
yamlOn: "on: create", |
|
expected: true, |
|
}, |
|
{ |
|
desc: "HookEventIssues(issues) `opened` action matches GithubEventIssues(issues)", |
|
triggedEvent: webhook_module.HookEventIssues, |
|
payload: &api.IssuePayload{Action: api.HookIssueOpened}, |
|
yamlOn: "on: issues", |
|
expected: true, |
|
}, |
|
{ |
|
desc: "HookEventIssues(issues) `milestoned` action matches GithubEventIssues(issues)", |
|
triggedEvent: webhook_module.HookEventIssues, |
|
payload: &api.IssuePayload{Action: api.HookIssueMilestoned}, |
|
yamlOn: "on: issues", |
|
expected: true, |
|
}, |
|
{ |
|
desc: "HookEventPullRequestSync(pull_request_sync) matches GithubEventPullRequest(pull_request)", |
|
triggedEvent: webhook_module.HookEventPullRequestSync, |
|
payload: &api.PullRequestPayload{Action: api.HookIssueSynchronized}, |
|
yamlOn: "on: pull_request", |
|
expected: true, |
|
}, |
|
{ |
|
desc: "HookEventPullRequest(pull_request) `label_updated` action doesn't match GithubEventPullRequest(pull_request) with no activity type", |
|
triggedEvent: webhook_module.HookEventPullRequest, |
|
payload: &api.PullRequestPayload{Action: api.HookIssueLabelUpdated}, |
|
yamlOn: "on: pull_request", |
|
expected: false, |
|
}, |
|
{ |
|
desc: "HookEventPullRequest(pull_request) `closed` action doesn't match GithubEventPullRequest(pull_request) with no activity type", |
|
triggedEvent: webhook_module.HookEventPullRequest, |
|
payload: &api.PullRequestPayload{Action: api.HookIssueClosed}, |
|
yamlOn: "on: pull_request", |
|
expected: false, |
|
}, |
|
{ |
|
desc: "HookEventPullRequest(pull_request) `closed` action doesn't match GithubEventPullRequest(pull_request) with branches", |
|
triggedEvent: webhook_module.HookEventPullRequest, |
|
payload: &api.PullRequestPayload{ |
|
Action: api.HookIssueClosed, |
|
PullRequest: &api.PullRequest{ |
|
Base: &api.PRBranchInfo{}, |
|
}, |
|
}, |
|
yamlOn: "on:\n pull_request:\n branches: [main]", |
|
expected: false, |
|
}, |
|
{ |
|
desc: "HookEventPullRequest(pull_request) `label_updated` action matches GithubEventPullRequest(pull_request) with `label` activity type", |
|
triggedEvent: webhook_module.HookEventPullRequest, |
|
payload: &api.PullRequestPayload{Action: api.HookIssueLabelUpdated}, |
|
yamlOn: "on:\n pull_request:\n types: [labeled]", |
|
expected: true, |
|
}, |
|
{ |
|
desc: "HookEventPullRequestReviewComment(pull_request_review_comment) matches GithubEventPullRequestReviewComment(pull_request_review_comment)", |
|
triggedEvent: webhook_module.HookEventPullRequestReviewComment, |
|
payload: &api.PullRequestPayload{Action: api.HookIssueReviewed}, |
|
yamlOn: "on:\n pull_request_review_comment:\n types: [created]", |
|
expected: true, |
|
}, |
|
{ |
|
desc: "HookEventPullRequestReviewRejected(pull_request_review_rejected) doesn't match GithubEventPullRequestReview(pull_request_review) with `dismissed` activity type (we don't support `dismissed` at present)", |
|
triggedEvent: webhook_module.HookEventPullRequestReviewRejected, |
|
payload: &api.PullRequestPayload{Action: api.HookIssueReviewed}, |
|
yamlOn: "on:\n pull_request_review:\n types: [dismissed]", |
|
expected: false, |
|
}, |
|
{ |
|
desc: "HookEventRelease(release) `published` action matches GithubEventRelease(release) with `published` activity type", |
|
triggedEvent: webhook_module.HookEventRelease, |
|
payload: &api.ReleasePayload{Action: api.HookReleasePublished}, |
|
yamlOn: "on:\n release:\n types: [published]", |
|
expected: true, |
|
}, |
|
{ |
|
desc: "HookEventPackage(package) `created` action doesn't match GithubEventRegistryPackage(registry_package) with `updated` activity type", |
|
triggedEvent: webhook_module.HookEventPackage, |
|
payload: &api.PackagePayload{Action: api.HookPackageCreated}, |
|
yamlOn: "on:\n registry_package:\n types: [updated]", |
|
expected: false, |
|
}, |
|
{ |
|
desc: "HookEventWiki(wiki) matches GithubEventGollum(gollum)", |
|
triggedEvent: webhook_module.HookEventWiki, |
|
payload: nil, |
|
yamlOn: "on: gollum", |
|
expected: true, |
|
}, |
|
{ |
|
desc: "HookEventSchedue(schedule) matches GithubEventSchedule(schedule)", |
|
triggedEvent: webhook_module.HookEventSchedule, |
|
payload: nil, |
|
yamlOn: "on: schedule", |
|
expected: true, |
|
}, |
|
{ |
|
desc: "push to tag matches workflow with paths condition (should skip paths check)", |
|
triggedEvent: webhook_module.HookEventPush, |
|
payload: &api.PushPayload{ |
|
Ref: "refs/tags/v1.0.0", |
|
Before: "0000000", |
|
Commits: []*api.PayloadCommit{ |
|
{ |
|
ID: "abcdef123456", |
|
Added: []string{"src/main.go"}, |
|
Message: "Release v1.0.0", |
|
}, |
|
}, |
|
}, |
|
commit: nil, |
|
yamlOn: "on:\n push:\n paths:\n - src/**", |
|
expected: true, |
|
}, |
|
} |
|
|
|
for _, tc := range testCases { |
|
t.Run(tc.desc, func(t *testing.T) { |
|
evts, err := GetEventsFromContent([]byte(tc.yamlOn)) |
|
assert.NoError(t, err) |
|
assert.Len(t, evts, 1) |
|
assert.Equal(t, tc.expected, detectMatched(nil, tc.commit, tc.triggedEvent, tc.payload, evts[0])) |
|
}) |
|
} |
|
} |
|
|
|
func TestMatchIssuesEvent(t *testing.T) { |
|
testCases := []struct { |
|
desc string |
|
payload *api.IssuePayload |
|
yamlOn string |
|
expected bool |
|
eventType string |
|
}{ |
|
{ |
|
desc: "Label deletion should trigger unlabeled event", |
|
payload: &api.IssuePayload{ |
|
Action: api.HookIssueLabelUpdated, |
|
Issue: &api.Issue{ |
|
Labels: []*api.Label{}, |
|
}, |
|
Changes: &api.ChangesPayload{ |
|
RemovedLabels: []*api.Label{ |
|
{ID: 123, Name: "deleted-label"}, |
|
}, |
|
}, |
|
}, |
|
yamlOn: "on:\n issues:\n types: [unlabeled]", |
|
expected: true, |
|
eventType: "unlabeled", |
|
}, |
|
{ |
|
desc: "Label deletion with existing labels should trigger unlabeled event", |
|
payload: &api.IssuePayload{ |
|
Action: api.HookIssueLabelUpdated, |
|
Issue: &api.Issue{ |
|
Labels: []*api.Label{ |
|
{ID: 456, Name: "existing-label"}, |
|
}, |
|
}, |
|
Changes: &api.ChangesPayload{ |
|
AddedLabels: nil, |
|
RemovedLabels: []*api.Label{ |
|
{ID: 123, Name: "deleted-label"}, |
|
}, |
|
}, |
|
}, |
|
yamlOn: "on:\n issues:\n types: [unlabeled]", |
|
expected: true, |
|
eventType: "unlabeled", |
|
}, |
|
{ |
|
desc: "Label addition should trigger labeled event", |
|
payload: &api.IssuePayload{ |
|
Action: api.HookIssueLabelUpdated, |
|
Issue: &api.Issue{ |
|
Labels: []*api.Label{ |
|
{ID: 123, Name: "new-label"}, |
|
}, |
|
}, |
|
Changes: &api.ChangesPayload{ |
|
AddedLabels: []*api.Label{ |
|
{ID: 123, Name: "new-label"}, |
|
}, |
|
RemovedLabels: []*api.Label{}, // Empty array, no labels removed |
|
}, |
|
}, |
|
yamlOn: "on:\n issues:\n types: [labeled]", |
|
expected: true, |
|
eventType: "labeled", |
|
}, |
|
{ |
|
desc: "Label clear should trigger unlabeled event", |
|
payload: &api.IssuePayload{ |
|
Action: api.HookIssueLabelCleared, |
|
Issue: &api.Issue{ |
|
Labels: []*api.Label{}, |
|
}, |
|
}, |
|
yamlOn: "on:\n issues:\n types: [unlabeled]", |
|
expected: true, |
|
eventType: "unlabeled", |
|
}, |
|
{ |
|
desc: "Both adding and removing labels should trigger labeled event", |
|
payload: &api.IssuePayload{ |
|
Action: api.HookIssueLabelUpdated, |
|
Issue: &api.Issue{ |
|
Labels: []*api.Label{ |
|
{ID: 789, Name: "new-label"}, |
|
}, |
|
}, |
|
Changes: &api.ChangesPayload{ |
|
AddedLabels: []*api.Label{ |
|
{ID: 789, Name: "new-label"}, |
|
}, |
|
RemovedLabels: []*api.Label{ |
|
{ID: 123, Name: "deleted-label"}, |
|
}, |
|
}, |
|
}, |
|
yamlOn: "on:\n issues:\n types: [labeled]", |
|
expected: true, |
|
eventType: "labeled", |
|
}, |
|
{ |
|
desc: "Both adding and removing labels should trigger unlabeled event", |
|
payload: &api.IssuePayload{ |
|
Action: api.HookIssueLabelUpdated, |
|
Issue: &api.Issue{ |
|
Labels: []*api.Label{ |
|
{ID: 789, Name: "new-label"}, |
|
}, |
|
}, |
|
Changes: &api.ChangesPayload{ |
|
AddedLabels: []*api.Label{ |
|
{ID: 789, Name: "new-label"}, |
|
}, |
|
RemovedLabels: []*api.Label{ |
|
{ID: 123, Name: "deleted-label"}, |
|
}, |
|
}, |
|
}, |
|
yamlOn: "on:\n issues:\n types: [unlabeled]", |
|
expected: true, |
|
eventType: "unlabeled", |
|
}, |
|
{ |
|
desc: "Both adding and removing labels should trigger both events", |
|
payload: &api.IssuePayload{ |
|
Action: api.HookIssueLabelUpdated, |
|
Issue: &api.Issue{ |
|
Labels: []*api.Label{ |
|
{ID: 789, Name: "new-label"}, |
|
}, |
|
}, |
|
Changes: &api.ChangesPayload{ |
|
AddedLabels: []*api.Label{ |
|
{ID: 789, Name: "new-label"}, |
|
}, |
|
RemovedLabels: []*api.Label{ |
|
{ID: 123, Name: "deleted-label"}, |
|
}, |
|
}, |
|
}, |
|
yamlOn: "on:\n issues:\n types: [labeled, unlabeled]", |
|
expected: true, |
|
eventType: "multiple", |
|
}, |
|
} |
|
|
|
for _, tc := range testCases { |
|
t.Run(tc.desc, func(t *testing.T) { |
|
evts, err := GetEventsFromContent([]byte(tc.yamlOn)) |
|
assert.NoError(t, err) |
|
assert.Len(t, evts, 1) |
|
|
|
// Test if the event matches as expected |
|
assert.Equal(t, tc.expected, matchIssuesEvent(tc.payload, evts[0])) |
|
|
|
// For extra validation, check that action mapping works correctly |
|
if tc.eventType == "multiple" { |
|
// Skip direct action mapping validation for multiple events case |
|
// as one action can map to multiple event types |
|
return |
|
} |
|
|
|
// Determine expected action for single event case |
|
var expectedAction string |
|
switch tc.payload.Action { |
|
case api.HookIssueLabelUpdated: |
|
if tc.eventType == "labeled" { |
|
expectedAction = "labeled" |
|
} else if tc.eventType == "unlabeled" { |
|
expectedAction = "unlabeled" |
|
} |
|
case api.HookIssueLabelCleared: |
|
expectedAction = "unlabeled" |
|
default: |
|
expectedAction = string(tc.payload.Action) |
|
} |
|
|
|
assert.Equal(t, expectedAction, tc.eventType, "Event type should match expected") |
|
}) |
|
} |
|
}
|
|
|