forgejo/tests/integration/comment_roles_test.go
Gusted a4642af51a
Some checks are pending
/ release (push) Waiting to run
testing-integration / test-unit (push) Waiting to run
testing-integration / test-sqlite (push) Waiting to run
testing-integration / test-mariadb (v10.6) (push) Waiting to run
testing-integration / test-mariadb (v11.8) (push) Waiting to run
testing / backend-checks (push) Waiting to run
testing / frontend-checks (push) Waiting to run
testing / test-unit (push) Blocked by required conditions
testing / test-e2e (push) Blocked by required conditions
testing / test-remote-cacher (redis) (push) Blocked by required conditions
testing / test-remote-cacher (valkey) (push) Blocked by required conditions
testing / test-remote-cacher (garnet) (push) Blocked by required conditions
testing / test-remote-cacher (redict) (push) Blocked by required conditions
testing / test-mysql (push) Blocked by required conditions
testing / test-pgsql (push) Blocked by required conditions
testing / test-sqlite (push) Blocked by required conditions
testing / security-check (push) Blocked by required conditions
feat: replace cross origin protection (#9830)
Replace the anti-CSRF token with a [cross origin protection by Go](https://go.dev/doc/go1.25#nethttppkgnethttp) that uses a stateless way of verifying if a request was cross origin or not. This allows is to remove al lot of code and replace it with a few lines of code and we no longer have to hand roll this protection. The new protection uses indicators by the browser itself that indicate if the request is cross-origin, thus we no longer have to take care of ensuring the generated CSRF token is passed back to the server any request by the the browser will have send this indicator.

Resolves forgejo/forgejo#3538

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9830
Reviewed-by: oliverpool <oliverpool@noreply.codeberg.org>
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
2025-10-29 22:43:22 +01:00

203 lines
8.3 KiB
Go

// Copyright 2024-2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package integration
import (
"net/http"
"net/url"
"path"
"strings"
"testing"
"forgejo.org/modules/translation"
"github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert"
)
// TestCommentRoles is a test for role labels of normal users in comment headers in PRs and issues.
func TestCommentRoles(t *testing.T) {
user := "user2"
repo := "repo1"
locale := translation.NewLocale("en-US")
authorTooltipPR := locale.TrString("repo.issues.author.tooltip.pr")
authorTooltipIssue := locale.TrString("repo.issues.author.tooltip.issue")
ownerTooltip := locale.TrString("repo.issues.role.owner_helper")
contributorTooltip := locale.TrString("repo.issues.role.contributor_helper")
newContributorTooltip := locale.TrString("repo.issues.role.first_time_contributor_helper")
// Test pulls
onApplicationRun(t, func(t *testing.T, giteaURL *url.URL) {
sessionUser1 := loginUser(t, "user1")
sessionUser2 := loginUser(t, "user2")
sessionUser11 := loginUser(t, "user11")
// Open a new PR as user2
testEditFileToNewBranch(t, sessionUser2, user, repo, "master", "comment-labels", "README.md", "test of comment labels\naline") // Owner
sessionUser2.MakeRequest(t, NewRequestWithValues(t, "POST", path.Join(user, repo, "compare", "master...comment-labels"),
map[string]string{
"title": "Pull used for testing commit labels",
},
), http.StatusOK)
// Pull number, expected to be 6
testID := "6"
// Add a few comments
// (first: Owner)
testEasyLeavePRReviewComment(t, sessionUser2, user, repo, testID, "README.md", "1", "New review comment from user2 on this line", "")
// Have to fetch reply ID for reviews
response := sessionUser2.MakeRequest(t, NewRequest(t, "GET", path.Join(user, repo, "pulls", testID)), http.StatusOK)
page := NewHTMLParser(t, response.Body)
replyID, _ := page.Find(".comment-form input[name='reply']").Attr("value")
testEasyLeavePRReviewComment(t, sessionUser1, user, repo, testID, "README.md", "1", "Reply comment from a contributor", replyID)
testEasyLeavePRComment(t, sessionUser2, user, repo, testID, "New comment from user2 on this PR") // Author, Owner
testEasyLeavePRComment(t, sessionUser1, user, repo, testID, "New comment from user1 on this PR") // Contributor
testEasyLeavePRComment(t, sessionUser11, user, repo, testID, "New comment from user11 on this PR") // First-time contributor
// Fetch the PR page
response = sessionUser2.MakeRequest(t, NewRequest(t, "GET", path.Join(user, repo, "pulls", testID)), http.StatusOK)
page = NewHTMLParser(t, response.Body)
reviewHeads := page.Find(".timeline .code-comment .header .comment-header-right")
assert.Equal(t, 2, reviewHeads.Length())
commentHeads := page.Find(".timeline .comment .comment-header .comment-header-right")
assert.Equal(t, 4, commentHeads.Length())
// === Review comments ===
// Test the first review comment labels
labels := reviewHeads.Eq(0).Find(".role-label")
assert.Equal(t, 2, labels.Length())
testIssueCommentUserLabel(t, labels.Eq(0), "Author", authorTooltipPR)
testIssueCommentUserLabel(t, labels.Eq(1), "Owner", ownerTooltip)
// Test the second review comment labels
labels = reviewHeads.Eq(1).Find(".role-label")
assert.Equal(t, 1, labels.Length())
testIssueCommentUserLabel(t, labels.Eq(0), "Contributor", contributorTooltip)
//== Top comment ==
// Top comment (PR description) never shows `Author` label because it is implied
labels = commentHeads.Eq(0).Find(".role-label")
assert.Equal(t, 1, labels.Length())
testIssueCommentUserLabel(t, labels.Eq(0), "Owner", ownerTooltip)
// === Regular comments ===
// Test the first regular comment labels
labels = commentHeads.Eq(1).Find(".role-label")
assert.Equal(t, 2, labels.Length())
testIssueCommentUserLabel(t, labels.Eq(0), "Author", authorTooltipPR)
testIssueCommentUserLabel(t, labels.Eq(1), "Owner", ownerTooltip)
// Test the second regular comment labels
labels = commentHeads.Eq(2).Find(".role-label")
assert.Equal(t, 1, labels.Length())
testIssueCommentUserLabel(t, labels.Eq(0), "Contributor", contributorTooltip)
// Test the third regular comment labels
labels = commentHeads.Eq(3).Find(".role-label")
assert.Equal(t, 1, labels.Length())
testIssueCommentUserLabel(t, labels.Eq(0), "First-time contributor", newContributorTooltip)
})
// Test issues
onApplicationRun(t, func(t *testing.T, giteaURL *url.URL) {
sessionUser1 := loginUser(t, "user1")
sessionUser2 := loginUser(t, "user2")
sessionUser5 := loginUser(t, "user5")
// Open a new issue in the same repo
sessionUser2.MakeRequest(t, NewRequestWithValues(t, "POST", path.Join(user, repo, "issues/new"),
map[string]string{
"title": "Issue used for testing commit labels",
},
), http.StatusOK)
// Issue number, expected to be 6
testID := "6"
// Add a few comments
// (first: Owner)
testEasyLeaveIssueComment(t, sessionUser2, user, repo, testID, "New comment from user2 on this issue") // Author, Owner
testEasyLeaveIssueComment(t, sessionUser1, user, repo, testID, "New comment from user1 on this issue") // Contributor
testEasyLeaveIssueComment(t, sessionUser5, user, repo, testID, "New comment from user5 on this issue") // no labels
// Fetch the issue page
response := sessionUser2.MakeRequest(t, NewRequest(t, "GET", path.Join(user, repo, "issues", testID)), http.StatusOK)
page := NewHTMLParser(t, response.Body)
commentHeads := page.Find(".timeline .comment .comment-header .comment-header-right")
assert.Equal(t, 4, commentHeads.Length())
// Test the first comment and it's label "Owner"
labels := commentHeads.Eq(0).Find(".role-label")
assert.Equal(t, 1, labels.Length())
testIssueCommentUserLabel(t, labels.Eq(0), "Owner", ownerTooltip)
// Test the second comment and it's labels "Author" and "Owner"
labels = commentHeads.Eq(1).Find(".role-label")
assert.Equal(t, 2, labels.Length())
testIssueCommentUserLabel(t, labels.Eq(0), "Author", authorTooltipIssue)
testIssueCommentUserLabel(t, labels.Eq(1), "Owner", ownerTooltip)
// Test the third comment and it's label "Contributor"
labels = commentHeads.Eq(2).Find(".role-label")
assert.Equal(t, 1, labels.Length())
testIssueCommentUserLabel(t, labels.Eq(0), "Contributor", contributorTooltip)
// Test the fifth comment and it's lack of labels
labels = commentHeads.Eq(3).Find(".role-label")
assert.Equal(t, 0, labels.Length())
})
}
// testIssueCommentUserLabel is used to verify properties of a user label from a comment
func testIssueCommentUserLabel(t *testing.T, label *goquery.Selection, expectedTitle, expectedTooltip string) {
t.Helper()
title := label.Text()
tooltip, exists := label.Attr("data-tooltip-content")
assert.True(t, exists)
assert.Equal(t, expectedTitle, strings.TrimSpace(title))
assert.Equal(t, expectedTooltip, strings.TrimSpace(tooltip))
}
// testEasyLeaveIssueComment is used to create a comment on an issue with minimum code and parameters
func testEasyLeaveIssueComment(t *testing.T, session *TestSession, user, repo, id, message string) {
t.Helper()
session.MakeRequest(t, NewRequestWithValues(t, "POST", path.Join(user, repo, "issues", id, "comments"), map[string]string{
"content": message,
"status": "",
}), 200)
}
// testEasyLeaveIssueComment is used to create a comment on a pull request with minimum code and parameters
// The POST request is supposed to use "issues" in the path. The CSRF is supposed to be generated for the PR page.
func testEasyLeavePRComment(t *testing.T, session *TestSession, user, repo, id, message string) {
t.Helper()
session.MakeRequest(t, NewRequestWithValues(t, "POST", path.Join(user, repo, "issues", id, "comments"), map[string]string{
"content": message,
"status": "",
}), 200)
}
// testEasyLeavePRReviewComment is used to add review comments to specific lines of changed files in the diff of the PR.
func testEasyLeavePRReviewComment(t *testing.T, session *TestSession, user, repo, id, file, line, message, replyID string) {
t.Helper()
values := map[string]string{
"origin": "diff",
"side": "proposed",
"line": line,
"path": file,
"content": message,
"single_review": "true",
}
if len(replyID) > 0 {
values["reply"] = replyID
}
session.MakeRequest(t, NewRequestWithValues(t, "POST", path.Join(user, repo, "pulls", id, "files/reviews/comments"), values), http.StatusOK)
}