diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index eec878201..15cfe0664 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2425,7 +2425,7 @@ settings.choose_branch = Choose a branch… settings.no_protected_branch = There are no protected branches. settings.edit_protected_branch = Edit settings.protected_branch_required_rule_name = Required rule name -settings.protected_branch_duplicate_rule_name = Duplicate rule name +settings.protected_branch_duplicate_rule_name = There is already a rule for this set of branches settings.protected_branch_required_approvals_min = Required approvals cannot be negative. settings.tags = Tags settings.tags.protection = Tag Protection diff --git a/routers/web/repo/setting/protected_branch.go b/routers/web/repo/setting/protected_branch.go index 85068f0ab..827ebea7f 100644 --- a/routers/web/repo/setting/protected_branch.go +++ b/routers/web/repo/setting/protected_branch.go @@ -132,12 +132,15 @@ func SettingsProtectedBranchPost(ctx *context.Context) { } } } else { - // FIXME: If a new ProtectBranch has a duplicate RuleName, an error should be returned. - // Currently, if a new ProtectBranch with a duplicate RuleName is created, the existing ProtectBranch will be updated. - // But we cannot modify this logic now because many unit tests rely on it. + // Check if a rule already exists with this rulename, if so redirect to it. protectBranch, err = git_model.GetProtectedBranchRuleByName(ctx, ctx.Repo.Repository.ID, f.RuleName) if err != nil { - ctx.ServerError("GetProtectBranchOfRepoByName", err) + ctx.ServerError("GetProtectedBranchRuleByName", err) + return + } + if protectBranch != nil { + ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_duplicate_rule_name")) + ctx.Redirect(fmt.Sprintf("%s/settings/branches/edit?rule_name=%s", ctx.Repo.RepoLink, protectBranch.RuleName)) return } } diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go index 01fdaf4c7..365e7f91b 100644 --- a/tests/integration/git_test.go +++ b/tests/integration/git_test.go @@ -18,6 +18,7 @@ import ( auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" @@ -413,12 +414,17 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes func doProtectBranch(ctx APITestContext, branch, userToWhitelist, unprotectedFilePatterns string) func(t *testing.T) { // We are going to just use the owner to set the protection. return func(t *testing.T) { + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: ctx.Reponame, OwnerName: ctx.Username}) + rule := &git_model.ProtectedBranch{RuleName: branch, RepoID: repo.ID} + unittest.LoadBeanIfExists(rule) + csrf := GetCSRF(t, ctx.Session, fmt.Sprintf("/%s/%s/settings/branches", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame))) if userToWhitelist == "" { // Change branch to protected req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/edit", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)), map[string]string{ "_csrf": csrf, + "rule_id": strconv.FormatInt(rule.ID, 10), "rule_name": branch, "unprotected_file_patterns": unprotectedFilePatterns, }) @@ -430,6 +436,7 @@ func doProtectBranch(ctx APITestContext, branch, userToWhitelist, unprotectedFil req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/edit", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)), map[string]string{ "_csrf": csrf, "rule_name": branch, + "rule_id": strconv.FormatInt(rule.ID, 10), "enable_push": "whitelist", "enable_whitelist": "on", "whitelist_users": strconv.FormatInt(user.ID, 10), diff --git a/tests/integration/repo_settings_test.go b/tests/integration/repo_settings_test.go index 16e0bb3d7..307614815 100644 --- a/tests/integration/repo_settings_test.go +++ b/tests/integration/repo_settings_test.go @@ -9,10 +9,12 @@ import ( "testing" "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" repo_service "code.gitea.io/gitea/services/repository" "code.gitea.io/gitea/tests" @@ -128,3 +130,43 @@ func TestRepoAddMoreUnits(t *testing.T) { assertAddMore(t, false) }) } + +func TestProtectedBranch(t *testing.T) { + defer tests.PrepareTestEnv(t)() + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1, OwnerID: user.ID}) + session := loginUser(t, user.Name) + + t.Run("Add", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + link := fmt.Sprintf("/%s/settings/branches/edit", repo.FullName()) + + req := NewRequestWithValues(t, "POST", link, map[string]string{ + "_csrf": GetCSRF(t, session, link), + "rule_name": "master", + "enable_push": "true", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + // Verify it was added. + unittest.AssertExistsIf(t, true, &git_model.ProtectedBranch{RuleName: "master", RepoID: repo.ID}) + }) + + t.Run("Add duplicate", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + link := fmt.Sprintf("/%s/settings/branches/edit", repo.FullName()) + + req := NewRequestWithValues(t, "POST", link, map[string]string{ + "_csrf": GetCSRF(t, session, link), + "rule_name": "master", + "require_signed_": "true", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + flashCookie := session.GetCookie(gitea_context.CookieNameFlash) + assert.NotNil(t, flashCookie) + assert.EqualValues(t, "error%3DThere%2Bis%2Balready%2Ba%2Brule%2Bfor%2Bthis%2Bset%2Bof%2Bbranches", flashCookie.Value) + + // Verify it wasn't added. + unittest.AssertCount(t, &git_model.ProtectedBranch{RuleName: "master", RepoID: repo.ID}, 1) + }) +}