Move linking comments with change messages to Gerrit's backend

There are currently 2 different APIs for getting change messages and
published comments (GET /changes/{change_id}/detail and
GET/changes/{change_id}/comments respectively). The comments objects do
not contain an ID that references the change message to which they
belong.

This linking is done in the front end, and this change is moving this
implementation to the backend.

Change-Id: Id6dbec7325eaa7ae582a4f435ed0ed77a25306bd
This commit is contained in:
Youssef Elghareeb
2020-02-03 12:00:52 +01:00
parent 96110f5945
commit 7fdfa44975
10 changed files with 354 additions and 23 deletions

View File

@@ -6226,6 +6226,10 @@ invocations of the REST call are required.
Whether or not the comment must be addressed by the user. The state of
resolution of a comment thread is stored in the last comment in that thread
chronologically.
|`change_message_id` |optional|
Available with published comments. Contains the
link:rest-api-changes.html#change-message-info[id] of the change message
that this comment is linked to.
|===========================
[[comment-input]]

View File

@@ -20,6 +20,7 @@ import java.util.Objects;
public class CommentInfo extends Comment {
public AccountInfo author;
public String tag;
public String changeMessageId;
@Override
public boolean equals(Object o) {

View File

@@ -16,6 +16,8 @@ package com.google.gerrit.server;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ComparisonChain;
@@ -24,6 +26,7 @@ import com.google.common.collect.Ordering;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.entities.Comment;
import com.google.gerrit.entities.Patch;
import com.google.gerrit.entities.PatchSet;
@@ -229,6 +232,39 @@ public class CommentsUtil {
return commentsOnPatchSet(notes.load().getRobotComments().values(), psId);
}
/**
* This method populates the "changeMessageId" field of the comments parameter based on timestamp
* matching. The comments objects will be modified.
*
* <p>Each comment will be matched to the nearest next change message in timestamp
*
* @param comments the list of comments
* @param changeMessages list of change messages
*/
public static void linkCommentsToChangeMessages(
List<? extends CommentInfo> comments, List<ChangeMessage> changeMessages) {
ArrayList<ChangeMessage> sortedChangeMessages =
changeMessages.stream()
.sorted(comparing(ChangeMessage::getWrittenOn))
.collect(toCollection(ArrayList::new));
ArrayList<CommentInfo> sortedCommentInfos =
comments.stream().sorted(comparing(c -> c.updated)).collect(toCollection(ArrayList::new));
int cmItr = 0;
for (CommentInfo comment : sortedCommentInfos) {
// Keep advancing the change message pointer until we associate the comment to the next change
// message in timestamp
while (cmItr < sortedChangeMessages.size()
&& comment.updated.after(sortedChangeMessages.get(cmItr).getWrittenOn())) {
cmItr += 1;
}
if (cmItr < changeMessages.size()) {
comment.changeMessageId = sortedChangeMessages.get(cmItr).getKey().uuid();
}
}
}
/**
* For the commit message the A side in a diff view is always empty when a comparison against an
* ancestor is done, so there can't be any comments on this ancestor. However earlier we showed

View File

@@ -14,12 +14,16 @@
package com.google.gerrit.server.restapi.change;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.entities.Comment;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -33,6 +37,7 @@ import java.util.Map;
@Singleton
public class ListChangeComments implements RestReadView<ChangeResource> {
private final ChangeMessagesUtil changeMessagesUtil;
private final ChangeData.Factory changeDataFactory;
private final Provider<CommentJson> commentJson;
private final CommentsUtil commentsUtil;
@@ -41,10 +46,12 @@ public class ListChangeComments implements RestReadView<ChangeResource> {
ListChangeComments(
ChangeData.Factory changeDataFactory,
Provider<CommentJson> commentJson,
CommentsUtil commentsUtil) {
CommentsUtil commentsUtil,
ChangeMessagesUtil changeMessagesUtil) {
this.changeDataFactory = changeDataFactory;
this.commentJson = commentJson;
this.commentsUtil = commentsUtil;
this.changeMessagesUtil = changeMessagesUtil;
}
@Override
@@ -53,7 +60,7 @@ public class ListChangeComments implements RestReadView<ChangeResource> {
if (!rsrc.getUser().isIdentifiedUser()) {
throw new AuthException("Authentication required");
}
return Response.ok(getAsMap(listComments(rsrc)));
return Response.ok(getAsMap(listComments(rsrc), rsrc));
}
public List<CommentInfo> getComments(ChangeResource rsrc)
@@ -61,7 +68,7 @@ public class ListChangeComments implements RestReadView<ChangeResource> {
if (!rsrc.getUser().isIdentifiedUser()) {
throw new AuthException("Authentication required");
}
return getAsList(listComments(rsrc));
return getAsList(listComments(rsrc), rsrc);
}
private Iterable<Comment> listComments(ChangeResource rsrc) {
@@ -69,14 +76,22 @@ public class ListChangeComments implements RestReadView<ChangeResource> {
return commentsUtil.publishedByChange(cd.notes());
}
private ImmutableList<CommentInfo> getAsList(Iterable<Comment> comments)
private ImmutableList<CommentInfo> getAsList(Iterable<Comment> comments, ChangeResource rsrc)
throws PermissionBackendException {
return getCommentFormatter().formatAsList(comments);
ImmutableList<CommentInfo> commentInfos = getCommentFormatter().formatAsList(comments);
List<ChangeMessage> changeMessages = changeMessagesUtil.byChange(rsrc.getNotes());
CommentsUtil.linkCommentsToChangeMessages(commentInfos, changeMessages);
return commentInfos;
}
private Map<String, List<CommentInfo>> getAsMap(Iterable<Comment> comments)
private Map<String, List<CommentInfo>> getAsMap(Iterable<Comment> comments, ChangeResource rsrc)
throws PermissionBackendException {
return getCommentFormatter().format(comments);
Map<String, List<CommentInfo>> commentInfosMap = getCommentFormatter().format(comments);
List<CommentInfo> commentInfos =
commentInfosMap.values().stream().flatMap(List::stream).collect(toList());
List<ChangeMessage> changeMessages = changeMessagesUtil.byChange(rsrc.getNotes());
CommentsUtil.linkCommentsToChangeMessages(commentInfos, changeMessages);
return commentInfosMap;
}
private CommentFormatter getCommentFormatter() {

View File

@@ -14,10 +14,14 @@
package com.google.gerrit.server.restapi.change;
import static java.util.stream.Collectors.toList;
import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.extensions.common.RobotCommentInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -31,27 +35,35 @@ public class ListChangeRobotComments implements RestReadView<ChangeResource> {
private final ChangeData.Factory changeDataFactory;
private final Provider<CommentJson> commentJson;
private final CommentsUtil commentsUtil;
private final ChangeMessagesUtil changeMessagesUtil;
@Inject
ListChangeRobotComments(
ChangeData.Factory changeDataFactory,
Provider<CommentJson> commentJson,
CommentsUtil commentsUtil) {
CommentsUtil commentsUtil,
ChangeMessagesUtil changeMessagesUtil) {
this.changeDataFactory = changeDataFactory;
this.commentJson = commentJson;
this.commentsUtil = commentsUtil;
this.changeMessagesUtil = changeMessagesUtil;
}
@Override
public Response<Map<String, List<RobotCommentInfo>>> apply(ChangeResource rsrc)
throws AuthException, PermissionBackendException {
ChangeData cd = changeDataFactory.create(rsrc.getNotes());
return Response.ok(
Map<String, List<RobotCommentInfo>> robotCommentsMap =
commentJson
.get()
.setFillAccounts(true)
.setFillPatchSet(true)
.newRobotCommentFormatter()
.format(commentsUtil.robotCommentsByChange(cd.notes())));
.format(commentsUtil.robotCommentsByChange(cd.notes()));
List<RobotCommentInfo> commentInfos =
robotCommentsMap.values().stream().flatMap(List::stream).collect(toList());
List<ChangeMessage> changeMessages = changeMessagesUtil.byChange(rsrc.getNotes());
CommentsUtil.linkCommentsToChangeMessages(commentInfos, changeMessages);
return Response.ok(robotCommentsMap);
}
}

View File

@@ -14,14 +14,19 @@
package com.google.gerrit.server.restapi.change;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.extensions.common.RobotCommentInfo;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.restapi.change.CommentJson.RobotCommentFormatter;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -32,34 +37,52 @@ import java.util.Map;
public class ListRobotComments implements RestReadView<RevisionResource> {
protected final Provider<CommentJson> commentJson;
protected final CommentsUtil commentsUtil;
protected final ChangeMessagesUtil changeMessagesUtil;
@Inject
ListRobotComments(Provider<CommentJson> commentJson, CommentsUtil commentsUtil) {
ListRobotComments(
Provider<CommentJson> commentJson,
CommentsUtil commentsUtil,
ChangeMessagesUtil changeMessagesUtil) {
this.commentJson = commentJson;
this.commentsUtil = commentsUtil;
this.changeMessagesUtil = changeMessagesUtil;
}
@Override
public Response<Map<String, List<RobotCommentInfo>>> apply(RevisionResource rsrc)
throws PermissionBackendException {
return Response.ok(
commentJson
.get()
.setFillAccounts(true)
.newRobotCommentFormatter()
.format(listComments(rsrc)));
return Response.ok(getAsMap(listComments(rsrc), rsrc));
}
public ImmutableList<RobotCommentInfo> getComments(RevisionResource rsrc)
throws PermissionBackendException {
return commentJson
.get()
.setFillAccounts(true)
.newRobotCommentFormatter()
.formatAsList(listComments(rsrc));
return getAsList(listComments(rsrc), rsrc);
}
private ImmutableList<RobotCommentInfo> getAsList(
Iterable<RobotComment> comments, RevisionResource rsrc) throws PermissionBackendException {
ImmutableList<RobotCommentInfo> commentInfos = getCommentFormatter().formatAsList(comments);
List<ChangeMessage> changeMessages = changeMessagesUtil.byChange(rsrc.getNotes());
CommentsUtil.linkCommentsToChangeMessages(commentInfos, changeMessages);
return commentInfos;
}
private Map<String, List<RobotCommentInfo>> getAsMap(
Iterable<RobotComment> comments, RevisionResource rsrc) throws PermissionBackendException {
Map<String, List<RobotCommentInfo>> commentInfosMap = getCommentFormatter().format(comments);
List<RobotCommentInfo> commentInfos =
commentInfosMap.values().stream().flatMap(List::stream).collect(toList());
List<ChangeMessage> changeMessages = changeMessagesUtil.byChange(rsrc.getNotes());
CommentsUtil.linkCommentsToChangeMessages(commentInfos, changeMessages);
return commentInfosMap;
}
private Iterable<RobotComment> listComments(RevisionResource rsrc) {
return commentsUtil.robotCommentsByPatchSet(rsrc.getNotes(), rsrc.getPatchSet().id());
}
private RobotCommentFormatter getCommentFormatter() {
return commentJson.get().setFillAccounts(true).newRobotCommentFormatter();
}
}

View File

@@ -135,10 +135,15 @@ public class TestCommentHelper {
public void addRobotComment(String targetChangeId, RobotCommentInput robotCommentInput)
throws Exception {
addRobotComment(targetChangeId, robotCommentInput, "robot comment test");
}
public void addRobotComment(
String targetChangeId, RobotCommentInput robotCommentInput, String message) throws Exception {
ReviewInput reviewInput = new ReviewInput();
reviewInput.robotComments =
Collections.singletonMap(robotCommentInput.path, ImmutableList.of(robotCommentInput));
reviewInput.message = "robot comment test";
reviewInput.message = message;
gApi.changes().id(targetChangeId).current().review(reviewInput);
}
}

View File

@@ -14,8 +14,10 @@
package com.google.gerrit.acceptance.api.revision;
import static com.google.common.collect.MoreCollectors.onlyElement;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.PushOneCommit.SUBJECT;
import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES;
import static com.google.gerrit.extensions.common.testing.DiffInfoSubject.assertThat;
import static com.google.gerrit.extensions.common.testing.EditInfoSubject.assertThat;
import static com.google.gerrit.extensions.common.testing.RobotCommentInfoSubject.assertThatList;
@@ -27,12 +29,14 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.UseClockStep;
import com.google.gerrit.acceptance.config.GerritConfig;
import com.google.gerrit.entities.Patch;
import com.google.gerrit.extensions.api.changes.PublishChangeEditInput;
import com.google.gerrit.extensions.api.changes.ReviewInput.RobotCommentInput;
import com.google.gerrit.extensions.client.Comment;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.common.ChangeType;
import com.google.gerrit.extensions.common.DiffInfo;
import com.google.gerrit.extensions.common.DiffInfo.IntraLineStatus;
@@ -47,6 +51,7 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.testing.BinaryResultSubject;
import com.google.gerrit.testing.TestCommentHelper;
import com.google.gerrit.testing.TestTimeUtil;
import com.google.inject.Inject;
import java.util.Arrays;
import java.util.Collections;
@@ -54,6 +59,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
@@ -139,6 +145,91 @@ public class RobotCommentsIT extends AbstractDaemonTest {
assertRobotComment(comment2, in2, false);
}
@UseClockStep
@Test
public void addedRobotCommentsAreLinkedToChangeMessages() throws Exception {
TestTimeUtil.resetWithClockStep(0, TimeUnit.SECONDS);
PushOneCommit.Result r = createChange();
/* Advancing the time after creating the change so that the first robot comment is not in the same timestamp as with the change creation */
TestTimeUtil.incrementClock(5, TimeUnit.SECONDS);
RobotCommentInput c1 = TestCommentHelper.createRobotCommentInput(FILE_NAME);
RobotCommentInput c2 = TestCommentHelper.createRobotCommentInput(FILE_NAME);
RobotCommentInput c3 = TestCommentHelper.createRobotCommentInput(FILE_NAME);
/* Give the robot comments identifiable names for testing */
c1.message = "robot comment 1";
c2.message = "robot comment 2";
c3.message = "robot comment 3";
testCommentHelper.addRobotComment(changeId, c1, "robot message 1");
TestTimeUtil.incrementClock(5, TimeUnit.SECONDS);
testCommentHelper.addRobotComment(changeId, c2, "robot message 2");
TestTimeUtil.incrementClock(5, TimeUnit.SECONDS);
testCommentHelper.addRobotComment(changeId, c3, "robot message 3");
TestTimeUtil.incrementClock(5, TimeUnit.SECONDS);
Map<String, List<RobotCommentInfo>> robotComments = gApi.changes().id(changeId).robotComments();
List<RobotCommentInfo> robotCommentsList =
robotComments.values().stream().flatMap(List::stream).collect(toList());
List<ChangeMessageInfo> allMessages =
gApi.changes().id(changeId).get(MESSAGES).messages.stream().collect(toList());
assertThat(allMessages.stream().map(cm -> cm.message).collect(toList()))
.containsExactly(
"Uploaded patch set 1.",
"Patch Set 1:\n\n(1 comment)\n\nrobot message 1",
"Patch Set 1:\n\n(1 comment)\n\nrobot message 2",
"Patch Set 1:\n\n(1 comment)\n\nrobot message 3");
assertThat(robotCommentsList.stream().map(c -> c.message).collect(toList()))
.containsExactly("robot comment 1", "robot comment 2", "robot comment 3");
String uploadPsMessageId =
allMessages.stream()
.filter(c -> c.message.equals("Uploaded patch set 1."))
.collect(onlyElement())
.id;
String message2ChangeId =
allMessages.stream()
.filter(c -> c.message.contains("robot message 2"))
.collect(onlyElement())
.id;
String message3ChangeId =
allMessages.stream()
.filter(c -> c.message.contains("robot message 3"))
.collect(onlyElement())
.id;
String comment1MessageId =
robotCommentsList.stream()
.filter(c -> c.message.equals("robot comment 1"))
.collect(onlyElement())
.changeMessageId;
String comment2MessageId =
robotCommentsList.stream()
.filter(c -> c.message.equals("robot comment 2"))
.collect(onlyElement())
.changeMessageId;
String comment3MessageId =
robotCommentsList.stream()
.filter(c -> c.message.equals("robot comment 3"))
.collect(onlyElement())
.changeMessageId;
/**
* Upload PS message, robot message 1 & robot comment 1 all have the same timestamp. The robot
* comment is matched to the PS upload message because it occurs first in the list. A comment is
* matched with the first change message having a timestamp not less than the comment.
* TODO(ghareeb): enhance the matching to ignore auto-generated messages.
*/
assertThat(uploadPsMessageId).isEqualTo(comment1MessageId);
assertThat(message2ChangeId).isEqualTo(comment2MessageId);
assertThat(message3ChangeId).isEqualTo(comment3MessageId);
}
@Test
public void robotCommentsCanBeRetrievedAsList() throws Exception {
RobotCommentInput robotCommentInput = TestCommentHelper.createRobotCommentInput(FILE_NAME);

View File

@@ -48,6 +48,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.MoreCollectors;
import com.google.common.collect.Streams;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.ExtensionRegistry;
@@ -103,6 +104,7 @@ import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.project.testing.TestLabels;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.testing.FakeEmailSender.Message;
import com.google.gerrit.testing.TestTimeUtil;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.Collection;
@@ -114,6 +116,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import org.eclipse.jgit.api.errors.GitAPIException;
@@ -2021,6 +2024,70 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
assertThat(info.status).isEqualTo(ChangeStatus.NEW);
}
@Test
public void publishedCommentsAssignedToChangeMessages() throws Exception {
TestTimeUtil.resetWithClockStep(0, TimeUnit.SECONDS);
PushOneCommit.Result r = createChange(); // creating the change with patch set 1
TestTimeUtil.incrementClock(5, TimeUnit.SECONDS);
/** Create and publish a comment on PS2. Increment the clock step */
String rev1 = r.getCommit().name();
addDraft(r.getChangeId(), rev1, newDraft(FILE_NAME, 1, "comment_PS2."));
r = amendChange(r.getChangeId(), "refs/for/master%publish-comments");
assertThat(getPublishedComments(r.getChangeId())).isNotEmpty();
TestTimeUtil.incrementClock(5, TimeUnit.SECONDS);
/** Create and publish a comment on PS3 */
String rev2 = r.getCommit().name();
addDraft(r.getChangeId(), rev2, newDraft(FILE_NAME, 1, "comment_PS3."));
amendChange(r.getChangeId(), "refs/for/master%publish-comments");
Collection<CommentInfo> comments = getPublishedComments(r.getChangeId());
List<ChangeMessageInfo> allMessages = getMessages(r.getChangeId());
assertThat(allMessages.stream().map(m -> m.message).collect(toList()))
.containsExactly(
"Uploaded patch set 1.",
"Uploaded patch set 2.",
"Patch Set 2:\n\n(1 comment)",
"Uploaded patch set 3.",
"Patch Set 3:\n\n(1 comment)")
.inOrder();
/**
* Note that the following 3 items have the same timestamp: comment "comment_PS2", message
* "Uploaded patch set 2.", and message "Patch Set 2:\n\n(1 comment)". For same change message
* timestamps, the matching to a specific message is not guaranteed. TODO(ghareeb) enhance the
* matching to ignore change messages with "auto-generated" tag
*/
String commentPs2MessageId =
comments.stream()
.filter(c -> c.message.equals("comment_PS2."))
.collect(MoreCollectors.onlyElement())
.changeMessageId;
String commentPs3MessageId =
comments.stream()
.filter(c -> c.message.equals("comment_PS3."))
.collect(MoreCollectors.onlyElement())
.changeMessageId;
String message2Id =
allMessages.stream()
.filter(m -> m.message.equals("Uploaded patch set 2."))
.collect(MoreCollectors.onlyElement())
.id;
String message3Id =
allMessages.stream()
.filter(m -> m.message.equals("Uploaded patch set 3."))
.collect(MoreCollectors.onlyElement())
.id;
assertThat(commentPs2MessageId).isEqualTo(message2Id);
assertThat(commentPs3MessageId).isEqualTo(message3Id);
}
@Test
public void publishCommentsOnPushPublishesDraftsOnAllRevisions() throws Exception {
PushOneCommit.Result r = createChange();

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gerrit.server.restapi.change;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.server.CommentsUtil;
import java.sql.Timestamp;
import org.junit.Test;
public class ListChangeCommentsTest {
@Test
public void commentsLinkedToChangeMessages() {
CommentInfo c1 = getNewCommentInfo("c1", Timestamp.valueOf("2018-01-01 09:01:00"));
CommentInfo c2 = getNewCommentInfo("c2", Timestamp.valueOf("2018-01-01 09:01:15"));
CommentInfo c3 = getNewCommentInfo("c3", Timestamp.valueOf("2018-01-01 09:01:25"));
ChangeMessage cm1 =
getNewChangeMessage("cm1key", "cm1", Timestamp.valueOf("2018-01-01 00:00:00"));
ChangeMessage cm2 =
getNewChangeMessage("cm2key", "cm2", Timestamp.valueOf("2018-01-01 09:01:15"));
ChangeMessage cm3 =
getNewChangeMessage("cm3key", "cm3", Timestamp.valueOf("2018-01-01 09:01:27"));
ChangeMessage cm4 =
getNewChangeMessage("cm4key", "cm4", Timestamp.valueOf("2018-01-01 09:01:32"));
assertThat(c1.changeMessageId).isNull();
assertThat(c2.changeMessageId).isNull();
assertThat(c3.changeMessageId).isNull();
ImmutableList<CommentInfo> comments = ImmutableList.of(c1, c2, c3);
ImmutableList<ChangeMessage> changeMessages = ImmutableList.of(cm1, cm2, cm3);
CommentsUtil.linkCommentsToChangeMessages(comments, changeMessages);
assertThat(c1.changeMessageId).isEqualTo(changeMessageKey(cm2));
assertThat(c2.changeMessageId).isEqualTo(changeMessageKey(cm2));
assertThat(c3.changeMessageId).isEqualTo(changeMessageKey(cm3));
}
private static CommentInfo getNewCommentInfo(String message, Timestamp ts) {
CommentInfo c = new CommentInfo();
c.message = message;
c.updated = ts;
return c;
}
private static ChangeMessage getNewChangeMessage(String id, String message, Timestamp ts) {
ChangeMessage.Key key = ChangeMessage.key(Change.id(1), id);
ChangeMessage cm = new ChangeMessage(key, null, ts, null);
cm.setMessage(message);
return cm;
}
private static String changeMessageKey(ChangeMessage changeMessage) {
return changeMessage.getKey().uuid();
}
}