Search in sources :

Example 46 with Timeout

use of io.vertx.junit5.Timeout in project hono by eclipse.

the class CommandAndControlMqttIT method testSendCommandViaKafkaFailsForMalformedMessage.

/**
 * Verifies that the adapter rejects malformed command messages sent by applications.
 * <p>
 * This test is applicable only if the messaging network type is Kafka.
 *
 * @param endpointConfig The endpoints to use for sending/receiving commands.
 * @param ctx The vert.x test context.
 * @throws InterruptedException if not all commands and responses are exchanged in time.
 */
@ParameterizedTest(name = IntegrationTestSupport.PARAMETERIZED_TEST_NAME_PATTERN)
@MethodSource("allCombinations")
@Timeout(timeUnit = TimeUnit.SECONDS, value = 20)
@AssumeMessagingSystem(type = MessagingType.kafka)
public void testSendCommandViaKafkaFailsForMalformedMessage(final MqttCommandEndpointConfiguration endpointConfig, final VertxTestContext ctx) throws InterruptedException {
    final String commandTargetDeviceId = endpointConfig.isSubscribeAsGateway() ? helper.setupGatewayDeviceBlocking(tenantId, deviceId, 5) : deviceId;
    final AtomicReference<GenericKafkaSender> kafkaSenderRef = new AtomicReference<>();
    final CountDownLatch expectedCommandResponses = new CountDownLatch(1);
    final VertxTestContext setup = new VertxTestContext();
    final Checkpoint ready = setup.checkpoint(2);
    final Future<MessageConsumer> kafkaAsyncErrorResponseConsumer = helper.createDeliveryFailureCommandResponseConsumer(ctx, tenantId, HttpURLConnection.HTTP_BAD_REQUEST, response -> expectedCommandResponses.countDown(), null);
    createConsumer(tenantId, msg -> {
        // expect empty notification with TTD -1
        setup.verify(() -> assertThat(msg.getContentType()).isEqualTo(EventConstants.CONTENT_TYPE_EMPTY_NOTIFICATION));
        final TimeUntilDisconnectNotification notification = msg.getTimeUntilDisconnectNotification().orElse(null);
        LOGGER.info("received notification [{}]", notification);
        if (notification.getTtd() == -1) {
            ready.flag();
        }
    }).compose(consumer -> helper.registry.addDeviceToTenant(tenantId, deviceId, password)).compose(ok -> connectToAdapter(IntegrationTestSupport.getUsername(deviceId, tenantId), password)).compose(conAck -> subscribeToCommands(commandTargetDeviceId, msg -> {
        // all commands should get rejected because they fail to pass the validity check
        ctx.failNow(new IllegalStateException("should not have received command"));
    }, endpointConfig, MqttQoS.AT_MOST_ONCE)).compose(ok -> helper.createGenericKafkaSender().onSuccess(kafkaSenderRef::set).mapEmpty()).compose(ok -> kafkaAsyncErrorResponseConsumer).onComplete(setup.succeeding(v -> ready.flag()));
    assertWithMessage("setup of adapter finished within %s seconds", IntegrationTestSupport.getTestSetupTimeout()).that(setup.awaitCompletion(IntegrationTestSupport.getTestSetupTimeout(), TimeUnit.SECONDS)).isTrue();
    if (setup.failed()) {
        ctx.failNow(setup.causeOfFailure());
        return;
    }
    final String commandTopic = new HonoTopic(HonoTopic.Type.COMMAND, tenantId).toString();
    LOGGER.debug("sending command message lacking subject and correlation ID - no failure response expected here");
    final Map<String, Object> properties1 = Map.of(MessageHelper.APP_PROPERTY_DEVICE_ID, deviceId, MessageHelper.SYS_PROPERTY_CONTENT_TYPE, MessageHelper.CONTENT_TYPE_OCTET_STREAM, KafkaRecordHelper.HEADER_RESPONSE_REQUIRED, true);
    kafkaSenderRef.get().sendAndWaitForOutcome(commandTopic, tenantId, deviceId, Buffer.buffer(), properties1).onComplete(ctx.succeeding(ok -> {
    }));
    LOGGER.debug("sending command message lacking subject");
    final String correlationId = "1";
    final Map<String, Object> properties2 = Map.of(MessageHelper.SYS_PROPERTY_CORRELATION_ID, correlationId, MessageHelper.APP_PROPERTY_DEVICE_ID, deviceId, MessageHelper.SYS_PROPERTY_CONTENT_TYPE, MessageHelper.CONTENT_TYPE_OCTET_STREAM, KafkaRecordHelper.HEADER_RESPONSE_REQUIRED, true);
    kafkaSenderRef.get().sendAndWaitForOutcome(commandTopic, tenantId, deviceId, Buffer.buffer(), properties2).onComplete(ctx.succeeding(ok -> {
    }));
    final long timeToWait = 2500;
    if (!expectedCommandResponses.await(timeToWait, TimeUnit.MILLISECONDS)) {
        LOGGER.info("Timeout of {} milliseconds reached, stop waiting for command response", timeToWait);
    }
    kafkaAsyncErrorResponseConsumer.result().close().onComplete(ar -> {
        if (expectedCommandResponses.getCount() == 0) {
            ctx.completeNow();
        } else {
            ctx.failNow(new java.lang.IllegalStateException("did not receive command response"));
        }
    });
}
Also used : GenericKafkaSender(org.eclipse.hono.tests.GenericKafkaSender) HttpURLConnection(java.net.HttpURLConnection) BeforeEach(org.junit.jupiter.api.BeforeEach) DownstreamMessage(org.eclipse.hono.application.client.DownstreamMessage) MqttPublishMessage(io.vertx.mqtt.messages.MqttPublishMessage) Timeout(io.vertx.junit5.Timeout) MessagingType(org.eclipse.hono.util.MessagingType) IntegrationTestSupport(org.eclipse.hono.tests.IntegrationTestSupport) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) ChannelPromise(io.netty.channel.ChannelPromise) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Map(java.util.Map) MqttClientImpl(io.vertx.mqtt.impl.MqttClientImpl) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) Method(java.lang.reflect.Method) MethodSource(org.junit.jupiter.params.provider.MethodSource) ChannelOutboundHandlerAdapter(io.netty.channel.ChannelOutboundHandlerAdapter) MessageContext(org.eclipse.hono.application.client.MessageContext) SubscriberRole(org.eclipse.hono.tests.CommandEndpointConfiguration.SubscriberRole) Truth.assertWithMessage(com.google.common.truth.Truth.assertWithMessage) IllegalStateException(javax.jms.IllegalStateException) MessageHelper(org.eclipse.hono.util.MessageHelper) VertxExtension(io.vertx.junit5.VertxExtension) EventConstants(org.eclipse.hono.util.EventConstants) Future(io.vertx.core.Future) CountDownLatch(java.util.concurrent.CountDownLatch) Stream(java.util.stream.Stream) Buffer(io.vertx.core.buffer.Buffer) Checkpoint(io.vertx.junit5.Checkpoint) NetSocketInternal(io.vertx.core.net.impl.NetSocketInternal) VertxTestContext(io.vertx.junit5.VertxTestContext) GenericKafkaSender(org.eclipse.hono.tests.GenericKafkaSender) MqttQoS(io.netty.handler.codec.mqtt.MqttQoS) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) ClientErrorException(org.eclipse.hono.client.ClientErrorException) AtomicReference(java.util.concurrent.atomic.AtomicReference) Function(java.util.function.Function) TimeUntilDisconnectNotification(org.eclipse.hono.util.TimeUntilDisconnectNotification) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) Message(org.apache.qpid.proton.message.Message) MqttPubAckMessage(io.netty.handler.codec.mqtt.MqttPubAckMessage) Promise(io.vertx.core.Promise) ServerErrorException(org.eclipse.hono.client.ServerErrorException) ProtonHelper(io.vertx.proton.ProtonHelper) KafkaRecordHelper(org.eclipse.hono.client.kafka.KafkaRecordHelper) Truth.assertThat(com.google.common.truth.Truth.assertThat) AssumeMessagingSystem(org.eclipse.hono.tests.AssumeMessagingSystem) TimeUnit(java.util.concurrent.TimeUnit) AtomicLong(java.util.concurrent.atomic.AtomicLong) HonoTopic(org.eclipse.hono.client.kafka.HonoTopic) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) MessageConsumer(org.eclipse.hono.application.client.MessageConsumer) SendMessageTimeoutException(org.eclipse.hono.client.SendMessageTimeoutException) NoopSpan(io.opentracing.noop.NoopSpan) GenericSenderLink(org.eclipse.hono.client.amqp.GenericSenderLink) Handler(io.vertx.core.Handler) IllegalStateException(javax.jms.IllegalStateException) MessageConsumer(org.eclipse.hono.application.client.MessageConsumer) VertxTestContext(io.vertx.junit5.VertxTestContext) AtomicReference(java.util.concurrent.atomic.AtomicReference) HonoTopic(org.eclipse.hono.client.kafka.HonoTopic) CountDownLatch(java.util.concurrent.CountDownLatch) Checkpoint(io.vertx.junit5.Checkpoint) TimeUntilDisconnectNotification(org.eclipse.hono.util.TimeUntilDisconnectNotification) AssumeMessagingSystem(org.eclipse.hono.tests.AssumeMessagingSystem) Timeout(io.vertx.junit5.Timeout) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) MethodSource(org.junit.jupiter.params.provider.MethodSource)

Example 47 with Timeout

use of io.vertx.junit5.Timeout in project hono by eclipse.

the class CommandAndControlMqttIT method testSendCommandSucceeds.

private void testSendCommandSucceeds(final VertxTestContext ctx, final String commandTargetDeviceId, final Handler<MqttPublishMessage> commandConsumer, final Function<Buffer, Future<Void>> commandSender, final MqttCommandEndpointConfiguration endpointConfig, final int totalNoOfCommandsToSend, final MqttQoS subscribeQos) throws InterruptedException {
    final VertxTestContext setup = new VertxTestContext();
    final Checkpoint ready = setup.checkpoint(2);
    helper.registry.addDeviceToTenant(tenantId, deviceId, password).compose(ok -> connectToAdapter(IntegrationTestSupport.getUsername(deviceId, tenantId), password)).compose(ok -> createConsumer(tenantId, msg -> {
        // expect empty notification with TTD -1
        setup.verify(() -> assertThat(msg.getContentType()).isEqualTo(EventConstants.CONTENT_TYPE_EMPTY_NOTIFICATION));
        final TimeUntilDisconnectNotification notification = msg.getTimeUntilDisconnectNotification().orElse(null);
        LOGGER.info("received notification [{}]", notification);
        setup.verify(() -> assertThat(notification).isNotNull());
        if (notification.getTtd() == -1) {
            ready.flag();
        }
    })).compose(conAck -> subscribeToCommands(commandTargetDeviceId, commandConsumer, endpointConfig, subscribeQos)).onComplete(setup.succeeding(ok -> ready.flag()));
    assertWithMessage("setup of adapter finished within %s seconds", IntegrationTestSupport.getTestSetupTimeout()).that(setup.awaitCompletion(IntegrationTestSupport.getTestSetupTimeout(), TimeUnit.SECONDS)).isTrue();
    if (setup.failed()) {
        ctx.failNow(setup.causeOfFailure());
        return;
    }
    final Checkpoint sendCommandsSucceeded = ctx.checkpoint();
    final CountDownLatch commandsSucceeded = new CountDownLatch(totalNoOfCommandsToSend);
    final AtomicInteger commandsSent = new AtomicInteger(0);
    final AtomicLong lastReceivedTimestamp = new AtomicLong(0);
    final long start = System.currentTimeMillis();
    while (commandsSent.get() < totalNoOfCommandsToSend) {
        final CountDownLatch commandSent = new CountDownLatch(1);
        context.runOnContext(go -> {
            commandsSent.getAndIncrement();
            final Buffer msg = commandsSent.get() % 2 == 0 ? Buffer.buffer("value: " + commandsSent.get()) : // use 'null' payload for half the commands, ensuring such commands also get forwarded
            null;
            commandSender.apply(msg).onComplete(sendAttempt -> {
                if (sendAttempt.failed()) {
                    LOGGER.info("error sending command {}", commandsSent.get(), sendAttempt.cause());
                } else {
                    lastReceivedTimestamp.set(System.currentTimeMillis());
                    commandsSucceeded.countDown();
                    if (commandsSucceeded.getCount() % 20 == 0) {
                        LOGGER.info("commands succeeded: {}", totalNoOfCommandsToSend - commandsSucceeded.getCount());
                    }
                }
                if (commandsSent.get() % 20 == 0) {
                    LOGGER.info("commands sent: " + commandsSent.get());
                }
                commandSent.countDown();
            });
        });
        commandSent.await();
    }
    final long timeToWait = totalNoOfCommandsToSend * 200;
    if (!commandsSucceeded.await(timeToWait, TimeUnit.MILLISECONDS)) {
        LOGGER.info("Timeout of {} milliseconds reached, stop waiting for commands to succeed", timeToWait);
    }
    if (lastReceivedTimestamp.get() == 0L) {
        // no message has been received at all
        lastReceivedTimestamp.set(System.currentTimeMillis());
    }
    final long commandsCompleted = totalNoOfCommandsToSend - commandsSucceeded.getCount();
    LOGGER.info("commands sent: {}, commands succeeded: {} after {} milliseconds", commandsSent.get(), commandsCompleted, lastReceivedTimestamp.get() - start);
    if (commandsCompleted == commandsSent.get()) {
        sendCommandsSucceeded.flag();
    } else {
        ctx.failNow(new IllegalStateException("did not complete all commands sent"));
    }
}
Also used : HttpURLConnection(java.net.HttpURLConnection) BeforeEach(org.junit.jupiter.api.BeforeEach) DownstreamMessage(org.eclipse.hono.application.client.DownstreamMessage) MqttPublishMessage(io.vertx.mqtt.messages.MqttPublishMessage) Timeout(io.vertx.junit5.Timeout) MessagingType(org.eclipse.hono.util.MessagingType) IntegrationTestSupport(org.eclipse.hono.tests.IntegrationTestSupport) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) ChannelPromise(io.netty.channel.ChannelPromise) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Map(java.util.Map) MqttClientImpl(io.vertx.mqtt.impl.MqttClientImpl) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) Method(java.lang.reflect.Method) MethodSource(org.junit.jupiter.params.provider.MethodSource) ChannelOutboundHandlerAdapter(io.netty.channel.ChannelOutboundHandlerAdapter) MessageContext(org.eclipse.hono.application.client.MessageContext) SubscriberRole(org.eclipse.hono.tests.CommandEndpointConfiguration.SubscriberRole) Truth.assertWithMessage(com.google.common.truth.Truth.assertWithMessage) IllegalStateException(javax.jms.IllegalStateException) MessageHelper(org.eclipse.hono.util.MessageHelper) VertxExtension(io.vertx.junit5.VertxExtension) EventConstants(org.eclipse.hono.util.EventConstants) Future(io.vertx.core.Future) CountDownLatch(java.util.concurrent.CountDownLatch) Stream(java.util.stream.Stream) Buffer(io.vertx.core.buffer.Buffer) Checkpoint(io.vertx.junit5.Checkpoint) NetSocketInternal(io.vertx.core.net.impl.NetSocketInternal) VertxTestContext(io.vertx.junit5.VertxTestContext) GenericKafkaSender(org.eclipse.hono.tests.GenericKafkaSender) MqttQoS(io.netty.handler.codec.mqtt.MqttQoS) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) ClientErrorException(org.eclipse.hono.client.ClientErrorException) AtomicReference(java.util.concurrent.atomic.AtomicReference) Function(java.util.function.Function) TimeUntilDisconnectNotification(org.eclipse.hono.util.TimeUntilDisconnectNotification) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) Message(org.apache.qpid.proton.message.Message) MqttPubAckMessage(io.netty.handler.codec.mqtt.MqttPubAckMessage) Promise(io.vertx.core.Promise) ServerErrorException(org.eclipse.hono.client.ServerErrorException) ProtonHelper(io.vertx.proton.ProtonHelper) KafkaRecordHelper(org.eclipse.hono.client.kafka.KafkaRecordHelper) Truth.assertThat(com.google.common.truth.Truth.assertThat) AssumeMessagingSystem(org.eclipse.hono.tests.AssumeMessagingSystem) TimeUnit(java.util.concurrent.TimeUnit) AtomicLong(java.util.concurrent.atomic.AtomicLong) HonoTopic(org.eclipse.hono.client.kafka.HonoTopic) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) MessageConsumer(org.eclipse.hono.application.client.MessageConsumer) SendMessageTimeoutException(org.eclipse.hono.client.SendMessageTimeoutException) NoopSpan(io.opentracing.noop.NoopSpan) GenericSenderLink(org.eclipse.hono.client.amqp.GenericSenderLink) Handler(io.vertx.core.Handler) Buffer(io.vertx.core.buffer.Buffer) IllegalStateException(javax.jms.IllegalStateException) Checkpoint(io.vertx.junit5.Checkpoint) AtomicLong(java.util.concurrent.atomic.AtomicLong) VertxTestContext(io.vertx.junit5.VertxTestContext) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) TimeUntilDisconnectNotification(org.eclipse.hono.util.TimeUntilDisconnectNotification) CountDownLatch(java.util.concurrent.CountDownLatch)

Example 48 with Timeout

use of io.vertx.junit5.Timeout in project hono by eclipse.

the class CommandAndControlMqttIT method testSendOneWayCommandSucceeds.

/**
 * Verifies that the adapter forwards on-way commands from
 * an application to a device.
 *
 * @param endpointConfig The endpoints to use for sending/receiving commands.
 * @param ctx The vert.x test context.
 * @throws InterruptedException if not all commands and responses are exchanged in time.
 */
@ParameterizedTest(name = IntegrationTestSupport.PARAMETERIZED_TEST_NAME_PATTERN)
@MethodSource("allCombinations")
@Timeout(timeUnit = TimeUnit.SECONDS, value = 10)
public void testSendOneWayCommandSucceeds(final MqttCommandEndpointConfiguration endpointConfig, final VertxTestContext ctx) throws InterruptedException {
    final String commandTargetDeviceId = endpointConfig.isSubscribeAsGateway() ? helper.setupGatewayDeviceBlocking(tenantId, deviceId, 5) : deviceId;
    final Checkpoint commandsReceived = ctx.checkpoint(COMMANDS_TO_SEND);
    final AtomicInteger counter = new AtomicInteger();
    testSendCommandSucceeds(ctx, commandTargetDeviceId, msg -> {
        LOGGER.trace("received one-way command [topic: {}]", msg.topicName());
        final ResourceIdentifier topic = ResourceIdentifier.fromString(msg.topicName());
        ctx.verify(() -> {
            endpointConfig.assertCommandPublishTopicStructure(topic, commandTargetDeviceId, true, "setValue");
        });
        commandsReceived.flag();
    }, payload -> {
        counter.incrementAndGet();
        return helper.sendOneWayCommand(tenantId, commandTargetDeviceId, "setValue", "text/plain", payload, helper.getSendCommandTimeout(counter.get() == 1));
    }, endpointConfig, COMMANDS_TO_SEND, MqttQoS.AT_MOST_ONCE);
}
Also used : ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) Checkpoint(io.vertx.junit5.Checkpoint) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Timeout(io.vertx.junit5.Timeout) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) MethodSource(org.junit.jupiter.params.provider.MethodSource)

Example 49 with Timeout

use of io.vertx.junit5.Timeout in project hono by eclipse.

the class HttpTestBase method testHandleConcurrentUploadWithTtd.

/**
 * Verifies that for two consecutive upload requests containing a TTD, sent in close succession so that the command
 * triggered by the first request isn't sent before the adapter has received the second upload request, the HTTP
 * adapter returns the command as response to the second upload request.
 *
 * @param ctx The test context.
 * @throws InterruptedException if the test is interrupted before having completed.
 */
@Test
@Timeout(timeUnit = TimeUnit.SECONDS, value = 20)
public void testHandleConcurrentUploadWithTtd(final VertxTestContext ctx) throws InterruptedException {
    final Tenant tenant = new Tenant();
    final CountDownLatch firstMessageReceived = new CountDownLatch(1);
    final CountDownLatch secondMessageReceived = new CountDownLatch(1);
    // GIVEN a registered device
    final VertxTestContext setup = new VertxTestContext();
    helper.registry.addDeviceForTenant(tenantId, tenant, deviceId, PWD).compose(ok -> createConsumer(tenantId, msg -> {
        logger.trace("received message: {}", msg);
        msg.getTimeUntilDisconnectNotification().ifPresent(notification -> {
            logger.debug("processing piggy backed message [ttd: {}]", notification.getTtd());
            ctx.verify(() -> {
                assertThat(notification.getTenantId()).isEqualTo(tenantId);
                assertThat(notification.getDeviceId()).isEqualTo(deviceId);
            });
        });
        switch(msg.getContentType()) {
            case "text/msg1":
                logger.debug("received first message");
                firstMessageReceived.countDown();
                break;
            case "text/msg2":
                logger.debug("received second message");
                secondMessageReceived.countDown();
                break;
            default:
        }
    })).compose(c -> {
        // might fail immediately because the link has not been established yet.
        return httpClient.create(getEndpointUri(), Buffer.buffer("trigger msg"), MultiMap.caseInsensitiveMultiMap().add(HttpHeaders.CONTENT_TYPE, "application/trigger").add(HttpHeaders.AUTHORIZATION, authorization).add(HttpHeaders.ORIGIN, ORIGIN_URI).add(Constants.HEADER_QOS_LEVEL, "1"), ResponsePredicate.status(200, 300));
    }).onComplete(setup.succeedingThenComplete());
    assertThat(setup.awaitCompletion(5, TimeUnit.SECONDS)).isTrue();
    if (setup.failed()) {
        ctx.failNow(setup.causeOfFailure());
        return;
    }
    // WHEN the device sends a first upload request
    MultiMap requestHeaders = MultiMap.caseInsensitiveMultiMap().add(HttpHeaders.CONTENT_TYPE, "text/msg1").add(HttpHeaders.AUTHORIZATION, authorization).add(HttpHeaders.ORIGIN, ORIGIN_URI).add(Constants.HEADER_TIME_TILL_DISCONNECT, "10");
    final Future<HttpResponse<Buffer>> firstRequest = httpClient.create(getEndpointUri(), Buffer.buffer("hello one"), requestHeaders, ResponsePredicate.status(200, 300)).map(httpResponse -> {
        logger.info("received response to first request");
        return httpResponse;
    });
    logger.info("sent first request");
    if (!firstMessageReceived.await(5, TimeUnit.SECONDS)) {
        ctx.failNow(new IllegalStateException("first message not received in time"));
    }
    // followed by a second request
    requestHeaders = MultiMap.caseInsensitiveMultiMap().add(HttpHeaders.CONTENT_TYPE, "text/msg2").add(HttpHeaders.AUTHORIZATION, authorization).add(HttpHeaders.ORIGIN, ORIGIN_URI).add(Constants.HEADER_TIME_TILL_DISCONNECT, "5");
    final Future<HttpResponse<Buffer>> secondRequest = httpClient.create(getEndpointUri(), Buffer.buffer("hello two"), requestHeaders, ResponsePredicate.status(200, 300)).map(httpResponse -> {
        logger.info("received response to second request");
        return httpResponse;
    });
    logger.info("sent second request");
    // wait for messages having been received
    if (!secondMessageReceived.await(5, TimeUnit.SECONDS)) {
        ctx.failNow(new IllegalStateException("second message not received in time"));
    }
    // send command
    final JsonObject inputData = new JsonObject().put(COMMAND_JSON_KEY, (int) (Math.random() * 100));
    final Future<Void> commandSent = helper.sendOneWayCommand(tenantId, deviceId, COMMAND_TO_SEND, "application/json", inputData.toBuffer(), 3000);
    logger.info("sent one-way command to device");
    // THEN both requests succeed
    CompositeFuture.all(commandSent, firstRequest, secondRequest).onComplete(ctx.succeeding(ok -> {
        ctx.verify(() -> {
            // and the response to the second request contains a command
            assertThat(secondRequest.result().getHeader(Constants.HEADER_COMMAND)).isEqualTo(COMMAND_TO_SEND);
            // while the response to the first request is empty
            assertThat(firstRequest.result().getHeader(Constants.HEADER_COMMAND)).isNull();
        });
        ctx.completeNow();
    }));
}
Also used : HttpURLConnection(java.net.HttpURLConnection) KeyPair(java.security.KeyPair) BeforeEach(org.junit.jupiter.api.BeforeEach) Arrays(java.util.Arrays) ResponsePredicate(io.vertx.ext.web.client.predicate.ResponsePredicate) DownstreamMessage(org.eclipse.hono.application.client.DownstreamMessage) LoggerFactory(org.slf4j.LoggerFactory) MultiMap(io.vertx.core.MultiMap) Tenant(org.eclipse.hono.service.management.tenant.Tenant) Timeout(io.vertx.junit5.Timeout) GeneralSecurityException(java.security.GeneralSecurityException) IntegrationTestSupport(org.eclipse.hono.tests.IntegrationTestSupport) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) BeforeAll(org.junit.jupiter.api.BeforeAll) JsonObject(io.vertx.core.json.JsonObject) Tenants(org.eclipse.hono.tests.Tenants) MethodSource(org.junit.jupiter.params.provider.MethodSource) Device(org.eclipse.hono.service.management.device.Device) MessageContext(org.eclipse.hono.application.client.MessageContext) SubscriberRole(org.eclipse.hono.tests.CommandEndpointConfiguration.SubscriberRole) Truth.assertWithMessage(com.google.common.truth.Truth.assertWithMessage) MetricsTags(org.eclipse.hono.service.metric.MetricsTags) Set(java.util.Set) HttpHeaders(io.vertx.core.http.HttpHeaders) DownstreamMessageAssertions(org.eclipse.hono.tests.DownstreamMessageAssertions) UUID(java.util.UUID) Future(io.vertx.core.Future) StandardCharsets(java.nio.charset.StandardCharsets) TestInfo(org.junit.jupiter.api.TestInfo) Test(org.junit.jupiter.api.Test) CountDownLatch(java.util.concurrent.CountDownLatch) Base64(java.util.Base64) Stream(java.util.stream.Stream) Buffer(io.vertx.core.buffer.Buffer) Optional(java.util.Optional) Checkpoint(io.vertx.junit5.Checkpoint) QoS(org.eclipse.hono.util.QoS) VertxTestContext(io.vertx.junit5.VertxTestContext) X500Principal(javax.security.auth.x500.X500Principal) HttpResponse(io.vertx.ext.web.client.HttpResponse) SelfSignedCertificate(io.vertx.core.net.SelfSignedCertificate) Function(java.util.function.Function) Constants(org.eclipse.hono.util.Constants) CompositeFuture(io.vertx.core.CompositeFuture) HttpClientOptions(io.vertx.core.http.HttpClientOptions) PemTrustOptions(io.vertx.core.net.PemTrustOptions) RegistryManagementConstants(org.eclipse.hono.util.RegistryManagementConstants) Logger(org.slf4j.Logger) Promise(io.vertx.core.Promise) Vertx(io.vertx.core.Vertx) Truth.assertThat(com.google.common.truth.Truth.assertThat) TimeUnit(java.util.concurrent.TimeUnit) Adapter(org.eclipse.hono.util.Adapter) AfterEach(org.junit.jupiter.api.AfterEach) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) CrudHttpClient(org.eclipse.hono.tests.CrudHttpClient) MessageConsumer(org.eclipse.hono.application.client.MessageConsumer) Handler(io.vertx.core.Handler) Collections(java.util.Collections) MultiMap(io.vertx.core.MultiMap) Tenant(org.eclipse.hono.service.management.tenant.Tenant) VertxTestContext(io.vertx.junit5.VertxTestContext) HttpResponse(io.vertx.ext.web.client.HttpResponse) JsonObject(io.vertx.core.json.JsonObject) CountDownLatch(java.util.concurrent.CountDownLatch) Test(org.junit.jupiter.api.Test) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) Timeout(io.vertx.junit5.Timeout)

Example 50 with Timeout

use of io.vertx.junit5.Timeout in project hono by eclipse.

the class KafkaBasedCommandConsumerFactoryImplIT method testCommandsGetForwardedIfOneConsumerInstanceGetsClosed.

/**
 * Verifies that records, published on the tenant-specific Kafka command topic, get received
 * and forwarded by consumers created by factory instances even if one factory and its contained
 * consumer gets closed in the middle of processing some of the commands.
 *
 * @param ctx The vert.x test context.
 * @throws InterruptedException if test execution gets interrupted.
 */
@Test
@Timeout(value = 10, timeUnit = TimeUnit.SECONDS)
public void testCommandsGetForwardedIfOneConsumerInstanceGetsClosed(final VertxTestContext ctx) throws InterruptedException {
    final String tenantId = "tenant_" + UUID.randomUUID();
    final VertxTestContext setup = new VertxTestContext();
    // Scenario to test:
    // - first command gets sent, forwarded and received without any imposed delay
    // - second command gets sent, received by the factory consumer instance; processing gets blocked
    // while trying to get the target adapter instance
    // - for the rest of the commands, retrieval of the target adapter instance is successful, but they won't
    // get forwarded until processing of the second command is finished
    // - now the factory consumer gets closed and a new factory/consumer gets started; at that point
    // also the processing of the second command gets finished
    // 
    // Expected outcome:
    // - processing of the second command and all following commands by the first consumer gets aborted, so that
    // these commands don't get forwarded on the internal command topic
    // - instead, the second consumer takes over at the offset of the first command (position must have been committed
    // when closing the first consumer) and processes and forwards all commands starting with the second command
    final int numTestCommands = 10;
    final List<KafkaConsumerRecord<String, Buffer>> receivedRecords = new ArrayList<>();
    final Promise<Void> firstRecordReceivedPromise = Promise.promise();
    final Promise<Void> allRecordsReceivedPromise = Promise.promise();
    final List<String> receivedCommandSubjects = new ArrayList<>();
    final Handler<KafkaConsumerRecord<String, Buffer>> recordHandler = record -> {
        receivedRecords.add(record);
        LOG.trace("received {}", record);
        receivedCommandSubjects.add(KafkaRecordHelper.getSubject(record.headers()).orElse(""));
        if (receivedRecords.size() == 1) {
            firstRecordReceivedPromise.complete();
        }
        if (receivedRecords.size() == numTestCommands) {
            allRecordsReceivedPromise.tryComplete();
        }
    };
    final Promise<Void> firstConsumerAllGetAdapterInstanceInvocationsDone = Promise.promise();
    final LinkedList<Promise<Void>> firstConsumerGetAdapterInstancePromisesQueue = new LinkedList<>();
    // don't let getting the target adapter instance finish immediately
    final Supplier<Future<Void>> firstConsumerGetAdapterInstanceSupplier = () -> {
        final Promise<Void> resultPromise = Promise.promise();
        firstConsumerGetAdapterInstancePromisesQueue.addFirst(resultPromise);
        // don't complete the future for the second command here yet
        if (firstConsumerGetAdapterInstancePromisesQueue.size() != 2) {
            resultPromise.complete();
        }
        if (firstConsumerGetAdapterInstancePromisesQueue.size() == numTestCommands) {
            firstConsumerAllGetAdapterInstanceInvocationsDone.complete();
        }
        return resultPromise.future();
    };
    final AtomicReference<KafkaBasedCommandConsumerFactoryImpl> consumerFactory1Ref = new AtomicReference<>();
    final Context vertxContext = vertx.getOrCreateContext();
    vertxContext.runOnContext(v0 -> {
        final HonoKafkaConsumer internalConsumer = getInternalCommandConsumer(recordHandler);
        final KafkaBasedCommandConsumerFactoryImpl consumerFactory1 = getKafkaBasedCommandConsumerFactory(firstConsumerGetAdapterInstanceSupplier, tenantId);
        consumerFactory1Ref.set(consumerFactory1);
        CompositeFuture.join(internalConsumer.start(), consumerFactory1.start()).compose(f -> createCommandConsumer(tenantId, consumerFactory1)).onComplete(setup.succeedingThenComplete());
    });
    assertThat(setup.awaitCompletion(IntegrationTestSupport.getTestSetupTimeout(), TimeUnit.SECONDS)).isTrue();
    if (setup.failed()) {
        ctx.failNow(setup.causeOfFailure());
        return;
    }
    LOG.debug("command consumer started");
    final List<String> sentCommandSubjects = new ArrayList<>();
    IntStream.range(0, numTestCommands).forEach(i -> {
        final String subject = "cmd_" + i;
        sentCommandSubjects.add(subject);
        sendOneWayCommand(tenantId, "myDeviceId", subject);
    });
    final AtomicInteger secondConsumerGetAdapterInstanceInvocations = new AtomicInteger();
    // wait for first record on internal topic to have been received ...
    CompositeFuture.join(firstConsumerAllGetAdapterInstanceInvocationsDone.future(), firstRecordReceivedPromise.future()).compose(v -> {
        // ... and wait some more, making sure that the offset of the first record has been committed
        final Promise<Void> delayPromise = Promise.promise();
        vertx.setTimer(500, tid -> delayPromise.complete());
        return delayPromise.future();
    }).onComplete(v -> {
        LOG.info("stopping first consumer factory");
        consumerFactory1Ref.get().stop().onComplete(ctx.succeeding(ar -> {
            LOG.info("factory stopped");
            // no delay on getting the target adapter instance added here
            final KafkaBasedCommandConsumerFactoryImpl consumerFactory2 = getKafkaBasedCommandConsumerFactory(() -> {
                secondConsumerGetAdapterInstanceInvocations.incrementAndGet();
                return Future.succeededFuture();
            }, tenantId);
            consumerFactory2.start().onComplete(ctx.succeeding(ar2 -> {
                LOG.info("creating command consumer in new consumer factory");
                createCommandConsumer(tenantId, consumerFactory2).onComplete(ctx.succeeding(ar3 -> {
                    LOG.debug("consumer created");
                    firstConsumerGetAdapterInstancePromisesQueue.forEach(Promise::tryComplete);
                }));
            }));
        }));
    });
    final long timerId = vertx.setTimer(8000, tid -> {
        LOG.info("received records:\n{}", receivedRecords.stream().map(Object::toString).collect(Collectors.joining(",\n")));
        allRecordsReceivedPromise.tryFail(String.format("only received %d out of %d expected messages after 8s", receivedRecords.size(), numTestCommands));
    });
    allRecordsReceivedPromise.future().onComplete(ctx.succeeding(v -> {
        vertx.cancelTimer(timerId);
        ctx.verify(() -> {
            assertThat(receivedCommandSubjects).isEqualTo(sentCommandSubjects);
            // all but the first command should have been processed by the second consumer
            assertThat(secondConsumerGetAdapterInstanceInvocations.get()).isEqualTo(numTestCommands - 1);
        });
        ctx.completeNow();
    }));
}
Also used : BeforeEach(org.junit.jupiter.api.BeforeEach) MessagingKafkaConsumerConfigProperties(org.eclipse.hono.client.kafka.consumer.MessagingKafkaConsumerConfigProperties) LoggerFactory(org.slf4j.LoggerFactory) Context(io.vertx.core.Context) KafkaProducer(io.vertx.kafka.client.producer.KafkaProducer) Timeout(io.vertx.junit5.Timeout) AfterAll(org.junit.jupiter.api.AfterAll) MessagingType(org.eclipse.hono.util.MessagingType) IntegrationTestSupport(org.eclipse.hono.tests.IntegrationTestSupport) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) BeforeAll(org.junit.jupiter.api.BeforeAll) Map(java.util.Map) TracingMockSupport(org.eclipse.hono.test.TracingMockSupport) JsonObject(io.vertx.core.json.JsonObject) DeviceConnectionConstants(org.eclipse.hono.util.DeviceConnectionConstants) CachingKafkaProducerFactory(org.eclipse.hono.client.kafka.producer.CachingKafkaProducerFactory) Set(java.util.Set) CommandTargetMapper(org.eclipse.hono.commandrouter.CommandTargetMapper) ConsumerConfig(org.apache.kafka.clients.consumer.ConsumerConfig) UUID(java.util.UUID) TenantClient(org.eclipse.hono.client.registry.TenantClient) KafkaBasedCommandConsumerFactoryImpl(org.eclipse.hono.commandrouter.impl.kafka.KafkaBasedCommandConsumerFactoryImpl) VertxExtension(io.vertx.junit5.VertxExtension) Collectors(java.util.stream.Collectors) Future(io.vertx.core.Future) AsyncHandlingAutoCommitKafkaConsumer(org.eclipse.hono.client.kafka.consumer.AsyncHandlingAutoCommitKafkaConsumer) TestInfo(org.junit.jupiter.api.TestInfo) Test(org.junit.jupiter.api.Test) List(java.util.List) KafkaProducerFactory(org.eclipse.hono.client.kafka.producer.KafkaProducerFactory) Buffer(io.vertx.core.buffer.Buffer) KafkaConsumerRecord(io.vertx.kafka.client.consumer.KafkaConsumerRecord) Span(io.opentracing.Span) Mockito.mock(org.mockito.Mockito.mock) IntStream(java.util.stream.IntStream) VertxTestContext(io.vertx.junit5.VertxTestContext) X500Principal(javax.security.auth.x500.X500Principal) HonoKafkaConsumer(org.eclipse.hono.client.kafka.consumer.HonoKafkaConsumer) Lifecycle(org.eclipse.hono.util.Lifecycle) Deque(java.util.Deque) AtomicReference(java.util.concurrent.atomic.AtomicReference) Supplier(java.util.function.Supplier) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) CompositeFuture(io.vertx.core.CompositeFuture) Timer(io.micrometer.core.instrument.Timer) LinkedList(java.util.LinkedList) Logger(org.slf4j.Logger) Tracer(io.opentracing.Tracer) Promise(io.vertx.core.Promise) Vertx(io.vertx.core.Vertx) Mockito.when(org.mockito.Mockito.when) KafkaRecordHelper(org.eclipse.hono.client.kafka.KafkaRecordHelper) Truth.assertThat(com.google.common.truth.Truth.assertThat) AssumeMessagingSystem(org.eclipse.hono.tests.AssumeMessagingSystem) TenantObject(org.eclipse.hono.util.TenantObject) SpanContext(io.opentracing.SpanContext) TimeUnit(java.util.concurrent.TimeUnit) NoopKafkaClientMetricsSupport(org.eclipse.hono.client.kafka.metrics.NoopKafkaClientMetricsSupport) HonoTopic(org.eclipse.hono.client.kafka.HonoTopic) CommandRouterMetrics(org.eclipse.hono.commandrouter.CommandRouterMetrics) AfterEach(org.junit.jupiter.api.AfterEach) Handler(io.vertx.core.Handler) KafkaHeader(io.vertx.kafka.client.producer.KafkaHeader) KafkaProducerRecord(io.vertx.kafka.client.producer.KafkaProducerRecord) KafkaAdminClient(io.vertx.kafka.admin.KafkaAdminClient) Context(io.vertx.core.Context) VertxTestContext(io.vertx.junit5.VertxTestContext) SpanContext(io.opentracing.SpanContext) KafkaBasedCommandConsumerFactoryImpl(org.eclipse.hono.commandrouter.impl.kafka.KafkaBasedCommandConsumerFactoryImpl) VertxTestContext(io.vertx.junit5.VertxTestContext) ArrayList(java.util.ArrayList) KafkaConsumerRecord(io.vertx.kafka.client.consumer.KafkaConsumerRecord) AtomicReference(java.util.concurrent.atomic.AtomicReference) LinkedList(java.util.LinkedList) Promise(io.vertx.core.Promise) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) HonoKafkaConsumer(org.eclipse.hono.client.kafka.consumer.HonoKafkaConsumer) Future(io.vertx.core.Future) CompositeFuture(io.vertx.core.CompositeFuture) JsonObject(io.vertx.core.json.JsonObject) TenantObject(org.eclipse.hono.util.TenantObject) Test(org.junit.jupiter.api.Test) Timeout(io.vertx.junit5.Timeout)

Aggregations

Timeout (io.vertx.junit5.Timeout)73 VertxTestContext (io.vertx.junit5.VertxTestContext)73 Test (org.junit.jupiter.api.Test)71 TimeUnit (java.util.concurrent.TimeUnit)67 Truth.assertThat (com.google.common.truth.Truth.assertThat)65 Future (io.vertx.core.Future)54 HttpURLConnection (java.net.HttpURLConnection)52 IntegrationTestSupport (org.eclipse.hono.tests.IntegrationTestSupport)49 Handler (io.vertx.core.Handler)47 Buffer (io.vertx.core.buffer.Buffer)46 BeforeEach (org.junit.jupiter.api.BeforeEach)46 Promise (io.vertx.core.Promise)45 Vertx (io.vertx.core.Vertx)37 JsonObject (io.vertx.core.json.JsonObject)37 VertxExtension (io.vertx.junit5.VertxExtension)37 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)37 ExtendWith (org.junit.jupiter.api.extension.ExtendWith)37 ParameterizedTest (org.junit.jupiter.params.ParameterizedTest)37 Tenant (org.eclipse.hono.service.management.tenant.Tenant)36 MethodSource (org.junit.jupiter.params.provider.MethodSource)35