loading up the forgejo repo on tangled to test page performance
at forgejo 480 lines 18 kB view raw
1// Copyright 2023 The Forgejo Authors. All rights reserved. 2// SPDX-License-Identifier: MIT 3 4package integration 5 6import ( 7 "fmt" 8 "net/http" 9 "net/url" 10 "path" 11 "strconv" 12 "testing" 13 14 "forgejo.org/models/activities" 15 "forgejo.org/models/db" 16 issue_model "forgejo.org/models/issues" 17 repo_model "forgejo.org/models/repo" 18 "forgejo.org/models/unittest" 19 user_model "forgejo.org/models/user" 20 "forgejo.org/modules/translation" 21 forgejo_context "forgejo.org/services/context" 22 "forgejo.org/tests" 23 24 "github.com/stretchr/testify/assert" 25) 26 27func BlockUser(t *testing.T, doer, blockedUser *user_model.User) { 28 t.Helper() 29 30 unittest.AssertNotExistsBean(t, &user_model.BlockedUser{BlockID: blockedUser.ID, UserID: doer.ID}) 31 32 session := loginUser(t, doer.Name) 33 req := NewRequestWithValues(t, "POST", "/"+blockedUser.Name, map[string]string{ 34 "_csrf": GetCSRF(t, session, "/"+blockedUser.Name), 35 "action": "block", 36 }) 37 session.MakeRequest(t, req, http.StatusOK) 38 39 assert.True(t, unittest.BeanExists(t, &user_model.BlockedUser{BlockID: blockedUser.ID, UserID: doer.ID})) 40} 41 42// TestBlockUser ensures that users can execute blocking related actions can 43// happen under the correct conditions. 44func TestBlockUser(t *testing.T) { 45 defer tests.PrepareTestEnv(t)() 46 47 doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 8}) 48 blockedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) 49 session := loginUser(t, doer.Name) 50 51 t.Run("Block", func(t *testing.T) { 52 defer tests.PrintCurrentTest(t)() 53 BlockUser(t, doer, blockedUser) 54 }) 55 56 // Unblock user. 57 t.Run("Unblock", func(t *testing.T) { 58 defer tests.PrintCurrentTest(t)() 59 req := NewRequestWithValues(t, "POST", "/"+blockedUser.Name, map[string]string{ 60 "_csrf": GetCSRF(t, session, "/"+blockedUser.Name), 61 "action": "unblock", 62 }) 63 session.MakeRequest(t, req, http.StatusOK) 64 65 unittest.AssertNotExistsBean(t, &user_model.BlockedUser{BlockID: blockedUser.ID, UserID: doer.ID}) 66 }) 67 68 t.Run("Organization as target", func(t *testing.T) { 69 defer tests.PrintCurrentTest(t)() 70 targetOrg := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3, Type: user_model.UserTypeOrganization}) 71 72 t.Run("Block", func(t *testing.T) { 73 req := NewRequestWithValues(t, "POST", "/"+targetOrg.Name, map[string]string{ 74 "_csrf": GetCSRF(t, session, "/"+targetOrg.Name), 75 "action": "block", 76 }) 77 resp := session.MakeRequest(t, req, http.StatusBadRequest) 78 79 assert.Contains(t, resp.Body.String(), "Action \\\"block\\\" failed") 80 }) 81 82 t.Run("Unblock", func(t *testing.T) { 83 req := NewRequestWithValues(t, "POST", "/"+targetOrg.Name, map[string]string{ 84 "_csrf": GetCSRF(t, session, "/"+targetOrg.Name), 85 "action": "unblock", 86 }) 87 resp := session.MakeRequest(t, req, http.StatusBadRequest) 88 89 assert.Contains(t, resp.Body.String(), "Action \\\"unblock\\\" failed") 90 }) 91 }) 92} 93 94// TestBlockUserFromOrganization ensures that an organisation can block and unblock an user. 95func TestBlockUserFromOrganization(t *testing.T) { 96 defer tests.PrepareTestEnv(t)() 97 98 doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15}) 99 blockedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) 100 org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 17, Type: user_model.UserTypeOrganization}) 101 unittest.AssertNotExistsBean(t, &user_model.BlockedUser{BlockID: blockedUser.ID, UserID: org.ID}) 102 session := loginUser(t, doer.Name) 103 104 t.Run("Block user", func(t *testing.T) { 105 defer tests.PrintCurrentTest(t)() 106 107 req := NewRequestWithValues(t, "POST", org.OrganisationLink()+"/settings/blocked_users/block", map[string]string{ 108 "_csrf": GetCSRF(t, session, org.OrganisationLink()+"/settings/blocked_users"), 109 "uname": blockedUser.Name, 110 }) 111 session.MakeRequest(t, req, http.StatusSeeOther) 112 assert.True(t, unittest.BeanExists(t, &user_model.BlockedUser{BlockID: blockedUser.ID, UserID: org.ID})) 113 }) 114 115 t.Run("Unblock user", func(t *testing.T) { 116 defer tests.PrintCurrentTest(t)() 117 118 req := NewRequestWithValues(t, "POST", org.OrganisationLink()+"/settings/blocked_users/unblock", map[string]string{ 119 "_csrf": GetCSRF(t, session, org.OrganisationLink()+"/settings/blocked_users"), 120 "user_id": strconv.FormatInt(blockedUser.ID, 10), 121 }) 122 session.MakeRequest(t, req, http.StatusSeeOther) 123 unittest.AssertNotExistsBean(t, &user_model.BlockedUser{BlockID: blockedUser.ID, UserID: org.ID}) 124 }) 125 126 t.Run("Organization as target", func(t *testing.T) { 127 targetOrg := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3, Type: user_model.UserTypeOrganization}) 128 129 t.Run("Block", func(t *testing.T) { 130 defer tests.PrintCurrentTest(t)() 131 132 req := NewRequestWithValues(t, "POST", org.OrganisationLink()+"/settings/blocked_users/block", map[string]string{ 133 "_csrf": GetCSRF(t, session, org.OrganisationLink()+"/settings/blocked_users"), 134 "uname": targetOrg.Name, 135 }) 136 session.MakeRequest(t, req, http.StatusInternalServerError) 137 unittest.AssertNotExistsBean(t, &user_model.BlockedUser{BlockID: blockedUser.ID, UserID: targetOrg.ID}) 138 }) 139 140 t.Run("Unblock", func(t *testing.T) { 141 defer tests.PrintCurrentTest(t)() 142 143 req := NewRequestWithValues(t, "POST", org.OrganisationLink()+"/settings/blocked_users/unblock", map[string]string{ 144 "_csrf": GetCSRF(t, session, org.OrganisationLink()+"/settings/blocked_users"), 145 "user_id": strconv.FormatInt(targetOrg.ID, 10), 146 }) 147 session.MakeRequest(t, req, http.StatusInternalServerError) 148 }) 149 }) 150 151 t.Run("Block the doer", func(t *testing.T) { 152 defer tests.PrintCurrentTest(t)() 153 154 req := NewRequestWithValues(t, "POST", org.OrganisationLink()+"/settings/blocked_users/block", map[string]string{ 155 "_csrf": GetCSRF(t, session, org.OrganisationLink()+"/settings/blocked_users"), 156 "uname": doer.Name, 157 }) 158 session.MakeRequest(t, req, http.StatusSeeOther) 159 assert.False(t, unittest.BeanExists(t, &user_model.BlockedUser{BlockID: doer.ID, UserID: org.ID})) 160 flashCookie := session.GetCookie(forgejo_context.CookieNameFlash) 161 assert.NotNil(t, flashCookie) 162 assert.Equal(t, "error%3DYou%2Bcannot%2Bblock%2Byourself.", flashCookie.Value) 163 }) 164} 165 166// TestBlockActions ensures that certain actions cannot be performed as a doer 167// and as a blocked user and are handled cleanly after the blocking has taken 168// place. 169func TestBlockActions(t *testing.T) { 170 defer tests.AddFixtures("tests/integration/fixtures/TestBlockActions/")() 171 defer tests.PrepareTestEnv(t)() 172 173 doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 174 blockedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) 175 blockedUser2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10}) 176 repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1, OwnerID: doer.ID}) 177 repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2, OwnerID: doer.ID}) 178 repo7 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 7, OwnerID: blockedUser2.ID}) 179 issue4 := unittest.AssertExistsAndLoadBean(t, &issue_model.Issue{ID: 4, RepoID: repo2.ID}) 180 issue4URL := fmt.Sprintf("/%s/issues/%d", repo2.FullName(), issue4.Index) 181 repo42 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 42, OwnerID: doer.ID}) 182 issue10 := unittest.AssertExistsAndLoadBean(t, &issue_model.Issue{ID: 10, RepoID: repo42.ID}, unittest.Cond("poster_id != ?", doer.ID)) 183 issue10URL := fmt.Sprintf("/%s/issues/%d", repo42.FullName(), issue10.Index) 184 // NOTE: Sessions shouldn't be shared, because in some situations flash 185 // messages are persistent and that would interfere with accurate test 186 // results. 187 188 BlockUser(t, doer, blockedUser) 189 BlockUser(t, doer, blockedUser2) 190 191 type errorJSON struct { 192 Error string `json:"errorMessage"` 193 } 194 locale := translation.NewLocale("en-US") 195 196 // Ensures that issue creation on doer's owned repositories are blocked. 197 t.Run("Issue creation", func(t *testing.T) { 198 defer tests.PrintCurrentTest(t)() 199 200 session := loginUser(t, blockedUser.Name) 201 link := fmt.Sprintf("%s/issues/new", repo2.FullName()) 202 203 req := NewRequestWithValues(t, "POST", link, map[string]string{ 204 "_csrf": GetCSRF(t, session, link), 205 "title": "Title", 206 "content": "Hello!", 207 }) 208 resp := session.MakeRequest(t, req, http.StatusBadRequest) 209 210 var errorResp errorJSON 211 DecodeJSON(t, resp, &errorResp) 212 213 assert.EqualValues(t, locale.Tr("repo.issues.blocked_by_user"), errorResp.Error) 214 }) 215 216 // Ensures that pull creation on doer's owned repositories are blocked. 217 t.Run("Pull creation", func(t *testing.T) { 218 defer tests.PrintCurrentTest(t)() 219 220 session := loginUser(t, blockedUser.Name) 221 link := fmt.Sprintf("%s/compare/v1.1...master", repo1.FullName()) 222 223 req := NewRequestWithValues(t, "POST", link, map[string]string{ 224 "_csrf": GetCSRF(t, session, link), 225 "title": "Title", 226 "content": "Hello!", 227 }) 228 resp := session.MakeRequest(t, req, http.StatusBadRequest) 229 230 var errorResp errorJSON 231 DecodeJSON(t, resp, &errorResp) 232 233 assert.EqualValues(t, locale.Tr("repo.pulls.blocked_by_user"), errorResp.Error) 234 }) 235 236 // Ensures that comment creation on doer's owned repositories and doer's 237 // posted issues are blocked. 238 t.Run("Comment creation", func(t *testing.T) { 239 expectedMessage := locale.Tr("repo.comment.blocked_by_user") 240 241 t.Run("Blocked by repository owner", func(t *testing.T) { 242 defer tests.PrintCurrentTest(t)() 243 244 session := loginUser(t, blockedUser.Name) 245 246 req := NewRequestWithValues(t, "POST", path.Join(issue10URL, "/comments"), map[string]string{ 247 "_csrf": GetCSRF(t, session, issue10URL), 248 "content": "Not a kind comment", 249 }) 250 resp := session.MakeRequest(t, req, http.StatusBadRequest) 251 252 var errorResp errorJSON 253 DecodeJSON(t, resp, &errorResp) 254 255 assert.EqualValues(t, expectedMessage, errorResp.Error) 256 257 req = NewRequest(t, "GET", issue10URL) 258 resp = session.MakeRequest(t, req, http.StatusOK) 259 htmlDoc := NewHTMLParser(t, resp.Body) 260 msg := htmlDoc.doc.Find("div .warning").Text() 261 assert.Contains(t, msg, expectedMessage) 262 }) 263 264 t.Run("Blocked by issue poster", func(t *testing.T) { 265 defer tests.PrintCurrentTest(t)() 266 267 repo5 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 5}) 268 issue15 := unittest.AssertExistsAndLoadBean(t, &issue_model.Issue{ID: 15, RepoID: repo5.ID, PosterID: doer.ID}) 269 270 session := loginUser(t, blockedUser.Name) 271 issueURL := fmt.Sprintf("/%s/%s/issues/%d", url.PathEscape(repo5.OwnerName), url.PathEscape(repo5.Name), issue15.Index) 272 273 req := NewRequestWithValues(t, "POST", path.Join(issueURL, "/comments"), map[string]string{ 274 "_csrf": GetCSRF(t, session, issueURL), 275 "content": "Not a kind comment", 276 }) 277 resp := session.MakeRequest(t, req, http.StatusBadRequest) 278 279 var errorResp errorJSON 280 DecodeJSON(t, resp, &errorResp) 281 282 assert.EqualValues(t, expectedMessage, errorResp.Error) 283 284 req = NewRequest(t, "GET", issue10URL) 285 resp = session.MakeRequest(t, req, http.StatusOK) 286 htmlDoc := NewHTMLParser(t, resp.Body) 287 msg := htmlDoc.doc.Find("div .warning").Text() 288 assert.Contains(t, msg, expectedMessage) 289 }) 290 }) 291 292 // Ensures that reactions on doer's owned issues and doer's owned comments are 293 // blocked. 294 t.Run("Add a reaction", func(t *testing.T) { 295 type reactionResponse struct { 296 Empty bool `json:"empty"` 297 } 298 299 t.Run("On a issue", func(t *testing.T) { 300 defer tests.PrintCurrentTest(t)() 301 302 session := loginUser(t, blockedUser.Name) 303 304 req := NewRequestWithValues(t, "POST", path.Join(issue4URL, "/reactions/react"), map[string]string{ 305 "_csrf": GetCSRF(t, session, issue4URL), 306 "content": "eyes", 307 }) 308 resp := session.MakeRequest(t, req, http.StatusOK) 309 310 var respBody reactionResponse 311 DecodeJSON(t, resp, &respBody) 312 313 assert.True(t, respBody.Empty) 314 }) 315 316 t.Run("On a comment", func(t *testing.T) { 317 defer tests.PrintCurrentTest(t)() 318 319 comment := unittest.AssertExistsAndLoadBean(t, &issue_model.Comment{ID: 1008, PosterID: doer.ID, IssueID: issue4.ID}) 320 321 session := loginUser(t, blockedUser.Name) 322 323 req := NewRequestWithValues(t, "POST", fmt.Sprintf("%s/comments/%d/reactions/react", repo2.FullName(), comment.ID), map[string]string{ 324 "_csrf": GetCSRF(t, session, issue4URL), 325 "content": "eyes", 326 }) 327 resp := session.MakeRequest(t, req, http.StatusOK) 328 329 var respBody reactionResponse 330 DecodeJSON(t, resp, &respBody) 331 332 assert.True(t, respBody.Empty) 333 }) 334 }) 335 336 // Ensures that the doer and blocked user cannot follow each other. 337 t.Run("Follow", func(t *testing.T) { 338 // Sanity checks to make sure doing these tests are valid. 339 unittest.AssertNotExistsBean(t, &user_model.Follow{UserID: doer.ID, FollowID: blockedUser.ID}) 340 unittest.AssertNotExistsBean(t, &user_model.Follow{UserID: blockedUser.ID, FollowID: doer.ID}) 341 342 // Doer cannot follow blocked user. 343 t.Run("Doer follow blocked user", func(t *testing.T) { 344 defer tests.PrintCurrentTest(t)() 345 346 session := loginUser(t, doer.Name) 347 348 req := NewRequestWithValues(t, "POST", "/"+blockedUser.Name, map[string]string{ 349 "_csrf": GetCSRF(t, session, "/"+blockedUser.Name), 350 "action": "follow", 351 }) 352 resp := session.MakeRequest(t, req, http.StatusOK) 353 354 htmlDoc := NewHTMLParser(t, resp.Body) 355 assert.Contains(t, htmlDoc.Find("#flash-message").Text(), "You cannot follow this user because you have blocked this user or this user has blocked you.") 356 357 // Assert it still doesn't exist. 358 unittest.AssertNotExistsBean(t, &user_model.Follow{UserID: doer.ID, FollowID: blockedUser.ID}) 359 }) 360 361 // Blocked user cannot follow doer. 362 t.Run("Blocked user follow doer", func(t *testing.T) { 363 defer tests.PrintCurrentTest(t)() 364 365 session := loginUser(t, blockedUser.Name) 366 367 req := NewRequestWithValues(t, "POST", "/"+doer.Name, map[string]string{ 368 "_csrf": GetCSRF(t, session, "/"+doer.Name), 369 "action": "follow", 370 }) 371 resp := session.MakeRequest(t, req, http.StatusOK) 372 373 htmlDoc := NewHTMLParser(t, resp.Body) 374 assert.Contains(t, htmlDoc.Find("#flash-message").Text(), "You cannot follow this user because you have blocked this user or this user has blocked you.") 375 376 unittest.AssertNotExistsBean(t, &user_model.Follow{UserID: blockedUser.ID, FollowID: doer.ID}) 377 }) 378 }) 379 380 // Ensures that the doer and blocked user cannot add each each other as collaborators. 381 t.Run("Add collaborator", func(t *testing.T) { 382 t.Run("Doer Add BlockedUser", func(t *testing.T) { 383 defer tests.PrintCurrentTest(t)() 384 385 session := loginUser(t, doer.Name) 386 link := fmt.Sprintf("/%s/settings/collaboration", repo2.FullName()) 387 388 req := NewRequestWithValues(t, "POST", link, map[string]string{ 389 "_csrf": GetCSRF(t, session, link), 390 "collaborator": blockedUser2.Name, 391 }) 392 session.MakeRequest(t, req, http.StatusSeeOther) 393 394 flashCookie := session.GetCookie(forgejo_context.CookieNameFlash) 395 assert.NotNil(t, flashCookie) 396 assert.Equal(t, "error%3DCannot%2Badd%2Bthe%2Bcollaborator%252C%2Bbecause%2Bthe%2Brepository%2Bowner%2Bhas%2Bblocked%2Bthem.", flashCookie.Value) 397 }) 398 399 t.Run("BlockedUser Add doer", func(t *testing.T) { 400 defer tests.PrintCurrentTest(t)() 401 402 session := loginUser(t, blockedUser2.Name) 403 link := fmt.Sprintf("/%s/settings/collaboration", repo7.FullName()) 404 405 req := NewRequestWithValues(t, "POST", link, map[string]string{ 406 "_csrf": GetCSRF(t, session, link), 407 "collaborator": doer.Name, 408 }) 409 session.MakeRequest(t, req, http.StatusSeeOther) 410 411 flashCookie := session.GetCookie(forgejo_context.CookieNameFlash) 412 assert.NotNil(t, flashCookie) 413 assert.Equal(t, "error%3DCannot%2Badd%2Bthe%2Bcollaborator%252C%2Bbecause%2Bthey%2Bhave%2Bblocked%2Bthe%2Brepository%2Bowner.", flashCookie.Value) 414 }) 415 }) 416 417 // Ensures that the blocked user cannot transfer a repository to the doer. 418 t.Run("Repository transfer", func(t *testing.T) { 419 defer tests.PrintCurrentTest(t)() 420 421 session := loginUser(t, blockedUser2.Name) 422 link := fmt.Sprintf("%s/settings", repo7.FullName()) 423 424 req := NewRequestWithValues(t, "POST", link, map[string]string{ 425 "_csrf": GetCSRF(t, session, link), 426 "action": "transfer", 427 "repo_name": repo7.FullName(), 428 "new_owner_name": doer.Name, 429 }) 430 resp := session.MakeRequest(t, req, http.StatusOK) 431 432 htmlDoc := NewHTMLParser(t, resp.Body) 433 assert.Contains(t, 434 htmlDoc.doc.Find(".ui.negative.message").Text(), 435 translation.NewLocale("en-US").Tr("repo.settings.new_owner_blocked_doer"), 436 ) 437 }) 438} 439 440func TestBlockedNotification(t *testing.T) { 441 defer tests.AddFixtures("tests/integration/fixtures/TestBlockedNotifications")() 442 defer tests.PrepareTestEnv(t)() 443 444 doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 445 normalUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) 446 blockedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10}) 447 issue := unittest.AssertExistsAndLoadBean(t, &issue_model.Issue{ID: 1000}) 448 repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) 449 issueURL := fmt.Sprintf("%s/issues/%d", repo.FullName(), issue.Index) 450 notificationBean := &activities.Notification{UserID: doer.ID, RepoID: repo.ID, IssueID: issue.ID} 451 452 assert.False(t, user_model.IsBlocked(db.DefaultContext, doer.ID, normalUser.ID)) 453 BlockUser(t, doer, blockedUser) 454 455 mentionDoer := func(t *testing.T, session *TestSession) { 456 t.Helper() 457 458 req := NewRequestWithValues(t, "POST", issueURL+"/comments", map[string]string{ 459 "_csrf": GetCSRF(t, session, issueURL), 460 "content": "I'm annoying. Pinging @" + doer.Name, 461 }) 462 session.MakeRequest(t, req, http.StatusOK) 463 } 464 465 t.Run("Blocks notification of blocked user", func(t *testing.T) { 466 session := loginUser(t, blockedUser.Name) 467 468 unittest.AssertNotExistsBean(t, notificationBean) 469 mentionDoer(t, session) 470 unittest.AssertNotExistsBean(t, notificationBean) 471 }) 472 473 t.Run("Do not block notifications of normal user", func(t *testing.T) { 474 session := loginUser(t, normalUser.Name) 475 476 unittest.AssertNotExistsBean(t, notificationBean) 477 mentionDoer(t, session) 478 unittest.AssertExistsAndLoadBean(t, notificationBean) 479 }) 480}