Optimize 'refreshAccesses' to perform update without removing then adding (#35702)
- Optimize refreshAccesses with cross-comparison to minimize DB operations - Fix db.Find syntax in refreshAccesses optimization - Add test for refreshAccesses update path and fix db.Find syntax --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -15,11 +15,20 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAccessLevel(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
func TestAccess(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
t.Run("AccessLevel", testAccessLevel)
|
||||
t.Run("HasAccess", testHasAccess)
|
||||
t.Run("RecalculateAccesses", testRecalculateAccesses)
|
||||
t.Run("RecalculateAccesses2", testRecalculateAccesses2)
|
||||
t.Run("RecalculateAccessesUpdateMode", testRecalculateAccessesUpdateMode)
|
||||
t.Run("RecalculateAccessesRemoveAccess", testRecalculateAccessesRemoveAccess)
|
||||
}
|
||||
|
||||
func testAccessLevel(t *testing.T) {
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
|
||||
user29 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29})
|
||||
@@ -75,9 +84,7 @@ func TestAccessLevel(t *testing.T) {
|
||||
assert.Equal(t, perm_model.AccessModeRead, level)
|
||||
}
|
||||
|
||||
func TestHasAccess(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
func testHasAccess(t *testing.T) {
|
||||
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
|
||||
// A public repository owned by User 2
|
||||
@@ -101,9 +108,8 @@ func TestHasAccess(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestRepository_RecalculateAccesses(t *testing.T) {
|
||||
func testRecalculateAccesses(t *testing.T) {
|
||||
// test with organization repo
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
|
||||
assert.NoError(t, repo1.LoadOwner(t.Context()))
|
||||
|
||||
@@ -118,9 +124,8 @@ func TestRepository_RecalculateAccesses(t *testing.T) {
|
||||
assert.Equal(t, perm_model.AccessModeOwner, access.Mode)
|
||||
}
|
||||
|
||||
func TestRepository_RecalculateAccesses2(t *testing.T) {
|
||||
func testRecalculateAccesses2(t *testing.T) {
|
||||
// test with non-organization repo
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||
assert.NoError(t, repo1.LoadOwner(t.Context()))
|
||||
|
||||
@@ -132,3 +137,67 @@ func TestRepository_RecalculateAccesses2(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, has)
|
||||
}
|
||||
|
||||
func testRecalculateAccessesUpdateMode(t *testing.T) {
|
||||
// Test the update path in refreshAccesses optimization
|
||||
// Scenario: User's access mode changes from Read to Write
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||
assert.NoError(t, repo.LoadOwner(t.Context()))
|
||||
|
||||
// Verify initial access mode
|
||||
_ = db.Insert(t.Context(), &repo_model.Collaboration{UserID: 4, RepoID: 4, Mode: perm_model.AccessModeWrite})
|
||||
_ = db.Insert(t.Context(), &access_model.Access{UserID: 4, RepoID: 4, Mode: perm_model.AccessModeWrite})
|
||||
initialAccess := &access_model.Access{UserID: 4, RepoID: 4}
|
||||
has, err := db.GetEngine(t.Context()).Get(initialAccess)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
initialMode := initialAccess.Mode
|
||||
|
||||
// Change collaboration mode to trigger update path
|
||||
newMode := perm_model.AccessModeAdmin
|
||||
assert.NotEqual(t, initialMode, newMode, "New mode should differ from initial mode")
|
||||
|
||||
_, err = db.GetEngine(t.Context()).
|
||||
Where("user_id = ? AND repo_id = ?", 4, 4).
|
||||
Cols("mode").
|
||||
Update(&repo_model.Collaboration{Mode: newMode})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Recalculate accesses - should UPDATE existing access, not delete+insert
|
||||
assert.NoError(t, access_model.RecalculateAccesses(t.Context(), repo))
|
||||
|
||||
// Verify access was updated, not deleted and re-inserted
|
||||
updatedAccess := &access_model.Access{UserID: 4, RepoID: 4}
|
||||
has, err = db.GetEngine(t.Context()).Get(updatedAccess)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has, "Access should still exist")
|
||||
assert.Equal(t, newMode, updatedAccess.Mode, "Access mode should be updated to new collaboration mode")
|
||||
}
|
||||
|
||||
func testRecalculateAccessesRemoveAccess(t *testing.T) {
|
||||
// Test the delete path in refreshAccesses optimization
|
||||
// Scenario: Remove a user's collaboration, access should be deleted
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||
assert.NoError(t, repo.LoadOwner(t.Context()))
|
||||
|
||||
// Verify initial access exists
|
||||
initialAccess := &access_model.Access{UserID: 4, RepoID: 4}
|
||||
has, err := db.GetEngine(t.Context()).Get(initialAccess)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has, "Access should exist initially")
|
||||
|
||||
// Remove the collaboration to trigger delete path
|
||||
_, err = db.GetEngine(t.Context()).
|
||||
Where("user_id = ? AND repo_id = ?", 4, 4).
|
||||
Delete(&repo_model.Collaboration{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Recalculate accesses - should DELETE the access record
|
||||
assert.NoError(t, access_model.RecalculateAccesses(t.Context(), repo))
|
||||
|
||||
// Verify access was deleted
|
||||
removedAccess := &access_model.Access{UserID: 4, RepoID: 4}
|
||||
has, err = db.GetEngine(t.Context()).Get(removedAccess)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, has, "Access should be deleted after removing collaboration")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user