loading up the forgejo repo on tangled to test page performance
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}