@Override
    public Result reconcile(Request request) {
        var events = new LinkedHashSet<ApplicationEvent>();
        client.fetch(Post.class, request.name())
            .ifPresent(post -> {
                if (ExtensionOperator.isDeleted(post)) {
                    removeFinalizers(post.getMetadata(), Set.of(FINALIZER_NAME));
                    unPublishPost(post, events);
                    events.add(new PostDeletedEvent(this, post));
                    cleanUpResources(post);
                    // update post to be able to be collected by gc collector.
                    client.update(post);
                    // fire event after updating post
                    events.forEach(eventPublisher::publishEvent);
                    return;
                }
                addFinalizers(post.getMetadata(), Set.of(FINALIZER_NAME));

                populateLabels(post, events);

                schedulePublishIfNecessary(post);

                subscribeNewCommentNotification(post);

                var status = post.getStatus();
                if (status == null) {
                    status = new Post.PostStatus();
                    post.setStatus(status);
                }

                if (post.isPublished() && post.getSpec().getPublishTime() == null) {
                    post.getSpec().setPublishTime(Instant.now());
                }

                // calculate the sha256sum
                var configSha256sum = Hashing.sha256().hashString(post.getSpec().toString(), UTF_8)
                    .toString();

                var annotations = nullSafeAnnotations(post);
                var oldConfigChecksum = annotations.get(Constant.CHECKSUM_CONFIG_ANNO);
                if (!Objects.equals(oldConfigChecksum, configSha256sum)) {
                    // if the checksum doesn't match
                    events.add(new PostUpdatedEvent(this, post.getMetadata().getName()));
                    annotations.put(Constant.CHECKSUM_CONFIG_ANNO, configSha256sum);
                }

                if (shouldUnPublish(post)) {
                    unPublishPost(post, events);
                } else {
                    publishPost(post, events);
                }

                var permalinkPattern = postPermalinkPolicy.pattern();
                annotations.put(Constant.PERMALINK_PATTERN_ANNO, permalinkPattern);

                status.setPermalink(postPermalinkPolicy.permalink(post));
                if (status.getPhase() == null) {
                    status.setPhase(PostPhase.DRAFT.toString());
                }

                var excerpt = post.getSpec().getExcerpt();
                if (excerpt == null) {
                    excerpt = new Post.Excerpt();
                }
                var isAutoGenerate = defaultIfNull(excerpt.getAutoGenerate(), true);
                if (isAutoGenerate) {
                    status.setExcerpt(getExcerpt(post));
                } else {
                    status.setExcerpt(excerpt.getRaw());
                }

                var ref = Ref.of(post);
                // handle contributors
                var headSnapshot = post.getSpec().getHeadSnapshot();
                var contributors = listSnapshots(ref)
                    .stream()
                    .map(snapshot -> {
                        Set<String> usernames = snapshot.getSpec().getContributors();
                        return Objects.requireNonNullElseGet(usernames,
                            () -> new HashSet<String>());
                    })
                    .flatMap(Set::stream)
                    .distinct()
                    .sorted()
                    .toList();
                status.setContributors(contributors);

                // update in progress status
                status.setInProgress(
                    !StringUtils.equals(headSnapshot, post.getSpec().getReleaseSnapshot()));

                computeHiddenState(post);

                // version + 1 is required to truly equal version
                // as a version will be incremented after the update
                status.setObservedVersion(post.getMetadata().getVersion() + 1);
                client.update(post);

                // fire event after updating post
                events.forEach(eventPublisher::publishEvent);
            });
        return Result.doNotRetry();
    }
@Test
    void reconcile() {
        String name = "post-A";
        Post post = TestPost.postV1();
        post.getSpec().setPublish(false);
        post.getSpec().setHeadSnapshot("post-A-head-snapshot");
        when(client.fetch(eq(Post.class), eq(name)))
            .thenReturn(Optional.of(post));
        when(postService.getContent(eq(post.getSpec().getReleaseSnapshot()),
            eq(post.getSpec().getBaseSnapshot())))
            .thenReturn(Mono.empty());

        Snapshot snapshotV1 = TestPost.snapshotV1();
        Snapshot snapshotV2 = TestPost.snapshotV2();
        snapshotV1.getSpec().setContributors(Set.of("guqing"));
        snapshotV2.getSpec().setContributors(Set.of("guqing", "zhangsan"));
        when(client.listAll(eq(Snapshot.class), any(), any()))
            .thenReturn(List.of(snapshotV1, snapshotV2));

        ArgumentCaptor<Post> captor = ArgumentCaptor.forClass(Post.class);
        postReconciler.reconcile(new Reconciler.Request(name));

        verify(client, times(1)).update(captor.capture());

        verify(postPermalinkPolicy, times(1)).permalink(any());

        Post value = captor.getValue();
        assertThat(value.getStatus().getExcerpt()).isEmpty();
        assertThat(value.getStatus().getContributors()).isEqualTo(List.of("guqing", "zhangsan"));
    }