@Override
    public CompletableFuture<AckResult> changeInvisibleTime(ProxyContext ctx, ReceiptHandle handle, String messageId,
        ChangeInvisibleTimeRequestHeader requestHeader, long timeoutMillis) {
        SimpleChannel channel = channelManager.createChannel(ctx);
        ChannelHandlerContext channelHandlerContext = channel.getChannelHandlerContext();
        RemotingCommand command = LocalRemotingCommand.createRequestCommand(RequestCode.CHANGE_MESSAGE_INVISIBLETIME, requestHeader, ctx.getLanguage());
        CompletableFuture<RemotingCommand> future = new CompletableFuture<>();
        try {
            RemotingCommand response = brokerController.getChangeInvisibleTimeProcessor()
                .processRequest(channelHandlerContext, command);
            future.complete(response);
        } catch (Exception e) {
            log.error("Fail to process changeInvisibleTime command", e);
            future.completeExceptionally(e);
        }
        return future.thenApply(r -> {
            ChangeInvisibleTimeResponseHeader responseHeader = (ChangeInvisibleTimeResponseHeader) r.readCustomHeader();
            AckResult ackResult = new AckResult();
            if (ResponseCode.SUCCESS == r.getCode()) {
                ackResult.setStatus(AckStatus.OK);
            } else {
                ackResult.setStatus(AckStatus.NO_EXIST);
            }
            ackResult.setPopTime(responseHeader.getPopTime());
            ackResult.setExtraInfo(ReceiptHandle.builder()
                .startOffset(handle.getStartOffset())
                .retrieveTime(responseHeader.getPopTime())
                .invisibleTime(responseHeader.getInvisibleTime())
                .reviveQueueId(responseHeader.getReviveQid())
                .topicType(handle.getTopicType())
                .brokerName(handle.getBrokerName())
                .queueId(handle.getQueueId())
                .offset(handle.getOffset())
                .build()
                .encode());
            return ackResult;
        });
    }
@Test
    public void testChangeInvisibleTime() throws Exception {
        String messageId = "messageId";
        long popTime = System.currentTimeMillis();
        long invisibleTime = 3000L;
        int reviveQueueId = 1;
        ReceiptHandle handle = ReceiptHandle.builder()
            .startOffset(0L)
            .retrieveTime(popTime)
            .invisibleTime(invisibleTime)
            .reviveQueueId(reviveQueueId)
            .topicType(ReceiptHandle.NORMAL_TOPIC)
            .brokerName(brokerName)
            .queueId(queueId)
            .offset(queueOffset)
            .build();
        RemotingCommand remotingCommand = RemotingCommand.createResponseCommand(ChangeInvisibleTimeResponseHeader.class);
        remotingCommand.setCode(ResponseCode.SUCCESS);
        remotingCommand.setRemark("");
        long newPopTime = System.currentTimeMillis();
        long newInvisibleTime = 5000L;
        int newReviveQueueId = 2;
        ChangeInvisibleTimeResponseHeader responseHeader = (ChangeInvisibleTimeResponseHeader) remotingCommand.readCustomHeader();
        responseHeader.setReviveQid(newReviveQueueId);
        responseHeader.setInvisibleTime(newInvisibleTime);
        responseHeader.setPopTime(newPopTime);
        Mockito.when(changeInvisibleTimeProcessorMock.processRequest(Mockito.any(SimpleChannelHandlerContext.class), Mockito.argThat(argument -> {
            boolean first = argument.getCode() == RequestCode.CHANGE_MESSAGE_INVISIBLETIME;
            boolean second = argument.readCustomHeader() instanceof ChangeInvisibleTimeRequestHeader;
            return first && second;
        }))).thenReturn(remotingCommand);
        ChangeInvisibleTimeRequestHeader requestHeader = new ChangeInvisibleTimeRequestHeader();
        CompletableFuture<AckResult> future = localMessageService.changeInvisibleTime(proxyContext, handle, messageId,
            requestHeader, 1000L);
        AckResult ackResult = future.get();
        assertThat(ackResult.getStatus()).isEqualTo(AckStatus.OK);
        assertThat(ackResult.getPopTime()).isEqualTo(newPopTime);
        assertThat(ackResult.getExtraInfo()).isEqualTo(ReceiptHandle.builder()
            .startOffset(0L)
            .retrieveTime(newPopTime)
            .invisibleTime(newInvisibleTime)
            .reviveQueueId(newReviveQueueId)
            .topicType(ReceiptHandle.NORMAL_TOPIC)
            .brokerName(brokerName)
            .queueId(queueId)
            .offset(queueOffset)
            .build()
            .encode());
    }