mirror of
https://github.com/go-gitea/gitea.git
synced 2026-07-26 18:51:57 +00:00
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
1851 lines
67 KiB
Go
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)
|
|
}
|
|
}
|