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]>
173 lines
7.4 KiB
Go
173 lines
7.4 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package integration
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"testing"
|
|
|
|
issues_model "github.com/hanzoai/git/models/issues"
|
|
repo_model "github.com/hanzoai/git/models/repo"
|
|
"github.com/hanzoai/git/models/unittest"
|
|
"github.com/hanzoai/git/modules/test"
|
|
repo_service "github.com/hanzoai/git/services/repository"
|
|
"github.com/hanzoai/git/tests"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestPullCompare(t *testing.T) {
|
|
defer tests.PrepareTestEnv(t)()
|
|
|
|
t.Run("PullsNewRedirect", func(t *testing.T) {
|
|
req := NewRequest(t, "GET", "/user2/repo1/pulls/new/foo")
|
|
resp := MakeRequest(t, req, http.StatusSeeOther)
|
|
redirect := test.RedirectURL(resp)
|
|
assert.Equal(t, "/user2/repo1/compare/master...foo?expand=1", redirect)
|
|
|
|
req = NewRequest(t, "GET", "/user13/repo11/pulls/new/foo")
|
|
resp = MakeRequest(t, req, http.StatusSeeOther)
|
|
redirect = test.RedirectURL(resp)
|
|
assert.Equal(t, "/user12/repo10/compare/master...user13:foo?expand=1", redirect)
|
|
})
|
|
|
|
t.Run("ButtonsExist", func(t *testing.T) {
|
|
session := loginUser(t, "user2")
|
|
|
|
// test the "New PR" button
|
|
req := NewRequest(t, "GET", "/user2/repo1/pulls")
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
link, exists := htmlDoc.doc.Find(".new-pr-button").Attr("href")
|
|
assert.True(t, exists, "The template has changed")
|
|
req = NewRequest(t, "GET", link)
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
assert.Equal(t, http.StatusOK, resp.Code)
|
|
|
|
// test the edit button in the PR diff view
|
|
req = NewRequest(t, "GET", "/user2/repo1/pulls/3/files")
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
doc := NewHTMLParser(t, resp.Body)
|
|
editButtonCount := doc.doc.Find(".diff-file-header-actions a[href*='/_edit/']").Length()
|
|
assert.Positive(t, editButtonCount, "Expected to find a button to edit a file in the PR diff view but there were none")
|
|
})
|
|
|
|
onGitRun(t, func(t *testing.T, u *url.URL) {
|
|
defer tests.PrepareTestEnv(t)()
|
|
|
|
session := loginUser(t, "user1")
|
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
|
|
testCreateBranch(t, session, "user1", "repo1", "branch/master", "master1", http.StatusSeeOther)
|
|
testEditFile(t, session, "user1", "repo1", "master1", "README.md", "Hello, World (Edited)\n")
|
|
testPullCreate(t, session, "user1", "repo1", false, "master", "master1", "This is a pull title")
|
|
|
|
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "repo1"})
|
|
issueIndex := unittest.AssertExistsAndLoadBean(t, &issues_model.IssueIndex{GroupID: repo1.ID}, unittest.OrderBy("group_id ASC"))
|
|
prFilesURL := fmt.Sprintf("/user2/repo1/pulls/%d/files", issueIndex.MaxIndex)
|
|
req := NewRequest(t, "GET", prFilesURL)
|
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
|
doc := NewHTMLParser(t, resp.Body)
|
|
editButtonCount := doc.doc.Find(".diff-file-header-actions a[href*='/_edit/']").Length()
|
|
assert.Positive(t, editButtonCount, "Expected to find a button to edit a file in the PR diff view but there were none")
|
|
|
|
repoForked := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: "repo1"})
|
|
|
|
// delete the head repository and revisit the PR diff view
|
|
err := repo_service.DeleteRepositoryDirectly(t.Context(), repoForked.ID)
|
|
assert.NoError(t, err)
|
|
|
|
req = NewRequest(t, "GET", prFilesURL)
|
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
|
doc = NewHTMLParser(t, resp.Body)
|
|
editButtonCount = doc.doc.Find(".diff-file-header-actions a[href*='/_edit/']").Length()
|
|
assert.Equal(t, 0, editButtonCount, "Expected not to find a button to edit a file in the PR diff view because head repository has been deleted")
|
|
})
|
|
}
|
|
|
|
func TestPullCompare_EnableAllowEditsFromMaintainer(t *testing.T) {
|
|
onGitRun(t, func(t *testing.T, u *url.URL) {
|
|
// repo3 is private
|
|
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
|
|
assert.True(t, repo3.IsPrivate)
|
|
|
|
// user4 forks repo3
|
|
user4Session := loginUser(t, "user4")
|
|
forkedRepoName := "user4-forked-repo3"
|
|
testRepoFork(t, user4Session, repo3.OwnerName, repo3.Name, "user4", forkedRepoName, "")
|
|
forkedRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user4", Name: forkedRepoName})
|
|
assert.True(t, forkedRepo.IsPrivate)
|
|
|
|
// user4 creates a new branch and a PR
|
|
testEditFileToNewBranch(t, user4Session, "user4", forkedRepoName, "master", "user4/update-readme", "README.md", "Hello, World\n(Edited by user4)\n")
|
|
resp := testPullCreateDirectly(t, user4Session, createPullRequestOptions{
|
|
BaseRepoOwner: repo3.OwnerName,
|
|
BaseRepoName: repo3.Name,
|
|
BaseBranch: "master",
|
|
HeadRepoOwner: "user4",
|
|
HeadRepoName: forkedRepoName,
|
|
HeadBranch: "user4/update-readme",
|
|
Title: "PR for user4 forked repo3",
|
|
})
|
|
prURL := test.RedirectURL(resp)
|
|
|
|
// user2 (admin of repo3) goes to the PR files page
|
|
user2Session := loginUser(t, "user2")
|
|
resp = user2Session.MakeRequest(t, NewRequest(t, "GET", prURL+"/files"), http.StatusOK)
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
nodes := htmlDoc.doc.Find(".diff-file-box[data-new-filename=\"README.md\"] .diff-file-header-actions .tippy-target a")
|
|
if assert.Equal(t, 1, nodes.Length()) {
|
|
// there is only "View File" button, no "Edit File" button
|
|
assert.Equal(t, "View File", nodes.First().Text())
|
|
viewFileLink, exists := nodes.First().Attr("href")
|
|
if assert.True(t, exists) {
|
|
user2Session.MakeRequest(t, NewRequest(t, "GET", viewFileLink), http.StatusOK)
|
|
}
|
|
}
|
|
|
|
// user4 goes to the PR page and enable "Allow maintainers to edit"
|
|
resp = user4Session.MakeRequest(t, NewRequest(t, "GET", prURL), http.StatusOK)
|
|
htmlDoc = NewHTMLParser(t, resp.Body)
|
|
dataURL, exists := htmlDoc.doc.Find("#allow-edits-from-maintainers").Attr("data-url")
|
|
assert.True(t, exists)
|
|
req := NewRequestWithValues(t, "POST", dataURL+"/set_allow_maintainer_edit", map[string]string{
|
|
"allow_maintainer_edit": "true",
|
|
})
|
|
user4Session.MakeRequest(t, req, http.StatusOK)
|
|
|
|
// user2 (admin of repo3) goes to the PR files page again
|
|
resp = user2Session.MakeRequest(t, NewRequest(t, "GET", prURL+"/files"), http.StatusOK)
|
|
htmlDoc = NewHTMLParser(t, resp.Body)
|
|
nodes = htmlDoc.doc.Find(".diff-file-box[data-new-filename=\"README.md\"] .diff-file-header-actions .tippy-target a")
|
|
if assert.Equal(t, 2, nodes.Length()) {
|
|
// there are "View File" button and "Edit File" button
|
|
assert.Equal(t, "View File", nodes.First().Text())
|
|
viewFileLink, exists := nodes.First().Attr("href")
|
|
if assert.True(t, exists) {
|
|
user2Session.MakeRequest(t, NewRequest(t, "GET", viewFileLink), http.StatusOK)
|
|
}
|
|
|
|
assert.Equal(t, "Edit File", nodes.Last().Text())
|
|
editFileLink, exists := nodes.Last().Attr("href")
|
|
if assert.True(t, exists) {
|
|
// edit the file
|
|
resp := user2Session.MakeRequest(t, NewRequest(t, "GET", editFileLink), http.StatusOK)
|
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
|
lastCommit := htmlDoc.GetInputValueByName("last_commit")
|
|
assert.NotEmpty(t, lastCommit)
|
|
req := NewRequestWithValues(t, "POST", editFileLink, map[string]string{
|
|
"last_commit": lastCommit,
|
|
"tree_path": "README.md",
|
|
"content": "File is edited by the maintainer user2",
|
|
"commit_summary": "user2 updated the file",
|
|
"commit_choice": "direct",
|
|
})
|
|
resp = user2Session.MakeRequest(t, req, http.StatusOK)
|
|
assert.NotEmpty(t, test.RedirectURL(resp))
|
|
}
|
|
}
|
|
})
|
|
}
|