Files
forge/services/pull/merge_tree_test.go
zeekayandHanzo Dev aa87442fdd Record upstream authorship centrally in NOTICE instead of in every file header
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]>
2026-09-10 17:50:35 -07:00

155 lines
4.5 KiB
Go

// Copyright 2026 Hanzo AI, Inc. All rights reserved.
// SPDX-License-Identifier: MIT
package pull
import (
"context"
"fmt"
"testing"
issues_model "github.com/hanzoai/git/models/issues"
"github.com/hanzoai/git/models/unittest"
"github.com/hanzoai/git/modules/git/gitcmd"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func testPullRequestMergeCheck(t *testing.T,
targetFunc func(ctx context.Context, pr *issues_model.PullRequest) error,
pr *issues_model.PullRequest,
expectedStatus issues_model.PullRequestStatus,
expectedConflictedFiles []string,
expectedChangedProtectedFiles []string,
) {
assert.NoError(t, pr.LoadIssue(t.Context()))
assert.NoError(t, pr.LoadBaseRepo(t.Context()))
assert.NoError(t, pr.LoadHeadRepo(t.Context()))
pr.Status = issues_model.PullRequestStatusChecking
pr.ConflictedFiles = []string{"unrelated-conflicted-file"}
pr.ChangedProtectedFiles = []string{"unrelated-protected-file"}
pr.MergeBase = ""
pr.HeadCommitID = ""
err := targetFunc(t.Context(), pr)
require.NoError(t, err)
assert.Equal(t, expectedStatus, pr.Status)
assert.Equal(t, expectedConflictedFiles, pr.ConflictedFiles)
assert.Equal(t, expectedChangedProtectedFiles, pr.ChangedProtectedFiles)
assert.NotEmpty(t, pr.MergeBase)
assert.NotEmpty(t, pr.HeadCommitID)
}
func TestPullRequestMergeable(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
t.Run("NoConflict-MergeTree", func(t *testing.T) {
testPullRequestMergeCheck(t, checkPullRequestMergeableByMergeTree, pr, issues_model.PullRequestStatusMergeable, nil, nil)
})
t.Run("NoConflict-TmpRepo", func(t *testing.T) {
testPullRequestMergeCheck(t, checkPullRequestMergeableByTmpRepo, pr, issues_model.PullRequestStatusMergeable, nil, nil)
})
pr.BaseBranch, pr.HeadBranch = "test-merge-tree-conflict-base", "test-merge-tree-conflict-head"
conflictFiles := createConflictBranches(t, pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.HeadBranch)
t.Run("Conflict-MergeTree", func(t *testing.T) {
testPullRequestMergeCheck(t, checkPullRequestMergeableByMergeTree, pr, issues_model.PullRequestStatusConflict, conflictFiles, nil)
})
t.Run("Conflict-TmpRepo", func(t *testing.T) {
testPullRequestMergeCheck(t, checkPullRequestMergeableByTmpRepo, pr, issues_model.PullRequestStatusConflict, conflictFiles, nil)
})
pr.BaseBranch, pr.HeadBranch = "test-merge-tree-empty-base", "test-merge-tree-empty-head"
createEmptyBranches(t, pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.HeadBranch)
t.Run("Empty-MergeTree", func(t *testing.T) {
testPullRequestMergeCheck(t, checkPullRequestMergeableByMergeTree, pr, issues_model.PullRequestStatusEmpty, nil, nil)
})
t.Run("Empty-TmpRepo", func(t *testing.T) {
testPullRequestMergeCheck(t, checkPullRequestMergeableByTmpRepo, pr, issues_model.PullRequestStatusEmpty, nil, nil)
})
}
func createConflictBranches(t *testing.T, repoPath, baseBranch, headBranch string) []string {
conflictFile := "conflict.txt"
stdin := fmt.Sprintf(
`reset refs/heads/%[1]s
from refs/heads/master
commit refs/heads/%[1]s
mark :1
committer Test <[email protected]> 0 +0000
data 17
add conflict file
M 100644 inline %[3]s
data 4
base
commit refs/heads/%[1]s
mark :2
committer Test <[email protected]> 0 +0000
data 11
base change
from :1
M 100644 inline %[3]s
data 11
base change
reset refs/heads/%[2]s
from :1
commit refs/heads/%[2]s
mark :3
committer Test <[email protected]> 0 +0000
data 11
head change
from :1
M 100644 inline %[3]s
data 11
head change
`, baseBranch, headBranch, conflictFile)
err := gitcmd.NewCommand("fast-import").WithDir(repoPath).WithStdinBytes([]byte(stdin)).RunWithStderr(t.Context())
require.NoError(t, err)
return []string{conflictFile}
}
func createEmptyBranches(t *testing.T, repoPath, baseBranch, headBranch string) {
emptyFile := "empty.txt"
stdin := fmt.Sprintf(`reset refs/heads/%[1]s
from refs/heads/master
commit refs/heads/%[1]s
mark :1
committer Test <[email protected]> 0 +0000
data 14
add empty file
M 100644 inline %[3]s
data 4
base
reset refs/heads/%[2]s
from :1
commit refs/heads/%[2]s
mark :2
committer Test <[email protected]> 0 +0000
data 17
change empty file
from :1
M 100644 inline %[3]s
data 6
change
commit refs/heads/%[2]s
mark :3
committer Test <[email protected]> 0 +0000
data 17
revert empty file
from :2
M 100644 inline %[3]s
data 4
base
`, baseBranch, headBranch, emptyFile)
err := gitcmd.NewCommand("fast-import").WithDir(repoPath).WithStdinBytes([]byte(stdin)).RunWithStderr(t.Context())
require.NoError(t, err)
}