Files
forge/services/repository/transfer_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

169 lines
6.3 KiB
Go

// Copyright 2026 Hanzo AI, Inc. All rights reserved.
// SPDX-License-Identifier: MIT
package repository
import (
"sync"
"testing"
activities_model "github.com/hanzoai/git/models/activities"
"github.com/hanzoai/git/models/organization"
access_model "github.com/hanzoai/git/models/perm/access"
repo_model "github.com/hanzoai/git/models/repo"
"github.com/hanzoai/git/models/unittest"
user_model "github.com/hanzoai/git/models/user"
"github.com/hanzoai/git/modules/setting"
"github.com/hanzoai/git/modules/test"
"github.com/hanzoai/git/modules/util"
"github.com/hanzoai/git/services/feed"
notify_service "github.com/hanzoai/git/services/notify"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var notifySync sync.Once
func registerNotifier() {
notifySync.Do(func() {
notify_service.RegisterNotifier(feed.NewNotifier())
})
}
func TestTransferOwnership(t *testing.T) {
registerNotifier()
assert.NoError(t, unittest.PrepareTestDatabase())
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
assert.NoError(t, repo.LoadOwner(t.Context()))
repoTransfer := unittest.AssertExistsAndLoadBean(t, &repo_model.RepoTransfer{ID: 1})
assert.NoError(t, repoTransfer.LoadAttributes(t.Context()))
assert.NoError(t, AcceptTransferOwnership(t.Context(), repo, doer))
transferredRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
assert.EqualValues(t, 1, transferredRepo.OwnerID) // repo_transfer.yml id=1
unittest.AssertNotExistsBean(t, &repo_model.RepoTransfer{ID: 1})
exist, err := util.IsExist(repo_model.RepoPath("org3", "repo3"))
assert.NoError(t, err)
assert.False(t, exist)
exist, err = util.IsExist(repo_model.RepoPath("user1", "repo3"))
assert.NoError(t, err)
assert.True(t, exist)
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
OpType: activities_model.ActionTransferRepo,
ActUserID: 1,
RepoID: 3,
Content: "org3/repo3",
})
unittest.CheckConsistencyFor(t, &repo_model.Repository{}, &user_model.User{}, &organization.Team{})
}
func TestStartRepositoryTransferSetPermission(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
recipient := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
assert.NoError(t, repo.LoadOwner(t.Context()))
hasAccess, err := access_model.HasAnyUnitAccess(t.Context(), recipient.ID, repo)
assert.NoError(t, err)
assert.False(t, hasAccess)
assert.NoError(t, StartRepositoryTransfer(t.Context(), doer, recipient, repo, nil))
hasAccess, err = access_model.HasAnyUnitAccess(t.Context(), recipient.ID, repo)
assert.NoError(t, err)
assert.True(t, hasAccess)
unittest.CheckConsistencyFor(t, &repo_model.Repository{}, &user_model.User{}, &organization.Team{})
}
func TestRepositoryTransfer(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
transfer, err := repo_model.GetPendingRepositoryTransfer(t.Context(), repo)
assert.NoError(t, err)
assert.NotNil(t, transfer)
// Cancel transfer
assert.NoError(t, CancelRepositoryTransfer(t.Context(), transfer, doer))
transfer, err = repo_model.GetPendingRepositoryTransfer(t.Context(), repo)
assert.Error(t, err)
assert.Nil(t, transfer)
assert.True(t, repo_model.IsErrNoPendingTransfer(err))
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
assert.NoError(t, repo_model.CreatePendingRepositoryTransfer(t.Context(), doer, user2, repo.ID, nil))
transfer, err = repo_model.GetPendingRepositoryTransfer(t.Context(), repo)
assert.NoError(t, err)
assert.NoError(t, transfer.LoadAttributes(t.Context()))
assert.Equal(t, "user2", transfer.Recipient.Name)
org6 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
// Only transfer can be started at any given time
err = repo_model.CreatePendingRepositoryTransfer(t.Context(), doer, org6, repo.ID, nil)
assert.Error(t, err)
assert.True(t, repo_model.IsErrRepoTransferInProgress(err))
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
// Unknown user, transfer non-existent transfer repo id = 2
err = repo_model.CreatePendingRepositoryTransfer(t.Context(), doer, &user_model.User{ID: 1000, LowerName: "user1000"}, repo2.ID, nil)
assert.Error(t, err)
// Reject transfer
err = RejectRepositoryTransfer(t.Context(), repo2, doer)
assert.True(t, repo_model.IsErrNoPendingTransfer(err))
}
// Test transfer rejections
func TestRepositoryTransferRejection(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
// Set limit to 0 repositories so no repositories can be transferred
defer test.MockVariableValue(&setting.Repository.MaxCreationLimit, 0)()
defer test.MockVariableValue(&setting.Repository.UserMaxCreationLimit, 0)()
defer test.MockVariableValue(&setting.Repository.OrgMaxCreationLimit, 0)()
// Admin case
doerAdmin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 5})
transfer, err := repo_model.GetPendingRepositoryTransfer(t.Context(), repo)
require.NoError(t, err)
require.NotNil(t, transfer)
require.NoError(t, transfer.LoadRecipient(t.Context()))
require.True(t, doerAdmin.CanCreateRepoIn(transfer.Recipient)) // admin is not subject to limits
// Administrator should not be affected by the limits so transfer should be successful
assert.NoError(t, AcceptTransferOwnership(t.Context(), repo, doerAdmin))
// Non admin user case
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10})
repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 21})
transfer, err = repo_model.GetPendingRepositoryTransfer(t.Context(), repo)
require.NoError(t, err)
require.NotNil(t, transfer)
require.NoError(t, transfer.LoadRecipient(t.Context()))
require.False(t, doer.CanCreateRepoIn(transfer.Recipient)) // regular user is subject to limits
// Cannot accept because of the limit
err = AcceptTransferOwnership(t.Context(), repo, doer)
assert.Error(t, err)
assert.True(t, IsRepositoryLimitReached(err))
}