forgejo/modules/git/repo_compare_test.go
Gusted 89469d14e9 feat: avoid remote for codeowner's merge base (#9610)
The codeowner features computes the mergebase (I'm not exactly sure why, because this should be stored in the database in `merge_base` column, but if there's no harm to compute it again as that will always be the correct answer) in order to get the changed files between the merge base and the head commit. To do this a function was used that adds a remote... my best reasoning is that this was done because the only function that that was exported on the repository struct had this requirement. Add a new function that *simply* computes the merge base without requiring a remote.

The main benefit of not using a remote is that within Codeberg we are frequently seeing `config.lock` being lingered around (see forgejo/forgejo#1946) so its best to avoid modifying the config when possible - in this case it was completely unnecessary.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9610
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
2025-10-10 15:50:38 +02:00

329 lines
9.7 KiB
Go

// Copyright 2018 The Gitea Authors. All rights reserved.
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package git
import (
"bytes"
"io"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetFormatPatch(t *testing.T) {
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
clonedPath, err := cloneRepo(t, bareRepo1Path)
if err != nil {
require.NoError(t, err)
return
}
repo, err := openRepositoryWithDefaultContext(clonedPath)
if err != nil {
require.NoError(t, err)
return
}
defer repo.Close()
rd := &bytes.Buffer{}
err = repo.GetPatch("8d92fc95^", "8d92fc95", rd)
if err != nil {
require.NoError(t, err)
return
}
patchb, err := io.ReadAll(rd)
if err != nil {
require.NoError(t, err)
return
}
patch := string(patchb)
assert.Regexp(t, "^From 8d92fc95", patch)
assert.Contains(t, patch, "Subject: [PATCH] Add file2.txt")
}
func TestReadPatch(t *testing.T) {
// Ensure we can read the patch files
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
repo, err := openRepositoryWithDefaultContext(bareRepo1Path)
if err != nil {
require.NoError(t, err)
return
}
defer repo.Close()
// This patch doesn't exist
noFile, err := repo.ReadPatchCommit(0)
require.Error(t, err)
// This patch is an empty one (sometimes it's a 404)
noCommit, err := repo.ReadPatchCommit(1)
require.Error(t, err)
// This patch is legit and should return a commit
oldCommit, err := repo.ReadPatchCommit(2)
if err != nil {
require.NoError(t, err)
return
}
assert.Empty(t, noFile)
assert.Empty(t, noCommit)
assert.Len(t, oldCommit, 40)
assert.Equal(t, "6e8e2a6f9efd71dbe6917816343ed8415ad696c3", oldCommit)
}
func TestReadWritePullHead(t *testing.T) {
// Ensure we can write SHA1 head corresponding to PR and open them
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
// As we are writing we should clone the repository first
clonedPath, err := cloneRepo(t, bareRepo1Path)
if err != nil {
require.NoError(t, err)
return
}
repo, err := openRepositoryWithDefaultContext(clonedPath)
if err != nil {
require.NoError(t, err)
return
}
defer repo.Close()
// Try to open non-existing Pull
_, err = repo.GetRefCommitID(PullPrefix + "0/head")
require.Error(t, err)
// Write a fake sha1 with only 40 zeros
newCommit := "feaf4ba6bc635fec442f46ddd4512416ec43c2c2"
err = repo.SetReference(PullPrefix+"1/head", newCommit)
if err != nil {
require.NoError(t, err)
return
}
// Read the file created
headContents, err := repo.GetRefCommitID(PullPrefix + "1/head")
if err != nil {
require.NoError(t, err)
return
}
assert.Len(t, headContents, 40)
assert.Equal(t, newCommit, headContents)
// Remove file after the test
err = repo.RemoveReference(PullPrefix + "1/head")
require.NoError(t, err)
}
func TestGetCommitFilesChanged(t *testing.T) {
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
repo, err := openRepositoryWithDefaultContext(bareRepo1Path)
require.NoError(t, err)
defer repo.Close()
objectFormat, err := repo.GetObjectFormat()
require.NoError(t, err)
testCases := []struct {
base, head string
files []string
}{
{
objectFormat.EmptyObjectID().String(),
"95bb4d39648ee7e325106df01a621c530863a653",
[]string{"file1.txt"},
},
{
objectFormat.EmptyObjectID().String(),
"8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2",
[]string{"file2.txt"},
},
{
"95bb4d39648ee7e325106df01a621c530863a653",
"8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2",
[]string{"file2.txt"},
},
{
objectFormat.EmptyTree().String(),
"8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2",
[]string{"file1.txt", "file2.txt"},
},
}
for _, tc := range testCases {
changedFiles, err := repo.GetFilesChangedBetween(tc.base, tc.head)
require.NoError(t, err)
assert.ElementsMatch(t, tc.files, changedFiles)
}
}
func TestGetCommitShortStat(t *testing.T) {
t.Run("repo1_bare", func(t *testing.T) {
repo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo1_bare"))
if err != nil {
require.NoError(t, err)
return
}
defer repo.Close()
numFiles, totalAddition, totalDeletions, err := repo.GetCommitShortStat("ce064814f4a0d337b333e646ece456cd39fab612")
require.NoError(t, err)
assert.Equal(t, 0, numFiles)
assert.Equal(t, 0, totalAddition)
assert.Equal(t, 0, totalDeletions)
numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("feaf4ba6bc635fec442f46ddd4512416ec43c2c2")
require.NoError(t, err)
assert.Equal(t, 0, numFiles)
assert.Equal(t, 0, totalAddition)
assert.Equal(t, 0, totalDeletions)
numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("37991dec2c8e592043f47155ce4808d4580f9123")
require.NoError(t, err)
assert.Equal(t, 1, numFiles)
assert.Equal(t, 1, totalAddition)
assert.Equal(t, 0, totalDeletions)
numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("6fbd69e9823458e6c4a2fc5c0f6bc022b2f2acd1")
require.NoError(t, err)
assert.Equal(t, 2, numFiles)
assert.Equal(t, 2, totalAddition)
assert.Equal(t, 0, totalDeletions)
numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("8006ff9adbf0cb94da7dad9e537e53817f9fa5c0")
require.NoError(t, err)
assert.Equal(t, 2, numFiles)
assert.Equal(t, 2, totalAddition)
assert.Equal(t, 0, totalDeletions)
numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2")
require.NoError(t, err)
assert.Equal(t, 1, numFiles)
assert.Equal(t, 1, totalAddition)
assert.Equal(t, 0, totalDeletions)
numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("95bb4d39648ee7e325106df01a621c530863a653")
require.NoError(t, err)
assert.Equal(t, 1, numFiles)
assert.Equal(t, 1, totalAddition)
assert.Equal(t, 0, totalDeletions)
})
t.Run("repo6_blame_sha256", func(t *testing.T) {
repo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo6_blame_sha256"))
if err != nil {
require.NoError(t, err)
return
}
defer repo.Close()
numFiles, totalAddition, totalDeletions, err := repo.GetCommitShortStat("e2f5660e15159082902960af0ed74fc144921d2b0c80e069361853b3ece29ba3")
require.NoError(t, err)
assert.Equal(t, 1, numFiles)
assert.Equal(t, 1, totalAddition)
assert.Equal(t, 0, totalDeletions)
numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("9347b0198cd1f25017579b79d0938fa89dba34ad2514f0dd92f6bc975ed1a2fe")
require.NoError(t, err)
assert.Equal(t, 1, numFiles)
assert.Equal(t, 1, totalAddition)
assert.Equal(t, 1, totalDeletions)
numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("ab2b57a4fa476fb2edb74dafa577caf918561abbaa8fba0c8dc63c412e17a7cc")
require.NoError(t, err)
assert.Equal(t, 1, numFiles)
assert.Equal(t, 6, totalAddition)
assert.Equal(t, 0, totalDeletions)
})
}
func TestGetShortStat(t *testing.T) {
// https://github.com/git/git/blob/60f3f52f17cceefa5299709b189ce6fe2d181e7b/t/t4068-diff-symmetric-merge-base.sh#L10-L23
repo, err := OpenRepository(t.Context(), filepath.Join(testReposDir, "symmetric_repo"))
require.NoError(t, err)
defer repo.Close()
t.Run("Normal", func(t *testing.T) {
t.Run("Via merge base", func(t *testing.T) {
numFiles, totalAdditions, totalDeletions, err := repo.GetShortStat("br2", "main", true)
require.NoError(t, err)
assert.Equal(t, 1, numFiles)
assert.Equal(t, 1, totalAdditions)
assert.Zero(t, totalDeletions)
numFiles, totalAdditions, totalDeletions, err = repo.GetShortStat("main", "br2", true)
require.NoError(t, err)
assert.Equal(t, 1, numFiles)
assert.Equal(t, 1, totalAdditions)
assert.Zero(t, totalDeletions)
})
t.Run("Direct compare", func(t *testing.T) {
numFiles, totalAdditions, totalDeletions, err := repo.GetShortStat("main", "br2", false)
require.NoError(t, err)
assert.Equal(t, 2, numFiles)
assert.Equal(t, 1, totalAdditions)
assert.Equal(t, 1, totalDeletions)
numFiles, totalAdditions, totalDeletions, err = repo.GetShortStat("main", "br3", false)
require.NoError(t, err)
assert.Equal(t, 1, numFiles)
assert.Equal(t, 1, totalAdditions)
assert.Zero(t, totalDeletions)
numFiles, totalAdditions, totalDeletions, err = repo.GetShortStat("br3", "main", false)
require.NoError(t, err)
assert.Equal(t, 1, numFiles)
assert.Zero(t, totalAdditions)
assert.Equal(t, 1, totalDeletions)
})
})
t.Run("No merge base", func(t *testing.T) {
numFiles, totalAdditions, totalDeletions, err := repo.GetShortStat("main", "br3", true)
require.ErrorIs(t, err, ErrNoMergebaseFound)
assert.Zero(t, numFiles)
assert.Zero(t, totalAdditions)
assert.Zero(t, totalDeletions)
})
t.Run("Multiple merge base", func(t *testing.T) {
numFiles, totalAdditions, totalDeletions, err := repo.GetShortStat("main", "br1", true)
require.ErrorIs(t, err, ErrMultipleMergebasesFound)
assert.Zero(t, numFiles)
assert.Zero(t, totalAdditions)
assert.Zero(t, totalDeletions)
})
}
func TestGetMergeBaseSimple(t *testing.T) {
repo, err := OpenRepository(t.Context(), filepath.Join(testReposDir, "symmetric_repo"))
require.NoError(t, err)
defer repo.Close()
t.Run("Normal", func(t *testing.T) {
mergebase, err := repo.GetMergeBaseSimple("main", "br2")
require.NoError(t, err)
assert.Equal(t, "9d36f18c8ca14ad28c4751afd14f3e3146a785dc", mergebase)
})
t.Run("No mergebase", func(t *testing.T) {
mergebase, err := repo.GetMergeBaseSimple("main", "br3")
require.ErrorContains(t, err, "exit status 1")
assert.Empty(t, mergebase)
})
t.Run("Multiple mergebase", func(t *testing.T) {
mergebase, err := repo.GetMergeBaseSimple("main", "br1")
require.NoError(t, err)
assert.Equal(t, "9d36f18c8ca14ad28c4751afd14f3e3146a785dc", mergebase)
})
}