Commit b5b27ddb48
Verified · cmc
internal/store/mrs.go +49
| @@ -487,6 +487,55 @@ func (s *Store) ListMRComments(mrID int64) ([]IssueComment, error) { | ||
| 487 | 487 | return out, rows.Err() |
| 488 | 488 | } |
| 489 | 489 | |
| 490 | // MRCommentCounts totals, per MR, conversation comments plus diff-thread | |
| 491 | // roots — what the list page shows as one comment count. System comments, | |
| 492 | // diff-thread replies, and pending (unpublished) diff comments do not | |
| 493 | // count. The list handler asks for every row on a page in one call rather | |
| 494 | // than one query per MR. | |
| 495 | func (s *Store) MRCommentCounts(repoID int64, mrIDs []int64) (map[int64]int, error) { | |
| 496 | out := map[int64]int{} | |
| 497 | if len(mrIDs) == 0 { | |
| 498 | return out, nil | |
| 499 | } | |
| 500 | ph := "?" + strings.Repeat(",?", len(mrIDs)-1) | |
| 501 | args := make([]any, 0, len(mrIDs)+1) | |
| 502 | args = append(args, repoID) | |
| 503 | for _, id := range mrIDs { | |
| 504 | args = append(args, id) | |
| 505 | } | |
| 506 | add := func(query string) error { | |
| 507 | rows, err := s.DB.Query(query, args...) | |
| 508 | if err != nil { | |
| 509 | return err | |
| 510 | } | |
| 511 | defer rows.Close() | |
| 512 | for rows.Next() { | |
| 513 | var mrID int64 | |
| 514 | var n int | |
| 515 | if err := rows.Scan(&mrID, &n); err != nil { | |
| 516 | return err | |
| 517 | } | |
| 518 | out[mrID] += n | |
| 519 | } | |
| 520 | return rows.Err() | |
| 521 | } | |
| 522 | if err := add(` | |
| 523 | SELECT c.mr_id, COUNT(*) FROM mr_comments c | |
| 524 | JOIN merge_requests m ON m.id = c.mr_id | |
| 525 | WHERE m.repo_id = ? AND c.kind <> 'system' AND c.mr_id IN (` + ph + `) | |
| 526 | GROUP BY c.mr_id`); err != nil { | |
| 527 | return nil, err | |
| 528 | } | |
| 529 | if err := add(` | |
| 530 | SELECT c.mr_id, COUNT(*) FROM mr_diff_comments c | |
| 531 | JOIN merge_requests m ON m.id = c.mr_id | |
| 532 | WHERE m.repo_id = ? AND c.reply_to IS NULL AND c.pending = 0 AND c.mr_id IN (` + ph + `) | |
| 533 | GROUP BY c.mr_id`); err != nil { | |
| 534 | return nil, err | |
| 535 | } | |
| 536 | return out, nil | |
| 537 | } | |
| 538 | ||
| 490 | 539 | func (s *Store) AddMRReview(mrID, reviewerID int64, verdict, headSHA string) error { |
| 491 | 540 | _, err := s.DB.Exec( |
| 492 | 541 | "INSERT INTO mr_reviews (mr_id, reviewer_id, verdict, head_sha) VALUES (?, ?, ?, ?)", |
internal/store/mrs_test.go +50
| @@ -184,3 +184,53 @@ func TestPreferredVerifiedEmailDeterministicTiebreak(t *testing.T) { | ||
| 184 | 184 | t.Fatalf("tiebreak should be alphabetical: %q, %v", addr, err) |
| 185 | 185 | } |
| 186 | 186 | } |
| 187 | ||
| 188 | // MRCommentCounts folds conversation comments and diff-thread roots into | |
| 189 | // one count per MR, for the list page. System comments, diff-thread | |
| 190 | // replies, and pending (unpublished) diff comments do not count. | |
| 191 | func TestMRCommentCounts(t *testing.T) { | |
| 192 | s, repoID, uid := mrFixture(t) | |
| 193 | mr1, err := s.MRByNumber(repoID, 1) | |
| 194 | if err != nil { | |
| 195 | t.Fatal(err) | |
| 196 | } | |
| 197 | if _, err := s.CreateMR(repoID, uid, repoID, "feature2", "main", "t2", "", "def456", "md", false); err != nil { | |
| 198 | t.Fatal(err) | |
| 199 | } | |
| 200 | mr2, err := s.MRByNumber(repoID, 2) | |
| 201 | if err != nil { | |
| 202 | t.Fatal(err) | |
| 203 | } | |
| 204 | ||
| 205 | if err := s.AddMRComment(mr1.ID, uid, "hi", "md"); err != nil { | |
| 206 | t.Fatal(err) | |
| 207 | } | |
| 208 | if err := s.AddMRSystemComment(mr1.ID, uid, "merged"); err != nil { | |
| 209 | t.Fatal(err) | |
| 210 | } | |
| 211 | rootID, err := s.AddDiffComment(mr1.ID, uid, "abc123", "file.txt", "new", 1, "root", 0, false) | |
| 212 | if err != nil { | |
| 213 | t.Fatal(err) | |
| 214 | } | |
| 215 | if _, err := s.AddDiffComment(mr1.ID, uid, "abc123", "file.txt", "new", 1, "reply", rootID, false); err != nil { | |
| 216 | t.Fatal(err) | |
| 217 | } | |
| 218 | if _, err := s.AddDiffComment(mr1.ID, uid, "abc123", "file.txt", "new", 2, "pending root", 0, true); err != nil { | |
| 219 | t.Fatal(err) | |
| 220 | } | |
| 221 | ||
| 222 | counts, err := s.MRCommentCounts(repoID, []int64{mr1.ID, mr2.ID}) | |
| 223 | if err != nil { | |
| 224 | t.Fatal(err) | |
| 225 | } | |
| 226 | if counts[mr1.ID] != 2 { | |
| 227 | t.Fatalf("mr1 count = %d, want 2 (1 comment + 1 diff root)", counts[mr1.ID]) | |
| 228 | } | |
| 229 | if counts[mr2.ID] != 0 { | |
| 230 | t.Fatalf("mr2 count = %d, want 0", counts[mr2.ID]) | |
| 231 | } | |
| 232 | ||
| 233 | if empty, err := s.MRCommentCounts(repoID, nil); err != nil || len(empty) != 0 { | |
| 234 | t.Fatalf("MRCommentCounts(nil) = %v, %v", empty, err) | |
| 235 | } | |
| 236 | } | |