From fe5bd239d91552f99b6fc17cc4620be4901c94b9 Mon Sep 17 00:00:00 2001 From: Sorena Sarabadani Date: Mon, 26 Jan 2026 23:28:28 +0100 Subject: [PATCH 1/6] fix: GHPullRequestReviewComment.getLine() returning -1 --- .../org/kohsuke/github/GHPullRequest.java | 10 +++ .../kohsuke/github/GHPullRequestReview.java | 12 ++++ .../github/GHPullRequestReviewComment.java | 68 +++++++++++++++--- .../org/kohsuke/github/GHPullRequestTest.java | 9 +++ .../pullRequestReviews/__files/12-user.json | 36 ++++++++++ .../13-r_h_g_pulls_comments_1641771497.json | 69 +++++++++++++++++++ .../pullRequestReviews/mappings/12-user.json | 48 +++++++++++++ .../13-r_h_g_pulls_comments_1641771497.json | 48 +++++++++++++ 8 files changed, 292 insertions(+), 8 deletions(-) create mode 100644 src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/__files/12-user.json create mode 100644 src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/__files/13-r_h_g_pulls_comments_1641771497.json create mode 100644 src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/mappings/12-user.json create mode 100644 src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/mappings/13-r_h_g_pulls_comments_1641771497.json diff --git a/src/main/java/org/kohsuke/github/GHPullRequest.java b/src/main/java/org/kohsuke/github/GHPullRequest.java index a0acab5f9b..f98a035f61 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequest.java +++ b/src/main/java/org/kohsuke/github/GHPullRequest.java @@ -530,7 +530,17 @@ public PagedIterable listFiles() { /** * Obtains all the review comments associated with this pull request. * + *

+ * This method uses the pull request comments endpoint which returns complete comment data including + * {@link GHPullRequestReviewComment#getLine() line}, {@link GHPullRequestReviewComment#getOriginalLine() + * originalLine}, {@link GHPullRequestReviewComment#getSide() side}, and other position-related fields. + * + *

+ * If you need line number information, prefer this method over {@link GHPullRequestReview#listReviewComments()}, + * which uses a different API endpoint that does not return line-related fields. + * * @return the paged iterable + * @see GHPullRequestReview#listReviewComments() */ public PagedIterable listReviewComments() { return root().createRequest() diff --git a/src/main/java/org/kohsuke/github/GHPullRequestReview.java b/src/main/java/org/kohsuke/github/GHPullRequestReview.java index 10eb93ba4e..4601b6745c 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequestReview.java +++ b/src/main/java/org/kohsuke/github/GHPullRequestReview.java @@ -174,7 +174,19 @@ public GHUser getUser() throws IOException { /** * Obtains all the review comments associated with this pull request review. * + *

+ * Note: The GitHub API endpoint used by this method does not return line-related fields such as + * {@link GHPullRequestReviewComment#getLine() line}, {@link GHPullRequestReviewComment#getOriginalLine() + * originalLine}, {@link GHPullRequestReviewComment#getSide() side}, + * {@link GHPullRequestReviewComment#getStartLine() startLine}, etc. These fields will return their default values + * (-1 or UNKNOWN). + * + *

+ * If you need line number information, use {@link GHPullRequest#listReviewComments()} instead and filter by + * {@link GHPullRequestReviewComment#getPullRequestReviewId()} if needed. + * * @return the paged iterable + * @see GHPullRequest#listReviewComments() */ public PagedIterable listReviewComments() { return owner.root() diff --git a/src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java b/src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java index a21378ef86..d5ea0613d0 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java +++ b/src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java @@ -40,7 +40,7 @@ * @see GHPullRequest#createReviewComment(String, String, String, int) GHPullRequest#createReviewComment(String, String, * String, int) */ -public class GHPullRequestReviewComment extends GHObject implements Reactable { +public class GHPullRequestReviewComment extends GHObject implements Reactable, Refreshable { /** * The side of the diff to which the comment applies @@ -218,7 +218,13 @@ public long getInReplyToId() { /** * Gets The line of the blob to which the comment applies. The last line of the range for a multi-line comment. * - * @return the line to which the comment applies + *

+ * Note: This field is only populated when comments are retrieved via + * {@link GHPullRequest#listReviewComments()}. When retrieved via {@link GHPullRequestReview#listReviewComments()}, + * this method returns -1 due to limitations in the GitHub API. + * + * @return the line to which the comment applies, or -1 if not available + * @see GHPullRequest#listReviewComments() */ public int getLine() { return line; @@ -236,7 +242,13 @@ public String getOriginalCommitId() { /** * Gets The line of the blob to which the comment applies. The last line of the range for a multi-line comment. * - * @return the line to which the comment applies + *

+ * Note: This field is only populated when comments are retrieved via + * {@link GHPullRequest#listReviewComments()}. When retrieved via {@link GHPullRequestReview#listReviewComments()}, + * this method returns -1 due to limitations in the GitHub API. + * + * @return the line to which the comment applies, or -1 if not available + * @see GHPullRequest#listReviewComments() */ public int getOriginalLine() { return originalLine; @@ -254,7 +266,13 @@ public int getOriginalPosition() { /** * Gets The first line of the range for a multi-line comment. * - * @return the original start line + *

+ * Note: This field is only populated when comments are retrieved via + * {@link GHPullRequest#listReviewComments()}. When retrieved via {@link GHPullRequestReview#listReviewComments()}, + * this method returns -1 due to limitations in the GitHub API. + * + * @return the original start line, or -1 if not available or not a multi-line comment + * @see GHPullRequest#listReviewComments() */ public int getOriginalStartLine() { return originalStartLine != null ? originalStartLine : -1; @@ -318,9 +336,15 @@ public GHPullRequestReviewCommentReactions getReactions() { /** * Gets The side of the diff to which the comment applies. The side of the last line of the range for a multi-line - * comment + * comment. + * + *

+ * Note: This field is only populated when comments are retrieved via + * {@link GHPullRequest#listReviewComments()}. When retrieved via {@link GHPullRequestReview#listReviewComments()}, + * this method returns {@link Side#UNKNOWN} due to limitations in the GitHub API. * - * @return {@link Side} the side if the diff to which the comment applies + * @return {@link Side} the side of the diff to which the comment applies, or {@link Side#UNKNOWN} if not available + * @see GHPullRequest#listReviewComments() */ public Side getSide() { return Side.from(side); @@ -329,7 +353,13 @@ public Side getSide() { /** * Gets The first line of the range for a multi-line comment. * - * @return the start line + *

+ * Note: This field is only populated when comments are retrieved via + * {@link GHPullRequest#listReviewComments()}. When retrieved via {@link GHPullRequestReview#listReviewComments()}, + * this method returns -1 due to limitations in the GitHub API. + * + * @return the start line, or -1 if not available or not a multi-line comment + * @see GHPullRequest#listReviewComments() */ public int getStartLine() { return startLine != null ? startLine : -1; @@ -338,7 +368,13 @@ public int getStartLine() { /** * Gets The side of the first line of the range for a multi-line comment. * - * @return {@link Side} the side of the first line + *

+ * Note: This field is only populated when comments are retrieved via + * {@link GHPullRequest#listReviewComments()}. When retrieved via {@link GHPullRequestReview#listReviewComments()}, + * this method returns {@link Side#UNKNOWN} due to limitations in the GitHub API. + * + * @return {@link Side} the side of the first line, or {@link Side#UNKNOWN} if not available + * @see GHPullRequest#listReviewComments() */ public Side getStartSide() { return Side.from(startSide); @@ -367,6 +403,22 @@ public PagedIterable listReactions() { .toIterable(GHReaction[].class, item -> owner.root()); } + /** + * Refreshes this comment by fetching the full data from the API. + * + *

+ * This is useful when the comment was obtained via {@link GHPullRequestReview#listReviewComments()}, which uses a + * GitHub API endpoint that does not return line-related fields. After calling this method, fields like + * {@link #getLine()}, {@link #getOriginalLine()}, {@link #getSide()}, etc. will return their actual values. + * + * @throws IOException + * if an I/O error occurs + * @see GHPullRequest#listReviewComments() + */ + public void refresh() throws IOException { + owner.root().createRequest().withUrlPath(getApiRoute()).fetchInto(this).wrapUp(owner); + } + /** * Create a new comment that replies to this comment. * diff --git a/src/test/java/org/kohsuke/github/GHPullRequestTest.java b/src/test/java/org/kohsuke/github/GHPullRequestTest.java index 4220a4cacb..1eaffdb18d 100644 --- a/src/test/java/org/kohsuke/github/GHPullRequestTest.java +++ b/src/test/java/org/kohsuke/github/GHPullRequestTest.java @@ -743,6 +743,15 @@ public void pullRequestReviews() throws Exception { assertThat(comments.size(), equalTo(3)); GHPullRequestReviewComment comment = comments.get(0); assertThat(comment.getBody(), equalTo("Some niggle")); + + // Verify that line is not available when fetched via review.listReviewComments() + // due to GitHub API limitation (the review comments endpoint doesn't return line field) + assertThat(comment.getLine(), equalTo(-1)); + + // After refresh(), line information should be available + comment.refresh(); + assertThat(comment.getLine(), equalTo(1)); + comment = comments.get(1); assertThat(comment.getBody(), equalTo("A single line comment")); assertThat(comment.getPosition(), equalTo(4)); diff --git a/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/__files/12-user.json b/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/__files/12-user.json new file mode 100644 index 0000000000..fbc5eae788 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/__files/12-user.json @@ -0,0 +1,36 @@ +{ + "login": "Anonycoders", + "id": 40047636, + "node_id": "MDQ6VXNlcjQwMDQ3NjM2", + "avatar_url": "https://avatars.githubusercontent.com/u/40047636?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Anonycoders", + "html_url": "https://github.com/Anonycoders", + "followers_url": "https://api.github.com/users/Anonycoders/followers", + "following_url": "https://api.github.com/users/Anonycoders/following{/other_user}", + "gists_url": "https://api.github.com/users/Anonycoders/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Anonycoders/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Anonycoders/subscriptions", + "organizations_url": "https://api.github.com/users/Anonycoders/orgs", + "repos_url": "https://api.github.com/users/Anonycoders/repos", + "events_url": "https://api.github.com/users/Anonycoders/events{/privacy}", + "received_events_url": "https://api.github.com/users/Anonycoders/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false, + "name": "Sorena Sarabadani", + "company": "@Adevinta", + "blog": "", + "location": "Berlin, Germany", + "email": "sorena.sarabadani@gmail.com", + "hireable": null, + "bio": "Ex-Shopifyer - Adevinta/Kleinanzeigen", + "twitter_username": "sorena_s", + "notification_email": "sorena.sarabadani@gmail.com", + "public_repos": 12, + "public_gists": 0, + "followers": 38, + "following": 4, + "created_at": "2018-06-08T02:07:15Z", + "updated_at": "2026-01-24T22:07:12Z" +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/__files/13-r_h_g_pulls_comments_1641771497.json b/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/__files/13-r_h_g_pulls_comments_1641771497.json new file mode 100644 index 0000000000..a51974394d --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/__files/13-r_h_g_pulls_comments_1641771497.json @@ -0,0 +1,69 @@ +{ + "url": "https://api.github.com/repos/hub4j-test-org/github-api/pulls/comments/1641771497", + "pull_request_review_id": 2121304234, + "id": 1641771497, + "node_id": "PRRC_kwDODFTdCc5h23Hp", + "diff_hunk": "@@ -1,3 +1,4 @@\n-Java API for GitHub", + "path": "README.md", + "commit_id": "07374fe73aff1c2024a8d4114b32406c7a8e89b7", + "original_commit_id": "07374fe73aff1c2024a8d4114b32406c7a8e89b7", + "user": { + "login": "maximevw", + "id": 48218208, + "node_id": "MDQ6VXNlcjQ4MjE4MjA4", + "avatar_url": "https://avatars.githubusercontent.com/u/48218208?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/maximevw", + "html_url": "https://github.com/maximevw", + "followers_url": "https://api.github.com/users/maximevw/followers", + "following_url": "https://api.github.com/users/maximevw/following{/other_user}", + "gists_url": "https://api.github.com/users/maximevw/gists{/gist_id}", + "starred_url": "https://api.github.com/users/maximevw/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/maximevw/subscriptions", + "organizations_url": "https://api.github.com/users/maximevw/orgs", + "repos_url": "https://api.github.com/users/maximevw/repos", + "events_url": "https://api.github.com/users/maximevw/events{/privacy}", + "received_events_url": "https://api.github.com/users/maximevw/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + }, + "body": "Some niggle", + "created_at": "2024-06-16T09:55:53Z", + "updated_at": "2024-06-16T09:55:55Z", + "html_url": "https://github.com/hub4j-test-org/github-api/pull/482#discussion_r1641771497", + "pull_request_url": "https://api.github.com/repos/hub4j-test-org/github-api/pulls/482", + "_links": { + "self": { + "href": "https://api.github.com/repos/hub4j-test-org/github-api/pulls/comments/1641771497" + }, + "html": { + "href": "https://github.com/hub4j-test-org/github-api/pull/482#discussion_r1641771497" + }, + "pull_request": { + "href": "https://api.github.com/repos/hub4j-test-org/github-api/pulls/482" + } + }, + "reactions": { + "url": "https://api.github.com/repos/hub4j-test-org/github-api/pulls/comments/1641771497/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "start_line": null, + "original_start_line": null, + "start_side": null, + "line": 1, + "original_line": 1, + "side": "LEFT", + "author_association": "MEMBER", + "original_position": 1, + "position": 1, + "subject_type": "line" +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/mappings/12-user.json b/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/mappings/12-user.json new file mode 100644 index 0000000000..40afaf8cbf --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/mappings/12-user.json @@ -0,0 +1,48 @@ +{ + "id": "249d36bd-a54d-4106-a62f-f86c4f834d34", + "name": "user", + "request": { + "url": "/user", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "12-user.json", + "headers": { + "Date": "Mon, 26 Jan 2026 22:10:52 GMT", + "Content-Type": "application/json; charset=utf-8", + "Cache-Control": "private, max-age=60, s-maxage=60", + "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With", + "ETag": "W/\"15d7e1ad92a3639b979fc55254902e63ee0bfa5c8f6766990bf989044d491ce1\"", + "Last-Modified": "Sat, 24 Jan 2026 22:07:12 GMT", + "X-OAuth-Scopes": "repo", + "X-Accepted-OAuth-Scopes": "", + "github-authentication-token-expiration": "2026-02-19 19:55:13 UTC", + "X-GitHub-Media-Type": "github.v3; format=json", + "x-github-api-version-selected": "2022-11-28", + "Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset", + "Access-Control-Allow-Origin": "*", + "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", + "X-Frame-Options": "deny", + "X-Content-Type-Options": "nosniff", + "X-XSS-Protection": "0", + "Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin", + "Content-Security-Policy": "default-src 'none'", + "Server": "github.com", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4963", + "X-RateLimit-Reset": "1769467964", + "X-RateLimit-Used": "37", + "X-RateLimit-Resource": "core", + "X-GitHub-Request-Id": "D98D:30C18B:44C18DC:3CA8C0F:6977E66C" + } + }, + "uuid": "249d36bd-a54d-4106-a62f-f86c4f834d34", + "persistent": true, + "insertionIndex": 12 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/mappings/13-r_h_g_pulls_comments_1641771497.json b/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/mappings/13-r_h_g_pulls_comments_1641771497.json new file mode 100644 index 0000000000..6f44c6308a --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/mappings/13-r_h_g_pulls_comments_1641771497.json @@ -0,0 +1,48 @@ +{ + "id": "78aa6b0d-5dc4-4b90-8b1a-3864ad247264", + "name": "repos_hub4j-test-org_github-api_pulls_comments_1641771497", + "request": { + "url": "/repos/hub4j-test-org/github-api/pulls/comments/1641771497", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "13-r_h_g_pulls_comments_1641771497.json", + "headers": { + "Date": "Mon, 26 Jan 2026 22:10:53 GMT", + "Content-Type": "application/json; charset=utf-8", + "Cache-Control": "private, max-age=60, s-maxage=60", + "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With", + "ETag": "W/\"089067a7341750dff9af7d18f68314ec595952d4062b8605e17e39a85285a435\"", + "Last-Modified": "Fri, 23 Jan 2026 05:51:42 GMT", + "X-OAuth-Scopes": "repo", + "X-Accepted-OAuth-Scopes": "", + "github-authentication-token-expiration": "2026-02-19 19:55:13 UTC", + "X-GitHub-Media-Type": "github.v3; format=json", + "x-github-api-version-selected": "2022-11-28", + "Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset", + "Access-Control-Allow-Origin": "*", + "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", + "X-Frame-Options": "deny", + "X-Content-Type-Options": "nosniff", + "X-XSS-Protection": "0", + "Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin", + "Content-Security-Policy": "default-src 'none'", + "Server": "github.com", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "4958", + "X-RateLimit-Reset": "1769467964", + "X-RateLimit-Used": "42", + "X-RateLimit-Resource": "core", + "X-GitHub-Request-Id": "D98F:31BF39:4380FA4:3B615A1:6977E66D" + } + }, + "uuid": "78aa6b0d-5dc4-4b90-8b1a-3864ad247264", + "persistent": true, + "insertionIndex": 13 +} \ No newline at end of file From e4b6b375766d1a59bc20172b9bc5267f09a67115 Mon Sep 17 00:00:00 2001 From: Sorena Sarabadani Date: Sat, 21 Feb 2026 21:43:58 +0100 Subject: [PATCH 2/6] chore: address review comments --- .../org/kohsuke/github/GHPullRequest.java | 10 +- .../kohsuke/github/GHPullRequestReview.java | 203 ++++++++++++++++-- .../github/GHPullRequestReviewComment.java | 66 +++--- .../org/kohsuke/github/GHPullRequestTest.java | 19 +- .../pullRequestReviews/mappings/12-user.json | 14 +- .../13-r_h_g_pulls_comments_1641771497.json | 16 +- 6 files changed, 242 insertions(+), 86 deletions(-) diff --git a/src/main/java/org/kohsuke/github/GHPullRequest.java b/src/main/java/org/kohsuke/github/GHPullRequest.java index f98a035f61..47636ce108 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequest.java +++ b/src/main/java/org/kohsuke/github/GHPullRequest.java @@ -531,13 +531,9 @@ public PagedIterable listFiles() { * Obtains all the review comments associated with this pull request. * *

- * This method uses the pull request comments endpoint which returns complete comment data including - * {@link GHPullRequestReviewComment#getLine() line}, {@link GHPullRequestReviewComment#getOriginalLine() - * originalLine}, {@link GHPullRequestReviewComment#getSide() side}, and other position-related fields. - * - *

- * If you need line number information, prefer this method over {@link GHPullRequestReview#listReviewComments()}, - * which uses a different API endpoint that does not return line-related fields. + * Unlike {@link GHPullRequestReview#listReviewComments()}, this method returns full + * {@link GHPullRequestReviewComment} objects including line-related fields such as + * {@link GHPullRequestReviewComment#getLine() line}, {@link GHPullRequestReviewComment#getSide() side}, etc. * * @return the paged iterable * @see GHPullRequestReview#listReviewComments() diff --git a/src/main/java/org/kohsuke/github/GHPullRequestReview.java b/src/main/java/org/kohsuke/github/GHPullRequestReview.java index 4601b6745c..3cfb4ddde0 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequestReview.java +++ b/src/main/java/org/kohsuke/github/GHPullRequestReview.java @@ -43,6 +43,190 @@ @SuppressFBWarnings(value = { "UWF_UNWRITTEN_FIELD" }, justification = "JSON API") public class GHPullRequestReview extends GHObject { + /** + * Represents a review comment as returned by the review comments endpoint. This is a limited view that does not + * include line-related fields such as {@code line}, {@code originalLine}, {@code side}, etc. + * + *

+ * To obtain the full {@link GHPullRequestReviewComment} with all fields, call + * {@link #readPullRequestReviewComment()}. + * + * @see GHPullRequest#listReviewComments() + */ + @SuppressFBWarnings(value = { "UWF_UNWRITTEN_FIELD" }, justification = "JSON API") + public static class ReviewComment extends GHObject { + + private GHCommentAuthorAssociation authorAssociation; + private String body; + private String commitId; + private String diffHunk; + private String htmlUrl; + private String originalCommitId; + private int originalPosition = -1; + private String path; + private int position = -1; + private Long pullRequestReviewId = -1L; + private String pullRequestUrl; + private GHPullRequestReviewCommentReactions reactions; + private GHUser user; + + GHPullRequest owner; + + /** + * Create default ReviewComment instance + */ + public ReviewComment() { + } + + /** + * Gets the author association to the project. + * + * @return the author association to the project + */ + public GHCommentAuthorAssociation getAuthorAssociation() { + return authorAssociation; + } + + /** + * The comment itself. + * + * @return the body + */ + public String getBody() { + return body; + } + + /** + * Gets commit id. + * + * @return the commit id + */ + public String getCommitId() { + return commitId; + } + + /** + * Gets diff hunk. + * + * @return the diff hunk + */ + public String getDiffHunk() { + return diffHunk; + } + + /** + * Gets the html url. + * + * @return the html url + */ + public URL getHtmlUrl() { + return GitHubClient.parseURL(htmlUrl); + } + + /** + * Gets commit id. + * + * @return the original commit id + */ + public String getOriginalCommitId() { + return originalCommitId; + } + + /** + * Gets original position. + * + * @return the original position + */ + public int getOriginalPosition() { + return originalPosition; + } + + /** + * Gets path. + * + * @return the path + */ + public String getPath() { + return path; + } + + /** + * Gets position. + * + * @return the position + */ + public int getPosition() { + return position; + } + + /** + * Gets The ID of the pull request review to which the comment belongs. + * + * @return {@link Long} the ID of the pull request review + */ + public Long getPullRequestReviewId() { + return pullRequestReviewId != null ? pullRequestReviewId : -1; + } + + /** + * Gets URL for the pull request that the review comment belongs to. + * + * @return {@link URL} the URL of the pull request + */ + public URL getPullRequestUrl() { + return GitHubClient.parseURL(pullRequestUrl); + } + + /** + * Gets the Reaction Rollup. + * + * @return {@link GHPullRequestReviewCommentReactions} the reaction rollup + */ + public GHPullRequestReviewCommentReactions getReactions() { + return reactions; + } + + /** + * Gets the user who posted this comment. + * + * @return the user + * @throws IOException + * the io exception + */ + public GHUser getUser() throws IOException { + return owner.root().getUser(user.getLogin()); + } + + /** + * Fetches the full {@link GHPullRequestReviewComment} from the API, which includes all fields such as + * {@link GHPullRequestReviewComment#getLine() line}, {@link GHPullRequestReviewComment#getOriginalLine() + * originalLine}, {@link GHPullRequestReviewComment#getSide() side}, etc. + * + * @return the full {@link GHPullRequestReviewComment} + * @throws IOException + * if an I/O error occurs + */ + public GHPullRequestReviewComment readPullRequestReviewComment() throws IOException { + return owner.root() + .createRequest() + .withUrlPath("/repos/" + owner.getRepository().getFullName() + "/pulls/comments/" + getId()) + .fetch(GHPullRequestReviewComment.class) + .wrapUp(owner); + } + + /** + * Wrap up. + * + * @param owner + * the owner + * @return the review comment + */ + ReviewComment wrapUp(GHPullRequest owner) { + this.owner = owner; + return this; + } + } + private String body; private String commitId; @@ -175,24 +359,19 @@ public GHUser getUser() throws IOException { * Obtains all the review comments associated with this pull request review. * *

- * Note: The GitHub API endpoint used by this method does not return line-related fields such as - * {@link GHPullRequestReviewComment#getLine() line}, {@link GHPullRequestReviewComment#getOriginalLine() - * originalLine}, {@link GHPullRequestReviewComment#getSide() side}, - * {@link GHPullRequestReviewComment#getStartLine() startLine}, etc. These fields will return their default values - * (-1 or UNKNOWN). - * - *

- * If you need line number information, use {@link GHPullRequest#listReviewComments()} instead and filter by - * {@link GHPullRequestReviewComment#getPullRequestReviewId()} if needed. + * The GitHub API endpoint used by this method returns a limited set of fields. To obtain full comment data + * including line numbers, use {@link ReviewComment#readPullRequestReviewComment()} on individual comments, or use + * {@link GHPullRequest#listReviewComments()} instead. * - * @return the paged iterable + * @return the paged iterable of {@link ReviewComment} objects * @see GHPullRequest#listReviewComments() + * @see ReviewComment#readPullRequestReviewComment() */ - public PagedIterable listReviewComments() { + public PagedIterable listReviewComments() { return owner.root() .createRequest() .withUrlPath(getApiRoute() + "/comments") - .toIterable(GHPullRequestReviewComment[].class, item -> item.wrapUp(owner)); + .toIterable(ReviewComment[].class, item -> item.wrapUp(owner)); } /** diff --git a/src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java b/src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java index d5ea0613d0..4f1ab92946 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java +++ b/src/main/java/org/kohsuke/github/GHPullRequestReviewComment.java @@ -40,7 +40,7 @@ * @see GHPullRequest#createReviewComment(String, String, String, int) GHPullRequest#createReviewComment(String, String, * String, int) */ -public class GHPullRequestReviewComment extends GHObject implements Reactable, Refreshable { +public class GHPullRequestReviewComment extends GHObject implements Reactable { /** * The side of the diff to which the comment applies @@ -219,12 +219,12 @@ public long getInReplyToId() { * Gets The line of the blob to which the comment applies. The last line of the range for a multi-line comment. * *

- * Note: This field is only populated when comments are retrieved via - * {@link GHPullRequest#listReviewComments()}. When retrieved via {@link GHPullRequestReview#listReviewComments()}, - * this method returns -1 due to limitations in the GitHub API. + * This field is not available on {@link GHPullRequestReview.ReviewComment} objects returned by + * {@link GHPullRequestReview#listReviewComments()}. Use + * {@link GHPullRequestReview.ReviewComment#readPullRequestReviewComment()} or + * {@link GHPullRequest#listReviewComments()} to obtain this value. * * @return the line to which the comment applies, or -1 if not available - * @see GHPullRequest#listReviewComments() */ public int getLine() { return line; @@ -243,12 +243,12 @@ public String getOriginalCommitId() { * Gets The line of the blob to which the comment applies. The last line of the range for a multi-line comment. * *

- * Note: This field is only populated when comments are retrieved via - * {@link GHPullRequest#listReviewComments()}. When retrieved via {@link GHPullRequestReview#listReviewComments()}, - * this method returns -1 due to limitations in the GitHub API. + * This field is not available on {@link GHPullRequestReview.ReviewComment} objects returned by + * {@link GHPullRequestReview#listReviewComments()}. Use + * {@link GHPullRequestReview.ReviewComment#readPullRequestReviewComment()} or + * {@link GHPullRequest#listReviewComments()} to obtain this value. * * @return the line to which the comment applies, or -1 if not available - * @see GHPullRequest#listReviewComments() */ public int getOriginalLine() { return originalLine; @@ -267,12 +267,12 @@ public int getOriginalPosition() { * Gets The first line of the range for a multi-line comment. * *

- * Note: This field is only populated when comments are retrieved via - * {@link GHPullRequest#listReviewComments()}. When retrieved via {@link GHPullRequestReview#listReviewComments()}, - * this method returns -1 due to limitations in the GitHub API. + * This field is not available on {@link GHPullRequestReview.ReviewComment} objects returned by + * {@link GHPullRequestReview#listReviewComments()}. Use + * {@link GHPullRequestReview.ReviewComment#readPullRequestReviewComment()} or + * {@link GHPullRequest#listReviewComments()} to obtain this value. * * @return the original start line, or -1 if not available or not a multi-line comment - * @see GHPullRequest#listReviewComments() */ public int getOriginalStartLine() { return originalStartLine != null ? originalStartLine : -1; @@ -339,12 +339,12 @@ public GHPullRequestReviewCommentReactions getReactions() { * comment. * *

- * Note: This field is only populated when comments are retrieved via - * {@link GHPullRequest#listReviewComments()}. When retrieved via {@link GHPullRequestReview#listReviewComments()}, - * this method returns {@link Side#UNKNOWN} due to limitations in the GitHub API. + * This field is not available on {@link GHPullRequestReview.ReviewComment} objects returned by + * {@link GHPullRequestReview#listReviewComments()}. Use + * {@link GHPullRequestReview.ReviewComment#readPullRequestReviewComment()} or + * {@link GHPullRequest#listReviewComments()} to obtain this value. * * @return {@link Side} the side of the diff to which the comment applies, or {@link Side#UNKNOWN} if not available - * @see GHPullRequest#listReviewComments() */ public Side getSide() { return Side.from(side); @@ -354,12 +354,12 @@ public Side getSide() { * Gets The first line of the range for a multi-line comment. * *

- * Note: This field is only populated when comments are retrieved via - * {@link GHPullRequest#listReviewComments()}. When retrieved via {@link GHPullRequestReview#listReviewComments()}, - * this method returns -1 due to limitations in the GitHub API. + * This field is not available on {@link GHPullRequestReview.ReviewComment} objects returned by + * {@link GHPullRequestReview#listReviewComments()}. Use + * {@link GHPullRequestReview.ReviewComment#readPullRequestReviewComment()} or + * {@link GHPullRequest#listReviewComments()} to obtain this value. * * @return the start line, or -1 if not available or not a multi-line comment - * @see GHPullRequest#listReviewComments() */ public int getStartLine() { return startLine != null ? startLine : -1; @@ -369,12 +369,12 @@ public int getStartLine() { * Gets The side of the first line of the range for a multi-line comment. * *

- * Note: This field is only populated when comments are retrieved via - * {@link GHPullRequest#listReviewComments()}. When retrieved via {@link GHPullRequestReview#listReviewComments()}, - * this method returns {@link Side#UNKNOWN} due to limitations in the GitHub API. + * This field is not available on {@link GHPullRequestReview.ReviewComment} objects returned by + * {@link GHPullRequestReview#listReviewComments()}. Use + * {@link GHPullRequestReview.ReviewComment#readPullRequestReviewComment()} or + * {@link GHPullRequest#listReviewComments()} to obtain this value. * * @return {@link Side} the side of the first line, or {@link Side#UNKNOWN} if not available - * @see GHPullRequest#listReviewComments() */ public Side getStartSide() { return Side.from(startSide); @@ -403,22 +403,6 @@ public PagedIterable listReactions() { .toIterable(GHReaction[].class, item -> owner.root()); } - /** - * Refreshes this comment by fetching the full data from the API. - * - *

- * This is useful when the comment was obtained via {@link GHPullRequestReview#listReviewComments()}, which uses a - * GitHub API endpoint that does not return line-related fields. After calling this method, fields like - * {@link #getLine()}, {@link #getOriginalLine()}, {@link #getSide()}, etc. will return their actual values. - * - * @throws IOException - * if an I/O error occurs - * @see GHPullRequest#listReviewComments() - */ - public void refresh() throws IOException { - owner.root().createRequest().withUrlPath(getApiRoute()).fetchInto(this).wrapUp(owner); - } - /** * Create a new comment that replies to this comment. * diff --git a/src/test/java/org/kohsuke/github/GHPullRequestTest.java b/src/test/java/org/kohsuke/github/GHPullRequestTest.java index 1eaffdb18d..feef903b7c 100644 --- a/src/test/java/org/kohsuke/github/GHPullRequestTest.java +++ b/src/test/java/org/kohsuke/github/GHPullRequestTest.java @@ -739,25 +739,22 @@ public void pullRequestReviews() throws Exception { assertThat(review.getBody(), is("Some draft review")); assertThat(review.getCommitId(), notNullValue()); draftReview.submit("Some review comment", GHPullRequestReviewEvent.COMMENT); - List comments = review.listReviewComments().toList(); + List comments = review.listReviewComments().toList(); assertThat(comments.size(), equalTo(3)); - GHPullRequestReviewComment comment = comments.get(0); + GHPullRequestReview.ReviewComment comment = comments.get(0); assertThat(comment.getBody(), equalTo("Some niggle")); - - // Verify that line is not available when fetched via review.listReviewComments() - // due to GitHub API limitation (the review comments endpoint doesn't return line field) - assertThat(comment.getLine(), equalTo(-1)); - - // After refresh(), line information should be available - comment.refresh(); - assertThat(comment.getLine(), equalTo(1)); - comment = comments.get(1); assertThat(comment.getBody(), equalTo("A single line comment")); assertThat(comment.getPosition(), equalTo(4)); comment = comments.get(2); assertThat(comment.getBody(), equalTo("A multiline comment")); assertThat(comment.getPosition(), equalTo(5)); + + // Verify that readPullRequestReviewComment() fetches the full comment with line data + GHPullRequestReviewComment fullComment = comments.get(0).readPullRequestReviewComment(); + assertThat(fullComment.getBody(), equalTo("Some niggle")); + assertThat(fullComment.getLine(), equalTo(1)); + draftReview = p.createReview().body("Some new review").comment("Some niggle", "README.md", 1).create(); draftReview.delete(); } diff --git a/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/mappings/12-user.json b/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/mappings/12-user.json index 40afaf8cbf..5853e14e7a 100644 --- a/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/mappings/12-user.json +++ b/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/mappings/12-user.json @@ -1,5 +1,5 @@ { - "id": "249d36bd-a54d-4106-a62f-f86c4f834d34", + "id": "5531941e-d98d-4c37-be0e-cfe288bcc93d", "name": "user", "request": { "url": "/user", @@ -14,7 +14,7 @@ "status": 200, "bodyFileName": "12-user.json", "headers": { - "Date": "Mon, 26 Jan 2026 22:10:52 GMT", + "Date": "Tue, 10 Feb 2026 21:33:45 GMT", "Content-Type": "application/json; charset=utf-8", "Cache-Control": "private, max-age=60, s-maxage=60", "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With", @@ -35,14 +35,14 @@ "Content-Security-Policy": "default-src 'none'", "Server": "github.com", "X-RateLimit-Limit": "5000", - "X-RateLimit-Remaining": "4963", - "X-RateLimit-Reset": "1769467964", - "X-RateLimit-Used": "37", + "X-RateLimit-Remaining": "4997", + "X-RateLimit-Reset": "1770762770", + "X-RateLimit-Used": "3", "X-RateLimit-Resource": "core", - "X-GitHub-Request-Id": "D98D:30C18B:44C18DC:3CA8C0F:6977E66C" + "X-GitHub-Request-Id": "C400:31B50A:A9FC456:9DD9471:698BA438" } }, - "uuid": "249d36bd-a54d-4106-a62f-f86c4f834d34", + "uuid": "5531941e-d98d-4c37-be0e-cfe288bcc93d", "persistent": true, "insertionIndex": 12 } \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/mappings/13-r_h_g_pulls_comments_1641771497.json b/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/mappings/13-r_h_g_pulls_comments_1641771497.json index 6f44c6308a..6b14f69e19 100644 --- a/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/mappings/13-r_h_g_pulls_comments_1641771497.json +++ b/src/test/resources/org/kohsuke/github/GHPullRequestTest/wiremock/pullRequestReviews/mappings/13-r_h_g_pulls_comments_1641771497.json @@ -1,5 +1,5 @@ { - "id": "78aa6b0d-5dc4-4b90-8b1a-3864ad247264", + "id": "729a05bb-28a0-4e91-99a3-347eb78bce62", "name": "repos_hub4j-test-org_github-api_pulls_comments_1641771497", "request": { "url": "/repos/hub4j-test-org/github-api/pulls/comments/1641771497", @@ -14,12 +14,12 @@ "status": 200, "bodyFileName": "13-r_h_g_pulls_comments_1641771497.json", "headers": { - "Date": "Mon, 26 Jan 2026 22:10:53 GMT", + "Date": "Tue, 10 Feb 2026 21:33:47 GMT", "Content-Type": "application/json; charset=utf-8", "Cache-Control": "private, max-age=60, s-maxage=60", "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With", "ETag": "W/\"089067a7341750dff9af7d18f68314ec595952d4062b8605e17e39a85285a435\"", - "Last-Modified": "Fri, 23 Jan 2026 05:51:42 GMT", + "Last-Modified": "Tue, 10 Feb 2026 15:58:02 GMT", "X-OAuth-Scopes": "repo", "X-Accepted-OAuth-Scopes": "", "github-authentication-token-expiration": "2026-02-19 19:55:13 UTC", @@ -35,14 +35,14 @@ "Content-Security-Policy": "default-src 'none'", "Server": "github.com", "X-RateLimit-Limit": "5000", - "X-RateLimit-Remaining": "4958", - "X-RateLimit-Reset": "1769467964", - "X-RateLimit-Used": "42", + "X-RateLimit-Remaining": "4992", + "X-RateLimit-Reset": "1770762770", + "X-RateLimit-Used": "8", "X-RateLimit-Resource": "core", - "X-GitHub-Request-Id": "D98F:31BF39:4380FA4:3B615A1:6977E66D" + "X-GitHub-Request-Id": "C402:3085FD:A9A140B:9D63CD0:698BA43A" } }, - "uuid": "78aa6b0d-5dc4-4b90-8b1a-3864ad247264", + "uuid": "729a05bb-28a0-4e91-99a3-347eb78bce62", "persistent": true, "insertionIndex": 13 } \ No newline at end of file From 5f9fed5a03e66ac976a6203fe4577e4724141ef0 Mon Sep 17 00:00:00 2001 From: Sorena Sarabadani Date: Sat, 21 Feb 2026 21:57:38 +0100 Subject: [PATCH 3/6] fix: add ReviewComment reflection/serialization --- .../org.kohsuke/github-api/reflect-config.json | 15 +++++++++++++++ .../github-api/serialization-config.json | 3 +++ 2 files changed, 18 insertions(+) diff --git a/src/main/resources/META-INF/native-image/org.kohsuke/github-api/reflect-config.json b/src/main/resources/META-INF/native-image/org.kohsuke/github-api/reflect-config.json index 30be262b74..8fb6290b25 100644 --- a/src/main/resources/META-INF/native-image/org.kohsuke/github-api/reflect-config.json +++ b/src/main/resources/META-INF/native-image/org.kohsuke/github-api/reflect-config.json @@ -4334,6 +4334,21 @@ "allPublicClasses": true, "allDeclaredClasses": true }, + { + "name": "org.kohsuke.github.GHPullRequestReview$ReviewComment", + "allPublicFields": true, + "allDeclaredFields": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredConstructors": true, + "allPublicConstructors": true, + "allDeclaredConstructors": true, + "queryAllPublicMethods": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredMethods": true, + "allPublicClasses": true, + "allDeclaredClasses": true + }, { "name": "org.kohsuke.github.GHPullRequestReviewBuilder", "allPublicFields": true, diff --git a/src/main/resources/META-INF/native-image/org.kohsuke/github-api/serialization-config.json b/src/main/resources/META-INF/native-image/org.kohsuke/github-api/serialization-config.json index 412aa47e18..a8c04c0da3 100644 --- a/src/main/resources/META-INF/native-image/org.kohsuke/github-api/serialization-config.json +++ b/src/main/resources/META-INF/native-image/org.kohsuke/github-api/serialization-config.json @@ -869,6 +869,9 @@ { "name": "org.kohsuke.github.GHPullRequestReview" }, + { + "name": "org.kohsuke.github.GHPullRequestReview$ReviewComment" + }, { "name": "org.kohsuke.github.GHPullRequestReviewBuilder" }, From b922e2b44c0911d200d1af65b3ee402d15f118da Mon Sep 17 00:00:00 2001 From: Sorena Sarabadani Date: Sat, 21 Feb 2026 23:18:35 +0100 Subject: [PATCH 4/6] fix: cover more methods in thest --- src/test/java/org/kohsuke/github/GHPullRequestTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/java/org/kohsuke/github/GHPullRequestTest.java b/src/test/java/org/kohsuke/github/GHPullRequestTest.java index feef903b7c..e54f532ace 100644 --- a/src/test/java/org/kohsuke/github/GHPullRequestTest.java +++ b/src/test/java/org/kohsuke/github/GHPullRequestTest.java @@ -743,6 +743,12 @@ public void pullRequestReviews() throws Exception { assertThat(comments.size(), equalTo(3)); GHPullRequestReview.ReviewComment comment = comments.get(0); assertThat(comment.getBody(), equalTo("Some niggle")); + assertThat(comment.getPath(), equalTo("README.md")); + assertThat(comment.getCommitId(), equalTo("07374fe73aff1c2024a8d4114b32406c7a8e89b7")); + assertThat(comment.getOriginalCommitId(), equalTo("07374fe73aff1c2024a8d4114b32406c7a8e89b7")); + assertThat(comment.getDiffHunk(), notNullValue()); + assertThat(comment.getAuthorAssociation(), equalTo(GHCommentAuthorAssociation.MEMBER)); + assertThat(comment.getOriginalPosition(), equalTo(1)); comment = comments.get(1); assertThat(comment.getBody(), equalTo("A single line comment")); assertThat(comment.getPosition(), equalTo(4)); From 3d8f059abdfd89228f499a37559dc8d5e2152f96 Mon Sep 17 00:00:00 2001 From: Sorena Sarabadani Date: Sat, 21 Feb 2026 23:44:59 +0100 Subject: [PATCH 5/6] fix: cover more methods in test --- src/test/java/org/kohsuke/github/GHPullRequestTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/java/org/kohsuke/github/GHPullRequestTest.java b/src/test/java/org/kohsuke/github/GHPullRequestTest.java index e54f532ace..f23bee77d6 100644 --- a/src/test/java/org/kohsuke/github/GHPullRequestTest.java +++ b/src/test/java/org/kohsuke/github/GHPullRequestTest.java @@ -749,6 +749,11 @@ public void pullRequestReviews() throws Exception { assertThat(comment.getDiffHunk(), notNullValue()); assertThat(comment.getAuthorAssociation(), equalTo(GHCommentAuthorAssociation.MEMBER)); assertThat(comment.getOriginalPosition(), equalTo(1)); + assertThat(comment.getHtmlUrl(), notNullValue()); + assertThat(comment.getPullRequestReviewId(), equalTo(2121304234L)); + assertThat(comment.getPullRequestUrl(), notNullValue()); + assertThat(comment.getReactions(), notNullValue()); + assertThat(comment.getUser().getLogin(), equalTo("maximevw")); comment = comments.get(1); assertThat(comment.getBody(), equalTo("A single line comment")); assertThat(comment.getPosition(), equalTo(4)); From daf758370edbd4cda3387336bc475589ab08dfb7 Mon Sep 17 00:00:00 2001 From: Sorena Sarabadani Date: Sun, 22 Feb 2026 00:08:28 +0100 Subject: [PATCH 6/6] fix: remove getUser method call due to missing stub --- src/test/java/org/kohsuke/github/GHPullRequestTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/org/kohsuke/github/GHPullRequestTest.java b/src/test/java/org/kohsuke/github/GHPullRequestTest.java index f23bee77d6..c451128a0b 100644 --- a/src/test/java/org/kohsuke/github/GHPullRequestTest.java +++ b/src/test/java/org/kohsuke/github/GHPullRequestTest.java @@ -753,7 +753,6 @@ public void pullRequestReviews() throws Exception { assertThat(comment.getPullRequestReviewId(), equalTo(2121304234L)); assertThat(comment.getPullRequestUrl(), notNullValue()); assertThat(comment.getReactions(), notNullValue()); - assertThat(comment.getUser().getLogin(), equalTo("maximevw")); comment = comments.get(1); assertThat(comment.getBody(), equalTo("A single line comment")); assertThat(comment.getPosition(), equalTo(4));