fix: use per-review comment fetching for Forgejo compatibility

Forgejo doesn't have the flat GET /pulls/{number}/comments endpoint
(returns 404). Replace pulls.listReviewComments() with a 2-phase
approach: fetch reviews first, then pulls.listCommentsForReview()
per-review in parallel. Updates tests to mock the new per-review API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vlad MocanuandClaude Opus 4.6 committed 2026-02-25 20:52:25 +02:00
1 parent efe30422e7
commit b4a6c34853
2 files changed
+48 -25

No files matched your search

+23 -14
View File
@@ -29,11 +29,25 @@ function createMockOctokits({
filesData?: Record<string, unknown>[];
commitsData?: Record<string, unknown>[];
commentsData?: Record<string, unknown>[];
reviewsData?: Record<string, unknown>[];
reviewCommentsData?: Record<string, unknown>[];
reviewsData?: (Record<string, unknown> & { id?: number })[];
reviewCommentsData?: (Record<string, unknown> & {
pull_request_review_id?: number | null;
})[];
issueData?: Record<string, unknown>;
userData?: Record<string, unknown>;
} = {}) {
// Build per-review mock: listCommentsForReview returns comments matching
// the requested review_id (Forgejo doesn't have a flat review-comments endpoint).
const allReviewComments = reviewCommentsData ?? [];
const listCommentsForReviewMock = jest
.fn()
.mockImplementation((params: { review_id: number }) => {
const matching = allReviewComments.filter(
(c) => c.pull_request_review_id === params.review_id,
);
return Promise.resolve({ data: matching });
});
return {
rest: {
pulls: {
@@ -41,9 +55,7 @@ function createMockOctokits({
listFiles: jest.fn().mockResolvedValue({ data: filesData ?? [] }),
listCommits: jest.fn().mockResolvedValue({ data: commitsData ?? [] }),
listReviews: jest.fn().mockResolvedValue({ data: reviewsData ?? [] }),
listReviewComments: jest
.fn()
.mockResolvedValue({ data: reviewCommentsData ?? [] }),
listCommentsForReview: listCommentsForReviewMock,
},
issues: {
get: jest.fn().mockResolvedValue({ data: issueData ?? {} }),
@@ -114,7 +126,7 @@ const mockIssueRestResponse = {
describe("fetchForgejoData", () => {
describe("PR data fetching", () => {
it("should fetch PR details, files, commits, comments, reviews, and inline comments in parallel", async () => {
it("should fetch PR details, files, commits, comments, and reviews in parallel then per-review comments", async () => {
const mockOctokits = createMockOctokits({
prData: mockPrRestResponse,
filesData: mockFilesRestResponse,
@@ -128,7 +140,7 @@ describe("fetchForgejoData", () => {
isPR: true,
});
// Verify all six REST calls were made
// Verify phase 1 REST calls (parallel)
expect(mockOctokits.rest.pulls.get).toHaveBeenCalledWith({
owner: "test-owner",
repo: "test-repo",
@@ -158,12 +170,7 @@ describe("fetchForgejoData", () => {
pull_number: 42,
per_page: 100,
});
expect(mockOctokits.rest.pulls.listReviewComments).toHaveBeenCalledWith({
owner: "test-owner",
repo: "test-repo",
pull_number: 42,
per_page: 100,
});
// Phase 2: per-review comment fetching (no reviews in this test → no calls)
});
it("should map PR fields to GitHubPullRequest type", async () => {
@@ -412,7 +419,9 @@ describe("fetchForgejoData", () => {
expect(mockOctokits.rest.pulls.listFiles).not.toHaveBeenCalled();
expect(mockOctokits.rest.pulls.listCommits).not.toHaveBeenCalled();
expect(mockOctokits.rest.pulls.listReviews).not.toHaveBeenCalled();
expect(mockOctokits.rest.pulls.listReviewComments).not.toHaveBeenCalled();
expect(
mockOctokits.rest.pulls.listCommentsForReview,
).not.toHaveBeenCalled();
});
it("should map issue fields to GitHubIssue type", async () => {