Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workaround to clean up old reviews on creating a new one #28554

Merged
merged 25 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cf0f77d
Clean up old reviews on creating a new one
6543 Dec 20, 2023
05db236
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Dec 20, 2023
9245e65
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Dec 20, 2023
9a71752
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Dec 21, 2023
a6adb7e
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Dec 21, 2023
e08e306
else if
6543 Dec 21, 2023
00abbf6
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Dec 23, 2023
8a06ccb
handle ReviewTypePending
6543 Dec 23, 2023
9fcea4c
Update models/issues/review.go
6543 Dec 23, 2023
b03faf4
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Jan 13, 2024
0df942a
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Jan 19, 2024
84024c1
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Feb 12, 2024
f1b38c0
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Feb 12, 2024
f4fbcf5
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Feb 14, 2024
d4049d9
return result for unittest.AssertCount* helper func
6543 Feb 15, 2024
2db7e7c
Add TestAPIPullReviewStayDismissed test
6543 Feb 15, 2024
18dc9b3
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Feb 15, 2024
758dd63
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Feb 15, 2024
ab2ef38
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Feb 16, 2024
01dafcc
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Feb 16, 2024
36a2152
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Feb 19, 2024
68c4de2
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Feb 19, 2024
d23ace7
Merge branch 'main' into cleanup_old-reviews-on-review
lafriks Feb 19, 2024
b3d062d
test against rerequest and add some codecomments
6543 Feb 19, 2024
48fa3ad
Merge branch 'main' into cleanup_old-reviews-on-review
6543 Feb 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 34 additions & 5 deletions models/issues/review.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,14 @@ func IsOfficialReviewerTeam(ctx context.Context, issue *Issue, team *organizatio

// CreateReview creates a new review based on opts
func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error) {
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return nil, err
}
defer committer.Close()
sess := db.GetEngine(ctx)

review := &Review{
Type: opts.Type,
Issue: opts.Issue,
IssueID: opts.Issue.ID,
Reviewer: opts.Reviewer,
Expand All @@ -303,15 +309,38 @@ func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error
CommitID: opts.CommitID,
Stale: opts.Stale,
}

if opts.Reviewer != nil {
review.Type = opts.Type
review.ReviewerID = opts.Reviewer.ID
} else {
if review.Type != ReviewTypeRequest {
review.Type = ReviewTypeRequest

reviewCond := builder.Eq{"reviewer_id": opts.Reviewer.ID, "issue_id": opts.Issue.ID}
// make sure user review requests are cleared
if opts.Type != ReviewTypePending {
if _, err := sess.Where(reviewCond.And(builder.Eq{"type": ReviewTypeRequest})).Delete(new(Review)); err != nil {
return nil, err
}
}
// make sure if the created review gets dismissed no old review surface
if opts.Type == ReviewTypeApprove || opts.Type == ReviewTypeReject {
wxiaoguang marked this conversation as resolved.
Show resolved Hide resolved
if _, err := sess.Where(reviewCond.And(builder.In("type", ReviewTypeApprove, ReviewTypeReject))).
6543 marked this conversation as resolved.
Show resolved Hide resolved
Cols("dismissed").Update(&Review{Dismissed: true}); err != nil {
return nil, err
}
}

} else if opts.ReviewerTeam != nil {
review.Type = ReviewTypeRequest
review.ReviewerTeamID = opts.ReviewerTeam.ID

} else {
return nil, fmt.Errorf("provide either reviewer or reviewer team")
}

if _, err := sess.Insert(review); err != nil {
return nil, err
}
return review, db.Insert(ctx, review)
return review, committer.Commit()
}

// GetCurrentReview returns the current pending review of reviewer for given issue
Expand Down
8 changes: 4 additions & 4 deletions models/unittest/unit_tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ func AssertSuccessfulInsert(t assert.TestingT, beans ...any) {
}

// AssertCount assert the count of a bean
func AssertCount(t assert.TestingT, bean, expected any) {
assert.EqualValues(t, expected, GetCount(t, bean))
func AssertCount(t assert.TestingT, bean, expected any) bool {
return assert.EqualValues(t, expected, GetCount(t, bean))
}

// AssertInt64InRange assert value is in range [low, high]
Expand All @@ -150,7 +150,7 @@ func GetCountByCond(t assert.TestingT, tableName string, cond builder.Cond) int6
}

// AssertCountByCond test the count of database entries matching bean
func AssertCountByCond(t assert.TestingT, tableName string, cond builder.Cond, expected int) {
assert.EqualValues(t, expected, GetCountByCond(t, tableName, cond),
func AssertCountByCond(t assert.TestingT, tableName string, cond builder.Cond, expected int) bool {
return assert.EqualValues(t, expected, GetCountByCond(t, tableName, cond),
"Failed consistency test, the counted bean (of table %s) was %+v", tableName, cond)
}
116 changes: 116 additions & 0 deletions tests/integration/api_pull_review_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/json"
api "code.gitea.io/gitea/modules/structs"
issue_service "code.gitea.io/gitea/services/issue"
"code.gitea.io/gitea/tests"

"github.com/stretchr/testify/assert"
"xorm.io/builder"
)

func TestAPIPullReview(t *testing.T) {
Expand Down Expand Up @@ -314,3 +317,116 @@ func TestAPIPullReviewRequest(t *testing.T) {
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNoContent)
}

func TestAPIPullReviewStayDismissed(t *testing.T) {
// This test against issue https://github.com/go-gitea/gitea/issues/28542
// where old reviews surface after a review request got dismissed.
defer tests.PrepareTestEnv(t)()
pullIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3})
assert.NoError(t, pullIssue.LoadAttributes(db.DefaultContext))
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.RepoID})
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session2 := loginUser(t, user2.LoginName)
token2 := getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeWriteRepository)
user8 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 8})
session8 := loginUser(t, user8.LoginName)
token8 := getTokenForLoggedInUser(t, session8, auth_model.AccessTokenScopeWriteRepository)

// user2 request user8 again
req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo.OwnerName, repo.Name, pullIssue.Index), &api.PullReviewRequestOptions{
Reviewers: []string{"user8"},
}).AddTokenAuth(token2)
MakeRequest(t, req, http.StatusCreated)

reviewsCountCheck(t,
"check we have only one review request",
pullIssue.ID, user8.ID, 0, 1, 1, false)

// user8 reviews it as accept
req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", repo.OwnerName, repo.Name, pullIssue.Index), &api.CreatePullReviewOptions{
Event: "APPROVED",
Body: "lgtm",
}).AddTokenAuth(token8)
MakeRequest(t, req, http.StatusOK)

reviewsCountCheck(t,
"check we have one valid approval",
pullIssue.ID, user8.ID, 0, 0, 1, true)

// emulate of auto-dismiss lgtm on a protected branch that where a pull just got an update
_, err := db.GetEngine(db.DefaultContext).Where("issue_id = ? AND reviewer_id = ?", pullIssue.ID, user8.ID).
Cols("dismissed").Update(&issues_model.Review{Dismissed: true})
assert.NoError(t, err)

// user2 request user8 again
req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/requested_reviewers", repo.OwnerName, repo.Name, pullIssue.Index), &api.PullReviewRequestOptions{
Reviewers: []string{"user8"},
}).AddTokenAuth(token2)
MakeRequest(t, req, http.StatusCreated)

reviewsCountCheck(t,
"check we have no valid approval and one review request",
pullIssue.ID, user8.ID, 1, 1, 2, false)

// user8 dismiss review
_, err = issue_service.ReviewRequest(db.DefaultContext, pullIssue, user8, user8, false)
assert.NoError(t, err)

reviewsCountCheck(t,
"check new review request is now dismissed",
pullIssue.ID, user8.ID, 1, 0, 1, false)

// add a new valid approval
req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", repo.OwnerName, repo.Name, pullIssue.Index), &api.CreatePullReviewOptions{
Event: "APPROVED",
Body: "lgtm",
}).AddTokenAuth(token8)
MakeRequest(t, req, http.StatusOK)

reviewsCountCheck(t,
"check that old reviews requests are deleted",
pullIssue.ID, user8.ID, 1, 0, 2, true)

// now add a change request witch should dismiss the approval
req = NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/reviews", repo.OwnerName, repo.Name, pullIssue.Index), &api.CreatePullReviewOptions{
Event: "REQUEST_CHANGES",
Body: "please change XYZ",
}).AddTokenAuth(token8)
MakeRequest(t, req, http.StatusOK)

reviewsCountCheck(t,
"check that old reviews are dismissed",
pullIssue.ID, user8.ID, 2, 0, 3, false)
}

func reviewsCountCheck(t *testing.T, name string, issueID, reviewerID int64, expectedDismissed, expectedRequested, expectedTotal int, expectApproval bool) {
t.Run(name, func(t *testing.T) {
unittest.AssertCountByCond(t, "review", builder.Eq{
"issue_id": issueID,
"reviewer_id": reviewerID,
"dismissed": true,
}, expectedDismissed)

unittest.AssertCountByCond(t, "review", builder.Eq{
"issue_id": issueID,
"reviewer_id": reviewerID,
}, expectedTotal)

unittest.AssertCountByCond(t, "review", builder.Eq{
"issue_id": issueID,
"reviewer_id": reviewerID,
"type": issues_model.ReviewTypeRequest,
}, expectedRequested)

approvalCount := 0
if expectApproval {
approvalCount = 1
}
unittest.AssertCountByCond(t, "review", builder.Eq{
"issue_id": issueID,
"reviewer_id": reviewerID,
"type": issues_model.ReviewTypeApprove,
"dismissed": false,
}, approvalCount)
})
}
Loading