mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in NOTICE, alongside LICENSE. The header lint accepts any copyright line above the SPDX line, so it no longer demands an upstream one. Co-authored-by: Hanzo Dev <[email protected]>
246 lines
10 KiB
Go
246 lines
10 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package integration
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"path"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
auth_model "github.com/hanzoai/git/models/auth"
|
|
"github.com/hanzoai/git/models/db"
|
|
git_model "github.com/hanzoai/git/models/git"
|
|
"github.com/hanzoai/git/modules/commitstatus"
|
|
"github.com/hanzoai/git/modules/json"
|
|
"github.com/hanzoai/git/modules/setting"
|
|
api "github.com/hanzoai/git/modules/structs"
|
|
"github.com/hanzoai/git/tests"
|
|
|
|
"github.com/PuerkitoBio/goquery"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestRepoCommits(t *testing.T) {
|
|
defer tests.PrepareTestEnv(t)()
|
|
session := loginUser(t, "user2")
|
|
|
|
t.Run("CommitList", func(t *testing.T) {
|
|
req := NewRequest(t, "GET", "/user2/repo16/commits/branch/master")
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
var commits, userHrefs []string
|
|
doc := NewHTMLParser(t, resp.Body)
|
|
doc.doc.Find("#commits-table .commit-id-short").Each(func(i int, s *goquery.Selection) {
|
|
commits = append(commits, path.Base(s.AttrOr("href", "")))
|
|
})
|
|
doc.doc.Find("#commits-table .avatar-stack-names a.muted").Each(func(i int, s *goquery.Selection) {
|
|
userHrefs = append(userHrefs, s.AttrOr("href", ""))
|
|
})
|
|
assert.Equal(t, []string{"69554a64c1e6030f051e5c3f94bfbd773cd6a324", "27566bd5738fc8b4e3fef3c5e72cce608537bd95", "5099b81332712fe655e34e8dd63574f503f61811"}, commits)
|
|
assert.Equal(t, []string{
|
|
"/user2/repo16/commits/branch/master/search?q=author%3Auser2%40example.com",
|
|
"/user2/repo16/commits/branch/master/search?q=author%3Auser21%40example.com",
|
|
"/user2/repo16/commits/branch/master/search?q=author%3Auser2%40example.com",
|
|
}, userHrefs)
|
|
})
|
|
|
|
t.Run("LastCommit", func(t *testing.T) {
|
|
req := NewRequest(t, "GET", "/user2/repo16")
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
doc := NewHTMLParser(t, resp.Body)
|
|
commitHref := doc.doc.Find(".latest-commit .commit-id-short").AttrOr("href", "")
|
|
authorHref := doc.doc.Find(".latest-commit .avatar-stack-names a").AttrOr("href", "")
|
|
assert.Equal(t, "/user2/repo16/commit/69554a64c1e6030f051e5c3f94bfbd773cd6a324", commitHref)
|
|
assert.Equal(t, "/user2/repo16/commits/branch/master/search?q=author%3Auser2%40example.com", authorHref)
|
|
})
|
|
|
|
t.Run("CommitListNonExistingCommiter", func(t *testing.T) {
|
|
// check the commit list for a repository whose commit author has no account here
|
|
// * commit 985f0301dba5e7b34be866819cd15ad3d8f508ee (branch2)
|
|
// * Author: 6543 <[email protected]>
|
|
req := NewRequest(t, "GET", "/user2/repo1/commits/branch/branch2")
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
doc := NewHTMLParser(t, resp.Body)
|
|
commitHref := doc.doc.Find("#commits-table tr:first-child .commit-id-short").AttrOr("href", "")
|
|
assert.Equal(t, "/user2/repo1/commit/985f0301dba5e7b34be866819cd15ad3d8f508ee", commitHref)
|
|
authorElem := doc.doc.Find("#commits-table tr:first-child .avatar-stack-names")
|
|
assert.Equal(t, "6543", strings.TrimSpace(authorElem.Text()))
|
|
})
|
|
|
|
t.Run("CommitPageUsesCommitterDate", func(t *testing.T) {
|
|
const (
|
|
commitID = "5099b81332712fe655e34e8dd63574f503f61811"
|
|
expectedCommitterTime = "2017-08-06T19:56:13+02:00"
|
|
authorTime = "2017-08-06T19:55:01+02:00"
|
|
)
|
|
|
|
req := NewRequest(t, "GET", "/user2/repo16/commits/branch/master")
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
doc := NewHTMLParser(t, resp.Body)
|
|
|
|
var commitListTime string
|
|
doc.doc.Find("#commits-table tbody tr").EachWithBreak(func(_ int, row *goquery.Selection) bool {
|
|
if path.Base(row.Find(".commit-id-short").AttrOr("href", "")) != commitID {
|
|
return true
|
|
}
|
|
commitListTime = row.Find("td").Eq(3).Find("relative-time").AttrOr("datetime", "")
|
|
return false
|
|
})
|
|
require.Equal(t, expectedCommitterTime, commitListTime)
|
|
})
|
|
|
|
t.Run("LastCommitNonExistingCommiter", func(t *testing.T) {
|
|
req := NewRequest(t, "GET", "/user2/repo1/src/branch/branch2")
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
doc := NewHTMLParser(t, resp.Body)
|
|
commitHref := doc.doc.Find(".latest-commit .commit-id-short").AttrOr("href", "")
|
|
assert.Equal(t, "/user2/repo1/commit/985f0301dba5e7b34be866819cd15ad3d8f508ee", commitHref)
|
|
authorElem := doc.doc.Find(".latest-commit .avatar-stack-names")
|
|
assert.Equal(t, "6543", strings.TrimSpace(authorElem.Text()))
|
|
})
|
|
}
|
|
|
|
func TestRepoCommitsWithStatus(t *testing.T) {
|
|
defer tests.PrepareTestEnv(t)()
|
|
|
|
session := loginUser(t, "user2")
|
|
ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository)
|
|
|
|
requestCommitStatuses := func(t *testing.T, linkList, linkCombined string) (statuses []*api.CommitStatus, status api.CombinedStatus) {
|
|
assert.NoError(t, json.Unmarshal(session.MakeRequest(t, NewRequest(t, "GET", linkList), http.StatusOK).Body.Bytes(), &statuses))
|
|
assert.NoError(t, json.Unmarshal(session.MakeRequest(t, NewRequest(t, "GET", linkCombined), http.StatusOK).Body.Bytes(), &status))
|
|
return statuses, status
|
|
}
|
|
|
|
testRefMaster := func(t *testing.T, state commitstatus.CommitStatusState, classes ...string) {
|
|
_ = db.TruncateBeans(t.Context(), &git_model.CommitStatus{})
|
|
|
|
// Request repository commits page
|
|
req := NewRequest(t, "GET", "/user2/repo1/commits/branch/master")
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
doc := NewHTMLParser(t, resp.Body)
|
|
// Get first commit URL
|
|
commitURL, _ := doc.doc.Find("#commits-table .commit-id-short").Attr("href")
|
|
require.NotEmpty(t, commitURL)
|
|
commitID := path.Base(commitURL)
|
|
|
|
// Call API to add status for commit
|
|
doAPICreateCommitStatusTest(ctx, path.Base(commitURL), state, "testci")(t)
|
|
|
|
req = NewRequest(t, "GET", "/user2/repo1/commits/branch/master")
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
doc = NewHTMLParser(t, resp.Body)
|
|
// Check if commit status is displayed in message column (.tippy-target to ignore the tippy trigger)
|
|
sel := doc.doc.Find("#commits-table .message .tippy-target .commit-status")
|
|
assert.Equal(t, 1, sel.Length())
|
|
for _, class := range classes {
|
|
assert.True(t, sel.HasClass(class))
|
|
}
|
|
|
|
testRepoCommitsWithStatus := func(t *testing.T, linkList, linkCombined string, state commitstatus.CommitStatusState) {
|
|
statuses, status := requestCommitStatuses(t, linkList, linkCombined)
|
|
require.Len(t, statuses, 1)
|
|
require.NotNil(t, status)
|
|
|
|
assert.Equal(t, state, statuses[0].State)
|
|
assert.Equal(t, setting.AppURL+"v1/repos/user2/repo1/statuses/"+commitID, statuses[0].URL)
|
|
assert.Equal(t, "http://test.ci/", statuses[0].TargetURL)
|
|
assert.Empty(t, statuses[0].Description)
|
|
assert.Equal(t, "testci", statuses[0].Context)
|
|
|
|
assert.Len(t, status.Statuses, 1)
|
|
assert.Equal(t, statuses[0], status.Statuses[0])
|
|
assert.Equal(t, commitID, status.SHA)
|
|
}
|
|
// By SHA
|
|
testRepoCommitsWithStatus(t, "/v1/repos/user2/repo1/commits/"+commitID+"/statuses", "/v1/repos/user2/repo1/commits/"+commitID+"/status", state)
|
|
// By short SHA
|
|
testRepoCommitsWithStatus(t, "/v1/repos/user2/repo1/commits/"+commitID[:7]+"/statuses", "/v1/repos/user2/repo1/commits/"+commitID[:7]+"/status", state)
|
|
// By Ref
|
|
testRepoCommitsWithStatus(t, "/v1/repos/user2/repo1/commits/master/statuses", "/v1/repos/user2/repo1/commits/master/status", state)
|
|
// Tag "v1.1" points to master
|
|
testRepoCommitsWithStatus(t, "/v1/repos/user2/repo1/commits/v1.1/statuses", "/v1/repos/user2/repo1/commits/v1.1/status", state)
|
|
}
|
|
|
|
t.Run("pending", func(t *testing.T) { testRefMaster(t, "pending", "octicon-dot-fill", "tw-text-yellow") })
|
|
t.Run("success", func(t *testing.T) { testRefMaster(t, "success", "octicon-check", "tw-text-green") })
|
|
t.Run("error", func(t *testing.T) { testRefMaster(t, "error", "git-exclamation", "tw-text-red") })
|
|
t.Run("failure", func(t *testing.T) { testRefMaster(t, "failure", "octicon-x", "tw-text-red") })
|
|
t.Run("warning", func(t *testing.T) { testRefMaster(t, "warning", "git-exclamation", "tw-text-yellow") })
|
|
t.Run("BranchWithSlash", func(t *testing.T) {
|
|
_ = db.TruncateBeans(t.Context(), &git_model.CommitStatus{})
|
|
|
|
linkList, linkCombined := "/v1/repos/user2/repo1/commits/feature%2F1/statuses", "/v1/repos/user2/repo1/commits/feature/1/status"
|
|
statuses, status := requestCommitStatuses(t, linkList, linkCombined)
|
|
assert.Empty(t, statuses)
|
|
assert.Empty(t, status.Statuses)
|
|
doAPICreateCommitStatusTest(ctx, "feature/1", commitstatus.CommitStatusSuccess, "testci")(t)
|
|
statuses, status = requestCommitStatuses(t, linkList, linkCombined)
|
|
assert.NotEmpty(t, statuses)
|
|
assert.NotEmpty(t, status.Statuses)
|
|
})
|
|
}
|
|
|
|
func TestRepoCommitsStatusParallel(t *testing.T) {
|
|
defer tests.PrepareTestEnv(t)()
|
|
|
|
session := loginUser(t, "user2")
|
|
|
|
// Request repository commits page
|
|
req := NewRequest(t, "GET", "/user2/repo1/commits/branch/master")
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
doc := NewHTMLParser(t, resp.Body)
|
|
// Get first commit URL
|
|
commitURL, exists := doc.doc.Find("#commits-table .commit-id-short").Attr("href")
|
|
assert.True(t, exists)
|
|
assert.NotEmpty(t, commitURL)
|
|
|
|
var wg sync.WaitGroup
|
|
for i := range 10 {
|
|
wg.Go(func() {
|
|
t.Run(fmt.Sprintf("ParallelCreateStatus_%d", i), func(t *testing.T) {
|
|
ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository)
|
|
doAPICreateCommitStatusTest(ctx, path.Base(commitURL), commitstatus.CommitStatusPending, "testci")(t)
|
|
})
|
|
})
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestRepoCommitsStatusMultiple(t *testing.T) {
|
|
defer tests.PrepareTestEnv(t)()
|
|
|
|
session := loginUser(t, "user2")
|
|
|
|
// Request repository commits page
|
|
req := NewRequest(t, "GET", "/user2/repo1/commits/branch/master")
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
doc := NewHTMLParser(t, resp.Body)
|
|
// Get first commit URL
|
|
commitURL, exists := doc.doc.Find("#commits-table .commit-id-short").Attr("href")
|
|
assert.True(t, exists)
|
|
assert.NotEmpty(t, commitURL)
|
|
|
|
// Call API to add status for commit
|
|
ctx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository)
|
|
t.Run("CreateStatus", doAPICreateCommitStatusTest(ctx, path.Base(commitURL), commitstatus.CommitStatusSuccess, "testci"))
|
|
t.Run("CreateStatus", doAPICreateCommitStatusTest(ctx, path.Base(commitURL), commitstatus.CommitStatusSuccess, "other_context"))
|
|
req = NewRequest(t, "GET", "/user2/repo1/commits/branch/master")
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
doc = NewHTMLParser(t, resp.Body)
|
|
// Check that the data-global-init="initCommitStatuses" (for trigger) and commit-status (svg) are present
|
|
sel := doc.doc.Find(`#commits-table .message [data-global-init="initCommitStatuses"] .commit-status`)
|
|
assert.Equal(t, 1, sel.Length())
|
|
}
|