refactor: add creationTime field for comment extension to resolve migration issue (#3341)

#### What type of PR is this?
/kind improvement
/area core
/milestone 2.3.x
/kind api-change

#### What this PR does / why we need it:
1. spec 中新增 creationTime
2. 旧数据的 spec.creationTime 默认等于 approvedTime
3. 按照 metadata.creationTimestamp 排序的使用 spec.creationTime 代替

how to test it?
1. 使用迁移插件迁移看评论和回复的排序是否正确
2. 使用评论插件创建评论和回复看顺序是否正确
#### Which issue(s) this PR fixes:
Fixes #3330

#### Special notes for your reviewer:
/cc @halo-dev/sig-halo 
#### Does this PR introduce a user-facing change?

```release-note
评论和回复新增创建时间以兼容迁移数据的排序
```
pull/3379/head
guqing 2023-02-23 18:06:12 +08:00 committed by GitHub
parent 6c2064f1e0
commit 3a1587bab5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 218 additions and 61 deletions

View File

@ -93,6 +93,9 @@ public class CommentServiceImpl implements CommentService {
if (comment.getSpec().getOwner() != null) { if (comment.getSpec().getOwner() != null) {
return Mono.just(comment); return Mono.just(comment);
} }
if (comment.getSpec().getCreationTime() == null) {
comment.getSpec().setCreationTime(Instant.now());
}
// populate owner from current user // populate owner from current user
return fetchCurrentUser() return fetchCurrentUser()
.map(this::toCommentOwner) .map(this::toCommentOwner)

View File

@ -30,12 +30,12 @@ public enum CommentSorter {
static Comparator<Comment> from(CommentSorter sorter) { static Comparator<Comment> from(CommentSorter sorter) {
if (sorter == null) { if (sorter == null) {
return defaultCommentComparator(); return lastReplyTimeComparator();
} }
if (CREATE_TIME.equals(sorter)) { if (CREATE_TIME.equals(sorter)) {
Function<Comment, Instant> comparatorFunc = Function<Comment, Instant> comparatorFunc =
comment -> comment.getMetadata().getCreationTimestamp(); comment -> comment.getSpec().getCreationTime();
return Comparator.comparing(comparatorFunc) return Comparator.comparing(comparatorFunc, Comparators.nullsLow())
.thenComparing(name); .thenComparing(name);
} }
@ -65,12 +65,12 @@ public enum CommentSorter {
return null; return null;
} }
static Comparator<Comment> defaultCommentComparator() { static Comparator<Comment> lastReplyTimeComparator() {
Function<Comment, Instant> comparatorFunc = Function<Comment, Instant> comparatorFunc =
comment -> { comment -> {
Instant lastReplyTime = comment.getStatusOrDefault().getLastReplyTime(); Instant lastReplyTime = comment.getStatusOrDefault().getLastReplyTime();
return Optional.ofNullable( return Optional.ofNullable(lastReplyTime)
lastReplyTime).orElse(comment.getMetadata().getCreationTimestamp()); .orElse(comment.getSpec().getCreationTime());
}; };
return Comparator.comparing(comparatorFunc, Comparators.nullsLow()) return Comparator.comparing(comparatorFunc, Comparators.nullsLow())
.thenComparing(name); .thenComparing(name);

View File

@ -1,5 +1,9 @@
package run.halo.app.content.comment; package run.halo.app.content.comment;
import java.time.Instant;
import java.util.Comparator;
import java.util.function.Function;
import org.springframework.util.comparator.Comparators;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import run.halo.app.core.extension.content.Reply; import run.halo.app.core.extension.content.Reply;
import run.halo.app.extension.ListResult; import run.halo.app.extension.ListResult;
@ -15,4 +19,20 @@ public interface ReplyService {
Mono<Reply> create(String commentName, Reply reply); Mono<Reply> create(String commentName, Reply reply);
Mono<ListResult<ListedReply>> list(ReplyQuery query); Mono<ListResult<ListedReply>> list(ReplyQuery query);
/**
* Ascending order by creation time.
*
* @return reply comparator
*/
static Comparator<Reply> creationTimeAscComparator() {
Function<Reply, Instant> creationTime = reply -> reply.getSpec().getCreationTime();
Function<Reply, Instant> metadataCreationTime =
reply -> reply.getMetadata().getCreationTimestamp();
// ascending order by creation time
// asc nulls high will be placed at the end
return Comparator.comparing(creationTime, Comparators.nullsHigh())
.thenComparing(metadataCreationTime)
.thenComparing(reply -> reply.getMetadata().getName());
}
} }

View File

@ -3,7 +3,6 @@ package run.halo.app.content.comment;
import static run.halo.app.extension.router.selector.SelectorUtil.labelAndFieldSelectorToPredicate; import static run.halo.app.extension.router.selector.SelectorUtil.labelAndFieldSelectorToPredicate;
import java.time.Instant; import java.time.Instant;
import java.util.Comparator;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
@ -51,6 +50,9 @@ public class ReplyServiceImpl implements ReplyService {
if (reply.getSpec().getPriority() == null) { if (reply.getSpec().getPriority() == null) {
reply.getSpec().setPriority(0); reply.getSpec().setPriority(0);
} }
if (reply.getSpec().getCreationTime() == null) {
reply.getSpec().setCreationTime(Instant.now());
}
return environmentFetcher.fetchComment() return environmentFetcher.fetchComment()
.map(commentSetting -> { .map(commentSetting -> {
if (Boolean.FALSE.equals(commentSetting.getEnable())) { if (Boolean.FALSE.equals(commentSetting.getEnable())) {
@ -104,7 +106,8 @@ public class ReplyServiceImpl implements ReplyService {
@Override @Override
public Mono<ListResult<ListedReply>> list(ReplyQuery query) { public Mono<ListResult<ListedReply>> list(ReplyQuery query) {
return client.list(Reply.class, getReplyPredicate(query), defaultComparator(), return client.list(Reply.class, getReplyPredicate(query),
ReplyService.creationTimeAscComparator(),
query.getPage(), query.getSize()) query.getPage(), query.getSize())
.flatMap(list -> Flux.fromStream(list.get() .flatMap(list -> Flux.fromStream(list.get()
.map(this::toListedReply)) .map(this::toListedReply))
@ -140,12 +143,6 @@ public class ReplyServiceImpl implements ReplyService {
"Unsupported owner kind: " + owner.getKind()); "Unsupported owner kind: " + owner.getKind());
} }
Comparator<Reply> defaultComparator() {
Function<Reply, Instant> createTime = reply -> reply.getMetadata().getCreationTimestamp();
return Comparator.comparing(createTime)
.thenComparing(reply -> reply.getMetadata().getName());
}
Predicate<Reply> getReplyPredicate(ReplyQuery query) { Predicate<Reply> getReplyPredicate(ReplyQuery query) {
Predicate<Reply> predicate = reply -> true; Predicate<Reply> predicate = reply -> true;
if (query.getCommentName() != null) { if (query.getCommentName() != null) {

View File

@ -1,5 +1,7 @@
package run.halo.app.core.extension.content; package run.halo.app.core.extension.content;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import java.time.Instant; import java.time.Instant;
@ -70,6 +72,11 @@ public class Comment extends AbstractExtension {
private Instant approvedTime; private Instant approvedTime;
/**
* The user-defined creation time default is <code>metadata.creationTimestamp</code>.
*/
private Instant creationTime;
@Schema(required = true, defaultValue = "0") @Schema(required = true, defaultValue = "0")
private Integer priority; private Integer priority;
@ -84,6 +91,15 @@ public class Comment extends AbstractExtension {
@Schema(required = true, defaultValue = "false") @Schema(required = true, defaultValue = "false")
private Boolean hidden; private Boolean hidden;
/**
* If the creation time is null, use approvedTime.
*
* @return if creationTime is null returned approved time, otherwise creationTime.
*/
public Instant getCreationTime() {
return defaultIfNull(creationTime, approvedTime);
}
} }
@Data @Data
@ -130,8 +146,9 @@ public class Comment extends AbstractExtension {
if (lastReadTime == null) { if (lastReadTime == null) {
return true; return true;
} }
return existingReply.getMetadata().getCreationTimestamp() Instant creationTime = defaultIfNull(existingReply.getSpec().getCreationTime(),
.isAfter(lastReadTime); existingReply.getMetadata().getCreationTimestamp());
return creationTime.isAfter(lastReadTime);
}) })
.count(); .count();
return (int) unreadReplyCount; return (int) unreadReplyCount;

View File

@ -1,16 +1,15 @@
package run.halo.app.core.extension.reconciler; package run.halo.app.core.extension.reconciler;
import java.time.Instant; import java.time.Instant;
import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import run.halo.app.content.comment.ReplyService;
import run.halo.app.core.extension.Counter; import run.halo.app.core.extension.Counter;
import run.halo.app.core.extension.content.Comment; import run.halo.app.core.extension.content.Comment;
import run.halo.app.core.extension.content.Constant; import run.halo.app.core.extension.content.Constant;
@ -110,7 +109,7 @@ public class CommentReconciler implements Reconciler<Reconciler.Request> {
List<Reply> replies = client.list(Reply.class, List<Reply> replies = client.list(Reply.class,
reply -> commentName.equals(reply.getSpec().getCommentName()) reply -> commentName.equals(reply.getSpec().getCommentName())
&& reply.getMetadata().getDeletionTimestamp() == null, && reply.getMetadata().getDeletionTimestamp() == null,
defaultReplyComparator()); ReplyService.creationTimeAscComparator());
// calculate unread reply count // calculate unread reply count
comment.getStatusOrDefault() comment.getStatusOrDefault()
@ -181,11 +180,4 @@ public class CommentReconciler implements Reconciler<Reconciler.Request> {
} }
return new GroupVersionKind(ref.getGroup(), ref.getVersion(), ref.getKind()); return new GroupVersionKind(ref.getGroup(), ref.getVersion(), ref.getKind());
} }
Comparator<Reply> defaultReplyComparator() {
Function<Reply, Instant> createTime = reply -> reply.getMetadata().getCreationTimestamp();
return Comparator.comparing(createTime)
.thenComparing(reply -> reply.getMetadata().getName())
.reversed();
}
} }

View File

@ -1,14 +1,15 @@
package run.halo.app.metrics; package run.halo.app.metrics;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.function.Function;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.SmartLifecycle; import org.springframework.context.SmartLifecycle;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import run.halo.app.content.comment.ReplyService;
import run.halo.app.core.extension.content.Comment; import run.halo.app.core.extension.content.Comment;
import run.halo.app.core.extension.content.Reply; import run.halo.app.core.extension.content.Reply;
import run.halo.app.event.post.ReplyEvent; import run.halo.app.event.post.ReplyEvent;
@ -51,20 +52,24 @@ public class ReplyEventReconciler implements Reconciler<ReplyEvent>, SmartLifecy
.filter(comment -> comment.getMetadata().getDeletionTimestamp() == null) .filter(comment -> comment.getMetadata().getDeletionTimestamp() == null)
.ifPresent(comment -> { .ifPresent(comment -> {
// order by reply creation time desc to get first as last reply time
List<Reply> replies = client.list(Reply.class, List<Reply> replies = client.list(Reply.class,
record -> commentName.equals(record.getSpec().getCommentName()) record -> commentName.equals(record.getSpec().getCommentName())
&& record.getMetadata().getDeletionTimestamp() == null, && record.getMetadata().getDeletionTimestamp() == null,
defaultReplyComparator()); ReplyService.creationTimeAscComparator().reversed());
Comment.CommentStatus status = comment.getStatusOrDefault(); Comment.CommentStatus status = comment.getStatusOrDefault();
// total reply count // total reply count
status.setReplyCount(replies.size()); status.setReplyCount(replies.size());
// calculate last reply time // calculate last reply time
if (!replies.isEmpty()) { Instant lastReplyTime = replies.stream()
Instant lastReplyTime = replies.get(0).getMetadata().getCreationTimestamp(); .findFirst()
status.setLastReplyTime(lastReplyTime); .map(reply -> defaultIfNull(reply.getSpec().getCreationTime(),
} reply.getMetadata().getCreationTimestamp())
)
.orElse(null);
status.setLastReplyTime(lastReplyTime);
Instant lastReadTime = comment.getSpec().getLastReadTime(); Instant lastReadTime = comment.getSpec().getLastReadTime();
status.setUnreadReplyCount(Comment.getUnreadReplyCount(replies, lastReadTime)); status.setUnreadReplyCount(Comment.getUnreadReplyCount(replies, lastReadTime));
@ -74,13 +79,6 @@ public class ReplyEventReconciler implements Reconciler<ReplyEvent>, SmartLifecy
return new Result(false, null); return new Result(false, null);
} }
Comparator<Reply> defaultReplyComparator() {
Function<Reply, Instant> createTime = reply -> reply.getMetadata().getCreationTimestamp();
return Comparator.comparing(createTime)
.thenComparing(reply -> reply.getMetadata().getName())
.reversed();
}
@Override @Override
public Controller setupWith(ControllerBuilder builder) { public Controller setupWith(ControllerBuilder builder) {
return new DefaultController<>( return new DefaultController<>(

View File

@ -1,10 +1,8 @@
package run.halo.app.theme.finders.impl; package run.halo.app.theme.finders.impl;
import java.security.Principal; import java.security.Principal;
import java.time.Instant;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
@ -13,9 +11,11 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContext;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.comparator.Comparators;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import run.halo.app.content.comment.OwnerInfo; import run.halo.app.content.comment.OwnerInfo;
import run.halo.app.content.comment.ReplyService;
import run.halo.app.core.extension.User; import run.halo.app.core.extension.User;
import run.halo.app.core.extension.content.Comment; import run.halo.app.core.extension.content.Comment;
import run.halo.app.core.extension.content.Reply; import run.halo.app.core.extension.content.Reply;
@ -70,12 +70,11 @@ public class CommentFinderImpl implements CommentFinder {
@Override @Override
public Mono<ListResult<ReplyVo>> listReply(String commentName, Integer page, Integer size) { public Mono<ListResult<ReplyVo>> listReply(String commentName, Integer page, Integer size) {
Comparator<Reply> comparator =
Comparator.comparing(reply -> reply.getMetadata().getCreationTimestamp());
return fixedReplyPredicate(commentName) return fixedReplyPredicate(commentName)
.flatMap(fixedPredicate -> .flatMap(fixedPredicate ->
client.list(Reply.class, fixedPredicate, client.list(Reply.class, fixedPredicate,
comparator.reversed(), pageNullSafe(page), sizeNullSafe(size)) ReplyService.creationTimeAscComparator(), pageNullSafe(page),
sizeNullSafe(size))
.flatMap(list -> Flux.fromStream(list.get().map(this::toReplyVo)) .flatMap(list -> Flux.fromStream(list.get().map(this::toReplyVo))
.concatMap(Function.identity()) .concatMap(Function.identity())
.collectList() .collectList()
@ -166,19 +165,36 @@ public class CommentFinderImpl implements CommentFinder {
} }
static Comparator<Comment> defaultComparator() { static Comparator<Comment> defaultComparator() {
Function<Comment, Boolean> top = return new CommentComparator();
comment -> Objects.requireNonNullElse(comment.getSpec().getTop(), false); }
Function<Comment, Integer> priority =
comment -> Objects.requireNonNullElse(comment.getSpec().getPriority(), 0); static class CommentComparator implements Comparator<Comment> {
Function<Comment, Instant> creationTimestamp = @Override
comment -> comment.getMetadata().getCreationTimestamp(); public int compare(Comment c1, Comment c2) {
Function<Comment, String> name = boolean c1Top = BooleanUtils.isTrue(c1.getSpec().getTop());
comment -> comment.getMetadata().getName(); boolean c2Top = BooleanUtils.isTrue(c2.getSpec().getTop());
return Comparator.comparing(top) if (c1Top == c2Top) {
.thenComparing(priority) int c1Priority = ObjectUtils.defaultIfNull(c1.getSpec().getPriority(), 0);
.thenComparing(creationTimestamp) int c2Priority = ObjectUtils.defaultIfNull(c2.getSpec().getPriority(), 0);
.thenComparing(name) if (c1Top) {
.reversed(); // 都置顶
return Integer.compare(c1Priority, c2Priority);
}
// 两个评论不置顶根据 creationTime 降序排列
return Comparator.comparing(
(Comment comment) -> comment.getSpec().getCreationTime(),
Comparators.nullsLow())
.thenComparing((Comment comment) -> comment.getMetadata().getName())
.compare(c2, c1);
} else if (c1Top) {
// 只有 c1 置顶c1 排前面
return -1;
} else {
// 只有c2置顶, c2排在前面
return 1;
}
}
} }
int pageNullSafe(Integer page) { int pageNullSafe(Integer page) {

View File

@ -127,6 +127,7 @@ class CommentServiceImplTest {
verify(client, times(1)).create(captor.capture()); verify(client, times(1)).create(captor.capture());
Comment comment = captor.getValue(); Comment comment = captor.getValue();
comment.getSpec().setCreationTime(null);
JSONAssert.assertEquals(""" JSONAssert.assertEquals("""
{ {
"spec": { "spec": {

View File

@ -102,7 +102,7 @@ class CommentSorterTest {
@Test @Test
void sortByDefaultDesc() { void sortByDefaultDesc() {
Comparator<Comment> defaultComparator = CommentSorter.defaultCommentComparator().reversed(); Comparator<Comment> defaultComparator = CommentSorter.lastReplyTimeComparator().reversed();
List<String> commentNames = comments().stream() List<String> commentNames = comments().stream()
.sorted(defaultComparator) .sorted(defaultComparator)
.map(comment -> comment.getMetadata().getName()) .map(comment -> comment.getMetadata().getName())
@ -124,8 +124,9 @@ class CommentSorterTest {
commentA.getMetadata().setName("A"); commentA.getMetadata().setName("A");
// create time // create time
commentA.getMetadata().setCreationTimestamp(now.plusSeconds(10)); commentA.getMetadata().setCreationTimestamp(now.plusSeconds(10));
commentA.setSpec(new Comment.CommentSpec()); commentA.setSpec(new Comment.CommentSpec());
commentA.getSpec().setCreationTime(commentA.getMetadata().getCreationTimestamp());
commentA.setStatus(new Comment.CommentStatus()); commentA.setStatus(new Comment.CommentStatus());
// last reply time // last reply time
commentA.getStatus().setLastReplyTime(now.plusSeconds(5)); commentA.getStatus().setLastReplyTime(now.plusSeconds(5));
@ -140,6 +141,7 @@ class CommentSorterTest {
commentB.setStatus(new Comment.CommentStatus()); commentB.setStatus(new Comment.CommentStatus());
commentB.getStatus().setLastReplyTime(now.plusSeconds(15)); commentB.getStatus().setLastReplyTime(now.plusSeconds(15));
commentB.getStatus().setReplyCount(8); commentB.getStatus().setReplyCount(8);
commentB.getSpec().setCreationTime(commentB.getMetadata().getCreationTimestamp());
Comment commentC = new Comment(); Comment commentC = new Comment();
commentC.setMetadata(new Metadata()); commentC.setMetadata(new Metadata());
@ -151,6 +153,7 @@ class CommentSorterTest {
commentC.setStatus(new Comment.CommentStatus()); commentC.setStatus(new Comment.CommentStatus());
commentC.getStatus().setLastReplyTime(now.plusSeconds(3)); commentC.getStatus().setLastReplyTime(now.plusSeconds(3));
commentC.getStatus().setReplyCount(10); commentC.getStatus().setReplyCount(10);
commentC.getSpec().setCreationTime(commentC.getMetadata().getCreationTimestamp());
return List.of(commentA, commentB, commentC); return List.of(commentA, commentB, commentC);
} }
@ -165,6 +168,7 @@ class CommentSorterTest {
commentD.getMetadata().setCreationTimestamp(now.plusSeconds(50)); commentD.getMetadata().setCreationTimestamp(now.plusSeconds(50));
commentD.setSpec(new Comment.CommentSpec()); commentD.setSpec(new Comment.CommentSpec());
commentD.getSpec().setCreationTime(commentD.getMetadata().getCreationTimestamp());
commentD.setStatus(new Comment.CommentStatus()); commentD.setStatus(new Comment.CommentStatus());
Comment commentE = new Comment(); Comment commentE = new Comment();
@ -172,8 +176,8 @@ class CommentSorterTest {
commentE.getMetadata().setName("E"); commentE.getMetadata().setName("E");
commentE.getMetadata().setCreationTimestamp(now.plusSeconds(20)); commentE.getMetadata().setCreationTimestamp(now.plusSeconds(20));
commentE.setSpec(new Comment.CommentSpec()); commentE.setSpec(new Comment.CommentSpec());
commentE.getSpec().setCreationTime(commentE.getMetadata().getCreationTimestamp());
commentE.setStatus(new Comment.CommentStatus()); commentE.setStatus(new Comment.CommentStatus());
List<Comment> comments = new ArrayList<>(comments()); List<Comment> comments = new ArrayList<>(comments());

View File

@ -0,0 +1,52 @@
package run.halo.app.content.comment;
import static org.assertj.core.api.Assertions.assertThat;
import java.time.Instant;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import run.halo.app.core.extension.content.Reply;
import run.halo.app.extension.Metadata;
/**
* Tests for {@link ReplyService}.
*
* @author guqing
* @since 2.0.0
*/
class ReplyServiceTest {
private final Instant now = Instant.now();
@Test
void creationTimeAscComparator() {
// creation time:
// 1. now + 5s, name: 1
// 2. now + 3s, name: 2
// 3. now + 3s, name: 3
// 5. now + 1s, name: 4
// 6. now - 1s, name: 5
// 7. null, name: 6
Reply reply1 = createReply("1", now.plusSeconds(5));
Reply reply2 = createReply("2", now.plusSeconds(3));
Reply reply3 = createReply("3", now.plusSeconds(3));
Reply reply4 = createReply("4", now.plusSeconds(1));
Reply reply5 = createReply("5", now.minusSeconds(1));
Reply reply6 = createReply("6", null);
String result = Stream.of(reply1, reply2, reply3, reply4, reply5, reply6)
.sorted(ReplyService.creationTimeAscComparator())
.map(reply -> reply.getMetadata().getName())
.collect(Collectors.joining(", "));
assertThat(result).isEqualTo("5, 4, 2, 3, 1, 6");
}
Reply createReply(String name, Instant creationTime) {
Reply reply = new Reply();
reply.setMetadata(new Metadata());
reply.getMetadata().setName(name);
reply.getMetadata().setCreationTimestamp(now);
reply.setSpec(new Reply.ReplySpec());
reply.getSpec().setCreationTime(creationTime);
return reply;
}
}

View File

@ -5,8 +5,10 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.time.Instant;
import java.util.List; import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -25,6 +27,7 @@ import run.halo.app.core.extension.content.Reply;
import run.halo.app.extension.GroupVersionKind; import run.halo.app.extension.GroupVersionKind;
import run.halo.app.extension.ListResult; import run.halo.app.extension.ListResult;
import run.halo.app.extension.Metadata; import run.halo.app.extension.Metadata;
import run.halo.app.extension.MetadataOperator;
import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.extension.Ref; import run.halo.app.extension.Ref;
import run.halo.app.infra.AnonymousUserConst; import run.halo.app.infra.AnonymousUserConst;
@ -100,6 +103,60 @@ class CommentFinderImplTest {
.verifyComplete(); .verifyComplete();
} }
@Test
void commentComparator() {
// 1, now + 1s, top, 0
// 2, now + 2s, top, 1
// 3, now + 3s, top, 2
// 4, now + 4s, top, 2
// 5, now + 4s, top, 3
// 6, now + 1s, no, 0
// 7, now + 2s, no, 0
// 8, now + 3s, no, 0
// 9, now + 3s, no, 0
// 10, null, no, 0
// 11, null, no, 1
// 12, null, no, 3
// 13, now + 3s, no, 3
Instant now = Instant.now();
var comment1 = commentForCompare("1", now.plusSeconds(1), true, 0);
var comment2 = commentForCompare("2", now.plusSeconds(2), true, 1);
var comment3 = commentForCompare("3", now.plusSeconds(3), true, 2);
var comment4 = commentForCompare("4", now.plusSeconds(4), true, 2);
var comment5 = commentForCompare("5", now.plusSeconds(4), true, 3);
var comment6 = commentForCompare("6", now.plusSeconds(4), true, 3);
var comment7 = commentForCompare("7", now.plusSeconds(1), false, 0);
var comment8 = commentForCompare("8", now.plusSeconds(2), false, 0);
var comment9 = commentForCompare("9", now.plusSeconds(3), false, 0);
var comment10 = commentForCompare("10", now.plusSeconds(3), false, 0);
var comment11 = commentForCompare("11", null, false, 0);
var comment12 = commentForCompare("12", null, false, 1);
var comment13 = commentForCompare("13", null, false, 3);
var comment14 = commentForCompare("14", now.plusSeconds(3), false, 3);
var result = Stream.of(comment1, comment2, comment3, comment4, comment5, comment6,
comment7, comment8, comment9, comment10, comment11, comment12, comment13,
comment14)
.sorted(CommentFinderImpl.defaultComparator())
.map(Comment::getMetadata)
.map(MetadataOperator::getName)
.collect(Collectors.joining(", "));
assertThat(result).isEqualTo("1, 2, 3, 4, 5, 6, 9, 14, 10, 8, 7, 13, 12, 11");
}
Comment commentForCompare(String name, Instant creationTime, boolean top, int priority) {
Comment comment = new Comment();
comment.setMetadata(new Metadata());
comment.getMetadata().setName(name);
comment.getMetadata().setCreationTimestamp(Instant.now());
comment.setSpec(new Comment.CommentSpec());
comment.getSpec().setCreationTime(creationTime);
comment.getSpec().setTop(top);
comment.getSpec().setPriority(priority);
return comment;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void mockWhenListComment() { private void mockWhenListComment() {
// Mock // Mock