use of io.vertx.junit5.Timeout in project hono by eclipse.
the class HonoConnectionImplTest method testCreateSenderFailsOnDisconnectBeforeOpen.
/**
* Verifies that the attempt to create a sender fails with a
* {@code ServerErrorException} if the connection gets disconnected
* before the remote peer has sent its attach frame. It is verified
* that this is done before the link establishment timeout.
*
* @param ctx The vert.x test context.
*/
@Test
public void testCreateSenderFailsOnDisconnectBeforeOpen(final VertxTestContext ctx) {
// choose a distinct value here
final long linkEstablishmentTimeout = 444L;
props.setLinkEstablishmentTimeout(linkEstablishmentTimeout);
// don't run linkEstablishmentTimeout timer handler
when(vertx.setTimer(eq(linkEstablishmentTimeout), VertxMockSupport.anyHandler())).thenAnswer(invocation -> 0L);
final ProtonSender sender = mock(ProtonSender.class);
when(sender.isOpen()).thenReturn(Boolean.TRUE);
when(session.createSender(anyString())).thenReturn(sender);
final Target target = new Target();
target.setAddress("someAddress");
when(sender.getRemoteTarget()).thenReturn(target);
when(sender.getCredit()).thenReturn(0);
// mock handlers
final Handler<String> remoteCloseHook = VertxMockSupport.mockHandler();
// GIVEN an established connection
honoConnection.connect().compose(c -> {
// WHEN creating a sender link with a close hook
final Future<ProtonSender> result = honoConnection.createSender("target", ProtonQoS.AT_LEAST_ONCE, remoteCloseHook);
// THEN the result is not completed at first
ctx.verify(() -> assertThat(result.isComplete()).isFalse());
// WHEN the downstream connection fails
connectionFactory.getDisconnectHandler().handle(con);
return result;
}).onComplete(ctx.failing(t -> {
ctx.verify(() -> assertThat(((ServerErrorException) t).getErrorCode()).isEqualTo(HttpURLConnection.HTTP_UNAVAILABLE));
ctx.completeNow();
}));
}
use of io.vertx.junit5.Timeout in project hono by eclipse.
the class RequestResponseClientTest method testCreateAndSendRequestSetsNoTimerIfRequestTimeoutZero.
/**
* Verifies that the client configured with a zero request timeout sets no timer for
* canceling the request in case of no received response.
*
* @param ctx The vert.x test context.
*/
@Test
public void testCreateAndSendRequestSetsNoTimerIfRequestTimeoutZero(final VertxTestContext ctx) {
// WHEN configuring the client with a zero request timeout and sending a request message
final JsonObject payload = new JsonObject().put("key", "value");
client.map(c -> {
c.setRequestTimeout(0);
return c;
}).onComplete(ctx.succeeding(c -> {
c.createAndSendRequest("get", null, payload.toBuffer(), "application/json", SimpleRequestResponseResult::from, span);
}));
// THEN the message is sent
final Message request = verifySenderSend();
assertThat(request).isNotNull();
assertThat(request.getBody()).isNotNull();
assertThat(request.getBody()).isInstanceOf(Data.class);
final Buffer body = MessageHelper.getPayload(request);
assertThat(body.getBytes()).isEqualTo(payload.toBuffer().getBytes());
// and no timer has been set to time out the request
verify(vertx, never()).setTimer(anyLong(), VertxMockSupport.anyHandler());
ctx.completeNow();
}
use of io.vertx.junit5.Timeout in project hono by eclipse.
the class TelemetryCoapIT method testUploadFailsForLargePayload.
/**
* Verifies that the upload of a telemetry message containing a payload that
* exceeds the CoAP adapter's configured max payload size fails with a 4.13
* response code.
*
* @param ctx The test context.
* @throws IOException if the CoAP request cannot be sent to the adapter.
* @throws ConnectorException if the CoAP request cannot be sent to the adapter.
*/
@Test
@Timeout(value = 10, timeUnit = TimeUnit.SECONDS)
public void testUploadFailsForLargePayload(final VertxTestContext ctx) throws ConnectorException, IOException {
final Tenant tenant = new Tenant();
helper.registry.addPskDeviceForTenant(tenantId, tenant, deviceId, SECRET).compose(ok -> {
final CoapClient client = getCoapsClient(deviceId, tenantId, SECRET);
final Request request = createCoapsRequest(Code.POST, Type.CON, getPostResource(), IntegrationTestSupport.getPayload(4096));
final Promise<OptionSet> result = Promise.promise();
client.advanced(getHandler(result, ResponseCode.REQUEST_ENTITY_TOO_LARGE), request);
return result.future();
}).onComplete(ctx.succeedingThenComplete());
}
use of io.vertx.junit5.Timeout in project hono by eclipse.
the class KafkaBasedCommandConsumerFactoryImplIT method testCommandsGetForwardedInIncomingOrder.
/**
* Verifies that records, published on the tenant-specific Kafka command topic, get received by
* the consumer created by the factory and get forwarded on the internal command topic in the
* same order they were published.
*
* @param ctx The vert.x test context.
* @throws InterruptedException if test execution gets interrupted.
*/
@Test
@Timeout(value = 10, timeUnit = TimeUnit.SECONDS)
public void testCommandsGetForwardedInIncomingOrder(final VertxTestContext ctx) throws InterruptedException {
final String tenantId = "tenant_" + UUID.randomUUID();
final VertxTestContext setup = new VertxTestContext();
final int numTestCommands = 10;
final List<KafkaConsumerRecord<String, Buffer>> receivedRecords = new ArrayList<>();
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() == numTestCommands) {
allRecordsReceivedPromise.tryComplete();
}
};
final Deque<Promise<Void>> completionPromisesQueue = new LinkedList<>();
// don't let getting the target adapter instance finish immediately
// - let the futures complete in the reverse order
final Supplier<Future<Void>> targetAdapterInstanceGetterCompletionFutureSupplier = () -> {
final Promise<Void> resultPromise = Promise.promise();
completionPromisesQueue.addFirst(resultPromise);
// complete all promises in reverse order when processing the last command
if (completionPromisesQueue.size() == numTestCommands) {
completionPromisesQueue.forEach(Promise::complete);
}
return resultPromise.future();
};
final Context vertxContext = vertx.getOrCreateContext();
vertxContext.runOnContext(v0 -> {
final HonoKafkaConsumer internalConsumer = getInternalCommandConsumer(recordHandler);
final KafkaBasedCommandConsumerFactoryImpl consumerFactory = getKafkaBasedCommandConsumerFactory(targetAdapterInstanceGetterCompletionFutureSupplier, tenantId);
CompositeFuture.join(internalConsumer.start(), consumerFactory.start()).compose(f -> createCommandConsumer(tenantId, consumerFactory)).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 long timerId = vertx.setTimer(8000, tid -> {
LOG.info("received records:{}{}", System.lineSeparator(), receivedRecords.stream().map(Object::toString).collect(Collectors.joining("," + System.lineSeparator())));
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);
});
ctx.completeNow();
}));
}
use of io.vertx.junit5.Timeout in project hono by eclipse.
the class CommandAndControlMqttIT method testSendCommandFailsForCommandNotAcknowledgedByDevice.
/**
* Verifies that the adapter forwards the <em>released</em> disposition back to the
* application if the device hasn't sent an acknowledgement for the command message
* published to the 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 = 20)
public void testSendCommandFailsForCommandNotAcknowledgedByDevice(final MqttCommandEndpointConfiguration endpointConfig, final VertxTestContext ctx) throws InterruptedException {
final MqttQoS subscribeQos = MqttQoS.AT_LEAST_ONCE;
final VertxTestContext setup = new VertxTestContext();
final Checkpoint ready = setup.checkpoint(2);
final String commandTargetDeviceId = endpointConfig.isSubscribeAsGateway() ? helper.setupGatewayDeviceBlocking(tenantId, deviceId, 5) : deviceId;
final int totalNoOfCommandsToSend = 2;
final CountDownLatch commandsFailed = new CountDownLatch(totalNoOfCommandsToSend);
final AtomicInteger receivedMessagesCounter = new AtomicInteger(0);
final AtomicInteger counter = new AtomicInteger();
final Handler<MqttPublishMessage> commandConsumer = msg -> {
LOGGER.trace("received command [{}] - no response sent here", msg.topicName());
final ResourceIdentifier topic = ResourceIdentifier.fromString(msg.topicName());
ctx.verify(() -> {
endpointConfig.assertCommandPublishTopicStructure(topic, commandTargetDeviceId, false, "setValue");
});
receivedMessagesCounter.incrementAndGet();
};
final Function<Buffer, Future<Void>> commandSender = payload -> {
counter.incrementAndGet();
return helper.sendCommand(tenantId, commandTargetDeviceId, "setValue", "text/plain", payload, helper.getSendCommandTimeout(counter.get() == 1)).mapEmpty();
};
helper.registry.addDeviceToTenant(tenantId, deviceId, password).compose(ok -> connectToAdapter(IntegrationTestSupport.getUsername(deviceId, tenantId), password)).compose(ok -> injectMqttClientPubAckBlocker(new AtomicBoolean(true))).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 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 -> {
final Buffer msg = Buffer.buffer("value: " + commandsSent.getAndIncrement());
commandSender.apply(msg).onComplete(sendAttempt -> {
if (sendAttempt.succeeded()) {
LOGGER.debug("sending command {} succeeded unexpectedly", commandsSent.get());
} else {
if (sendAttempt.cause() instanceof ServerErrorException && ((ServerErrorException) sendAttempt.cause()).getErrorCode() == HttpURLConnection.HTTP_UNAVAILABLE && !(sendAttempt.cause() instanceof SendMessageTimeoutException)) {
LOGGER.debug("sending command {} failed as expected: {}", commandsSent.get(), sendAttempt.cause().toString());
lastReceivedTimestamp.set(System.currentTimeMillis());
commandsFailed.countDown();
if (commandsFailed.getCount() % 20 == 0) {
LOGGER.info("commands failed as expected: {}", totalNoOfCommandsToSend - commandsFailed.getCount());
}
} else {
LOGGER.debug("sending command {} failed with an unexpected error", commandsSent.get(), sendAttempt.cause());
}
}
commandSent.countDown();
});
});
commandSent.await();
}
// have to wait an extra MqttAdapterProperties.DEFAULT_COMMAND_ACK_TIMEOUT (100ms) for each command message
final long timeToWait = totalNoOfCommandsToSend * 300;
if (!commandsFailed.await(timeToWait, TimeUnit.MILLISECONDS)) {
LOGGER.info("Timeout of {} milliseconds reached, stop waiting for commands", timeToWait);
}
assertThat(receivedMessagesCounter.get()).isEqualTo(totalNoOfCommandsToSend);
final long commandsCompleted = totalNoOfCommandsToSend - commandsFailed.getCount();
LOGGER.info("commands sent: {}, commands failed: {} after {} milliseconds", commandsSent.get(), commandsCompleted, lastReceivedTimestamp.get() - start);
if (commandsCompleted == commandsSent.get()) {
ctx.completeNow();
} else {
ctx.failNow(new java.lang.IllegalStateException("did not complete all commands sent"));
}
}
Aggregations