loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(sec): Forgejo Actions web routes (#6844)

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6844
Reviewed-by: 0ko <0ko@noreply.codeberg.org>

+396 -42
+5 -10
models/actions/runner.go
··· 282 282 } 283 283 284 284 // DeleteRunner deletes a runner by given ID. 285 - func DeleteRunner(ctx context.Context, id int64) error { 286 - runner, err := GetRunnerByID(ctx, id) 287 - if err != nil { 288 - return err 289 - } 290 - 285 + func DeleteRunner(ctx context.Context, r *ActionRunner) error { 291 286 // Replace the UUID, which was either based on the secret's first 16 bytes or an UUIDv4, 292 287 // with a sequence of 8 0xff bytes followed by the little-endian version of the record's 293 288 // identifier. This will prevent the deleted record's identifier from colliding with any 294 289 // new record. 295 290 b := make([]byte, 8) 296 - binary.LittleEndian.PutUint64(b, uint64(id)) 297 - runner.UUID = fmt.Sprintf("ffffffff-ffff-ffff-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x", 291 + binary.LittleEndian.PutUint64(b, uint64(r.ID)) 292 + r.UUID = fmt.Sprintf("ffffffff-ffff-ffff-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x", 298 293 b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]) 299 294 300 - err = UpdateRunner(ctx, runner, "UUID") 295 + err := UpdateRunner(ctx, r, "UUID") 301 296 if err != nil { 302 297 return err 303 298 } 304 299 305 - _, err = db.DeleteByID[ActionRunner](ctx, id) 300 + _, err = db.DeleteByID[ActionRunner](ctx, r.ID) 306 301 return err 307 302 } 308 303
+1 -1
models/actions/runner_test.go
··· 34 34 require.NoError(t, unittest.PrepareTestDatabase()) 35 35 before := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID}) 36 36 37 - err := DeleteRunner(db.DefaultContext, recordID) 37 + err := DeleteRunner(db.DefaultContext, &ActionRunner{ID: recordID}) 38 38 require.NoError(t, err) 39 39 40 40 var after ActionRunner
+4 -6
models/actions/variable.go
··· 86 86 } 87 87 88 88 func UpdateVariable(ctx context.Context, variable *ActionVariable) (bool, error) { 89 - count, err := db.GetEngine(ctx).ID(variable.ID).Cols("name", "data"). 89 + count, err := db.GetEngine(ctx).ID(variable.ID).Where("owner_id = ? AND repo_id = ?", variable.OwnerID, variable.RepoID).Cols("name", "data"). 90 90 Update(&ActionVariable{ 91 91 Name: variable.Name, 92 92 Data: variable.Data, ··· 94 94 return count != 0, err 95 95 } 96 96 97 - func DeleteVariable(ctx context.Context, id int64) error { 98 - if _, err := db.DeleteByID[ActionVariable](ctx, id); err != nil { 99 - return err 100 - } 101 - return nil 97 + func DeleteVariable(ctx context.Context, variableID, ownerID, repoID int64) (bool, error) { 98 + count, err := db.GetEngine(ctx).Table("action_variable").Where("id = ? AND owner_id = ? AND repo_id = ?", variableID, ownerID, repoID).Delete() 99 + return count != 0, err 102 100 } 103 101 104 102 func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) {
+1
options/locale/locale_en-US.ini
··· 3903 3903 variables.description = Variables will be passed to certain actions and cannot be read otherwise. 3904 3904 variables.id_not_exist = Variable with ID %d does not exist. 3905 3905 variables.edit = Edit Variable 3906 + variables.not_found = Failed to find the variable. 3906 3907 variables.deletion.failed = Failed to remove variable. 3907 3908 variables.deletion.success = The variable has been removed. 3908 3909 variables.creation.failed = Failed to add variable.
+1 -1
routers/api/v1/org/action.go
··· 475 475 if opt.Name == "" { 476 476 opt.Name = ctx.Params("variablename") 477 477 } 478 - if _, err := actions_service.UpdateVariable(ctx, v.ID, opt.Name, opt.Value); err != nil { 478 + if _, err := actions_service.UpdateVariable(ctx, v.ID, ctx.Org.Organization.ID, 0, opt.Name, opt.Value); err != nil { 479 479 if errors.Is(err, util.ErrInvalidArgument) { 480 480 ctx.Error(http.StatusBadRequest, "UpdateVariable", err) 481 481 } else {
+1 -1
routers/api/v1/repo/action.go
··· 414 414 if opt.Name == "" { 415 415 opt.Name = ctx.Params("variablename") 416 416 } 417 - if _, err := actions_service.UpdateVariable(ctx, v.ID, opt.Name, opt.Value); err != nil { 417 + if _, err := actions_service.UpdateVariable(ctx, v.ID, 0, ctx.Repo.Repository.ID, opt.Name, opt.Value); err != nil { 418 418 if errors.Is(err, util.ErrInvalidArgument) { 419 419 ctx.Error(http.StatusBadRequest, "UpdateVariable", err) 420 420 } else {
+1 -1
routers/api/v1/user/action.go
··· 228 228 if opt.Name == "" { 229 229 opt.Name = ctx.Params("variablename") 230 230 } 231 - if _, err := actions_service.UpdateVariable(ctx, v.ID, opt.Name, opt.Value); err != nil { 231 + if _, err := actions_service.UpdateVariable(ctx, v.ID, ctx.Doer.ID, 0, opt.Name, opt.Value); err != nil { 232 232 if errors.Is(err, util.ErrInvalidArgument) { 233 233 ctx.Error(http.StatusBadRequest, "UpdateVariable", err) 234 234 } else {
+1 -1
routers/web/repo/setting/runners.go
··· 179 179 ctx.ServerError("getRunnersCtx", err) 180 180 return 181 181 } 182 - actions_shared.RunnerDeletePost(ctx, ctx.ParamsInt64(":runnerid"), rCtx.RedirectLink, rCtx.RedirectLink+url.PathEscape(ctx.Params(":runnerid"))) 182 + actions_shared.RunnerDeletePost(ctx, ctx.ParamsInt64(":runnerid"), rCtx.OwnerID, rCtx.RepoID, rCtx.RedirectLink, rCtx.RedirectLink+url.PathEscape(ctx.Params(":runnerid"))) 183 183 } 184 184 185 185 func RedirectToDefaultSetting(ctx *context.Context) {
+2 -2
routers/web/repo/setting/variables.go
··· 127 127 return 128 128 } 129 129 130 - shared.UpdateVariable(ctx, vCtx.RedirectLink) 130 + shared.UpdateVariable(ctx, vCtx.OwnerID, vCtx.RepoID, vCtx.RedirectLink) 131 131 } 132 132 133 133 func VariableDelete(ctx *context.Context) { ··· 136 136 ctx.ServerError("getVariablesCtx", err) 137 137 return 138 138 } 139 - shared.DeleteVariable(ctx, vCtx.RedirectLink) 139 + shared.DeleteVariable(ctx, vCtx.OwnerID, vCtx.RepoID, vCtx.RedirectLink) 140 140 }
+13 -2
routers/web/shared/actions/runners.go
··· 142 142 } 143 143 144 144 // RunnerDeletePost response for deleting a runner 145 - func RunnerDeletePost(ctx *context.Context, runnerID int64, 145 + func RunnerDeletePost(ctx *context.Context, runnerID, ownerID, repoID int64, 146 146 successRedirectTo, failedRedirectTo string, 147 147 ) { 148 - if err := actions_model.DeleteRunner(ctx, runnerID); err != nil { 148 + runner, err := actions_model.GetRunnerByID(ctx, runnerID) 149 + if err != nil { 150 + ctx.ServerError("GetRunnerByID", err) 151 + return 152 + } 153 + 154 + if !runner.Editable(ownerID, repoID) { 155 + ctx.NotFound("Editable", util.NewPermissionDeniedErrorf("no permission to edit this runner")) 156 + return 157 + } 158 + 159 + if err := actions_model.DeleteRunner(ctx, runner); err != nil { 149 160 log.Warn("DeleteRunnerPost.UpdateRunner failed: %v, url: %s", err, ctx.Req.URL) 150 161 ctx.Flash.Warning(ctx.Tr("actions.runners.delete_runner_failed")) 151 162
+16 -8
routers/web/shared/actions/variables.go
··· 39 39 ctx.JSONRedirect(redirectURL) 40 40 } 41 41 42 - func UpdateVariable(ctx *context.Context, redirectURL string) { 42 + func UpdateVariable(ctx *context.Context, ownerID, repoID int64, redirectURL string) { 43 43 id := ctx.ParamsInt64(":variable_id") 44 44 form := web.GetForm(ctx).(*forms.EditVariableForm) 45 45 46 - if ok, err := actions_service.UpdateVariable(ctx, id, form.Name, form.Data); err != nil || !ok { 47 - log.Error("UpdateVariable: %v", err) 48 - ctx.JSONError(ctx.Tr("actions.variables.update.failed")) 46 + if ok, err := actions_service.UpdateVariable(ctx, id, ownerID, repoID, form.Name, form.Data); err != nil || !ok { 47 + if !ok { 48 + ctx.JSONError(ctx.Tr("actions.variables.not_found")) 49 + } else { 50 + log.Error("UpdateVariable: %v", err) 51 + ctx.JSONError(ctx.Tr("actions.variables.update.failed")) 52 + } 49 53 return 50 54 } 51 55 ctx.Flash.Success(ctx.Tr("actions.variables.update.success")) 52 56 ctx.JSONRedirect(redirectURL) 53 57 } 54 58 55 - func DeleteVariable(ctx *context.Context, redirectURL string) { 59 + func DeleteVariable(ctx *context.Context, ownerID, repoID int64, redirectURL string) { 56 60 id := ctx.ParamsInt64(":variable_id") 57 61 58 - if err := actions_service.DeleteVariableByID(ctx, id); err != nil { 59 - log.Error("Delete variable [%d] failed: %v", id, err) 60 - ctx.JSONError(ctx.Tr("actions.variables.deletion.failed")) 62 + if ok, err := actions_model.DeleteVariable(ctx, id, ownerID, repoID); err != nil || !ok { 63 + if !ok { 64 + ctx.JSONError(ctx.Tr("actions.variables.not_found")) 65 + } else { 66 + log.Error("Delete variable [%d] failed: %v", id, err) 67 + ctx.JSONError(ctx.Tr("actions.variables.deletion.failed")) 68 + } 61 69 return 62 70 } 63 71 ctx.Flash.Success(ctx.Tr("actions.variables.deletion.success"))
+8 -9
services/actions/variables.go
··· 31 31 return v, nil 32 32 } 33 33 34 - func UpdateVariable(ctx context.Context, variableID int64, name, data string) (bool, error) { 34 + func UpdateVariable(ctx context.Context, variableID, ownerID, repoID int64, name, data string) (bool, error) { 35 35 if err := secret_service.ValidateName(name); err != nil { 36 36 return false, err 37 37 } ··· 41 41 } 42 42 43 43 return actions_model.UpdateVariable(ctx, &actions_model.ActionVariable{ 44 - ID: variableID, 45 - Name: strings.ToUpper(name), 46 - Data: util.ReserveLineBreakForTextarea(data), 44 + ID: variableID, 45 + Name: strings.ToUpper(name), 46 + Data: util.ReserveLineBreakForTextarea(data), 47 + OwnerID: ownerID, 48 + RepoID: repoID, 47 49 }) 48 - } 49 - 50 - func DeleteVariableByID(ctx context.Context, variableID int64) error { 51 - return actions_model.DeleteVariable(ctx, variableID) 52 50 } 53 51 54 52 func DeleteVariableByName(ctx context.Context, ownerID, repoID int64, name string) error { ··· 69 67 return err 70 68 } 71 69 72 - return actions_model.DeleteVariable(ctx, v.ID) 70 + _, err = actions_model.DeleteVariable(ctx, v.ID, ownerID, repoID) 71 + return err 73 72 } 74 73 75 74 func GetVariable(ctx context.Context, opts actions_model.FindVariablesOpts) (*actions_model.ActionVariable, error) {
+150
tests/integration/actions_variables_test.go
··· 1 + // Copyright 2025 The Forgejo Authors. All rights reserved. 2 + // SPDX-License-Identifier: GPL-3.0-or-later 3 + 4 + package integration 5 + 6 + import ( 7 + "fmt" 8 + "net/http" 9 + "testing" 10 + 11 + actions_model "code.gitea.io/gitea/models/actions" 12 + repo_model "code.gitea.io/gitea/models/repo" 13 + "code.gitea.io/gitea/models/unittest" 14 + user_model "code.gitea.io/gitea/models/user" 15 + forgejo_context "code.gitea.io/gitea/services/context" 16 + "code.gitea.io/gitea/tests" 17 + 18 + "github.com/stretchr/testify/assert" 19 + ) 20 + 21 + func TestActionVariablesModification(t *testing.T) { 22 + defer tests.AddFixtures("tests/integration/fixtures/TestActionVariablesModification")() 23 + defer tests.PrepareTestEnv(t)() 24 + 25 + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 26 + userVariable := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionVariable{ID: 1001, OwnerID: user.ID}) 27 + userURL := "/user/settings/actions/variables" 28 + org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3, Type: user_model.UserTypeOrganization}) 29 + orgVariable := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionVariable{ID: 1002, OwnerID: org.ID}) 30 + orgURL := "/org/" + org.Name + "/settings/actions/variables" 31 + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1, OwnerID: user.ID}) 32 + repoVariable := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionVariable{ID: 1003, RepoID: repo.ID}) 33 + repoURL := "/" + repo.FullName() + "/settings/actions/variables" 34 + admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{IsAdmin: true}) 35 + globalVariable := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionVariable{ID: 1004}, "owner_id = 0 AND repo_id = 0") 36 + adminURL := "/admin/actions/variables" 37 + 38 + adminSess := loginUser(t, admin.Name) 39 + adminCSRF := GetCSRF(t, adminSess, "/") 40 + sess := loginUser(t, user.Name) 41 + csrf := GetCSRF(t, sess, "/") 42 + 43 + type errorJSON struct { 44 + Error string `json:"errorMessage"` 45 + } 46 + 47 + test := func(t *testing.T, fail bool, baseURL string, id int64) { 48 + defer tests.PrintCurrentTest(t, 1)() 49 + t.Helper() 50 + 51 + sess := sess 52 + csrf := csrf 53 + if baseURL == adminURL { 54 + sess = adminSess 55 + csrf = adminCSRF 56 + } 57 + 58 + req := NewRequestWithValues(t, "POST", baseURL+fmt.Sprintf("/%d/edit", id), map[string]string{ 59 + "_csrf": csrf, 60 + "name": "glados_quote", 61 + "data": "I'm fine. Two plus two is...ten, in base four, I'm fine!", 62 + }) 63 + if fail { 64 + resp := sess.MakeRequest(t, req, http.StatusBadRequest) 65 + var error errorJSON 66 + DecodeJSON(t, resp, &error) 67 + assert.EqualValues(t, "Failed to find the variable.", error.Error) 68 + } else { 69 + sess.MakeRequest(t, req, http.StatusOK) 70 + flashCookie := sess.GetCookie(forgejo_context.CookieNameFlash) 71 + assert.NotNil(t, flashCookie) 72 + assert.EqualValues(t, "success%3DThe%2Bvariable%2Bhas%2Bbeen%2Bedited.", flashCookie.Value) 73 + } 74 + 75 + req = NewRequestWithValues(t, "POST", baseURL+fmt.Sprintf("/%d/delete", id), map[string]string{ 76 + "_csrf": csrf, 77 + }) 78 + if fail { 79 + resp := sess.MakeRequest(t, req, http.StatusBadRequest) 80 + var error errorJSON 81 + DecodeJSON(t, resp, &error) 82 + assert.EqualValues(t, "Failed to find the variable.", error.Error) 83 + } else { 84 + sess.MakeRequest(t, req, http.StatusOK) 85 + flashCookie := sess.GetCookie(forgejo_context.CookieNameFlash) 86 + assert.NotNil(t, flashCookie) 87 + assert.EqualValues(t, "success%3DThe%2Bvariable%2Bhas%2Bbeen%2Bremoved.", flashCookie.Value) 88 + } 89 + } 90 + 91 + t.Run("User variable", func(t *testing.T) { 92 + t.Run("Organisation", func(t *testing.T) { 93 + test(t, true, orgURL, userVariable.ID) 94 + }) 95 + t.Run("Repository", func(t *testing.T) { 96 + test(t, true, repoURL, userVariable.ID) 97 + }) 98 + t.Run("Admin", func(t *testing.T) { 99 + test(t, true, adminURL, userVariable.ID) 100 + }) 101 + t.Run("User", func(t *testing.T) { 102 + test(t, false, userURL, userVariable.ID) 103 + }) 104 + }) 105 + 106 + t.Run("Organisation variable", func(t *testing.T) { 107 + t.Run("Repository", func(t *testing.T) { 108 + test(t, true, repoURL, orgVariable.ID) 109 + }) 110 + t.Run("User", func(t *testing.T) { 111 + test(t, true, userURL, orgVariable.ID) 112 + }) 113 + t.Run("Admin", func(t *testing.T) { 114 + test(t, true, adminURL, userVariable.ID) 115 + }) 116 + t.Run("Organisation", func(t *testing.T) { 117 + test(t, false, orgURL, orgVariable.ID) 118 + }) 119 + }) 120 + 121 + t.Run("Repository variable", func(t *testing.T) { 122 + t.Run("Organisation", func(t *testing.T) { 123 + test(t, true, orgURL, repoVariable.ID) 124 + }) 125 + t.Run("User", func(t *testing.T) { 126 + test(t, true, userURL, repoVariable.ID) 127 + }) 128 + t.Run("Admin", func(t *testing.T) { 129 + test(t, true, adminURL, userVariable.ID) 130 + }) 131 + t.Run("Repository", func(t *testing.T) { 132 + test(t, false, repoURL, repoVariable.ID) 133 + }) 134 + }) 135 + 136 + t.Run("Global variable", func(t *testing.T) { 137 + t.Run("Organisation", func(t *testing.T) { 138 + test(t, true, orgURL, globalVariable.ID) 139 + }) 140 + t.Run("User", func(t *testing.T) { 141 + test(t, true, userURL, globalVariable.ID) 142 + }) 143 + t.Run("Repository", func(t *testing.T) { 144 + test(t, true, repoURL, globalVariable.ID) 145 + }) 146 + t.Run("Admin", func(t *testing.T) { 147 + test(t, false, adminURL, globalVariable.ID) 148 + }) 149 + }) 150 + }
+31
tests/integration/fixtures/TestActionVariablesModification/action_variable.yml
··· 1 + - 2 + id: 1001 3 + name: GLADOS_QUOTE 4 + owner_id: 2 5 + repo_id: 0 6 + data: "" 7 + created_unix: 1737000000 8 + 9 + - 10 + id: 1002 11 + name: GLADOS_QUOTE 12 + owner_id: 3 13 + repo_id: 0 14 + data: "" 15 + created_unix: 1737000001 16 + 17 + - 18 + id: 1003 19 + name: GLADOS_QUOTE 20 + owner_id: 0 21 + repo_id: 1 22 + data: "" 23 + created_unix: 1737000002 24 + 25 + - 26 + id: 1004 27 + name: GLADOS_QUOTE 28 + owner_id: 0 29 + repo_id: 0 30 + data: "" 31 + created_unix: 1737000003
+31
tests/integration/fixtures/TestRunnerModification/action_runner.yml
··· 1 + - 2 + id: 1001 3 + uuid: "43b5d4d3-401b-42f9-94df-a9d45b447b82" 4 + name: "User runner" 5 + owner_id: 2 6 + repo_id: 0 7 + deleted: 0 8 + 9 + - 10 + id: 1002 11 + uuid: "bdc77f4f-2b2b-442d-bd44-e808f4306347" 12 + name: "Organisation runner" 13 + owner_id: 3 14 + repo_id: 0 15 + deleted: 0 16 + 17 + - 18 + id: 1003 19 + uuid: "9268bc8c-efbf-4dbe-aeb5-945733cdd098" 20 + name: "Repository runner" 21 + owner_id: 0 22 + repo_id: 1 23 + deleted: 0 24 + 25 + - 26 + id: 1004 27 + uuid: "fb857e63-c0ce-4571-a6c9-fde26c128073" 28 + name: "Global runner" 29 + owner_id: 0 30 + repo_id: 0 31 + deleted: 0
+130
tests/integration/runner_test.go
··· 1 + // Copyright 2025 The Forgejo Authors. All rights reserved. 2 + // SPDX-License-Identifier: GPL-3.0-or-later 3 + 4 + package integration 5 + 6 + import ( 7 + "fmt" 8 + "net/http" 9 + "testing" 10 + 11 + actions_model "code.gitea.io/gitea/models/actions" 12 + repo_model "code.gitea.io/gitea/models/repo" 13 + "code.gitea.io/gitea/models/unittest" 14 + user_model "code.gitea.io/gitea/models/user" 15 + forgejo_context "code.gitea.io/gitea/services/context" 16 + "code.gitea.io/gitea/tests" 17 + 18 + "github.com/stretchr/testify/assert" 19 + ) 20 + 21 + func TestRunnerModification(t *testing.T) { 22 + defer tests.AddFixtures("tests/integration/fixtures/TestRunnerModification")() 23 + defer tests.PrepareTestEnv(t)() 24 + 25 + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 26 + userRunner := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{ID: 1001, OwnerID: user.ID}) 27 + userURL := "/user/settings/actions/runners" 28 + org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3, Type: user_model.UserTypeOrganization}) 29 + orgRunner := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{ID: 1002, OwnerID: org.ID}) 30 + orgURL := "/org/" + org.Name + "/settings/actions/runners" 31 + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1, OwnerID: user.ID}) 32 + repoRunner := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{ID: 1003, RepoID: repo.ID}) 33 + repoURL := "/" + repo.FullName() + "/settings/actions/runners" 34 + admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{IsAdmin: true}) 35 + globalRunner := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunner{ID: 1004}, "owner_id = 0 AND repo_id = 0") 36 + adminURL := "/admin/actions/runners" 37 + 38 + adminSess := loginUser(t, admin.Name) 39 + adminCSRF := GetCSRF(t, adminSess, "/") 40 + sess := loginUser(t, user.Name) 41 + csrf := GetCSRF(t, sess, "/") 42 + 43 + test := func(t *testing.T, fail bool, baseURL string, id int64) { 44 + defer tests.PrintCurrentTest(t, 1)() 45 + t.Helper() 46 + 47 + sess := sess 48 + csrf := csrf 49 + if baseURL == adminURL { 50 + sess = adminSess 51 + csrf = adminCSRF 52 + } 53 + 54 + req := NewRequestWithValues(t, "POST", baseURL+fmt.Sprintf("/%d", id), map[string]string{ 55 + "_csrf": csrf, 56 + "description": "New Description", 57 + }) 58 + if fail { 59 + sess.MakeRequest(t, req, http.StatusNotFound) 60 + } else { 61 + sess.MakeRequest(t, req, http.StatusSeeOther) 62 + flashCookie := sess.GetCookie(forgejo_context.CookieNameFlash) 63 + assert.NotNil(t, flashCookie) 64 + assert.EqualValues(t, "success%3DRunner%2Bupdated%2Bsuccessfully", flashCookie.Value) 65 + } 66 + 67 + req = NewRequestWithValues(t, "POST", baseURL+fmt.Sprintf("/%d/delete", id), map[string]string{ 68 + "_csrf": csrf, 69 + }) 70 + if fail { 71 + sess.MakeRequest(t, req, http.StatusNotFound) 72 + } else { 73 + sess.MakeRequest(t, req, http.StatusOK) 74 + flashCookie := sess.GetCookie(forgejo_context.CookieNameFlash) 75 + assert.NotNil(t, flashCookie) 76 + assert.EqualValues(t, "success%3DRunner%2Bdeleted%2Bsuccessfully", flashCookie.Value) 77 + } 78 + } 79 + 80 + t.Run("User runner", func(t *testing.T) { 81 + t.Run("Organisation", func(t *testing.T) { 82 + test(t, true, orgURL, userRunner.ID) 83 + }) 84 + t.Run("Repository", func(t *testing.T) { 85 + test(t, true, repoURL, userRunner.ID) 86 + }) 87 + t.Run("User", func(t *testing.T) { 88 + test(t, false, userURL, userRunner.ID) 89 + }) 90 + }) 91 + 92 + t.Run("Organisation runner", func(t *testing.T) { 93 + t.Run("Repository", func(t *testing.T) { 94 + test(t, true, repoURL, orgRunner.ID) 95 + }) 96 + t.Run("User", func(t *testing.T) { 97 + test(t, true, userURL, orgRunner.ID) 98 + }) 99 + t.Run("Organisation", func(t *testing.T) { 100 + test(t, false, orgURL, orgRunner.ID) 101 + }) 102 + }) 103 + 104 + t.Run("Repository runner", func(t *testing.T) { 105 + t.Run("Organisation", func(t *testing.T) { 106 + test(t, true, orgURL, repoRunner.ID) 107 + }) 108 + t.Run("User", func(t *testing.T) { 109 + test(t, true, userURL, repoRunner.ID) 110 + }) 111 + t.Run("Repository", func(t *testing.T) { 112 + test(t, false, repoURL, repoRunner.ID) 113 + }) 114 + }) 115 + 116 + t.Run("Global runner", func(t *testing.T) { 117 + t.Run("Organisation", func(t *testing.T) { 118 + test(t, true, orgURL, globalRunner.ID) 119 + }) 120 + t.Run("User", func(t *testing.T) { 121 + test(t, true, userURL, globalRunner.ID) 122 + }) 123 + t.Run("Repository", func(t *testing.T) { 124 + test(t, true, repoURL, globalRunner.ID) 125 + }) 126 + t.Run("Admin", func(t *testing.T) { 127 + test(t, false, adminURL, globalRunner.ID) 128 + }) 129 + }) 130 + }