use of org.eclipse.hono.tests.AssumeMessagingSystem in project hono by eclipse.
the class CommandAndControlAmqpIT method testSendCommandViaAmqpFailsForMalformedMessage.
/**
* Verifies that the adapter rejects malformed command messages sent by applications.
* <p>
* This test is applicable only if the messaging network type is AMQP.
*
* @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)
@AssumeMessagingSystem(type = MessagingType.amqp)
public void testSendCommandViaAmqpFailsForMalformedMessage(final AmqpCommandEndpointConfiguration endpointConfig, final VertxTestContext ctx) throws InterruptedException {
final String commandTargetDeviceId = endpointConfig.isSubscribeAsGateway() ? helper.setupGatewayDeviceBlocking(tenantId, deviceId, 5) : deviceId;
final AtomicReference<GenericSenderLink> amqpCmdSenderRef = new AtomicReference<>();
final VertxTestContext setup = new VertxTestContext();
final Checkpoint setupDone = setup.checkpoint();
final Checkpoint preconditions = setup.checkpoint(2);
connectToAdapter(tenantId, deviceId, password, () -> createEventConsumer(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);
log.debug("received notification [{}]", notification);
setup.verify(() -> assertThat(notification).isNotNull());
if (notification.getTtd() == -1) {
preconditions.flag();
}
})).compose(con -> subscribeToCommands(endpointConfig, tenantId, commandTargetDeviceId).map(recv -> {
recv.handler((delivery, msg) -> ctx.failNow(new IllegalStateException("should not have received command")));
return null;
})).compose(ok -> helper.createGenericAmqpMessageSender(endpointConfig.getNorthboundEndpoint(), tenantId)).map(s -> {
log.debug("created generic sender for sending commands [target address: {}]", endpointConfig.getSenderLinkTargetAddress(tenantId));
amqpCmdSenderRef.set(s);
preconditions.flag();
return s;
}).onComplete(setup.succeeding(v -> setupDone.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 expectedFailures = ctx.checkpoint(2);
log.debug("sending command message lacking subject");
final Message messageWithoutSubject = ProtonHelper.message("input data");
messageWithoutSubject.setAddress(endpointConfig.getCommandMessageAddress(tenantId, commandTargetDeviceId));
messageWithoutSubject.setMessageId("message-id");
messageWithoutSubject.setReplyTo("reply/to/address");
amqpCmdSenderRef.get().sendAndWaitForOutcome(messageWithoutSubject, NoopSpan.INSTANCE).onComplete(ctx.failing(t -> {
ctx.verify(() -> assertThat(t).isInstanceOf(ClientErrorException.class));
expectedFailures.flag();
}));
log.debug("sending command message lacking message ID and correlation ID");
final Message messageWithoutId = ProtonHelper.message("input data");
messageWithoutId.setAddress(endpointConfig.getCommandMessageAddress(tenantId, commandTargetDeviceId));
messageWithoutId.setSubject("setValue");
messageWithoutId.setReplyTo("reply/to/address");
amqpCmdSenderRef.get().sendAndWaitForOutcome(messageWithoutId, NoopSpan.INSTANCE).onComplete(ctx.failing(t -> {
ctx.verify(() -> assertThat(t).isInstanceOf(ClientErrorException.class));
expectedFailures.flag();
}));
}
use of org.eclipse.hono.tests.AssumeMessagingSystem in project hono by eclipse.
the class TelemetryJmsQoS1IT method testTelemetryUpload.
/**
* Verifies that telemetry messages uploaded to the Hono server are all received
* by a downstream consumer.
*
* @throws Exception if the test fails.
*/
@Test
@AssumeMessagingSystem(type = MessagingType.amqp)
public void testTelemetryUpload() throws Exception {
final String tenantId = helper.getRandomTenantId();
final String deviceId = helper.getRandomDeviceId(tenantId);
final String username = IntegrationTestSupport.getUsername(deviceId, tenantId);
final String pwd = "secret";
final Tenant tenant = new Tenant();
final VertxTestContext setup = new VertxTestContext();
helper.registry.addDeviceForTenant(tenantId, tenant, deviceId, pwd).compose(ok -> getAmqpAdapterConnection(username, pwd)).onComplete(setup.succeeding(connection -> {
amqpAdapter = connection;
setup.completeNow();
}));
assertTrue(setup.awaitCompletion(IntegrationTestSupport.getTestSetupTimeout(), TimeUnit.SECONDS));
if (setup.failed()) {
fail(setup.causeOfFailure());
}
final CountDownLatch latch = new CountDownLatch(IntegrationTestSupport.MSG_COUNT);
final LongSummaryStatistics stats = new LongSummaryStatistics();
givenATelemetryConsumer(tenantId);
downstreamConsumer.setMessageListener(message -> {
latch.countDown();
gatherStatistics(stats, message);
if (LOG.isInfoEnabled()) {
final long messagesReceived = IntegrationTestSupport.MSG_COUNT - latch.getCount();
if (messagesReceived % 100 == 0) {
LOG.info("Received {} messages.", messagesReceived);
}
}
});
final MessageProducer messageProducer = amqpAdapter.createAnonymousProducer();
final Destination telemetryEndpoint = JmsBasedHonoConnection.getDestination(TelemetryConstants.TELEMETRY_ENDPOINT);
IntStream.range(1, IntegrationTestSupport.MSG_COUNT + 1).forEach(i -> {
try {
final Message message = amqpAdapter.newMessage("msg " + i, deviceId);
messageProducer.send(telemetryEndpoint, message, DELIVERY_MODE, Message.DEFAULT_PRIORITY, Message.DEFAULT_TIME_TO_LIVE);
if (i % 100 == 0) {
LOG.info("Sent {} messages", i);
}
} catch (final JMSException e) {
LOG.error("Error occurred while sending message: {}", e.getMessage(), e);
}
});
final long timeToWait = Math.max(DEFAULT_TEST_TIMEOUT, Math.round(IntegrationTestSupport.MSG_COUNT * 1.2));
// wait for messages to arrive
assertTrue(latch.await(timeToWait, TimeUnit.MILLISECONDS), () -> "did not receive all " + IntegrationTestSupport.MSG_COUNT + " messages");
LOG.info("Delivery statistics: {}", stats);
}
use of org.eclipse.hono.tests.AssumeMessagingSystem in project hono by eclipse.
the class DeviceRegistryNotificationsIT method testReceiveNotificationViaAmqp.
/**
* Verifies that AMQP-based notifications for adding tenant/device/credentials resources are received by a local
* client.
*
* @param ctx The vert.x test context.
* @throws InterruptedException if test is interrupted while running.
*/
@Test
@AssumeMessagingSystem(type = MessagingType.amqp)
public void testReceiveNotificationViaAmqp(final VertxTestContext ctx) throws InterruptedException {
final ClientConfigProperties messagingNetworkProperties = IntegrationTestSupport.getMessagingNetworkProperties();
// use user that may open receiver links on "notification/*" addresses
messagingNetworkProperties.setUsername(NOTIFICATION_TEST_USER);
messagingNetworkProperties.setPassword(NOTIFICATION_TEST_PWD);
receiver = new ProtonBasedNotificationReceiver(HonoConnection.newConnection(vertx, messagingNetworkProperties));
testReceiveNotification(ctx);
}
use of org.eclipse.hono.tests.AssumeMessagingSystem 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"));
}
});
}
use of org.eclipse.hono.tests.AssumeMessagingSystem in project hono by eclipse.
the class TelemetryMqttQoS1IT method testUploadMessagesWithNoConsumerSendsErrors.
/**
* Verifies that sending a number of telemetry messages to Hono's MQTT adapter while there is no consumer causes
* corresponding error messages to be published to the client if it is subscribed on the error topic.
*
* @param ctx The test context.
* @throws InterruptedException if the test fails.
*/
@Test
@AssumeMessagingSystem(type = MessagingType.amqp)
public void testUploadMessagesWithNoConsumerSendsErrors(final VertxTestContext ctx) throws InterruptedException {
final String tenantId = helper.getRandomTenantId();
final String deviceId = helper.getRandomDeviceId(tenantId);
final Tenant tenant = new Tenant();
final VertxTestContext setup = new VertxTestContext();
helper.registry.addDeviceForTenant(tenantId, tenant, deviceId, password).onComplete(setup.succeedingThenComplete());
assertThat(setup.awaitCompletion(5, TimeUnit.SECONDS)).isTrue();
if (setup.failed()) {
ctx.failNow(setup.causeOfFailure());
return;
}
// timeout value should be higher than the "hono.messaging.flowLatency" MQTT adapter configuration value
// so that no timeout doesn't while the MQTT adapter waits for credit after having created the first downstream
// sender link
final long publishCompletionTimeout = 1500;
doTestUploadMessages(ctx, tenantId, connectToAdapter(IntegrationTestSupport.getUsername(deviceId, tenantId), password), (payload) -> send(tenantId, deviceId, payload, true, null, publishCompletionTimeout).map(String::valueOf), // no consumer created here (future succeeded with null value)
(messageHandler) -> Future.succeededFuture(), (msg) -> {
final JsonObject payload = new JsonObject(msg.payload());
final String correlationId = payload.getString(MessageHelper.SYS_PROPERTY_CORRELATION_ID);
ctx.verify(() -> {
assertThat(payload.getInteger("code")).isEqualTo(HttpURLConnection.HTTP_UNAVAILABLE);
// error message should be the localized NoConsumerException message
assertThat(payload.getString("message")).isEqualTo(ServiceInvocationException.getLocalizedMessage(NoConsumerException.CLIENT_FACING_MESSAGE_KEY));
// validate topic segments; example: error//myDeviceId/telemetry/4/503
final String[] topicSegments = msg.topicName().split("/");
assertThat(topicSegments.length).isEqualTo(6);
assertThat(topicSegments[0]).isEqualTo("e");
assertThat(topicSegments[1]).isEqualTo(tenantId);
// device
assertThat(topicSegments[2]).isEmpty();
assertThat(topicSegments[3]).isEqualTo(TelemetryConstants.TELEMETRY_ENDPOINT_SHORT);
assertThat(topicSegments[4]).isEqualTo(correlationId);
assertThat(topicSegments[5]).isEqualTo(Integer.toString(HttpURLConnection.HTTP_UNAVAILABLE));
});
return Future.succeededFuture(correlationId);
}, String.format("e/%s//#", tenantId));
}
Aggregations