use of io.vertx.mqtt.MqttTopicSubscription in project hono by eclipse.
the class AbstractVertxBasedMqttProtocolAdapterTest method testOnSubscribeRegistersAndClosesConnection.
private void testOnSubscribeRegistersAndClosesConnection(final MqttQoS qos) {
// GIVEN a device connected to an adapter
givenAnAdapter(properties);
givenAnEventSenderForAnyTenant();
final MqttEndpoint endpoint = mockEndpoint();
// 10 seconds
when(endpoint.keepAliveTimeSeconds()).thenReturn(10);
// WHEN a device subscribes to commands
final CommandConsumer commandConsumer = mock(CommandConsumer.class);
when(commandConsumer.close(any())).thenReturn(Future.succeededFuture());
when(commandConsumerFactory.createCommandConsumer(eq("tenant"), eq("deviceId"), VertxMockSupport.anyHandler(), any(), any())).thenReturn(Future.succeededFuture(commandConsumer));
final List<MqttTopicSubscription> subscriptions = Collections.singletonList(newMockTopicSubscription(getCommandSubscriptionTopic("tenant", "deviceId"), qos));
final MqttSubscribeMessage msg = mock(MqttSubscribeMessage.class);
when(msg.messageId()).thenReturn(15);
when(msg.topicSubscriptions()).thenReturn(subscriptions);
final var mqttDeviceEndpoint = adapter.createMqttDeviceEndpoint(endpoint, null, OptionalInt.empty());
endpoint.closeHandler(handler -> mqttDeviceEndpoint.onClose());
mqttDeviceEndpoint.onSubscribe(msg);
// THEN the adapter creates a command consumer that is checked periodically
verify(commandConsumerFactory).createCommandConsumer(eq("tenant"), eq("deviceId"), VertxMockSupport.anyHandler(), any(), any());
// and the adapter registers a hook on the connection to the device
final ArgumentCaptor<Handler<Void>> closeHookCaptor = VertxMockSupport.argumentCaptorHandler();
verify(endpoint).closeHandler(closeHookCaptor.capture());
// which closes the command consumer when the device disconnects
closeHookCaptor.getValue().handle(null);
verify(commandConsumer).close(any());
// and sends an empty notification downstream with TTD 0
assertEmptyNotificationHasBeenSentDownstream("tenant", "deviceId", 0);
}
use of io.vertx.mqtt.MqttTopicSubscription in project hono by eclipse.
the class AbstractVertxBasedMqttProtocolAdapterTest method testAdapterSkipsTtdEventOnCmdConnectionCloseIfRemoveConsumerFails.
/**
* Verifies that the adapter doesn't send a 'disconnectedTtdEvent' on connection loss
* when removal of the command consumer mapping entry fails (which would be the case
* when another command consumer mapping had been registered in the mean time, meaning
* the device has already reconnected).
*/
@Test
public void testAdapterSkipsTtdEventOnCmdConnectionCloseIfRemoveConsumerFails() {
// GIVEN a device connected to an adapter
givenAnAdapter(properties);
givenAnEventSenderForAnyTenant();
final MqttEndpoint endpoint = mockEndpoint();
when(endpoint.isConnected()).thenReturn(true);
// 10 seconds
when(endpoint.keepAliveTimeSeconds()).thenReturn(10);
// WHEN a device subscribes to commands
final CommandConsumer commandConsumer = mock(CommandConsumer.class);
when(commandConsumer.close(any())).thenReturn(Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_PRECON_FAILED)));
when(commandConsumerFactory.createCommandConsumer(eq("tenant"), eq("deviceId"), VertxMockSupport.anyHandler(), any(), any())).thenReturn(Future.succeededFuture(commandConsumer));
final List<MqttTopicSubscription> subscriptions = Collections.singletonList(newMockTopicSubscription(getCommandSubscriptionTopic("tenant", "deviceId"), MqttQoS.AT_MOST_ONCE));
final MqttSubscribeMessage msg = mock(MqttSubscribeMessage.class);
when(msg.messageId()).thenReturn(15);
when(msg.topicSubscriptions()).thenReturn(subscriptions);
final var mqttDeviceEndpoint = adapter.createMqttDeviceEndpoint(endpoint, null, OptionalInt.empty());
endpoint.closeHandler(handler -> mqttDeviceEndpoint.onClose());
mqttDeviceEndpoint.onSubscribe(msg);
// THEN the adapter creates a command consumer that is checked periodically
verify(commandConsumerFactory).createCommandConsumer(eq("tenant"), eq("deviceId"), VertxMockSupport.anyHandler(), any(), any());
// and the adapter registers a hook on the connection to the device
final ArgumentCaptor<Handler<Void>> closeHookCaptor = VertxMockSupport.argumentCaptorHandler();
verify(endpoint).closeHandler(closeHookCaptor.capture());
// which closes the command consumer when the device disconnects
closeHookCaptor.getValue().handle(null);
when(endpoint.isConnected()).thenReturn(false);
verify(commandConsumer).close(any());
// and since closing the command consumer fails with a precon-failed exception
// there is only one notification sent during consumer creation,
assertEmptyNotificationHasBeenSentDownstream("tenant", "deviceId", -1);
// no 'disconnectedTtdEvent' event with TTD = 0
assertEmptyNotificationHasNotBeenSentDownstream("tenant", "deviceId", 0);
}
use of io.vertx.mqtt.MqttTopicSubscription in project hono by eclipse.
the class CommandSubscriptionTest method testSubscriptionSucceedsForAuthenticatedDevice.
/**
* Verifies that an authenticated device can successfully subscribe for commands
* targeted at itself using all variants of topic names.
*
* @param endpointName The endpoint name used in the topic.
* @param reqPartName The request part name used in the topic.
* @param qos The requested QoS.
*/
@ParameterizedTest
@MethodSource("endpointAndReqNamesWithQoS")
public void testSubscriptionSucceedsForAuthenticatedDevice(final String endpointName, final String reqPartName, final MqttQoS qos) {
final Command command = mock(Command.class);
when(command.isTargetedAtGateway()).thenReturn(false);
when(command.getTenant()).thenReturn(device.getTenantId());
when(command.getGatewayOrDeviceId()).thenReturn(device.getDeviceId());
when(command.getRequestId()).thenReturn("requestId");
when(command.getName()).thenReturn("doSomething");
// WHEN subscribing to commands using explicit topic
MqttTopicSubscription mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s/tenant/device/%s/#", endpointName, reqPartName), qos);
CommandSubscription subscription = CommandSubscription.fromTopic(mqttTopicSubscription, device);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo("tenant");
assertThat(subscription.getDeviceId()).isEqualTo("device");
assertThat(subscription.getEndpoint()).isEqualTo(endpointName);
assertThat(subscription.getRequestPart()).isEqualTo(reqPartName);
assertThat(subscription.getQos()).isEqualTo(qos);
// THEN the command topic does include both the tenant and device ID
assertThat(subscription.getCommandPublishTopic(command)).isEqualTo(String.format("%s/%s/%s/%s/requestId/doSomething", endpointName, device.getTenantId(), device.getDeviceId(), reqPartName));
// WHEN subscribing to commands including tenant only
mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s/tenant//%s/#", endpointName, reqPartName), qos);
subscription = CommandSubscription.fromTopic(mqttTopicSubscription, device);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo("tenant");
assertThat(subscription.getDeviceId()).isEqualTo("device");
assertThat(subscription.getEndpoint()).isEqualTo(endpointName);
assertThat(subscription.getRequestPart()).isEqualTo(reqPartName);
assertThat(subscription.getQos()).isEqualTo(qos);
// THEN the command topic does include the tenant as well
assertThat(subscription.getCommandPublishTopic(command)).isEqualTo(String.format("%s/%s//%s/requestId/doSomething", endpointName, device.getTenantId(), reqPartName));
// WHEN subscribing to commands including device ID only
mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s//device/%s/#", endpointName, reqPartName), qos);
subscription = CommandSubscription.fromTopic(mqttTopicSubscription, device);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo("tenant");
assertThat(subscription.getDeviceId()).isEqualTo("device");
assertThat(subscription.getEndpoint()).isEqualTo(endpointName);
assertThat(subscription.getRequestPart()).isEqualTo(reqPartName);
assertThat(subscription.getQos()).isEqualTo(qos);
// THEN the command topic does include the device ID as well
assertThat(subscription.getCommandPublishTopic(command)).isEqualTo(String.format("%s//%s/%s/requestId/doSomething", endpointName, device.getDeviceId(), reqPartName));
// WHEN subscribing to commands using implicit topic
mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s///%s/#", endpointName, reqPartName), qos);
subscription = CommandSubscription.fromTopic(mqttTopicSubscription, device);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo("tenant");
assertThat(subscription.getDeviceId()).isEqualTo("device");
assertThat(subscription.getEndpoint()).isEqualTo(endpointName);
assertThat(subscription.getRequestPart()).isEqualTo(reqPartName);
assertThat(subscription.getQos()).isEqualTo(qos);
// THEN the command topic does not include tenant nor device ID
assertThat(subscription.getCommandPublishTopic(command)).isEqualTo(String.format("%s///%s/requestId/doSomething", endpointName, reqPartName));
// using a tenant other than the tenant that the device belongs to should fail
mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s/otherTenant/device/%s/#", endpointName, reqPartName), qos);
assertThat(CommandSubscription.fromTopic(mqttTopicSubscription, device)).isNull();
}
use of io.vertx.mqtt.MqttTopicSubscription in project hono by eclipse.
the class CommandSubscriptionTest method testSubscriptionSucceedsForAuthenticatedGateway.
/**
* Verifies that an authenticated gateway can successfully subscribe for commands
* targeted at one of devices that it is authorized to act on behalf of.
*
* @param endpointName The endpoint name used in the topic.
* @param reqPartName The request part name used in the topic.
* @param qos The requested QoS.
*/
@ParameterizedTest
@MethodSource("endpointAndReqNamesWithQoS")
public void testSubscriptionSucceedsForAuthenticatedGateway(final String endpointName, final String reqPartName, final MqttQoS qos) {
final String gatewayManagedDeviceId = "gatewayManagedDevice";
final Command command = mock(Command.class);
when(command.isTargetedAtGateway()).thenReturn(true);
when(command.getTenant()).thenReturn(gw.getTenantId());
when(command.getGatewayId()).thenReturn(gw.getDeviceId());
when(command.getGatewayOrDeviceId()).thenReturn(gw.getDeviceId());
when(command.getDeviceId()).thenReturn(gatewayManagedDeviceId);
when(command.getRequestId()).thenReturn("requestId");
when(command.getName()).thenReturn("doSomething");
// WHEN subscribing to commands for a specific device omitting tenant
MqttTopicSubscription mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s//%s/%s/#", endpointName, gatewayManagedDeviceId, reqPartName), qos);
CommandSubscription subscription = CommandSubscription.fromTopic(mqttTopicSubscription, gw);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo(gw.getTenantId());
assertThat(subscription.getDeviceId()).isEqualTo(gatewayManagedDeviceId);
assertThat(subscription.getAuthenticatedDeviceId()).isEqualTo(gw.getDeviceId());
assertThat(subscription.getQos()).isEqualTo(qos);
assertThat(subscription.isGatewaySubscriptionForSpecificDevice()).isEqualTo(true);
// THEN the command topic does not include the tenant either
assertThat(subscription.getCommandPublishTopic(command)).isEqualTo(String.format("%s//%s/%s/requestId/doSomething", endpointName, gatewayManagedDeviceId, reqPartName));
// WHEN subscribing to commands for a specific device including the tenant
mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s/%s/%s/%s/#", endpointName, device.getTenantId(), gatewayManagedDeviceId, reqPartName), qos);
subscription = CommandSubscription.fromTopic(mqttTopicSubscription, gw);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo(gw.getTenantId());
assertThat(subscription.getDeviceId()).isEqualTo(gatewayManagedDeviceId);
assertThat(subscription.getAuthenticatedDeviceId()).isEqualTo(gw.getDeviceId());
assertThat(subscription.getQos()).isEqualTo(qos);
assertThat(subscription.isGatewaySubscriptionForSpecificDevice()).isEqualTo(true);
// THEN the command topic does include the tenant as well
assertThat(subscription.getCommandPublishTopic(command)).isEqualTo(String.format("%s/%s/%s/%s/requestId/doSomething", endpointName, gw.getTenantId(), gatewayManagedDeviceId, reqPartName));
// WHEN subscribing to commands for all devices omitting tenant
mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s//+/%s/#", endpointName, reqPartName), qos);
subscription = CommandSubscription.fromTopic(mqttTopicSubscription, gw);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo(gw.getTenantId());
assertThat(subscription.getDeviceId()).isEqualTo(gw.getDeviceId());
assertThat(subscription.getAuthenticatedDeviceId()).isEqualTo(gw.getDeviceId());
assertThat(subscription.getQos()).isEqualTo(qos);
assertThat(subscription.isGatewaySubscriptionForSpecificDevice()).isEqualTo(false);
// THEN the command topic does not include the tenant either
assertThat(subscription.getCommandPublishTopic(command)).isEqualTo(String.format("%s//%s/%s/requestId/doSomething", endpointName, gatewayManagedDeviceId, reqPartName));
// WHEN subscribing to commands for all devices including the tenant
mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s/%s/+/%s/#", endpointName, device.getTenantId(), reqPartName), qos);
subscription = CommandSubscription.fromTopic(mqttTopicSubscription, gw);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo(gw.getTenantId());
assertThat(subscription.getDeviceId()).isEqualTo(gw.getDeviceId());
assertThat(subscription.getAuthenticatedDeviceId()).isEqualTo(gw.getDeviceId());
assertThat(subscription.getQos()).isEqualTo(qos);
assertThat(subscription.isGatewaySubscriptionForSpecificDevice()).isEqualTo(false);
// THEN the command topic does include the tenant as well
assertThat(subscription.getCommandPublishTopic(command)).isEqualTo(String.format("%s/%s/%s/%s/requestId/doSomething", endpointName, gw.getTenantId(), gatewayManagedDeviceId, reqPartName));
// using a tenant other than the tenant that the gateway belongs to should fail
mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s/otherTenant/+/%s/#", endpointName, reqPartName), qos);
assertThat(CommandSubscription.fromTopic(mqttTopicSubscription, gw)).isNull();
}
use of io.vertx.mqtt.MqttTopicSubscription in project hono by eclipse.
the class CommandSubscriptionTest method testSubscriptionSucceedsForUnauthenticatedDevice.
/**
* Verifies that an unauthenticated device can successfully subscribe for commands
* using the default topic.
*
* @param endpointName The endpoint name used in the topic.
* @param reqPartName The request part name used in the topic.
* @param qos The requested QoS.
*/
@ParameterizedTest
@MethodSource("endpointAndReqNamesWithQoS")
public void testSubscriptionSucceedsForUnauthenticatedDevice(final String endpointName, final String reqPartName, final MqttQoS qos) {
final MqttTopicSubscription mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s/tenant1/deviceA/%s/#", endpointName, reqPartName), qos);
final CommandSubscription subscription = CommandSubscription.fromTopic(mqttTopicSubscription, null);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo("tenant1");
assertThat(subscription.getDeviceId()).isEqualTo("deviceA");
assertThat(subscription.getEndpoint()).isEqualTo(endpointName);
assertThat(subscription.getRequestPart()).isEqualTo(reqPartName);
assertThat(subscription.getQos()).isEqualTo(qos);
}
Aggregations