Skip to content

Commit aafa231

Browse files
committed
refactor(gitdiff): Extract hidden comment calculation to shared method and testing
1 parent 3e0c892 commit aafa231

File tree

5 files changed

+251
-94
lines changed

5 files changed

+251
-94
lines changed

routers/web/repo/compare.go

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -882,32 +882,10 @@ func attachCommentsToLines(section *gitdiff.DiffSection, lineComments map[int64]
882882
}
883883
}
884884

885-
// calculateHiddenCommentIDs finds comment IDs that are in the hidden range of an expand button
886-
func calculateHiddenCommentIDs(line *gitdiff.DiffLine, lineComments map[int64][]*issues_model.Comment) []int64 {
887-
if line.Type != gitdiff.DiffLineSection || line.SectionInfo == nil {
888-
return nil
889-
}
890-
891-
var hiddenCommentIDs []int64
892-
for commentLineNum, comments := range lineComments {
893-
absLineNum := commentLineNum
894-
if absLineNum < 0 {
895-
absLineNum = -absLineNum
896-
}
897-
// Check if comments are in the hidden range
898-
if int(absLineNum) > line.SectionInfo.LastRightIdx && int(absLineNum) < line.SectionInfo.RightIdx {
899-
for _, comment := range comments {
900-
hiddenCommentIDs = append(hiddenCommentIDs, comment.ID)
901-
}
902-
}
903-
}
904-
return hiddenCommentIDs
905-
}
906-
907885
// attachHiddenCommentIDs calculates and attaches hidden comment IDs to expand buttons
908886
func attachHiddenCommentIDs(section *gitdiff.DiffSection, lineComments map[int64][]*issues_model.Comment) {
909887
for _, line := range section.Lines {
910-
if hiddenIDs := calculateHiddenCommentIDs(line, lineComments); len(hiddenIDs) > 0 {
888+
if hiddenIDs := gitdiff.CalculateHiddenCommentIDsForLine(line, lineComments); len(hiddenIDs) > 0 {
911889
line.HiddenCommentIDs = hiddenIDs
912890
}
913891
}

routers/web/repo/compare_test.go

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -12,57 +12,6 @@ import (
1212
"github.com/stretchr/testify/assert"
1313
)
1414

15-
func TestCalculateHiddenCommentIDs(t *testing.T) {
16-
tests := []struct {
17-
name string
18-
line *gitdiff.DiffLine
19-
lineComments map[int64][]*issues_model.Comment
20-
expected []int64
21-
}{
22-
{
23-
name: "comments in hidden range",
24-
line: &gitdiff.DiffLine{
25-
Type: gitdiff.DiffLineSection,
26-
SectionInfo: &gitdiff.DiffLineSectionInfo{
27-
LastRightIdx: 10,
28-
RightIdx: 20,
29-
},
30-
},
31-
lineComments: map[int64][]*issues_model.Comment{
32-
15: {{ID: 100}, {ID: 101}},
33-
12: {{ID: 102}},
34-
},
35-
expected: []int64{100, 101, 102},
36-
},
37-
{
38-
name: "comments outside hidden range",
39-
line: &gitdiff.DiffLine{
40-
Type: gitdiff.DiffLineSection,
41-
SectionInfo: &gitdiff.DiffLineSectionInfo{
42-
LastRightIdx: 10,
43-
RightIdx: 20,
44-
},
45-
},
46-
lineComments: map[int64][]*issues_model.Comment{
47-
5: {{ID: 100}},
48-
25: {{ID: 101}},
49-
},
50-
expected: nil,
51-
},
52-
}
53-
54-
for _, tt := range tests {
55-
t.Run(tt.name, func(t *testing.T) {
56-
result := calculateHiddenCommentIDs(tt.line, tt.lineComments)
57-
if tt.expected == nil {
58-
assert.Nil(t, result)
59-
} else {
60-
assert.ElementsMatch(t, tt.expected, result)
61-
}
62-
})
63-
}
64-
}
65-
6615
func TestAttachHiddenCommentIDs(t *testing.T) {
6716
section := &gitdiff.DiffSection{
6817
Lines: []*gitdiff.DiffLine{

services/gitdiff/gitdiff.go

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,28 @@ func (d *DiffLine) GetExpandDirection() DiffLineExpandDirection {
183183
return DiffLineExpandSingle
184184
}
185185

186+
// CalculateHiddenCommentIDsForLine finds comment IDs that are in the hidden range of an expand button
187+
func CalculateHiddenCommentIDsForLine(line *DiffLine, lineComments map[int64][]*issues_model.Comment) []int64 {
188+
if line.Type != DiffLineSection || line.SectionInfo == nil {
189+
return nil
190+
}
191+
192+
var hiddenCommentIDs []int64
193+
for commentLineNum, comments := range lineComments {
194+
absLineNum := int(commentLineNum)
195+
if commentLineNum < 0 {
196+
absLineNum = int(-commentLineNum)
197+
}
198+
// Check if comments are in the hidden range
199+
if absLineNum > line.SectionInfo.LastRightIdx && absLineNum < line.SectionInfo.RightIdx {
200+
for _, comment := range comments {
201+
hiddenCommentIDs = append(hiddenCommentIDs, comment.ID)
202+
}
203+
}
204+
}
205+
return hiddenCommentIDs
206+
}
207+
186208
func getDiffLineSectionInfo(treePath, line string, lastLeftIdx, lastRightIdx int) *DiffLineSectionInfo {
187209
leftLine, leftHunk, rightLine, rightHunk := git.ParseDiffHunkString(line)
188210

@@ -488,24 +510,8 @@ func (diff *Diff) LoadComments(ctx context.Context, issue *issues_model.Issue, c
488510
})
489511

490512
// Mark expand buttons that have comments in hidden lines
491-
if line.Type == DiffLineSection && line.SectionInfo != nil {
492-
var hiddenCommentIDs []int64
493-
// Check if there are comments in the hidden range
494-
for commentLineNum, comments := range lineCommits {
495-
absLineNum := int(commentLineNum)
496-
if commentLineNum < 0 {
497-
absLineNum = int(-commentLineNum)
498-
}
499-
if absLineNum > line.SectionInfo.LastRightIdx && absLineNum < line.SectionInfo.RightIdx {
500-
// Collect comment IDs
501-
for _, comment := range comments {
502-
hiddenCommentIDs = append(hiddenCommentIDs, comment.ID)
503-
}
504-
}
505-
}
506-
if len(hiddenCommentIDs) > 0 {
507-
line.HiddenCommentIDs = hiddenCommentIDs
508-
}
513+
if hiddenIDs := CalculateHiddenCommentIDsForLine(line, lineCommits); len(hiddenIDs) > 0 {
514+
line.HiddenCommentIDs = hiddenIDs
509515
}
510516
}
511517
}
@@ -1427,6 +1433,11 @@ func GeneratePatchForUnchangedLine(gitRepo *git.Repository, commitID, treePath s
14271433
}
14281434
defer dataRc.Close()
14291435

1436+
return generatePatchForUnchangedLineFromReader(dataRc, treePath, line, contextLines)
1437+
}
1438+
1439+
// generatePatchForUnchangedLineFromReader is the testable core logic that generates a patch from a reader
1440+
func generatePatchForUnchangedLineFromReader(reader io.Reader, treePath string, line int64, contextLines int) (string, error) {
14301441
// Calculate line range (commented line + lines above it)
14311442
commentLine := int(line)
14321443
if line < 0 {
@@ -1436,7 +1447,7 @@ func GeneratePatchForUnchangedLine(gitRepo *git.Repository, commitID, treePath s
14361447
endLine := commentLine
14371448

14381449
// Read only the needed lines efficiently
1439-
scanner := bufio.NewScanner(dataRc)
1450+
scanner := bufio.NewScanner(reader)
14401451
currentLine := 0
14411452
var lines []string
14421453
for scanner.Scan() {

services/gitdiff/gitdiff_test.go

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,3 +640,223 @@ func TestNoCrashes(t *testing.T) {
640640
ParsePatch(t.Context(), setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(testcase.gitdiff), "")
641641
}
642642
}
643+
644+
func TestGeneratePatchForUnchangedLineFromReader(t *testing.T) {
645+
tests := []struct {
646+
name string
647+
content string
648+
treePath string
649+
line int64
650+
contextLines int
651+
want string
652+
wantErr bool
653+
}{
654+
{
655+
name: "single line with context",
656+
content: "line1\nline2\nline3\nline4\nline5\n",
657+
treePath: "test.txt",
658+
line: 3,
659+
contextLines: 1,
660+
want: `diff --git a/test.txt b/test.txt
661+
--- a/test.txt
662+
+++ b/test.txt
663+
@@ -2,2 +2,2 @@
664+
line2
665+
line3
666+
`,
667+
},
668+
{
669+
name: "negative line number (left side)",
670+
content: "line1\nline2\nline3\nline4\nline5\n",
671+
treePath: "test.txt",
672+
line: -3,
673+
contextLines: 1,
674+
want: `diff --git a/test.txt b/test.txt
675+
--- a/test.txt
676+
+++ b/test.txt
677+
@@ -2,2 +2,2 @@
678+
line2
679+
line3
680+
`,
681+
},
682+
{
683+
name: "line near start of file",
684+
content: "line1\nline2\nline3\n",
685+
treePath: "test.txt",
686+
line: 2,
687+
contextLines: 5,
688+
want: `diff --git a/test.txt b/test.txt
689+
--- a/test.txt
690+
+++ b/test.txt
691+
@@ -1,2 +1,2 @@
692+
line1
693+
line2
694+
`,
695+
},
696+
{
697+
name: "first line with context",
698+
content: "line1\nline2\nline3\n",
699+
treePath: "test.txt",
700+
line: 1,
701+
contextLines: 3,
702+
want: `diff --git a/test.txt b/test.txt
703+
--- a/test.txt
704+
+++ b/test.txt
705+
@@ -1,1 +1,1 @@
706+
line1
707+
`,
708+
},
709+
{
710+
name: "zero context lines",
711+
content: "line1\nline2\nline3\n",
712+
treePath: "test.txt",
713+
line: 2,
714+
contextLines: 0,
715+
want: `diff --git a/test.txt b/test.txt
716+
--- a/test.txt
717+
+++ b/test.txt
718+
@@ -2,1 +2,1 @@
719+
line2
720+
`,
721+
},
722+
{
723+
name: "multi-line context",
724+
content: "package main\n\nfunc main() {\n\tfmt.Println(\"Hello\")\n}\n",
725+
treePath: "main.go",
726+
line: 4,
727+
contextLines: 2,
728+
want: `diff --git a/main.go b/main.go
729+
--- a/main.go
730+
+++ b/main.go
731+
@@ -2,3 +2,3 @@
732+
733+
func main() {
734+
fmt.Println("Hello")
735+
`,
736+
},
737+
{
738+
name: "empty file",
739+
content: "",
740+
treePath: "empty.txt",
741+
line: 1,
742+
contextLines: 1,
743+
wantErr: true,
744+
},
745+
}
746+
747+
for _, tt := range tests {
748+
t.Run(tt.name, func(t *testing.T) {
749+
reader := strings.NewReader(tt.content)
750+
got, err := generatePatchForUnchangedLineFromReader(reader, tt.treePath, tt.line, tt.contextLines)
751+
if tt.wantErr {
752+
assert.Error(t, err)
753+
} else {
754+
require.NoError(t, err)
755+
assert.Equal(t, tt.want, got)
756+
}
757+
})
758+
}
759+
}
760+
761+
func TestCalculateHiddenCommentIDsForLine(t *testing.T) {
762+
tests := []struct {
763+
name string
764+
line *DiffLine
765+
lineComments map[int64][]*issues_model.Comment
766+
expected []int64
767+
}{
768+
{
769+
name: "comments in hidden range",
770+
line: &DiffLine{
771+
Type: DiffLineSection,
772+
SectionInfo: &DiffLineSectionInfo{
773+
LastRightIdx: 10,
774+
RightIdx: 20,
775+
},
776+
},
777+
lineComments: map[int64][]*issues_model.Comment{
778+
15: {{ID: 100}, {ID: 101}},
779+
12: {{ID: 102}},
780+
},
781+
expected: []int64{100, 101, 102},
782+
},
783+
{
784+
name: "comments outside hidden range",
785+
line: &DiffLine{
786+
Type: DiffLineSection,
787+
SectionInfo: &DiffLineSectionInfo{
788+
LastRightIdx: 10,
789+
RightIdx: 20,
790+
},
791+
},
792+
lineComments: map[int64][]*issues_model.Comment{
793+
5: {{ID: 100}},
794+
25: {{ID: 101}},
795+
},
796+
expected: nil,
797+
},
798+
{
799+
name: "negative line numbers (left side)",
800+
line: &DiffLine{
801+
Type: DiffLineSection,
802+
SectionInfo: &DiffLineSectionInfo{
803+
LastRightIdx: 10,
804+
RightIdx: 20,
805+
},
806+
},
807+
lineComments: map[int64][]*issues_model.Comment{
808+
-15: {{ID: 100}},
809+
},
810+
expected: []int64{100},
811+
},
812+
{
813+
name: "boundary conditions - exclusive range",
814+
line: &DiffLine{
815+
Type: DiffLineSection,
816+
SectionInfo: &DiffLineSectionInfo{
817+
LastRightIdx: 10,
818+
RightIdx: 20,
819+
},
820+
},
821+
lineComments: map[int64][]*issues_model.Comment{
822+
10: {{ID: 100}}, // at LastRightIdx, should not be included
823+
20: {{ID: 101}}, // at RightIdx, should not be included
824+
11: {{ID: 102}}, // just inside range, should be included
825+
19: {{ID: 103}}, // just inside range, should be included
826+
},
827+
expected: []int64{102, 103},
828+
},
829+
{
830+
name: "not a section line",
831+
line: &DiffLine{
832+
Type: DiffLinePlain,
833+
},
834+
lineComments: map[int64][]*issues_model.Comment{
835+
15: {{ID: 100}},
836+
},
837+
expected: nil,
838+
},
839+
{
840+
name: "section line without section info",
841+
line: &DiffLine{
842+
Type: DiffLineSection,
843+
SectionInfo: nil,
844+
},
845+
lineComments: map[int64][]*issues_model.Comment{
846+
15: {{ID: 100}},
847+
},
848+
expected: nil,
849+
},
850+
}
851+
852+
for _, tt := range tests {
853+
t.Run(tt.name, func(t *testing.T) {
854+
result := CalculateHiddenCommentIDsForLine(tt.line, tt.lineComments)
855+
if tt.expected == nil {
856+
assert.Nil(t, result)
857+
} else {
858+
assert.ElementsMatch(t, tt.expected, result)
859+
}
860+
})
861+
}
862+
}

0 commit comments

Comments
 (0)