use of org.eclipse.hono.application.client.MessageConsumer in project hono by eclipse.
the class HttpTestBase method testUploadMessages.
/**
* Uploads messages to the HTTP endpoint.
*
* @param ctx The test context to run on.
* @param tenantId The tenant that the device belongs to.
* @param messageConsumer Consumer that is invoked when a message was received.
* @param requestSender The test device that will publish the data.
* @param numberOfMessages The number of messages that are uploaded.
* @param expectedQos The expected QoS level, may be {@code null} leading to expecting the default for event or telemetry.
* @throws InterruptedException if the test is interrupted before it has finished.
*/
protected void testUploadMessages(final VertxTestContext ctx, final String tenantId, final Function<DownstreamMessage<? extends MessageContext>, Future<Void>> messageConsumer, final Function<Integer, Future<HttpResponse<Buffer>>> requestSender, final int numberOfMessages, final QoS expectedQos) throws InterruptedException {
final VertxTestContext messageSending = new VertxTestContext();
final Checkpoint messageSent = messageSending.checkpoint(numberOfMessages);
final Checkpoint messageReceived = messageSending.laxCheckpoint(numberOfMessages);
final AtomicInteger receivedMessageCount = new AtomicInteger(0);
final VertxTestContext setup = new VertxTestContext();
createConsumer(tenantId, msg -> {
logger.trace("received {}", msg);
ctx.verify(() -> {
DownstreamMessageAssertions.assertTelemetryMessageProperties(msg, tenantId);
assertThat(msg.getQos()).isEqualTo(getExpectedQoS(expectedQos));
assertAdditionalMessageProperties(msg);
});
Optional.ofNullable(messageConsumer).map(consumer -> consumer.apply(msg)).orElseGet(() -> Future.succeededFuture()).onComplete(attempt -> {
if (attempt.succeeded()) {
receivedMessageCount.incrementAndGet();
messageReceived.flag();
} else {
logger.error("failed to process message from device", attempt.cause());
messageSending.failNow(attempt.cause());
}
});
if (receivedMessageCount.get() % 20 == 0) {
logger.info("messages received: {}", receivedMessageCount.get());
}
}).onComplete(setup.succeedingThenComplete());
assertThat(setup.awaitCompletion(5, TimeUnit.SECONDS)).isTrue();
if (setup.failed()) {
ctx.failNow(setup.causeOfFailure());
return;
}
final long start = System.currentTimeMillis();
int messageCount = 0;
while (messageCount < numberOfMessages && !messageSending.failed()) {
messageCount++;
final int currentMessage = messageCount;
final CountDownLatch sending = new CountDownLatch(1);
requestSender.apply(currentMessage).compose(this::assertHttpResponse).onComplete(attempt -> {
try {
if (attempt.succeeded()) {
logger.debug("sent message {}", currentMessage);
messageSent.flag();
} else {
logger.info("failed to send message {}: {}", currentMessage, attempt.cause().getMessage());
messageSending.failNow(attempt.cause());
}
} finally {
sending.countDown();
}
});
sending.await();
if (currentMessage % 20 == 0) {
logger.info("messages sent: " + currentMessage);
}
}
final long timeToWait = Math.max(TEST_TIMEOUT_MILLIS - 50 - (System.currentTimeMillis() - testStartTimeMillis), 1);
assertThat(messageSending.awaitCompletion(timeToWait, TimeUnit.MILLISECONDS)).isTrue();
if (messageSending.failed()) {
logger.error("test execution failed", messageSending.causeOfFailure());
ctx.failNow(messageSending.causeOfFailure());
} else {
logger.info("successfully sent {} and received {} messages after {} milliseconds", messageCount, receivedMessageCount.get(), System.currentTimeMillis() - start);
ctx.completeNow();
}
}
use of org.eclipse.hono.application.client.MessageConsumer 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"));
}
});
}
Aggregations