@@ -35,6 +35,7 @@ import (
3535 "forgejo.org/modules/test"
3636 issue_service "forgejo.org/services/issue"
3737 "forgejo.org/services/mailer"
38+ pull_service "forgejo.org/services/pull"
3839 repo_service "forgejo.org/services/repository"
3940 files_service "forgejo.org/services/repository/files"
4041 "forgejo.org/tests"
@@ -2016,10 +2017,15 @@ func TestPullRequestCommentPlacement(t *testing.T) {
20162017 // Push a second commit that changes lines BEFORE the range (removing lines 1-10),
20172018 // which shifts the range but keeps it contiguous.
20182019 content = strings .Replace (content , "Line 1\n Line 2\n Line 3\n Line 4\n Line 5\n Line 6\n Line 7\n Line 8\n Line 9\n Line 10\n " , "" , 1 )
2019- tester .changeFile ("file1.md" , content )
2020+ newSHA := tester .changeFile ("file1.md" , content )
2021+
2022+ // Run the invalidation pass synchronously instead of waiting for the async
2023+ // goroutine, then check the comment is still valid.
2024+ pr , err := issues_model .GetPullRequestByIndex (t .Context (), tester .repo .ID , tester .pr .Index )
2025+ require .NoError (t , err )
2026+ require .NoError (t , pull_service .InvalidateCodeComments (t .Context (),
2027+ issues_model.PullRequestList {pr }, tester .user , tester .repo , newSHA ))
20202028
2021- // Wait a bit for async invalidation to run, then check the comment is still valid.
2022- time .Sleep (2 * time .Second )
20232029 commentReloaded := unittest .AssertExistsAndLoadBean (t , & issues_model.Comment {ID : comment .ID })
20242030 assert .False (t , commentReloaded .Invalidated )
20252031
@@ -2038,6 +2044,92 @@ func TestPullRequestCommentPlacement(t *testing.T) {
20382044 }
20392045 tester .assertFilesChangedDiff (diff )
20402046 })
2047+
2048+ // Helper: comment on lines 48-50 (anchor 48, middle 49, last 50, all modified by the PR), then a
2049+ // later commit applies `secondCommit` to the file and we check the resulting invalidation state.
2050+ runRangeInvalidation := func (t * testing.T , secondCommit func (content string ) string , wantInvalidated bool ) {
2051+ tester := newPullRequestCommentPlacementTester (t )
2052+
2053+ content := tester .fileContent
2054+ content = strings .Replace (content , "Line 48\n " , "Line 48--modified\n " , 1 )
2055+ content = strings .Replace (content , "Line 49\n " , "Line 49--modified\n " , 1 )
2056+ content = strings .Replace (content , "Line 50\n " , "Line 50--modified\n " , 1 )
2057+ tester .changeFile ("file1.md" , content )
2058+ tester .createPR ()
2059+
2060+ comment := tester .multiLineCommentFromFilesChanged ("file1.md" , 48 , 2 )
2061+ assert .EqualValues (t , 48 , comment .Line )
2062+ assert .EqualValues (t , 2 , comment .ExtraLinesCount )
2063+ assert .False (t , comment .Invalidated )
2064+
2065+ newSHA := tester .changeFile ("file1.md" , secondCommit (content ))
2066+
2067+ if wantInvalidated {
2068+ assert .EventuallyWithT (t , func (t * assert.CollectT ) {
2069+ commentReloaded := unittest .AssertExistsAndLoadBean (t , & issues_model.Comment {ID : comment .ID })
2070+ assert .True (t , commentReloaded .Invalidated )
2071+ }, 5 * time .Second , 50 * time .Millisecond )
2072+ } else {
2073+ // Run the invalidation pass synchronously instead of waiting for the async
2074+ // goroutine, then assert the comment stayed valid.
2075+ pr , err := issues_model .GetPullRequestByIndex (t .Context (), tester .repo .ID , tester .pr .Index )
2076+ require .NoError (t , err )
2077+ require .NoError (t , pull_service .InvalidateCodeComments (t .Context (),
2078+ issues_model.PullRequestList {pr }, tester .user , tester .repo , newSHA ))
2079+
2080+ commentReloaded := unittest .AssertExistsAndLoadBean (t , & issues_model.Comment {ID : comment .ID })
2081+ assert .False (t , commentReloaded .Invalidated )
2082+ }
2083+ }
2084+
2085+ t .Run ("multi-line comment invalidated when a middle line is deleted by a later commit" , func (t * testing.T ) {
2086+ defer tests .PrintCurrentTest (t )()
2087+ runRangeInvalidation (t , func (content string ) string {
2088+ return strings .Replace (content , "Line 49--modified\n " , "" , 1 )
2089+ }, true )
2090+ })
2091+
2092+ t .Run ("multi-line comment invalidated when a middle line content changes in a later commit" , func (t * testing.T ) {
2093+ defer tests .PrintCurrentTest (t )()
2094+ runRangeInvalidation (t , func (content string ) string {
2095+ return strings .Replace (content , "Line 49--modified\n " , "Line 49--changed-again\n " , 1 )
2096+ }, true )
2097+ })
2098+
2099+ t .Run ("multi-line comment invalidated when the last line content changes in a later commit" , func (t * testing.T ) {
2100+ defer tests .PrintCurrentTest (t )()
2101+ runRangeInvalidation (t , func (content string ) string {
2102+ return strings .Replace (content , "Line 50--modified\n " , "Line 50--changed-again\n " , 1 )
2103+ }, true )
2104+ })
2105+
2106+ t .Run ("multi-line comment not invalidated when a line outside the range changes" , func (t * testing.T ) {
2107+ defer tests .PrintCurrentTest (t )()
2108+ runRangeInvalidation (t , func (content string ) string {
2109+ return strings .Replace (content , "Line 60\n " , "Line 60--modified\n " , 1 )
2110+ }, false )
2111+ })
2112+
2113+ t .Run ("multi-line comment invalidated when the last line is deleted by a later commit" , func (t * testing.T ) {
2114+ defer tests .PrintCurrentTest (t )()
2115+ runRangeInvalidation (t , func (content string ) string {
2116+ return strings .Replace (content , "Line 50--modified\n " , "" , 1 )
2117+ }, true )
2118+ })
2119+
2120+ t .Run ("multi-line comment invalidated when the first (anchor) line content changes in a later commit" , func (t * testing.T ) {
2121+ defer tests .PrintCurrentTest (t )()
2122+ runRangeInvalidation (t , func (content string ) string {
2123+ return strings .Replace (content , "Line 48--modified\n " , "Line 48--changed-again\n " , 1 )
2124+ }, true )
2125+ })
2126+
2127+ t .Run ("multi-line comment invalidated when the first (anchor) line is deleted by a later commit" , func (t * testing.T ) {
2128+ defer tests .PrintCurrentTest (t )()
2129+ runRangeInvalidation (t , func (content string ) string {
2130+ return strings .Replace (content , "Line 48--modified\n " , "" , 1 )
2131+ }, true )
2132+ })
20412133 })
20422134}
20432135
0 commit comments