use of io.vertx.junit5.Timeout in project hono by eclipse.
the class CoapTestBase method testUploadMessageFailsForDisabledDevice.
/**
* Verifies that the CoAP adapter rejects messages from a disabled device.
*
* @param ctx The test context
*/
@Test
@Timeout(value = 10, timeUnit = TimeUnit.SECONDS)
public void testUploadMessageFailsForDisabledDevice(final VertxTestContext ctx) {
// GIVEN a disabled device
final Tenant tenant = new Tenant();
final Device deviceData = new Device();
deviceData.setEnabled(false);
helper.registry.addPskDeviceForTenant(tenantId, tenant, deviceId, deviceData, SECRET).compose(ok -> {
// WHEN the device tries to upload a message
final CoapClient client = getCoapsClient(deviceId, tenantId, SECRET);
final Promise<OptionSet> result = Promise.promise();
// THEN a NOT_FOUND response code is returned
client.advanced(getHandler(result, ResponseCode.NOT_FOUND), createCoapsRequest(Code.POST, getPostResource(), 0));
return result.future();
}).onComplete(ctx.succeedingThenComplete());
}
use of io.vertx.junit5.Timeout in project hono by eclipse.
the class CoapTestBase method testUploadMessagesWithTtdThatReplyWithOneWayCommand.
/**
* Verifies that the CoAP adapter delivers a one-way command to a device.
*
* @param endpointConfig The endpoints to use for sending/receiving commands.
* @param ctx The test context
* @throws InterruptedException if the test fails.
*/
@ParameterizedTest(name = IntegrationTestSupport.PARAMETERIZED_TEST_NAME_PATTERN)
@MethodSource("commandAndControlVariants")
@Timeout(value = 10, timeUnit = TimeUnit.SECONDS)
public void testUploadMessagesWithTtdThatReplyWithOneWayCommand(final CoapCommandEndpointConfiguration endpointConfig, final VertxTestContext ctx) throws InterruptedException {
final Tenant tenant = new Tenant();
final String expectedCommand = String.format("%s=%s", Constants.HEADER_COMMAND, COMMAND_TO_SEND);
final VertxTestContext setup = new VertxTestContext();
if (endpointConfig.isSubscribeAsUnauthenticatedDevice()) {
helper.registry.addDeviceForTenant(tenantId, tenant, deviceId, SECRET).onComplete(setup.succeedingThenComplete());
} else {
helper.registry.addPskDeviceForTenant(tenantId, tenant, deviceId, SECRET).onComplete(setup.succeedingThenComplete());
}
ctx.verify(() -> assertThat(setup.awaitCompletion(5, TimeUnit.SECONDS)).isTrue());
final CoapClient client = endpointConfig.isSubscribeAsUnauthenticatedDevice() ? getCoapClient() : getCoapsClient(deviceId, tenantId, SECRET);
final String commandTargetDeviceId = endpointConfig.isSubscribeAsGateway() ? helper.setupGatewayDeviceBlocking(tenantId, deviceId, 5) : deviceId;
final String subscribingDeviceId = endpointConfig.isSubscribeAsGatewayForSingleDevice() ? commandTargetDeviceId : deviceId;
testUploadMessages(ctx, tenantId, () -> warmUp(client, createCoapsRequest(Code.POST, getPostResource(), 0)), msg -> {
final Integer ttd = msg.getTimeTillDisconnect();
logger.debug("north-bound message received {}, ttd: {}", msg, ttd);
msg.getTimeUntilDisconnectNotification().ifPresent(notification -> {
ctx.verify(() -> {
assertThat(notification.getTenantId()).isEqualTo(tenantId);
assertThat(notification.getDeviceId()).isEqualTo(subscribingDeviceId);
});
logger.debug("send one-way-command");
final JsonObject inputData = new JsonObject().put(COMMAND_JSON_KEY, (int) (Math.random() * 100));
helper.sendOneWayCommand(tenantId, commandTargetDeviceId, COMMAND_TO_SEND, "application/json", inputData.toBuffer(), notification.getMillisecondsUntilExpiry() / 2);
});
}, count -> {
final Promise<OptionSet> result = Promise.promise();
final Request request = createCoapsOrCoapRequest(endpointConfig, commandTargetDeviceId, count);
request.getOptions().addUriQuery(String.format("%s=%d", Constants.HEADER_TIME_TILL_DISCONNECT, 4));
logger.debug("south-bound send {}", request);
client.advanced(getHandler(result, ResponseCode.CHANGED), request);
return result.future().map(responseOptions -> {
ctx.verify(() -> {
assertResponseContainsOneWayCommand(endpointConfig, responseOptions, expectedCommand, tenantId, commandTargetDeviceId);
});
return responseOptions;
});
});
}
use of io.vertx.junit5.Timeout in project hono by eclipse.
the class CoapTestBase method testUploadFailsForNonMatchingTrustAnchor.
/**
* Verifies that the adapter fails to authenticate a device if the device's client certificate's signature cannot be
* validated using the trust anchor that is registered for the tenant that the device belongs to.
*
* @param ctx The vert.x test context.
* @throws GeneralSecurityException if the tenant's trust anchor cannot be generated
*/
@Test
@Timeout(timeUnit = TimeUnit.SECONDS, value = 20)
public void testUploadFailsForNonMatchingTrustAnchor(final VertxTestContext ctx) throws GeneralSecurityException {
final var keyLoader = KeyLoader.fromFiles(vertx, PATH_DEVICE_KEY, PATH_DEVICE_CERT);
// GIVEN a tenant configured with a trust anchor
final KeyPair keyPair = helper.newEcKeyPair();
final var clientCert = (X509Certificate) keyLoader.getCertificateChain()[0];
final Tenant tenant = Tenants.createTenantForTrustAnchor(clientCert.getIssuerX500Principal().getName(X500Principal.RFC2253), keyPair.getPublic().getEncoded(), keyPair.getPublic().getAlgorithm());
helper.registry.addDeviceForTenant(tenantId, tenant, deviceId, clientCert).compose(ok -> {
final CoapClient client = getCoapsClient(keyLoader);
final Promise<OptionSet> result = Promise.promise();
client.advanced(getHandler(result), createCoapsRequest(Code.POST, getPostResource(), 0));
return result.future();
}).onComplete(ctx.failing(t -> {
// THEN the request fails because the DTLS handshake cannot be completed
assertStatus(ctx, HttpURLConnection.HTTP_UNAVAILABLE, t);
ctx.completeNow();
}));
}
use of io.vertx.junit5.Timeout in project hono by eclipse.
the class MqttPublishTestBase method doTestUploadMessages.
/**
* Uploads a number of messages and verifies that they are either received via the northbound consumer or that
* corresponding error messages are published to the client on the error topic.
*
* @param ctx The test context.
* @param tenantId The tenant identifier.
* @param connection The MQTT connection future.
* @param sender The message sender. The Future result is the correlation/message id of the sent message.
* @param consumerSupplier The message consumer. The result may be succeeded with a {@code null} value in case
* error message handling for a non-existing consumer shall get tested.
* @param errorMsgHandler The handler to invoke with received error messages or {@code null} if no error messages
* are expected. The future result is the error message correlation id.
* @param errorTopic The errorTopic to subscribe to. Will be ignored of errorMsgHandler is {@code null}.
* @throws InterruptedException if the test fails.
*/
protected void doTestUploadMessages(final VertxTestContext ctx, final String tenantId, final Future<MqttConnAckMessage> connection, final Function<Buffer, Future<String>> sender, final Function<Handler<DownstreamMessage<? extends MessageContext>>, Future<MessageConsumer>> consumerSupplier, final Function<MqttPublishMessage, Future<String>> errorMsgHandler, final String errorTopic) throws InterruptedException {
final boolean errorMessagesExpected = errorMsgHandler != null;
final CountDownLatch received = new CountDownLatch(MESSAGES_TO_SEND);
final AtomicInteger messageCount = new AtomicInteger(0);
final AtomicLong lastReceivedTimestamp = new AtomicLong(0);
// <correlation id of the sent telemetry/event message, errorMessageReceived promise>
final Map<String, Promise<Void>> pendingErrorMessages = new HashMap<>();
final AtomicBoolean consumerIsSet = new AtomicBoolean();
final VertxTestContext setup = new VertxTestContext();
connection.compose(ok -> consumerSupplier.apply(msg -> {
if (errorMessagesExpected) {
ctx.failNow(new IllegalStateException("consumer received message although sending was supposed to fail"));
return;
}
LOGGER.trace("received {}", msg);
ctx.verify(() -> {
DownstreamMessageAssertions.assertTelemetryMessageProperties(msg, tenantId);
assertThat(msg.getQos().ordinal()).isEqualTo(getQos().ordinal());
assertAdditionalMessageProperties(msg);
});
received.countDown();
lastReceivedTimestamp.set(System.currentTimeMillis());
if (received.getCount() % 50 == 0) {
LOGGER.info("messages received: {}", MESSAGES_TO_SEND - received.getCount());
}
})).compose(msgConsumer -> {
consumerIsSet.set(msgConsumer != null);
if (errorMsgHandler == null) {
return Future.succeededFuture();
}
mqttClient.publishHandler(msg -> {
LOGGER.trace("received error message [topic: {}]", msg.topicName());
errorMsgHandler.apply(msg).onSuccess(correlationId -> {
// correlate the error message with the corresponding publish operation and complete the publish operation promise here
pendingErrorMessages.compute(correlationId, (key, oldValue) -> {
final Promise<Void> promise = Optional.ofNullable(oldValue).orElseGet(Promise::promise);
promise.tryComplete();
// remove mapping if oldValue is set
return oldValue != null ? null : promise;
});
}).onFailure(ctx::failNow);
});
return subscribeToErrorTopic(errorTopic);
}).onComplete(setup.succeedingThenComplete());
assertThat(setup.awaitCompletion(5, TimeUnit.SECONDS)).isTrue();
if (setup.failed()) {
ctx.failNow(setup.causeOfFailure());
return;
}
customizeConnectedClient();
final long start = System.currentTimeMillis();
while (messageCount.get() < MESSAGES_TO_SEND) {
final CountDownLatch messageHandlingCompleted = new CountDownLatch(errorMessagesExpected ? 2 : 1);
context.runOnContext(go -> {
final Buffer msg = Buffer.buffer("hello " + messageCount.getAndIncrement());
sender.apply(msg).onComplete(sendAttempt -> {
if (sendAttempt.failed()) {
LOGGER.error("error sending message {}", messageCount.get(), sendAttempt.cause());
}
if (messageCount.get() % 50 == 0) {
LOGGER.info("messages sent: " + messageCount.get());
}
messageHandlingCompleted.countDown();
if (errorMessagesExpected) {
if (sendAttempt.failed()) {
messageHandlingCompleted.countDown();
} else {
// wait til error message has been received
final String correlationId = sendAttempt.result();
final long timerId = vertx.setTimer(1000, tid -> {
Optional.ofNullable(pendingErrorMessages.remove(correlationId)).ifPresent(promise -> promise.tryFail(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "timeout waiting for error response")));
});
final Handler<AsyncResult<Void>> errorMessageReceivedOrTimeoutHandler = ar -> {
vertx.cancelTimer(timerId);
if (ar.succeeded()) {
received.countDown();
lastReceivedTimestamp.set(System.currentTimeMillis());
if (received.getCount() % 50 == 0) {
LOGGER.info("error messages received: {}", MESSAGES_TO_SEND - received.getCount());
}
} else {
LOGGER.warn("failed to handle error message with correlation id [{}]", correlationId, ar.cause());
}
messageHandlingCompleted.countDown();
};
pendingErrorMessages.compute(correlationId, (key, oldValue) -> {
final Promise<Void> promise = Optional.ofNullable(oldValue).orElseGet(Promise::promise);
promise.future().onComplete(errorMessageReceivedOrTimeoutHandler);
// remove mapping if oldValue is set
return oldValue != null ? null : promise;
});
}
}
});
});
messageHandlingCompleted.await();
}
// in case no consumer is set, waiting time needs to be longer (adapter will wait for credit when creating the first downstream sender)
final long timeToWait = getTimeToWait() + (!consumerIsSet.get() ? 2000 : 0);
if (!received.await(timeToWait, TimeUnit.MILLISECONDS)) {
LOGGER.info("Timeout of {} milliseconds reached, stop waiting to receive messages.", timeToWait);
}
if (lastReceivedTimestamp.get() == 0L) {
// no message has been received at all
lastReceivedTimestamp.set(System.currentTimeMillis());
}
final long messagesReceived = MESSAGES_TO_SEND - received.getCount();
LOGGER.info("sent {} and received {} messages in {} milliseconds", messageCount.get(), messagesReceived, lastReceivedTimestamp.get() - start);
assertMessageReceivedRatio(messagesReceived, messageCount.get(), ctx);
}
use of io.vertx.junit5.Timeout in project hono by eclipse.
the class DeviceRegistrationApiTests method testAssertRegistrationSucceedsForDeviceViaGatewayGroup.
/**
* Verifies that the registry succeeds a request to assert the registration status of a device that connects via a
* gateway that is authorized through its group membership.
*
* @param ctx The vert.x test context.
*/
@Timeout(value = 5, timeUnit = TimeUnit.SECONDS)
@Test
public void testAssertRegistrationSucceedsForDeviceViaGatewayGroup(final VertxTestContext ctx) {
final String gatewayId = getHelper().getRandomDeviceId(tenantId);
final String deviceId = getHelper().getRandomDeviceId(tenantId);
final Device device = new Device();
device.setViaGroups(List.of("group"));
final Device gateway = new Device();
gateway.setMemberOf(List.of("group"));
Future.succeededFuture().compose(ok -> getHelper().registry.registerDevice(tenantId, gatewayId, gateway)).compose(ok -> getHelper().registry.registerDevice(tenantId, deviceId, device)).compose(ok -> getClient().assertRegistration(tenantId, deviceId, gatewayId, NoopSpan.INSTANCE.context())).onComplete(ctx.succeeding(resp -> {
ctx.verify(() -> {
assertThat(resp.getDeviceId()).isEqualTo(deviceId);
assertThat(resp.getAuthorizedGateways()).containsExactly(gatewayId);
});
ctx.completeNow();
}));
}
Aggregations