Files
gitea/tests/integration/repo_webhook_test.go
wxiaoguang 9dc04289aa refactor: make git package handle all git operations (#38543)
before: gitrepo vs git packages
after: git package fully handle all git operations

by the way, use `WithRepo(repo)` instead of `WithDir(repo.Path)` to hide
path details.

benefits:

1. remove all unnecessary wrappers, developers no need to struggle with
"which package should be used"
2. simplify code, RepositoryFacade can (will) be used everywhere, all
"path" details are (will be) hidden
2026-07-20 16:07:38 +00:00

1851 lines
67 KiB
Go

// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package integration
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/url"
"path"
"strconv"
"strings"
"testing"
"time"
runnerv1 "gitea.dev/actions-proto-go/runner/v1"
actions_model "gitea.dev/models/actions"
auth_model "gitea.dev/models/auth"
db_model "gitea.dev/models/db"
"gitea.dev/models/perm"
"gitea.dev/models/repo"
"gitea.dev/models/unittest"
user_model "gitea.dev/models/user"
"gitea.dev/models/webhook"
"gitea.dev/modules/commitstatus"
"gitea.dev/modules/git"
"gitea.dev/modules/json"
"gitea.dev/modules/setting"
api "gitea.dev/modules/structs"
"gitea.dev/modules/test"
webhook_module "gitea.dev/modules/webhook"
"gitea.dev/services/actions"
"gitea.dev/tests"
"github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewWebHookLink(t *testing.T) {
defer tests.PrepareTestEnv(t)()
require.NoError(t, db_model.Insert(t.Context(), &webhook.Webhook{
RepoID: 1,
URL: "http://localhost/gitea-test-webhook-link",
ContentType: webhook.ContentTypeJSON,
Events: `{}`,
IsActive: true,
}))
hook := unittest.AssertExistsAndLoadBean(t, &webhook.Webhook{RepoID: 1})
session := loginUser(t, "user2")
webhooksBaseHref := "/user2/repo1/settings/hooks"
cases := []string{
webhooksBaseHref,
webhooksBaseHref + "/gitea/new",
webhooksBaseHref + "/" + strconv.FormatInt(hook.ID, 10), // edit webhook
}
for _, reqHref := range cases {
resp := session.MakeRequest(t, NewRequest(t, "GET", reqHref), http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
menus := htmlDoc.doc.Find(".ui.top.attached.header .ui.dropdown .menu a")
menus.Each(func(i int, menu *goquery.Selection) {
foundHref := menu.AttrOr("href", "")
assert.True(t, strings.HasPrefix(foundHref, webhooksBaseHref))
})
}
}
func testAPICreateWebhookForRepo(t *testing.T, session *TestSession, userName, repoName, url, event string, branchFilter ...string) {
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll)
var branchFilterString string
if len(branchFilter) > 0 {
branchFilterString = branchFilter[0]
}
req := NewRequestWithJSON(t, "POST", "/api/v1/repos/"+userName+"/"+repoName+"/hooks", api.CreateHookOption{
Type: "gitea",
Config: api.CreateHookOptionConfig{
"content_type": "json",
"url": url,
},
Events: []string{event},
Active: true,
BranchFilter: branchFilterString,
}).AddTokenAuth(token)
MakeRequest(t, req, http.StatusCreated)
}
func testCreateWebhookForRepo(t *testing.T, session *TestSession, webhookType, userName, repoName, url, eventKind string) {
req := NewRequestWithValues(t, "POST", "/"+userName+"/"+repoName+"/settings/hooks/"+webhookType+"/new", map[string]string{
"payload_url": url,
"events": eventKind,
"active": "true",
"content_type": fmt.Sprintf("%d", webhook.ContentTypeJSON),
"http_method": "POST",
})
session.MakeRequest(t, req, http.StatusSeeOther)
}
func testAPICreateWebhookForOrg(t *testing.T, session *TestSession, userName, url, event string) {
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll)
req := NewRequestWithJSON(t, "POST", "/api/v1/orgs/"+userName+"/hooks", api.CreateHookOption{
Type: "gitea",
Config: api.CreateHookOptionConfig{
"content_type": "json",
"url": url,
},
Events: []string{event},
Active: true,
}).AddTokenAuth(token)
MakeRequest(t, req, http.StatusCreated)
}
type mockWebhookProvider struct {
server *httptest.Server
}
func newMockWebhookProvider(callback func(r *http.Request), status int) *mockWebhookProvider {
m := &mockWebhookProvider{}
m.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
callback(r)
w.WriteHeader(status)
}))
return m
}
func (m *mockWebhookProvider) URL() string {
if m.server == nil {
return ""
}
return m.server.URL
}
// Close closes the mock webhook http server
func (m *mockWebhookProvider) Close() {
if m.server != nil {
m.server.Close()
m.server = nil
}
}
func Test_WebhookCreate(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.CreatePayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.CreatePayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = string(webhook_module.HookEventCreate)
}, http.StatusOK)
defer provider.Close()
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "create")
// 2. trigger the webhook
testAPICreateBranch(t, session, "user2", "repo1", "master", "master2", http.StatusCreated)
// 3. validate the webhook is triggered
assert.Len(t, payloads, 1)
assert.Equal(t, string(webhook_module.HookEventCreate), triggeredEvent)
assert.Equal(t, "repo1", payloads[0].Repo.Name)
assert.Equal(t, "user2/repo1", payloads[0].Repo.FullName)
assert.Equal(t, "master2", payloads[0].Ref)
assert.Equal(t, "branch", payloads[0].RefType)
})
}
func Test_WebhookDelete(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.DeletePayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.DeletePayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "delete"
}, http.StatusOK)
defer provider.Close()
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "delete")
// 2. trigger the webhook
testAPICreateBranch(t, session, "user2", "repo1", "master", "master2", http.StatusCreated)
testAPIDeleteBranch(t, "master2", http.StatusNoContent)
// 3. validate the webhook is triggered
assert.Equal(t, "delete", triggeredEvent)
assert.Len(t, payloads, 1)
assert.Equal(t, "repo1", payloads[0].Repo.Name)
assert.Equal(t, "user2/repo1", payloads[0].Repo.FullName)
assert.Equal(t, "master2", payloads[0].Ref)
assert.Equal(t, "branch", payloads[0].RefType)
})
}
func Test_WebhookFork(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.ForkPayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.ForkPayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "fork"
}, http.StatusOK)
defer provider.Close()
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user1")
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "fork")
// 2. trigger the webhook
testRepoFork(t, session, "user2", "repo1", "user1", "repo1-fork", "master")
// 3. validate the webhook is triggered
assert.Equal(t, "fork", triggeredEvent)
assert.Len(t, payloads, 1)
assert.Equal(t, "repo1-fork", payloads[0].Repo.Name)
assert.Equal(t, "user1/repo1-fork", payloads[0].Repo.FullName)
assert.Equal(t, "repo1", payloads[0].Forkee.Name)
assert.Equal(t, "user2/repo1", payloads[0].Forkee.FullName)
})
}
func Test_WebhookIssueComment(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.IssueCommentPayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.IssueCommentPayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "issue_comment"
}, http.StatusOK)
defer provider.Close()
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "issue_comment")
t.Run("create comment", func(t *testing.T) {
// 2. trigger the webhook
issueURL := testNewIssue(t, session, "user2", "repo1", "Title2", "Description2")
testIssueAddComment(t, session, issueURL, "issue title2 comment1", "")
// 3. validate the webhook is triggered
assert.Equal(t, "issue_comment", triggeredEvent)
assert.Len(t, payloads, 1)
assert.EqualValues(t, "created", payloads[0].Action)
assert.Equal(t, "repo1", payloads[0].Issue.Repo.Name)
assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName)
assert.Equal(t, "Title2", payloads[0].Issue.Title)
assert.Equal(t, "Description2", payloads[0].Issue.Body)
assert.Equal(t, "issue title2 comment1", payloads[0].Comment.Body)
})
t.Run("update comment", func(t *testing.T) {
payloads = make([]api.IssueCommentPayload, 0, 2)
triggeredEvent = ""
// 2. trigger the webhook
issueURL := testNewIssue(t, session, "user2", "repo1", "Title3", "Description3")
commentID := testIssueAddComment(t, session, issueURL, "issue title3 comment1", "")
modifiedContent := "issue title2 comment1 - modified"
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/comments/%d", "user2", "repo1", commentID), map[string]string{
"content": modifiedContent,
})
session.MakeRequest(t, req, http.StatusOK)
// 3. validate the webhook is triggered
assert.Equal(t, "issue_comment", triggeredEvent)
assert.Len(t, payloads, 2)
assert.EqualValues(t, "edited", payloads[1].Action)
assert.Equal(t, "repo1", payloads[1].Issue.Repo.Name)
assert.Equal(t, "user2/repo1", payloads[1].Issue.Repo.FullName)
assert.Equal(t, "Title3", payloads[1].Issue.Title)
assert.Equal(t, "Description3", payloads[1].Issue.Body)
assert.Equal(t, modifiedContent, payloads[1].Comment.Body)
})
t.Run("Update comment with no content change", func(t *testing.T) {
payloads = make([]api.IssueCommentPayload, 0, 2)
triggeredEvent = ""
commentContent := "issue title3 comment1"
// 2. trigger the webhook
issueURL := testNewIssue(t, session, "user2", "repo1", "Title3", "Description3")
commentID := testIssueAddComment(t, session, issueURL, commentContent, "")
payloads = make([]api.IssueCommentPayload, 0, 2)
triggeredEvent = ""
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/comments/%d", "user2", "repo1", commentID), map[string]string{
"content": commentContent,
})
session.MakeRequest(t, req, http.StatusOK)
// 3. validate the webhook is not triggered because no content change
assert.Empty(t, triggeredEvent)
assert.Empty(t, payloads)
})
})
}
func Test_WebhookRelease(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.ReleasePayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.ReleasePayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "release"
}, http.StatusOK)
defer provider.Close()
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "release")
// 2. trigger the webhook
createNewRelease(t, session, "/user2/repo1", "v0.0.99", "v0.0.99", false, false)
// 3. validate the webhook is triggered
assert.Equal(t, "release", triggeredEvent)
assert.Len(t, payloads, 1)
assert.Equal(t, "repo1", payloads[0].Repository.Name)
assert.Equal(t, "user2/repo1", payloads[0].Repository.FullName)
assert.Equal(t, "v0.0.99", payloads[0].Release.TagName)
assert.False(t, payloads[0].Release.IsDraft)
assert.False(t, payloads[0].Release.IsPrerelease)
})
}
func Test_WebhookPush(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.PushPayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.PushPayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "push"
}, http.StatusOK)
defer provider.Close()
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "push")
// 2. trigger the webhook
testCreateFile(t, session, "user2", "repo1", "master", "", "test_webhook_push.md", "# a test file for webhook push")
// 3. validate the webhook is triggered
assert.Equal(t, "push", triggeredEvent)
assert.Len(t, payloads, 1)
assert.Equal(t, "repo1", payloads[0].Repo.Name)
assert.Equal(t, "user2/repo1", payloads[0].Repo.FullName)
assert.Len(t, payloads[0].Commits, 1)
assert.Equal(t, []string{"test_webhook_push.md"}, payloads[0].Commits[0].Added)
})
}
func Test_WebhookPushDevBranch(t *testing.T) {
var payloads []api.PushPayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.PushPayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "push"
}, http.StatusOK)
defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
// only for dev branch
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "push", "develop")
// 2. this should not trigger the webhook
testCreateFile(t, session, "user2", "repo1", "master", "", "test_webhook_push.md", "# a test file for webhook push")
assert.Empty(t, triggeredEvent)
assert.Empty(t, payloads)
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
gitRepo, err := git.OpenRepository(repo1)
assert.NoError(t, err)
defer gitRepo.Close()
beforeCommitID, err := gitRepo.GetBranchCommitID(t.Context(), "develop")
assert.NoError(t, err)
// 3. trigger the webhook
testCreateFile(t, session, "user2", "repo1", "develop", "", "test_webhook_push.md", "# a test file for webhook push")
afterCommitID, err := gitRepo.GetBranchCommitID(t.Context(), "develop")
assert.NoError(t, err)
// 4. validate the webhook is triggered
assert.Equal(t, "push", triggeredEvent)
assert.Len(t, payloads, 1)
assert.Equal(t, "refs/heads/develop", payloads[0].Ref)
assert.Equal(t, beforeCommitID, payloads[0].Before)
assert.Equal(t, afterCommitID, payloads[0].After)
assert.Equal(t, "repo1", payloads[0].Repo.Name)
assert.Equal(t, "develop", payloads[0].Branch())
assert.Equal(t, "user2/repo1", payloads[0].Repo.FullName)
assert.Len(t, payloads[0].Commits, 1)
assert.Equal(t, afterCommitID, payloads[0].Commits[0].ID)
assert.Equal(t, setting.AppURL+"user2/repo1/compare/"+beforeCommitID+"..."+afterCommitID, payloads[0].CompareURL)
assert.Equal(t, []string{"test_webhook_push.md"}, payloads[0].Commits[0].Added)
assert.Empty(t, payloads[0].Commits[0].Removed)
})
}
func Test_WebhookPushToNewBranch(t *testing.T) {
var payloads []api.PushPayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.PushPayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "push"
}, http.StatusOK)
defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
// only for dev branch
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "push", "new_branch")
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
gitRepo, err := git.OpenRepository(repo1)
assert.NoError(t, err)
defer gitRepo.Close()
beforeCommitID, err := gitRepo.GetBranchCommitID(t.Context(), "master")
assert.NoError(t, err)
// 2. trigger the webhook
testCreateFile(t, session, "user2", "repo1", "master", "new_branch", "test_webhook_push.md", "# a new push from new branch")
afterCommitID, err := gitRepo.GetBranchCommitID(t.Context(), "new_branch")
assert.NoError(t, err)
emptyCommitID := git.Sha1ObjectFormat.EmptyObjectID().String()
// 4. validate the webhook is triggered
assert.Equal(t, "push", triggeredEvent)
assert.Len(t, payloads, 1)
assert.Equal(t, "refs/heads/new_branch", payloads[0].Ref)
assert.Equal(t, emptyCommitID, payloads[0].Before)
assert.Equal(t, afterCommitID, payloads[0].After)
assert.Equal(t, "repo1", payloads[0].Repo.Name)
assert.Equal(t, "new_branch", payloads[0].Branch())
assert.Equal(t, "user2/repo1", payloads[0].Repo.FullName)
assert.Len(t, payloads[0].Commits, 1)
assert.Equal(t, afterCommitID, payloads[0].Commits[0].ID)
assert.Equal(t, setting.AppURL+"user2/repo1/compare/"+beforeCommitID+"..."+afterCommitID, payloads[0].CompareURL)
assert.Equal(t, []string{"test_webhook_push.md"}, payloads[0].Commits[0].Added)
assert.Empty(t, payloads[0].Commits[0].Removed)
})
}
func Test_WebhookIssue(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.IssuePayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.IssuePayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "issues"
}, http.StatusOK)
defer provider.Close()
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "issues")
// 2. trigger the webhook
testNewIssue(t, session, "user2", "repo1", "Title1", "Description1")
// 3. validate the webhook is triggered
assert.Equal(t, "issues", triggeredEvent)
assert.Len(t, payloads, 1)
assert.EqualValues(t, "opened", payloads[0].Action)
assert.Equal(t, "repo1", payloads[0].Issue.Repo.Name)
assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName)
assert.Equal(t, "Title1", payloads[0].Issue.Title)
assert.Equal(t, "Description1", payloads[0].Issue.Body)
assert.Positive(t, payloads[0].Issue.Created.Unix())
assert.Positive(t, payloads[0].Issue.Updated.Unix())
})
}
func Test_WebhookIssueDelete(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.IssuePayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.IssuePayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "issue"
}, http.StatusOK)
defer provider.Close()
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "issues")
issueURL := testNewIssue(t, session, "user2", "repo1", "Title1", "Description1")
// 2. trigger the webhook
testIssueDelete(t, session, issueURL)
// 3. validate the webhook is triggered
assert.Equal(t, "issue", triggeredEvent)
require.Len(t, payloads, 2)
assert.EqualValues(t, "deleted", payloads[1].Action)
assert.Equal(t, "repo1", payloads[1].Issue.Repo.Name)
assert.Equal(t, "user2/repo1", payloads[1].Issue.Repo.FullName)
assert.Equal(t, "Title1", payloads[1].Issue.Title)
assert.Equal(t, "Description1", payloads[1].Issue.Body)
})
}
func Test_WebhookIssueAssign(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.PullRequestPayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.PullRequestPayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "pull_request_assign"
}, http.StatusOK)
defer provider.Close()
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "pull_request_assign")
// 2. trigger the webhook, issue 2 is a pull request
testIssueAssign(t, session, repo1.Link(), 2, user2.ID)
// 3. validate the webhook is triggered
assert.Equal(t, "pull_request_assign", triggeredEvent)
assert.Len(t, payloads, 1)
assert.EqualValues(t, "assigned", payloads[0].Action)
assert.Equal(t, "repo1", payloads[0].PullRequest.Base.Repository.Name)
assert.Equal(t, "user2/repo1", payloads[0].PullRequest.Base.Repository.FullName)
assert.Equal(t, "issue2", payloads[0].PullRequest.Title)
assert.Equal(t, "content for the second issue", payloads[0].PullRequest.Body)
assert.Equal(t, user2.ID, payloads[0].PullRequest.Assignee.ID)
})
}
func Test_WebhookIssueMilestone(t *testing.T) {
var payloads []api.IssuePayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.IssuePayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "issues"
}, http.StatusOK)
defer provider.Close()
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "issue_milestone")
t.Run("assign a milestone", func(t *testing.T) {
// trigger the webhook
testIssueChangeMilestone(t, session, repo1.Link(), 1, 1)
// validate the webhook is triggered
assert.Equal(t, "issues", triggeredEvent)
assert.Len(t, payloads, 1)
assert.Equal(t, "milestoned", string(payloads[0].Action))
assert.Equal(t, "repo1", payloads[0].Issue.Repo.Name)
assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName)
assert.Equal(t, "issue1", payloads[0].Issue.Title)
assert.Equal(t, "content for the first issue", payloads[0].Issue.Body)
assert.EqualValues(t, 1, payloads[0].Issue.Milestone.ID)
})
t.Run("change a milestong", func(t *testing.T) {
// trigger the webhook again
triggeredEvent = ""
payloads = make([]api.IssuePayload, 0, 1)
// change milestone to 2
testIssueChangeMilestone(t, session, repo1.Link(), 1, 2)
// validate the webhook is triggered
assert.Equal(t, "issues", triggeredEvent)
assert.Len(t, payloads, 1)
assert.Equal(t, "milestoned", string(payloads[0].Action))
assert.Equal(t, "repo1", payloads[0].Issue.Repo.Name)
assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName)
assert.Equal(t, "issue1", payloads[0].Issue.Title)
assert.Equal(t, "content for the first issue", payloads[0].Issue.Body)
assert.EqualValues(t, 2, payloads[0].Issue.Milestone.ID)
})
t.Run("remove a milestone", func(t *testing.T) {
// trigger the webhook again
triggeredEvent = ""
payloads = make([]api.IssuePayload, 0, 1)
// change milestone to 0
testIssueChangeMilestone(t, session, repo1.Link(), 1, 0)
// validate the webhook is triggered
assert.Equal(t, "issues", triggeredEvent)
assert.Len(t, payloads, 1)
assert.Equal(t, "demilestoned", string(payloads[0].Action))
assert.Equal(t, "repo1", payloads[0].Issue.Repo.Name)
assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName)
assert.Equal(t, "issue1", payloads[0].Issue.Title)
assert.Equal(t, "content for the first issue", payloads[0].Issue.Body)
assert.Nil(t, payloads[0].Issue.Milestone)
})
})
}
func Test_WebhookPullRequest(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.PullRequestPayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.PullRequestPayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "pull_request"
}, http.StatusOK)
defer provider.Close()
testCtx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeAll)
// add user4 as collabrator so that it can be a reviewer
doAPIAddCollaborator(testCtx, "user4", perm.AccessModeWrite)(t)
// 1. create a new webhook with special webhook for repo1
sessionUser2 := loginUser(t, "user2")
sessionUser4 := loginUser(t, "user4")
// ignore the possible review_requested event to keep the test deterministic
testAPICreateWebhookForRepo(t, sessionUser2, "user2", "repo1", provider.URL(), "pull_request_only")
testAPICreateBranch(t, sessionUser2, "user2", "repo1", "master", "master2", http.StatusCreated)
// 2. trigger the webhook
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
testPullCreateDirectly(t, sessionUser4, createPullRequestOptions{
BaseRepoOwner: repo1.OwnerName,
BaseRepoName: repo1.Name,
BaseBranch: repo1.DefaultBranch,
HeadRepoOwner: "",
HeadRepoName: "",
HeadBranch: "master2",
Title: "first pull request",
ReviewerIDs: "2", // add user2 as reviewer
})
// 3. validate the webhook is triggered
assert.Equal(t, "pull_request", triggeredEvent)
require.Len(t, payloads, 1)
assert.Equal(t, "repo1", payloads[0].PullRequest.Base.Repository.Name)
assert.Equal(t, "user2/repo1", payloads[0].PullRequest.Base.Repository.FullName)
assert.Equal(t, "repo1", payloads[0].PullRequest.Head.Repository.Name)
assert.Equal(t, "user2/repo1", payloads[0].PullRequest.Head.Repository.FullName)
assert.Equal(t, 0, *payloads[0].PullRequest.Additions)
assert.Equal(t, 0, *payloads[0].PullRequest.ChangedFiles)
assert.Equal(t, 0, *payloads[0].PullRequest.Deletions)
assert.Len(t, payloads[0].PullRequest.RequestedReviewers, 1)
assert.Equal(t, int64(2), payloads[0].PullRequest.RequestedReviewers[0].ID)
})
}
func Test_WebhookPullRequestDelete(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.PullRequestPayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.PullRequestPayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "pull_request"
}, http.StatusOK)
defer provider.Close()
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "pull_request")
testAPICreateBranch(t, session, "user2", "repo1", "master", "master2", http.StatusCreated)
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
issueURL := testCreatePullToDefaultBranch(t, session, repo1, repo1, "master2", "first pull request")
// 2. trigger the webhook
testIssueDelete(t, session, path.Join(repo1.Link(), "pulls", issueURL))
// 3. validate the webhook is triggered
assert.Equal(t, "pull_request", triggeredEvent)
require.Len(t, payloads, 2)
assert.EqualValues(t, "deleted", payloads[1].Action)
assert.Equal(t, "repo1", payloads[1].PullRequest.Base.Repository.Name)
assert.Equal(t, "user2/repo1", payloads[1].PullRequest.Base.Repository.FullName)
assert.Equal(t, 0, *payloads[1].PullRequest.Additions)
assert.Equal(t, 0, *payloads[1].PullRequest.ChangedFiles)
assert.Equal(t, 0, *payloads[1].PullRequest.Deletions)
})
}
func Test_WebhookPullRequestComment(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.IssueCommentPayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.IssueCommentPayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "pull_request_comment"
}, http.StatusOK)
defer provider.Close()
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "pull_request_comment")
// 2. trigger the webhook
testAPICreateBranch(t, session, "user2", "repo1", "master", "master2", http.StatusCreated)
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
prID := testCreatePullToDefaultBranch(t, session, repo1, repo1, "master2", "first pull request")
testIssueAddComment(t, session, "/user2/repo1/pulls/"+prID, "pull title2 comment1", "")
// 3. validate the webhook is triggered
assert.Equal(t, "pull_request_comment", triggeredEvent)
assert.Len(t, payloads, 1)
assert.EqualValues(t, "created", payloads[0].Action)
assert.Equal(t, "repo1", payloads[0].Issue.Repo.Name)
assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName)
assert.Equal(t, "first pull request", payloads[0].Issue.Title)
assert.Empty(t, payloads[0].Issue.Body)
assert.Equal(t, "pull title2 comment1", payloads[0].Comment.Body)
})
}
func Test_WebhookWiki(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.WikiPayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.WikiPayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "wiki"
}, http.StatusOK)
defer provider.Close()
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "wiki")
// 2. trigger the webhook
testAPICreateWikiPage(t, session, "user2", "repo1", "Test Wiki Page", http.StatusCreated)
// 3. validate the webhook is triggered
assert.Equal(t, "wiki", triggeredEvent)
assert.Len(t, payloads, 1)
assert.EqualValues(t, "created", payloads[0].Action)
assert.Equal(t, "repo1", payloads[0].Repository.Name)
assert.Equal(t, "user2/repo1", payloads[0].Repository.FullName)
assert.Equal(t, "Test-Wiki-Page", payloads[0].Page)
})
}
func Test_WebhookRepository(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.RepositoryPayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.RepositoryPayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "repository"
}, http.StatusOK)
defer provider.Close()
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user1")
testAPICreateWebhookForOrg(t, session, "org3", provider.URL(), "repository")
// 2. trigger the webhook
testAPIOrgCreateRepo(t, session, "org3", "repo_new", http.StatusCreated)
// 3. validate the webhook is triggered
assert.Equal(t, "repository", triggeredEvent)
assert.Len(t, payloads, 1)
assert.EqualValues(t, "created", payloads[0].Action)
assert.Equal(t, "org3", payloads[0].Organization.UserName)
assert.Equal(t, "repo_new", payloads[0].Repository.Name)
assert.Equal(t, "org3/repo_new", payloads[0].Repository.FullName)
})
}
func Test_WebhookPackage(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.PackagePayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
content, _ := io.ReadAll(r.Body)
var payload api.PackagePayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "package"
}, http.StatusOK)
defer provider.Close()
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user1")
testAPICreateWebhookForOrg(t, session, "org3", provider.URL(), "package")
// 2. trigger the webhook
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll)
url := fmt.Sprintf("/api/packages/%s/generic/%s/%s", "org3", "gitea", "v1.24.0")
req := NewRequestWithBody(t, "PUT", url+"/gitea", strings.NewReader("This is a dummy file")).
AddTokenAuth(token)
MakeRequest(t, req, http.StatusCreated)
// 3. validate the webhook is triggered
assert.Equal(t, "package", triggeredEvent)
assert.Len(t, payloads, 1)
assert.EqualValues(t, "created", payloads[0].Action)
assert.Equal(t, "gitea", payloads[0].Package.Name)
assert.Equal(t, "generic", payloads[0].Package.Type)
assert.Equal(t, "org3", payloads[0].Organization.UserName)
assert.Equal(t, "v1.24.0", payloads[0].Package.Version)
})
}
func Test_WebhookStatus(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.CommitStatusPayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
assert.Contains(t, r.Header["X-Github-Event-Type"], "status", "X-GitHub-Event-Type should contain status")
assert.Contains(t, r.Header["X-Github-Hook-Installation-Target-Type"], "repository", "X-GitHub-Hook-Installation-Target-Type should contain repository")
assert.Contains(t, r.Header["X-Gitea-Event-Type"], "status", "X-Gitea-Event-Type should contain status")
assert.Contains(t, r.Header["X-Gitea-Hook-Installation-Target-Type"], "repository", "X-Gitea-Hook-Installation-Target-Type should contain repository")
assert.Contains(t, r.Header["X-Gogs-Event-Type"], "status", "X-Gogs-Event-Type should contain status")
content, _ := io.ReadAll(r.Body)
var payload api.CommitStatusPayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "status"
}, http.StatusOK)
defer provider.Close()
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "status")
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
gitRepo1, err := git.OpenRepository(repo1)
assert.NoError(t, err)
commitID, err := gitRepo1.GetBranchCommitID(t.Context(), repo1.DefaultBranch)
assert.NoError(t, err)
// 2. trigger the webhook
testCtx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeAll)
// update a status for a commit via API
doAPICreateCommitStatusTest(testCtx, commitID, commitstatus.CommitStatusSuccess, "testci")(t)
// 3. validate the webhook is triggered
assert.Equal(t, "status", triggeredEvent)
assert.Len(t, payloads, 1)
assert.Equal(t, commitID, payloads[0].Commit.ID)
assert.Equal(t, "repo1", payloads[0].Repo.Name)
assert.Equal(t, "user2/repo1", payloads[0].Repo.FullName)
assert.Equal(t, "testci", payloads[0].Context)
assert.Equal(t, commitID, payloads[0].SHA)
})
}
func Test_WebhookStatus_NoWrongTrigger(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var trigger string
provider := newMockWebhookProvider(func(r *http.Request) {
assert.NotContains(t, r.Header["X-Github-Event-Type"], "status", "X-GitHub-Event-Type should not contain status")
assert.NotContains(t, r.Header["X-Gitea-Event-Type"], "status", "X-Gitea-Event-Type should not contain status")
assert.NotContains(t, r.Header["X-Gogs-Event-Type"], "status", "X-Gogs-Event-Type should not contain status")
trigger = "push"
}, http.StatusOK)
defer provider.Close()
// 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2")
// create a push_only webhook from web UI
testCreateWebhookForRepo(t, session, "gitea", "user2", "repo1", provider.URL(), "push_only")
// 2. trigger the webhook with a push action
testCreateFile(t, session, "user2", "repo1", "master", "", "test_webhook_push.md", "# a test file for webhook push")
// 3. validate the webhook is triggered with right event
assert.Equal(t, "push", trigger)
})
}
func Test_WebhookWorkflowJob(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
var payloads []api.WorkflowJobPayload
var triggeredEvent string
provider := newMockWebhookProvider(func(r *http.Request) {
assert.Contains(t, r.Header["X-Github-Event-Type"], "workflow_job", "X-GitHub-Event-Type should contain workflow_job")
assert.Contains(t, r.Header["X-Gitea-Event-Type"], "workflow_job", "X-Gitea-Event-Type should contain workflow_job")
assert.Contains(t, r.Header["X-Gogs-Event-Type"], "workflow_job", "X-Gogs-Event-Type should contain workflow_job")
content, _ := io.ReadAll(r.Body)
var payload api.WorkflowJobPayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
payloads = append(payloads, payload)
triggeredEvent = "workflow_job"
}, http.StatusOK)
defer provider.Close()
// 1. create a new webhook with special webhook for repo1
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session := loginUser(t, "user2")
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "workflow_job")
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
gitRepo1, err := git.OpenRepository(repo1)
assert.NoError(t, err)
runner := newMockRunner()
runner.registerAsRepoRunner(t, "user2", "repo1", "mock-runner", []string{"ubuntu-latest"}, false)
// 2. trigger the webhooks
// add workflow file to the repo
// init the workflow
wfTreePath := ".gitea/workflows/push.yml"
wfFileContent := `name: Push
on: push
jobs:
wf1-job:
runs-on: ubuntu-latest
steps:
- run: echo 'test the webhook'
wf2-job:
runs-on: ubuntu-latest
needs: wf1-job
steps:
- run: echo 'cmd 1'
- run: echo 'cmd 2'
`
opts := getWorkflowCreateFileOptions(user2, repo1.DefaultBranch, "create "+wfTreePath, wfFileContent)
createWorkflowFile(t, token, "user2", "repo1", wfTreePath, opts)
commitID, err := gitRepo1.GetBranchCommitID(t.Context(), repo1.DefaultBranch)
assert.NoError(t, err)
// 3. validate the webhook is triggered
assert.Equal(t, "workflow_job", triggeredEvent)
assert.Len(t, payloads, 2)
assert.Equal(t, "queued", payloads[0].Action)
assert.Equal(t, "queued", payloads[0].WorkflowJob.Status)
assert.Equal(t, []string{"ubuntu-latest"}, payloads[0].WorkflowJob.Labels)
assert.Equal(t, commitID, payloads[0].WorkflowJob.HeadSha)
assert.Equal(t, "repo1", payloads[0].Repo.Name)
assert.Equal(t, "user2/repo1", payloads[0].Repo.FullName)
assert.Equal(t, "waiting", payloads[1].Action)
assert.Equal(t, "waiting", payloads[1].WorkflowJob.Status)
assert.Equal(t, commitID, payloads[1].WorkflowJob.HeadSha)
assert.Equal(t, "repo1", payloads[1].Repo.Name)
assert.Equal(t, "user2/repo1", payloads[1].Repo.FullName)
// 4. Execute a single Job
task := runner.fetchTask(t)
outcome := &mockTaskOutcome{
result: runnerv1.Result_RESULT_SUCCESS,
}
runner.execTask(t, task, outcome)
// 5. validate the webhook is triggered
assert.Equal(t, "workflow_job", triggeredEvent)
assert.Len(t, payloads, 5)
assert.Equal(t, "in_progress", payloads[2].Action)
assert.Equal(t, "in_progress", payloads[2].WorkflowJob.Status)
assert.Equal(t, "mock-runner", payloads[2].WorkflowJob.RunnerName)
assert.Equal(t, commitID, payloads[2].WorkflowJob.HeadSha)
assert.Equal(t, "repo1", payloads[2].Repo.Name)
assert.Equal(t, "user2/repo1", payloads[2].Repo.FullName)
assert.Equal(t, "completed", payloads[3].Action)
assert.Equal(t, "completed", payloads[3].WorkflowJob.Status)
assert.Equal(t, "mock-runner", payloads[3].WorkflowJob.RunnerName)
assert.Equal(t, "success", payloads[3].WorkflowJob.Conclusion)
assert.Equal(t, commitID, payloads[3].WorkflowJob.HeadSha)
assert.Equal(t, "repo1", payloads[3].Repo.Name)
assert.Equal(t, "user2/repo1", payloads[3].Repo.FullName)
assert.Contains(t, payloads[3].WorkflowJob.URL, fmt.Sprintf("/actions/jobs/%d", payloads[3].WorkflowJob.ID))
assert.Contains(t, payloads[3].WorkflowJob.HTMLURL, fmt.Sprintf("/jobs/%d", payloads[3].WorkflowJob.ID))
assert.Len(t, payloads[3].WorkflowJob.Steps, 1)
assert.Equal(t, "queued", payloads[4].Action)
assert.Equal(t, "queued", payloads[4].WorkflowJob.Status)
assert.Equal(t, []string{"ubuntu-latest"}, payloads[4].WorkflowJob.Labels)
assert.Equal(t, commitID, payloads[4].WorkflowJob.HeadSha)
assert.Equal(t, "repo1", payloads[4].Repo.Name)
assert.Equal(t, "user2/repo1", payloads[4].Repo.FullName)
// 6. Execute a single Job
task = runner.fetchTask(t)
outcome = &mockTaskOutcome{
result: runnerv1.Result_RESULT_FAILURE,
}
runner.execTask(t, task, outcome)
// 7. validate the webhook is triggered
assert.Equal(t, "workflow_job", triggeredEvent)
assert.Len(t, payloads, 7)
assert.Equal(t, "in_progress", payloads[5].Action)
assert.Equal(t, "in_progress", payloads[5].WorkflowJob.Status)
assert.Equal(t, "mock-runner", payloads[5].WorkflowJob.RunnerName)
assert.Equal(t, commitID, payloads[5].WorkflowJob.HeadSha)
assert.Equal(t, "repo1", payloads[5].Repo.Name)
assert.Equal(t, "user2/repo1", payloads[5].Repo.FullName)
assert.Equal(t, "completed", payloads[6].Action)
assert.Equal(t, "completed", payloads[6].WorkflowJob.Status)
assert.Equal(t, "failure", payloads[6].WorkflowJob.Conclusion)
assert.Equal(t, "mock-runner", payloads[6].WorkflowJob.RunnerName)
assert.Equal(t, commitID, payloads[6].WorkflowJob.HeadSha)
assert.Equal(t, "repo1", payloads[6].Repo.Name)
assert.Equal(t, "user2/repo1", payloads[6].Repo.FullName)
assert.Contains(t, payloads[6].WorkflowJob.URL, fmt.Sprintf("/actions/jobs/%d", payloads[6].WorkflowJob.ID))
assert.Contains(t, payloads[6].WorkflowJob.HTMLURL, fmt.Sprintf("/jobs/%d", payloads[6].WorkflowJob.ID))
assert.Len(t, payloads[6].WorkflowJob.Steps, 2)
})
}
type workflowRunWebhook struct {
URL string
payloads []api.WorkflowRunPayload
triggeredEvent string
}
func Test_WebhookWorkflowRun(t *testing.T) {
testCases := []struct {
name string
testFunc func(t *testing.T, webhookData *workflowRunWebhook)
}{
{
name: "WorkflowRun",
testFunc: testWebhookWorkflowRun,
},
{
name: "WorkflowRunDepthLimit",
testFunc: testWebhookWorkflowRunDepthLimit,
},
{
name: "WorkflowRunEvents",
testFunc: testWorkflowRunEvents,
},
{
name: "WorkflowRunEventsOnRerun",
testFunc: testWorkflowRunEventsOnRerun,
},
{
name: "WorkflowRunEventsOnCancellingAbandonedRunAllJobsAbandoned",
testFunc: func(t *testing.T, webhookData *workflowRunWebhook) {
testWorkflowRunEventsOnCancellingAbandonedRun(t, webhookData, true)
},
},
{
name: "WorkflowRunEventsOnCancellingAbandonedRunPartiallyAbandoned",
testFunc: func(t *testing.T, webhookData *workflowRunWebhook) {
testWorkflowRunEventsOnCancellingAbandonedRun(t, webhookData, false)
},
},
{
name: "WorkflowRunOnStoppingEndlessTasksForMultipleRuns",
testFunc: testWorkflowRunOnStoppingEndlessTasksForMultipleRuns,
},
}
for _, obj := range testCases {
t.Run(obj.name, func(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
webhookData := &workflowRunWebhook{}
provider := newMockWebhookProvider(func(r *http.Request) {
assert.Contains(t, r.Header["X-Github-Event-Type"], "workflow_run", "X-GitHub-Event-Type should contain workflow_run")
assert.Contains(t, r.Header["X-Gitea-Event-Type"], "workflow_run", "X-Gitea-Event-Type should contain workflow_run")
assert.Contains(t, r.Header["X-Gogs-Event-Type"], "workflow_run", "X-Gogs-Event-Type should contain workflow_run")
content, _ := io.ReadAll(r.Body)
var payload api.WorkflowRunPayload
err := json.Unmarshal(content, &payload)
assert.NoError(t, err)
webhookData.payloads = append(webhookData.payloads, payload)
webhookData.triggeredEvent = "workflow_run"
}, http.StatusOK)
defer provider.Close()
webhookData.URL = provider.URL()
webhookData.payloads = nil
webhookData.triggeredEvent = ""
obj.testFunc(t, webhookData)
})
})
}
}
func testWorkflowRunEvents(t *testing.T, webhookData *workflowRunWebhook) {
// 1. create a new webhook with special webhook for repo1
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session := loginUser(t, "user2")
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
testAPICreateWebhookForRepo(t, session, "user2", "repo1", webhookData.URL, "workflow_run")
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
gitRepo1, err := git.OpenRepository(repo1)
assert.NoError(t, err)
// 2.2 trigger the webhooks
// add workflow file to the repo
// init the workflow
wfTreePath := ".gitea/workflows/push.yml"
wfFileContent := `on:
push:
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: exit 0
test2:
needs: [test]
runs-on: ubuntu-latest
steps:
- run: exit 0
test3:
needs: [test, test2]
runs-on: ubuntu-latest
steps:
- run: exit 0
test4:
needs: [test, test2, test3]
runs-on: ubuntu-latest
steps:
- run: exit 0
test5:
needs: [test, test2, test4]
runs-on: ubuntu-latest
steps:
- run: exit 0
test6:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04]
needs: [test, test2, test3]
runs-on: ${{ matrix.os }}
steps:
- run: exit 0
test7:
needs: test6
runs-on: ubuntu-latest
steps:
- run: exit 0
test8:
runs-on: ubuntu-latest
steps:
- run: exit 0
test9:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04, ubuntu-25.04, windows-2022, windows-2025, macos-13, macos-14, macos-15]
runs-on: ${{ matrix.os }}
steps:
- run: exit 0
test10:
runs-on: ubuntu-latest
steps:
- run: exit 0`
opts := getWorkflowCreateFileOptions(user2, repo1.DefaultBranch, "create "+wfTreePath, wfFileContent)
createWorkflowFile(t, token, "user2", "repo1", wfTreePath, opts)
commitID, err := gitRepo1.GetBranchCommitID(t.Context(), repo1.DefaultBranch)
assert.NoError(t, err)
// 3. validate the webhook is triggered
assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
assert.Len(t, webhookData.payloads, 1)
assert.Equal(t, "requested", webhookData.payloads[0].Action)
assert.Equal(t, "queued", webhookData.payloads[0].WorkflowRun.Status)
assert.Equal(t, repo1.DefaultBranch, webhookData.payloads[0].WorkflowRun.HeadBranch)
assert.Equal(t, commitID, webhookData.payloads[0].WorkflowRun.HeadSha)
assert.Equal(t, "repo1", webhookData.payloads[0].Repo.Name)
assert.Equal(t, "user2/repo1", webhookData.payloads[0].Repo.FullName)
// Call cancel ui api
// Only a web UI API exists for cancelling workflow runs, so use the UI endpoint.
cancelURL := fmt.Sprintf("/user2/repo1/actions/runs/%d/cancel", webhookData.payloads[0].WorkflowRun.ID)
req := NewRequest(t, "POST", cancelURL)
session.MakeRequest(t, req, http.StatusOK)
assert.Len(t, webhookData.payloads, 2)
// 4. Validate the second webhook payload
assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
assert.Equal(t, "completed", webhookData.payloads[1].Action)
assert.Equal(t, "push", webhookData.payloads[1].WorkflowRun.Event)
assert.Equal(t, "completed", webhookData.payloads[1].WorkflowRun.Status)
assert.Equal(t, repo1.DefaultBranch, webhookData.payloads[1].WorkflowRun.HeadBranch)
assert.Equal(t, commitID, webhookData.payloads[1].WorkflowRun.HeadSha)
assert.Equal(t, "repo1", webhookData.payloads[1].Repo.Name)
assert.Equal(t, "user2/repo1", webhookData.payloads[1].Repo.FullName)
}
func testWorkflowRunEventsOnRerun(t *testing.T, webhookData *workflowRunWebhook) {
// 1. create a new webhook with special webhook for repo1
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session := loginUser(t, "user2")
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
runners := make([]*mockRunner, 2)
for i := range runners {
runners[i] = newMockRunner()
runners[i].registerAsRepoRunner(t, "user2", "repo1", fmt.Sprintf("mock-runner-%d", i), []string{"ubuntu-latest"}, false)
}
testAPICreateWebhookForRepo(t, session, "user2", "repo1", webhookData.URL, "workflow_run")
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
gitRepo1, err := git.OpenRepository(repo1)
assert.NoError(t, err)
// 2.2 trigger the webhooks
// add workflow file to the repo
// init the workflow
wfTreePath := ".gitea/workflows/push.yml"
wfFileContent := `on:
push:
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: exit 0
test2:
needs: [test]
runs-on: ubuntu-latest
steps:
- run: exit 0
test3:
needs: [test, test2]
runs-on: ubuntu-latest
steps:
- run: exit 0
test4:
needs: [test, test2, test3]
runs-on: ubuntu-latest
steps:
- run: exit 0
test5:
needs: [test, test2, test4]
runs-on: ubuntu-latest
steps:
- run: exit 0
test6:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04]
needs: [test, test2, test3]
runs-on: ${{ matrix.os }}
steps:
- run: exit 0
test7:
needs: test6
runs-on: ubuntu-latest
steps:
- run: exit 0
test8:
runs-on: ubuntu-latest
steps:
- run: exit 0
test9:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04, ubuntu-25.04, windows-2022, windows-2025, macos-13, macos-14, macos-15]
runs-on: ${{ matrix.os }}
steps:
- run: exit 0
test10:
runs-on: ubuntu-latest
steps:
- run: exit 0`
opts := getWorkflowCreateFileOptions(user2, repo1.DefaultBranch, "create "+wfTreePath, wfFileContent)
createWorkflowFile(t, token, "user2", "repo1", wfTreePath, opts)
commitID, err := gitRepo1.GetBranchCommitID(t.Context(), repo1.DefaultBranch)
assert.NoError(t, err)
// 3. validate the webhook is triggered
assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
assert.Len(t, webhookData.payloads, 1)
assert.Equal(t, "requested", webhookData.payloads[0].Action)
assert.Equal(t, "queued", webhookData.payloads[0].WorkflowRun.Status)
assert.Equal(t, repo1.DefaultBranch, webhookData.payloads[0].WorkflowRun.HeadBranch)
assert.Equal(t, commitID, webhookData.payloads[0].WorkflowRun.HeadSha)
assert.Equal(t, "repo1", webhookData.payloads[0].Repo.Name)
assert.Equal(t, "user2/repo1", webhookData.payloads[0].Repo.FullName)
runID := webhookData.payloads[0].WorkflowRun.ID
// The first runner to pick up a task fires in_progress (Started.IsZero() is true only once per run).
// The second runner picking up an independent job does not fire another in_progress event.
for _, runner := range runners {
task := runner.fetchTask(t)
runner.execTask(t, task, &mockTaskOutcome{
result: runnerv1.Result_RESULT_SUCCESS,
})
}
// Call cancel ui api
// Only a web UI API exists for cancelling workflow runs, so use the UI endpoint.
cancelURL := fmt.Sprintf("/user2/repo1/actions/runs/%d/cancel", runID)
req := NewRequest(t, "POST", cancelURL)
session.MakeRequest(t, req, http.StatusOK)
assert.Len(t, webhookData.payloads, 3)
// 4. Validate the second webhook payload (in_progress, fired when the first runner picked up a job)
assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
assert.Equal(t, "in_progress", webhookData.payloads[1].Action)
assert.Equal(t, "in_progress", webhookData.payloads[1].WorkflowRun.Status)
assert.Equal(t, "push", webhookData.payloads[1].WorkflowRun.Event)
assert.Equal(t, runID, webhookData.payloads[1].WorkflowRun.ID)
assert.Equal(t, repo1.DefaultBranch, webhookData.payloads[1].WorkflowRun.HeadBranch)
assert.Equal(t, commitID, webhookData.payloads[1].WorkflowRun.HeadSha)
assert.Equal(t, "repo1", webhookData.payloads[1].Repo.Name)
assert.Equal(t, "user2/repo1", webhookData.payloads[1].Repo.FullName)
// 5. Validate the third webhook payload (completed, fired after cancel)
assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
assert.Equal(t, "completed", webhookData.payloads[2].Action)
assert.Equal(t, "push", webhookData.payloads[2].WorkflowRun.Event)
assert.Equal(t, "completed", webhookData.payloads[2].WorkflowRun.Status)
assert.Equal(t, runID, webhookData.payloads[2].WorkflowRun.ID)
assert.Equal(t, repo1.DefaultBranch, webhookData.payloads[2].WorkflowRun.HeadBranch)
assert.Equal(t, commitID, webhookData.payloads[2].WorkflowRun.HeadSha)
assert.Equal(t, "repo1", webhookData.payloads[2].Repo.Name)
assert.Equal(t, "user2/repo1", webhookData.payloads[2].Repo.FullName)
// Call rerun ui api
// Only a web UI API exists for rerunning workflow runs, so use the UI endpoint.
rerunURL := fmt.Sprintf("/user2/repo1/actions/runs/%d/rerun", runID)
req = NewRequest(t, "POST", rerunURL)
session.MakeRequest(t, req, http.StatusOK)
assert.Len(t, webhookData.payloads, 4)
// 6. Validate the fourth webhook payload (requested, fired after rerun)
assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
assert.Equal(t, "requested", webhookData.payloads[3].Action)
assert.Equal(t, "queued", webhookData.payloads[3].WorkflowRun.Status)
assert.Equal(t, "push", webhookData.payloads[3].WorkflowRun.Event)
assert.Equal(t, repo1.DefaultBranch, webhookData.payloads[3].WorkflowRun.HeadBranch)
assert.Equal(t, commitID, webhookData.payloads[3].WorkflowRun.HeadSha)
assert.Equal(t, "repo1", webhookData.payloads[3].Repo.Name)
assert.Equal(t, "user2/repo1", webhookData.payloads[3].Repo.FullName)
}
func testWorkflowRunEventsOnCancellingAbandonedRun(t *testing.T, webhookData *workflowRunWebhook, allJobsAbandoned bool) {
defer test.MockVariableValue(&setting.Actions.AbandonedJobTimeout, 0*time.Nanosecond)()
// 1. create a new webhook with special webhook for repo1
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session := loginUser(t, "user2")
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
repoName := "test-workflow-run-cancelling-abandoned-run"
testRepo := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: createActionsTestRepo(t, token, repoName, false).ID})
runners := make([]*mockRunner, 2)
for i := range runners {
runners[i] = newMockRunner()
runners[i].registerAsRepoRunner(t, "user2", repoName,
fmt.Sprintf("mock-runner-%d", i), []string{"ubuntu-latest"}, false)
}
testAPICreateWebhookForRepo(t, session, "user2", repoName, webhookData.URL, "workflow_run")
ctx := t.Context()
gitRepo, err := git.OpenRepository(testRepo)
assert.NoError(t, err)
// 2.2 trigger the webhooks
// add workflow file to the repo
// init the workflow
wfilename := "push.yml"
wfTreePath := ".gitea/workflows/" + wfilename
wfFileContent := `on:
push:
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: exit 0
test2:
needs: [test]
runs-on: ubuntu-latest
steps:
- run: exit 0
test3:
needs: [test, test2]
runs-on: ubuntu-latest
steps:
- run: exit 0
test4:
needs: [test, test2, test3]
runs-on: ubuntu-latest
steps:
- run: exit 0
test5:
needs: [test, test2, test4]
runs-on: ubuntu-latest
steps:
- run: exit 0
test6:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04]
needs: [test, test2, test3]
runs-on: ${{ matrix.os }}
steps:
- run: exit 0
test7:
needs: test6
runs-on: ubuntu-latest
steps:
- run: exit 0
test8:
runs-on: ubuntu-latest
steps:
- run: exit 0
test9:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04, ubuntu-25.04, windows-2022, windows-2025, macos-13, macos-14, macos-15]
runs-on: ${{ matrix.os }}
steps:
- run: exit 0
test10:
runs-on: ubuntu-latest
steps:
- run: exit 0`
opts := getWorkflowCreateFileOptions(user2, testRepo.DefaultBranch, "create "+wfTreePath, wfFileContent)
createWorkflowFile(t, token, "user2", repoName, wfTreePath, opts)
commitID, err := gitRepo.GetBranchCommitID(t.Context(), testRepo.DefaultBranch)
assert.NoError(t, err)
// 3. validate the webhook is triggered
assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
assert.Len(t, webhookData.payloads, 1)
assert.Equal(t, "requested", webhookData.payloads[0].Action)
assert.Equal(t, "queued", webhookData.payloads[0].WorkflowRun.Status)
assert.Equal(t, testRepo.DefaultBranch, webhookData.payloads[0].WorkflowRun.HeadBranch)
assert.Equal(t, commitID, webhookData.payloads[0].WorkflowRun.HeadSha)
assert.Equal(t, repoName, webhookData.payloads[0].Repo.Name)
assert.Equal(t, "user2/"+repoName, webhookData.payloads[0].Repo.FullName)
if !allJobsAbandoned {
for _, runner := range runners {
task := runner.fetchTask(t)
runner.execTask(t, task, &mockTaskOutcome{
result: runnerv1.Result_RESULT_SUCCESS,
})
}
}
// Add this sleep to ensure the func can find the tasks by timestamp.
time.Sleep(time.Second)
err = actions.CancelAbandonedJobs(ctx)
assert.NoError(t, err)
if allJobsAbandoned {
// No runner picked up any task, so no in_progress event was fired.
assert.Len(t, webhookData.payloads, 2)
assert.Equal(t, "completed", webhookData.payloads[1].Action)
assert.Equal(t, "completed", webhookData.payloads[1].WorkflowRun.Status)
assert.Equal(t, testRepo.DefaultBranch, webhookData.payloads[1].WorkflowRun.HeadBranch)
assert.Equal(t, commitID, webhookData.payloads[1].WorkflowRun.HeadSha)
assert.Equal(t, repoName, webhookData.payloads[1].Repo.Name)
assert.Equal(t, "user2/"+repoName, webhookData.payloads[1].Repo.FullName)
} else {
// The first runner pick-up fired in_progress before the run was abandoned.
assert.Len(t, webhookData.payloads, 3)
assert.Equal(t, "in_progress", webhookData.payloads[1].Action)
assert.Equal(t, "in_progress", webhookData.payloads[1].WorkflowRun.Status)
assert.Equal(t, "completed", webhookData.payloads[2].Action)
assert.Equal(t, "completed", webhookData.payloads[2].WorkflowRun.Status)
assert.Equal(t, testRepo.DefaultBranch, webhookData.payloads[2].WorkflowRun.HeadBranch)
assert.Equal(t, commitID, webhookData.payloads[2].WorkflowRun.HeadSha)
assert.Equal(t, repoName, webhookData.payloads[2].Repo.Name)
assert.Equal(t, "user2/"+repoName, webhookData.payloads[2].Repo.FullName)
}
}
func testWorkflowRunOnStoppingEndlessTasksForMultipleRuns(t *testing.T, webhookData *workflowRunWebhook) {
defer test.MockVariableValue(&setting.Actions.EndlessTaskTimeout, time.Second)()
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session := loginUser(t, "user2")
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
repoName := "test-workflow-run-stop-endless-tasks"
testRepo := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: createActionsTestRepo(t, token, repoName, false).ID})
testAPICreateWebhookForRepo(t, session, "user2", repoName, webhookData.URL, "workflow_run")
runners := make([]*mockRunner, 2)
for i := range runners {
runners[i] = newMockRunner()
runners[i].registerAsRepoRunner(t, "user2", repoName, fmt.Sprintf("mock-runner-%d", i), []string{"ubuntu-latest"}, false)
}
workflowPath1 := ".gitea/workflows/endless-1.yml"
workflowPath2 := ".gitea/workflows/endless-2.yml"
workflowContent1 := `name: endless-1
on:
push:
paths:
- '.gitea/workflows/endless-1.yml'
jobs:
job-1:
runs-on: ubuntu-latest
steps:
- run: echo 'job-1'
`
workflowContent2 := `name: endless-2
on:
push:
paths:
- '.gitea/workflows/endless-2.yml'
jobs:
job-2:
runs-on: ubuntu-latest
steps:
- run: echo 'job-2'
`
opts1 := getWorkflowCreateFileOptions(user2, testRepo.DefaultBranch, "create "+workflowPath1, workflowContent1)
createWorkflowFile(t, token, "user2", repoName, workflowPath1, opts1)
opts2 := getWorkflowCreateFileOptions(user2, testRepo.DefaultBranch, "create "+workflowPath2, workflowContent2)
createWorkflowFile(t, token, "user2", repoName, workflowPath2, opts2)
task1 := runners[0].fetchTask(t)
task2 := runners[1].fetchTask(t)
_, job1, _ := getTaskAndJobAndRunByTaskID(t, task1.Id)
_, job2, _ := getTaskAndJobAndRunByTaskID(t, task2.Id)
require.NotEqual(t, job1.RunID, job2.RunID)
initialRunEventsLen := len(webhookData.payloads)
time.Sleep(2 * time.Second)
require.NoError(t, actions.StopEndlessTasks(t.Context()))
require.Len(t, webhookData.payloads, initialRunEventsLen+2)
var completedRunIDs []int64
for _, payload := range webhookData.payloads[initialRunEventsLen:] {
assert.Equal(t, "completed", payload.Action)
assert.Equal(t, "completed", payload.WorkflowRun.Status)
completedRunIDs = append(completedRunIDs, payload.WorkflowRun.ID)
}
assert.Len(t, completedRunIDs, 2)
assert.Contains(t, completedRunIDs, job1.RunID)
assert.Contains(t, completedRunIDs, job2.RunID)
run1 := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: job1.RunID})
run2 := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: job2.RunID})
assert.Equal(t, actions_model.StatusFailure, run1.Status)
assert.Equal(t, actions_model.StatusFailure, run2.Status)
}
func testWebhookWorkflowRun(t *testing.T, webhookData *workflowRunWebhook) {
// 1. create a new webhook with special webhook for repo1
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session := loginUser(t, "user2")
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
testAPICreateWebhookForRepo(t, session, "user2", "repo1", webhookData.URL, "workflow_run")
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
gitRepo1, err := git.OpenRepository(repo1)
assert.NoError(t, err)
runner := newMockRunner()
runner.registerAsRepoRunner(t, "user2", "repo1", "mock-runner", []string{"ubuntu-latest"}, false)
// 2.1 add workflow_run workflow file to the repo
opts := getWorkflowCreateFileOptions(user2, repo1.DefaultBranch, "create "+"dispatch.yml", `
on:
workflow_run:
workflows: ["Push"]
types:
- completed
jobs:
dispatch:
runs-on: ubuntu-latest
steps:
- run: echo 'test the webhook'
`)
createWorkflowFile(t, token, "user2", "repo1", ".gitea/workflows/dispatch.yml", opts)
// 2.2 trigger the webhooks
// add workflow file to the repo
// init the workflow
wfTreePath := ".gitea/workflows/push.yml"
wfFileContent := `name: Push
on: push
jobs:
wf1-job:
runs-on: ubuntu-latest
steps:
- run: echo 'test the webhook'
wf2-job:
runs-on: ubuntu-latest
needs: wf1-job
steps:
- run: echo 'cmd 1'
- run: echo 'cmd 2'
`
opts = getWorkflowCreateFileOptions(user2, repo1.DefaultBranch, "create "+wfTreePath, wfFileContent)
createWorkflowFile(t, token, "user2", "repo1", wfTreePath, opts)
commitID, err := gitRepo1.GetBranchCommitID(t.Context(), repo1.DefaultBranch)
assert.NoError(t, err)
// 3. validate the webhook is triggered
assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
assert.Len(t, webhookData.payloads, 1)
assert.Equal(t, "requested", webhookData.payloads[0].Action)
assert.Equal(t, "queued", webhookData.payloads[0].WorkflowRun.Status)
assert.Equal(t, repo1.DefaultBranch, webhookData.payloads[0].WorkflowRun.HeadBranch)
assert.Equal(t, commitID, webhookData.payloads[0].WorkflowRun.HeadSha)
assert.Equal(t, "repo1", webhookData.payloads[0].Repo.Name)
assert.Equal(t, "user2/repo1", webhookData.payloads[0].Repo.FullName)
// 4. Execute two Jobs
task := runner.fetchTask(t)
outcome := &mockTaskOutcome{
result: runnerv1.Result_RESULT_SUCCESS,
}
runner.execTask(t, task, outcome)
task = runner.fetchTask(t)
outcome = &mockTaskOutcome{
result: runnerv1.Result_RESULT_FAILURE,
}
runner.execTask(t, task, outcome)
// 7. validate the webhook is triggered
assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
assert.Len(t, webhookData.payloads, 4)
// payloads[1] is the in_progress event fired when the runner picked up wf1-job
assert.Equal(t, "in_progress", webhookData.payloads[1].Action)
assert.Equal(t, "in_progress", webhookData.payloads[1].WorkflowRun.Status)
assert.Equal(t, "push", webhookData.payloads[1].WorkflowRun.Event)
assert.Equal(t, "completed", webhookData.payloads[2].Action)
assert.Equal(t, "push", webhookData.payloads[2].WorkflowRun.Event)
// 8. validate the webhook is triggered (requested, wf2 triggered by wf1 completion)
assert.Len(t, webhookData.payloads, 4)
assert.Equal(t, "requested", webhookData.payloads[3].Action)
assert.Equal(t, "queued", webhookData.payloads[3].WorkflowRun.Status)
assert.Equal(t, "workflow_run", webhookData.payloads[3].WorkflowRun.Event)
assert.Equal(t, repo1.DefaultBranch, webhookData.payloads[3].WorkflowRun.HeadBranch)
assert.Equal(t, commitID, webhookData.payloads[3].WorkflowRun.HeadSha)
assert.Equal(t, "repo1", webhookData.payloads[3].Repo.Name)
assert.Equal(t, "user2/repo1", webhookData.payloads[3].Repo.FullName)
}
func testWebhookWorkflowRunDepthLimit(t *testing.T, webhookData *workflowRunWebhook) {
// 1. create a new webhook with special webhook for repo1
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session := loginUser(t, "user2")
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
testAPICreateWebhookForRepo(t, session, "user2", "repo1", webhookData.URL, "workflow_run")
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
gitRepo1, err := git.OpenRepository(repo1)
assert.NoError(t, err)
// 2. trigger the webhooks
// add workflow file to the repo
// init the workflow
wfTreePath := ".gitea/workflows/push.yml"
wfFileContent := `name: Endless Loop
on:
push:
workflow_run:
types:
- requested
jobs:
dispatch:
runs-on: ubuntu-latest
steps:
- run: echo 'test the webhook'
`
opts := getWorkflowCreateFileOptions(user2, repo1.DefaultBranch, "create "+wfTreePath, wfFileContent)
createWorkflowFile(t, token, "user2", "repo1", wfTreePath, opts)
commitID, err := gitRepo1.GetBranchCommitID(t.Context(), repo1.DefaultBranch)
assert.NoError(t, err)
// 3. validate the webhook is triggered
assert.Equal(t, "workflow_run", webhookData.triggeredEvent)
// 1x push + 5x workflow_run requested chain
assert.Len(t, webhookData.payloads, 6)
for i := range 6 {
assert.Equal(t, "requested", webhookData.payloads[i].Action)
assert.Equal(t, "queued", webhookData.payloads[i].WorkflowRun.Status)
assert.Equal(t, repo1.DefaultBranch, webhookData.payloads[i].WorkflowRun.HeadBranch)
assert.Equal(t, commitID, webhookData.payloads[i].WorkflowRun.HeadSha)
if i == 0 {
assert.Equal(t, "push", webhookData.payloads[i].WorkflowRun.Event)
} else {
assert.Equal(t, "workflow_run", webhookData.payloads[i].WorkflowRun.Event)
}
assert.Equal(t, "repo1", webhookData.payloads[i].Repo.Name)
assert.Equal(t, "user2/repo1", webhookData.payloads[i].Repo.FullName)
}
}