Feature dmf integration tests (#459)
* Add rabbit mq integration test * Change DMF Api for ActionUpdateStatus JSON * Add test for invalid target messages. * Insert tests for AmqpAuthenticationMessageHandler Signed-off-by: Melanie Retter <melanie.retter@bosch-si.com> * - Added vhost creation to tests - Added missing test - Created beans for dmf client and vhost - refactoring Signed-off-by: Jonathan Philip Knoblauch <JonathanPhilip.Knoblauch@bosch-si.com> * Completed missing tests Signed-off-by: Jonathan Philip Knoblauch <JonathanPhilip.Knoblauch@bosch-si.com> * Create vhost after every test class Signed-off-by: SirWayne <dennis.melzer@bosch-si.com> * Add readme.md Signed-off-by: SirWayne <dennis.melzer@bosch-si.com> * Add check if rabbit mq management is available Signed-off-by: SirWayne <dennis.melzer@bosch-si.com> * refactoring test Signed-off-by: Jonathan Philip Knoblauch <JonathanPhilip.Knoblauch@bosch-si.com> * Create integration test for amqpAuthentication Add test description Signed-off-by: SirWayne <dennis.melzer@bosch-si.com> * Fix sonar issue Add rabbitmq username and password Signed-off-by: SirWayne <dennis.melzer@bosch-si.com> * Merge master Signed-off-by: SirWayne <dennis.melzer@bosch-si.com> * Fix some code smells Signed-off-by: SirWayne <dennis.melzer@bosch-si.com> * Make some member immutable Change executor Signed-off-by: SirWayne <dennis.melzer@bosch-si.com>
This commit is contained in:
@@ -30,7 +30,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -96,7 +95,7 @@ public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
|
||||
* the amqp message
|
||||
* @return the rpc message back to supplier.
|
||||
*/
|
||||
@RabbitListener(queues = "${hawkbit.dmf.rabbitmq.authenticationReceiverQueue}", containerFactory = "listenerContainerFactory")
|
||||
@RabbitListener(queues = "${hawkbit.dmf.rabbitmq.authenticationReceiverQueue:authentication_receiver}", containerFactory = "listenerContainerFactory")
|
||||
public Message onAuthenticationRequest(final Message message) {
|
||||
checkContentTypeJson(message);
|
||||
final SecurityContext oldContext = SecurityContextHolder.getContext();
|
||||
@@ -157,6 +156,11 @@ public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
|
||||
|
||||
private Optional<org.eclipse.hawkbit.repository.model.Artifact> findArtifactByFileResource(
|
||||
final FileResource fileResource) {
|
||||
|
||||
if (fileResource == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (fileResource.getSha1() != null) {
|
||||
return artifactManagement.findFirstArtifactBySHA1(fileResource.getSha1());
|
||||
}
|
||||
@@ -188,9 +192,7 @@ public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
|
||||
|
||||
private Message handleAuthenticationMessage(final Message message) {
|
||||
final DownloadResponse authentificationResponse = new DownloadResponse();
|
||||
final MessageProperties messageProperties = message.getMessageProperties();
|
||||
final TenantSecurityToken secruityToken = convertMessage(message, TenantSecurityToken.class);
|
||||
|
||||
final FileResource fileResource = secruityToken.getFileResource();
|
||||
try {
|
||||
SecurityContextHolder.getContext().setAuthentication(authenticationManager.doAuthenticate(secruityToken));
|
||||
@@ -228,7 +230,7 @@ public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
|
||||
authentificationResponse.setMessage(errorMessage);
|
||||
}
|
||||
|
||||
return getMessageConverter().toMessage(authentificationResponse, messageProperties);
|
||||
return getMessageConverter().toMessage(authentificationResponse, message.getMessageProperties());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -117,19 +117,18 @@ public class AmqpControllerAuthentication {
|
||||
/**
|
||||
* Performs authentication with the security token.
|
||||
*
|
||||
* @param secruityToken
|
||||
* @param securityToken
|
||||
* the authentication request object
|
||||
* @return the authentication object
|
||||
*/
|
||||
public Authentication doAuthenticate(final TenantSecurityToken secruityToken) {
|
||||
resolveTenant(secruityToken);
|
||||
|
||||
public Authentication doAuthenticate(final TenantSecurityToken securityToken) {
|
||||
resolveTenant(securityToken);
|
||||
PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(null, null);
|
||||
for (final PreAuthentificationFilter filter : filterChain) {
|
||||
final PreAuthenticatedAuthenticationToken authenticationRest = createAuthentication(filter, secruityToken);
|
||||
final PreAuthenticatedAuthenticationToken authenticationRest = createAuthentication(filter, securityToken);
|
||||
if (authenticationRest != null) {
|
||||
authentication = authenticationRest;
|
||||
authentication.setDetails(new TenantAwareAuthenticationDetails(secruityToken.getTenant(), true));
|
||||
authentication.setDetails(new TenantAwareAuthenticationDetails(securityToken.getTenant(), true));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,10 +109,10 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
LOG.debug("targetAssignDistributionSet retrieved for controller {}. I will forward it to DMF broker.",
|
||||
assignedEvent.getControllerId());
|
||||
|
||||
targetManagement.findTargetByControllerID(assignedEvent.getControllerId()).ifPresent(target -> sendUpdateMessageToTarget(assignedEvent.getTenant(),
|
||||
target,
|
||||
assignedEvent.getActionId(), assignedEvent.getModules()));
|
||||
|
||||
targetManagement.findTargetByControllerID(assignedEvent.getControllerId())
|
||||
.ifPresent(target -> sendUpdateMessageToTarget(assignedEvent.getTenant(), target,
|
||||
assignedEvent.getActionId(), assignedEvent.getModules()));
|
||||
|
||||
}
|
||||
|
||||
void sendUpdateMessageToTarget(final String tenant, final Target target, final Long actionId,
|
||||
@@ -165,7 +165,6 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
if (!IpUtil.isAmqpUri(address)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Message message = getMessageConverter().toMessage(actionId,
|
||||
createConnectorMessageProperties(tenant, controllerId, EventTopic.CANCEL_DOWNLOAD));
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
*
|
||||
* @return a message if <null> no message is send back to sender
|
||||
*/
|
||||
@RabbitListener(queues = "${hawkbit.dmf.rabbitmq.receiverQueue}", containerFactory = "listenerContainerFactory")
|
||||
@RabbitListener(queues = "${hawkbit.dmf.rabbitmq.receiverQueue:dmf_receiver}", containerFactory = "listenerContainerFactory")
|
||||
public Message onMessage(final Message message, @Header(MessageHeaderKey.TYPE) final String type,
|
||||
@Header(MessageHeaderKey.TENANT) final String tenant) {
|
||||
return onMessage(message, type, tenant, getRabbitTemplate().getConnectionFactory().getVirtualHost());
|
||||
@@ -169,7 +169,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
final String replyTo = message.getMessageProperties().getReplyTo();
|
||||
|
||||
if (StringUtils.isEmpty(replyTo)) {
|
||||
logAndThrowMessageError(message, "No ReplyTo was set for the createThing TenantAwareEvent.");
|
||||
logAndThrowMessageError(message, "No ReplyTo was set for the createThing message.");
|
||||
}
|
||||
|
||||
final URI amqpUri = IpUtil.createAmqpUri(virtualHost, replyTo);
|
||||
@@ -180,8 +180,10 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
}
|
||||
|
||||
private void lookIfUpdateAvailable(final Target target) {
|
||||
|
||||
final Optional<Action> actionOptional = controllerManagement
|
||||
.findOldestActiveActionByTarget(target.getControllerId());
|
||||
|
||||
if (!actionOptional.isPresent()) {
|
||||
return;
|
||||
}
|
||||
@@ -206,7 +208,6 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
* the topic of the event.
|
||||
*/
|
||||
private void handleIncomingEvent(final Message message, final EventTopic topic) {
|
||||
|
||||
switch (topic) {
|
||||
case UPDATE_ACTION_STATUS:
|
||||
updateActionStatus(message);
|
||||
@@ -294,11 +295,11 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
private Status hanldeCancelRejectedState(final Message message, final Action action) {
|
||||
if (action.isCancelingOrCanceled()) {
|
||||
return Status.CANCEL_REJECTED;
|
||||
} else {
|
||||
logAndThrowMessageError(message,
|
||||
"Cancel recjected message is not allowed, if action is on state: " + action.getStatus());
|
||||
return null;
|
||||
}
|
||||
logAndThrowMessageError(message,
|
||||
"Cancel rejected message is not allowed, if action is on state: " + action.getStatus());
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
private void updateLastPollTime(final Target target) {
|
||||
@@ -321,13 +322,10 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
@SuppressWarnings("squid:S3655")
|
||||
private Action checkActionExist(final Message message, final ActionUpdateStatus actionUpdateStatus) {
|
||||
final Long actionId = actionUpdateStatus.getActionId();
|
||||
|
||||
LOG.debug("Target notifies intermediate about action {} with status {}.", actionId,
|
||||
actionUpdateStatus.getActionStatus());
|
||||
|
||||
if (actionId == null) {
|
||||
logAndThrowMessageError(message, "Invalid message no action id");
|
||||
}
|
||||
|
||||
final Optional<Action> findActionWithDetails = controllerManagement.findActionWithDetails(actionId);
|
||||
if (!findActionWithDetails.isPresent()) {
|
||||
logAndThrowMessageError(message,
|
||||
|
||||
@@ -8,11 +8,10 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.amqp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
|
||||
@@ -20,6 +19,7 @@ import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
|
||||
import org.springframework.amqp.support.converter.MessageConversionException;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
|
||||
/**
|
||||
@@ -58,45 +58,27 @@ public class BaseAmqpService {
|
||||
* @return the converted object
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T convertMessage(final Message message, final Class<T> clazz) {
|
||||
if (isMessageBodyEmpty(message)) {
|
||||
return null;
|
||||
}
|
||||
public <T> T convertMessage(@NotNull final Message message, final Class<T> clazz) {
|
||||
checkMessageBody(message);
|
||||
message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME,
|
||||
clazz.getName());
|
||||
return (T) rabbitTemplate.getMessageConverter().fromMessage(message);
|
||||
}
|
||||
|
||||
private static boolean isMessageBodyEmpty(final Message message) {
|
||||
return message == null || message.getBody() == null || message.getBody().length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is needed to convert a incoming message to is originally list object
|
||||
* type.
|
||||
*
|
||||
* @param message
|
||||
* the message to convert.
|
||||
* @param clazz
|
||||
* the class of the list content.
|
||||
* @return the list of converted objects
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> List<T> convertMessageList(final Message message, final Class<T> clazz) {
|
||||
if (isMessageBodyEmpty(message)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME,
|
||||
ArrayList.class.getName());
|
||||
message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CONTENT_CLASSID_FIELD_NAME,
|
||||
clazz.getName());
|
||||
return (List<T>) rabbitTemplate.getMessageConverter().fromMessage(message);
|
||||
}
|
||||
|
||||
public MessageConverter getMessageConverter() {
|
||||
protected MessageConverter getMessageConverter() {
|
||||
return rabbitTemplate.getMessageConverter();
|
||||
}
|
||||
|
||||
private static boolean isMessageBodyEmpty(final Message message) {
|
||||
return message.getBody() == null || message.getBody().length == 0;
|
||||
}
|
||||
|
||||
protected void checkMessageBody(@NotNull final Message message) {
|
||||
if (isMessageBodyEmpty(message)) {
|
||||
throw new MessageConversionException("Message body cannot be null");
|
||||
}
|
||||
}
|
||||
|
||||
protected String getStringHeaderKey(final Message message, final String key, final String errorMessageIfNull) {
|
||||
final Map<String, Object> header = message.getMessageProperties().getHeaders();
|
||||
final Object value = header.get(key);
|
||||
|
||||
@@ -8,23 +8,33 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.hawkbit.RabbitMqSetupService.AlivenessException;
|
||||
import org.eclipse.hawkbit.amqp.AmqpProperties;
|
||||
import org.eclipse.hawkbit.amqp.AmqpSenderService;
|
||||
import org.eclipse.hawkbit.amqp.DefaultAmqpSenderService;
|
||||
import org.eclipse.hawkbit.api.HostnameResolver;
|
||||
import org.eclipse.hawkbit.integration.listener.DeadletterListener;
|
||||
import org.eclipse.hawkbit.integration.listener.ReplyToListener;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.SystemSecurityContextHolder;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.core.Binding;
|
||||
import org.springframework.amqp.core.BindingBuilder;
|
||||
import org.springframework.amqp.core.FanoutExchange;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.rabbit.junit.BrokerRunning;
|
||||
import org.springframework.amqp.rabbit.test.RabbitListenerTest;
|
||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -33,14 +43,21 @@ import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.google.common.base.Throwables;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({ AmqpProperties.class })
|
||||
@RabbitListenerTest
|
||||
public class AmqpTestConfiguration {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AmqpTestConfiguration.class);
|
||||
|
||||
public static final String REPLY_TO_EXCHANGE = "reply.queue";
|
||||
public static final String REPLY_TO_QUEUE = "reply_queue";
|
||||
|
||||
/**
|
||||
* @return the {@link SystemSecurityContext} singleton bean which make it
|
||||
* accessible in beans which cannot access the service directly,
|
||||
@@ -51,56 +68,15 @@ public class AmqpTestConfiguration {
|
||||
return SystemSecurityContextHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to set the Jackson2JsonMessageConverter.
|
||||
*
|
||||
* @return the Jackson2JsonMessageConverter
|
||||
*/
|
||||
@Bean
|
||||
public MessageConverter jsonMessageConverter() {
|
||||
return new Jackson2JsonMessageConverter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default amqp sender service bean.
|
||||
*
|
||||
* @param rabbitTemplate
|
||||
*
|
||||
* @return the default amqp sender service bean
|
||||
*/
|
||||
@Bean
|
||||
@Autowired
|
||||
public AmqpSenderService amqpSenderServiceBean(final RabbitTemplate rabbitTemplate) {
|
||||
return new DefaultAmqpSenderService(rabbitTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ExecutorService with security context availability in thread
|
||||
* execution..
|
||||
*/
|
||||
@Bean(destroyMethod = "shutdown")
|
||||
@ConditionalOnMissingBean
|
||||
public Executor asyncExecutor() {
|
||||
return new DelegatingSecurityContextExecutorService(threadPoolExecutor());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return central ThreadPoolExecutor for general purpose multi threaded
|
||||
* operations. Tries an orderly shutdown when destroyed.
|
||||
*/
|
||||
private ThreadPoolExecutor threadPoolExecutor() {
|
||||
final BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(10);
|
||||
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 10, 1000, TimeUnit.MILLISECONDS,
|
||||
blockingQueue, new ThreadFactoryBuilder().setNameFormat("central-executor-pool-%d").build());
|
||||
|
||||
return threadPoolExecutor;
|
||||
Executor asyncExecutor() {
|
||||
return new DelegatingSecurityContextExecutorService(Executors.newSingleThreadExecutor());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link TaskExecutor} for task execution
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TaskExecutor taskExecutor() {
|
||||
return new ConcurrentTaskExecutor(asyncExecutor());
|
||||
}
|
||||
@@ -110,7 +86,6 @@ public class AmqpTestConfiguration {
|
||||
* {@link #threadPoolTaskScheduler()}.
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ScheduledExecutorService scheduledExecutorService() {
|
||||
return threadPoolTaskScheduler().getScheduledExecutor();
|
||||
}
|
||||
@@ -119,8 +94,83 @@ public class AmqpTestConfiguration {
|
||||
* @return {@link ThreadPoolTaskScheduler} for scheduled operations.
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
|
||||
return new ThreadPoolTaskScheduler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HostnameResolver hostnameResolver(final HawkbitServerProperties serverProperties) {
|
||||
return () -> {
|
||||
try {
|
||||
return new URL(serverProperties.getUrl());
|
||||
} catch (final MalformedURLException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean(name = "dmfClient")
|
||||
public RabbitTemplate dmfClient(ConnectionFactory connectionFactory) {
|
||||
final RabbitTemplate template = new RabbitTemplate(connectionFactory);
|
||||
template.setMessageConverter(new Jackson2JsonMessageConverter());
|
||||
template.setReceiveTimeout(TimeUnit.SECONDS.toMillis(5));
|
||||
return template;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue replyToQueue() {
|
||||
return new Queue(REPLY_TO_QUEUE, false, false, true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FanoutExchange replyToExchange() {
|
||||
return new FanoutExchange(REPLY_TO_EXCHANGE, false, true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Binding bindQueueToReplyToExchange() {
|
||||
return BindingBuilder.bind(replyToQueue()).to(replyToExchange());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DeadletterListener deadletterListener() {
|
||||
return new DeadletterListener();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ReplyToListener replyToListener() {
|
||||
return new ReplyToListener();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ConnectionFactory rabbitConnectionFactory(final RabbitMqSetupService rabbitmqSetupService)
|
||||
throws AlivenessException {
|
||||
final CachingConnectionFactory factory = new CachingConnectionFactory();
|
||||
factory.setHost(rabbitmqSetupService.getHostname());
|
||||
factory.setPort(5672);
|
||||
factory.setUsername(rabbitmqSetupService.getUsername());
|
||||
factory.setPassword(rabbitmqSetupService.getPassword());
|
||||
try {
|
||||
factory.setVirtualHost(rabbitmqSetupService.createVirtualHost());
|
||||
} catch (final Exception e) {
|
||||
Throwables.propagateIfInstanceOf(e, AlivenessException.class);
|
||||
LOG.error("Cannot create virtual host {}", e.getMessage());
|
||||
}
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RabbitMqSetupService rabbitmqSetupService(RabbitProperties properties) {
|
||||
return new RabbitMqSetupService(properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BrokerRunning brokerRunning(RabbitMqSetupService rabbitmqSetupService) {
|
||||
final BrokerRunning brokerRunning = BrokerRunning.isRunning();
|
||||
brokerRunning.setHostName(rabbitmqSetupService.getHostname());
|
||||
brokerRunning.getConnectionFactory().setUsername(rabbitmqSetupService.getUsername());
|
||||
brokerRunning.getConnectionFactory().setPassword(rabbitmqSetupService.getPassword());
|
||||
return brokerRunning;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.rabbitmq.http.client.Client;
|
||||
import com.rabbitmq.http.client.domain.UserPermissions;
|
||||
|
||||
/**
|
||||
* Creates and deletes a new virtual host if the rabbit mq management api is
|
||||
* available.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class RabbitMqSetupService {
|
||||
|
||||
private static Client rabbitmqHttpClient;
|
||||
private String virtualHost;
|
||||
|
||||
private final String hostname;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
public RabbitMqSetupService(RabbitProperties properties) {
|
||||
hostname = properties.getHost();
|
||||
username = properties.getUsername();
|
||||
if (StringUtils.isEmpty(username)) {
|
||||
username = "guest";
|
||||
}
|
||||
|
||||
password = properties.getPassword();
|
||||
if (StringUtils.isEmpty(password)) {
|
||||
password = "guest";
|
||||
}
|
||||
}
|
||||
|
||||
private Client getRabbitmqHttpClient() {
|
||||
if (rabbitmqHttpClient == null) {
|
||||
try {
|
||||
rabbitmqHttpClient = new Client("http://" + getHostname() + ":15672/api/", getUsername(),
|
||||
getPassword());
|
||||
} catch (MalformedURLException | URISyntaxException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
return rabbitmqHttpClient;
|
||||
}
|
||||
|
||||
String createVirtualHost() throws JsonProcessingException, AlivenessException {
|
||||
if (!getRabbitmqHttpClient().alivenessTest("/")) {
|
||||
throw new AlivenessException(getHostname());
|
||||
|
||||
}
|
||||
virtualHost = UUID.randomUUID().toString();
|
||||
getRabbitmqHttpClient().createVhost(virtualHost);
|
||||
getRabbitmqHttpClient().updatePermissions(virtualHost, getUsername(), createUserPermissionsFullAccess());
|
||||
return virtualHost;
|
||||
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
void deleteVirtualHost() {
|
||||
if (StringUtils.isEmpty(virtualHost)) {
|
||||
return;
|
||||
}
|
||||
getRabbitmqHttpClient().deleteVhost(virtualHost);
|
||||
}
|
||||
|
||||
public String getHostname() {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
private UserPermissions createUserPermissionsFullAccess() {
|
||||
final UserPermissions permissions = new UserPermissions();
|
||||
permissions.setVhost(virtualHost);
|
||||
permissions.setRead(".*");
|
||||
permissions.setConfigure(".*");
|
||||
permissions.setWrite(".*");
|
||||
return permissions;
|
||||
}
|
||||
|
||||
static class AlivenessException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public AlivenessException(String hostname) {
|
||||
super("Aliveness test failed for " + hostname
|
||||
+ ":15672 guest/quest; rabbit mq management api not available");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -277,10 +277,10 @@ public class AmqpMessageHandlerServiceTest {
|
||||
@Test
|
||||
@Description("Tests the update of an action of a target without a exist action id")
|
||||
public void updateActionStatusWithoutActionId() {
|
||||
when(controllerManagementMock.findActionWithDetails(any())).thenReturn(Optional.empty());
|
||||
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
|
||||
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
|
||||
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
|
||||
actionUpdateStatus.setActionStatus(ActionStatus.DOWNLOAD);
|
||||
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(1L, ActionStatus.DOWNLOAD);
|
||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(actionUpdateStatus,
|
||||
messageProperties);
|
||||
|
||||
@@ -442,10 +442,8 @@ public class AmqpMessageHandlerServiceTest {
|
||||
}
|
||||
|
||||
private ActionUpdateStatus createActionUpdateStatus(final ActionStatus status, final Long id) {
|
||||
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
|
||||
actionUpdateStatus.setActionId(id);
|
||||
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(id, status);
|
||||
actionUpdateStatus.setSoftwareModuleId(Long.valueOf(2));
|
||||
actionUpdateStatus.setActionStatus(status);
|
||||
return actionUpdateStatus;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,14 @@
|
||||
package org.eclipse.hawkbit.amqp;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.dmf.json.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.dmf.json.model.ActionUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -24,6 +26,7 @@ import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||
import org.springframework.amqp.support.converter.MessageConversionException;
|
||||
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
@@ -49,57 +52,68 @@ public class BaseAmqpServiceTest {
|
||||
@Test
|
||||
@Description("Verify that the message conversion works")
|
||||
public void convertMessageTest() {
|
||||
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
|
||||
actionUpdateStatus.setActionId(1L);
|
||||
actionUpdateStatus.setSoftwareModuleId(2L);
|
||||
actionUpdateStatus.addMessage("Message 1");
|
||||
actionUpdateStatus.addMessage("Message 2");
|
||||
final ActionUpdateStatus actionUpdateStatus = createActionStatus();
|
||||
|
||||
final Message message = rabbitTemplate.getMessageConverter().toMessage(actionUpdateStatus,
|
||||
new MessageProperties());
|
||||
ActionUpdateStatus convertedActionUpdateStatus = baseAmqpService.convertMessage(message,
|
||||
createJsonProperties());
|
||||
final ActionUpdateStatus convertedActionUpdateStatus = baseAmqpService.convertMessage(message,
|
||||
ActionUpdateStatus.class);
|
||||
|
||||
assertThat(convertedActionUpdateStatus).as("Converted Action Status is wrong")
|
||||
.isEqualToComparingFieldByField(actionUpdateStatus);
|
||||
assertThat(convertedActionUpdateStatus).isEqualToComparingFieldByField(actionUpdateStatus);
|
||||
|
||||
convertedActionUpdateStatus = baseAmqpService.convertMessage(null, ActionUpdateStatus.class);
|
||||
assertThat(convertedActionUpdateStatus).as("Converted Object should be null when message is null").isNull();
|
||||
|
||||
convertedActionUpdateStatus = baseAmqpService.convertMessage(new Message(null, new MessageProperties()),
|
||||
ActionUpdateStatus.class);
|
||||
assertThat(convertedActionUpdateStatus).as("Converted Object should be null when message body is null")
|
||||
.isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that a conversion of a list from a message works")
|
||||
public void convertMessageListTest() {
|
||||
final List<ActionUpdateStatus> actionUpdateStatusList = new ArrayList<>();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus();
|
||||
actionUpdateStatus.setActionId(Long.valueOf(i));
|
||||
actionUpdateStatus.setSoftwareModuleId(Long.valueOf(i));
|
||||
actionUpdateStatusList.add(actionUpdateStatus);
|
||||
@Description("Tests invalid null message content")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void convertMessageWithNullContent() {
|
||||
try {
|
||||
baseAmqpService.convertMessage(new Message(null, createJsonProperties()), ActionUpdateStatus.class);
|
||||
fail("Expected MessageConversionException for inavlid JSON");
|
||||
} catch (final MessageConversionException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
final Message message = rabbitTemplate.getMessageConverter().toMessage(actionUpdateStatusList,
|
||||
new MessageProperties());
|
||||
List<ActionUpdateStatus> convertedActionUpdateStatus = baseAmqpService.convertMessageList(message,
|
||||
ActionUpdateStatus.class);
|
||||
}
|
||||
|
||||
assertThat(convertedActionUpdateStatus).as("Converted Action Status list is wrong")
|
||||
.hasSameClassAs(actionUpdateStatusList);
|
||||
assertThat(convertedActionUpdateStatus).as("Converted Action Status list is wrong")
|
||||
.hasSameSizeAs(actionUpdateStatusList);
|
||||
@Test
|
||||
@Description("Tests invalid empty message content")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void updateActionStatusWithEmptyContent() {
|
||||
try {
|
||||
baseAmqpService.convertMessage(new Message("".getBytes(), createJsonProperties()),
|
||||
ActionUpdateStatus.class);
|
||||
fail("Expected MessageConversionException for inavlid JSON");
|
||||
} catch (final MessageConversionException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
convertedActionUpdateStatus = baseAmqpService.convertMessageList(null, ActionUpdateStatus.class);
|
||||
assertThat(convertedActionUpdateStatus).as("Converted list should be empty when message is null").isEmpty();
|
||||
private MessageProperties createJsonProperties() {
|
||||
final MessageProperties messageProperties = new MessageProperties();
|
||||
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
|
||||
return messageProperties;
|
||||
}
|
||||
|
||||
convertedActionUpdateStatus = baseAmqpService.convertMessageList(new Message(null, new MessageProperties()),
|
||||
ActionUpdateStatus.class);
|
||||
assertThat(convertedActionUpdateStatus).as("Converted list should be empty when message body is null")
|
||||
.isEmpty();
|
||||
@Test
|
||||
@Description("Tests invalid json message content")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void updateActionStatusWithInvalidJsonContent() {
|
||||
try {
|
||||
baseAmqpService.convertMessage(new Message("Invalid Json".getBytes(), createJsonProperties()),
|
||||
ActionUpdateStatus.class);
|
||||
fail("Expected MessageConversionException for inavlid JSON");
|
||||
} catch (final MessageConversionException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
private ActionUpdateStatus createActionStatus() {
|
||||
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(1L, ActionStatus.RUNNING);
|
||||
actionUpdateStatus.setSoftwareModuleId(2L);
|
||||
actionUpdateStatus.addMessage("Message 1");
|
||||
actionUpdateStatus.addMessage("Message 2");
|
||||
return actionUpdateStatus;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.integration;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import org.eclipse.hawkbit.AmqpTestConfiguration;
|
||||
import org.eclipse.hawkbit.amqp.DmfApiConfiguration;
|
||||
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
|
||||
import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.rabbit.junit.BrokerRunning;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
||||
|
||||
import com.jayway.awaitility.Awaitility;
|
||||
import com.jayway.awaitility.core.ConditionFactory;
|
||||
|
||||
@SpringApplicationConfiguration(classes = { RepositoryApplicationConfiguration.class, AmqpTestConfiguration.class,
|
||||
DmfApiConfiguration.class })
|
||||
// Dirty context is necessary to create a new vhost and recreate all necessary
|
||||
// beans after every test class.
|
||||
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
|
||||
public abstract class AbstractAmqpIntegrationTest extends AbstractIntegrationTest {
|
||||
|
||||
@Rule
|
||||
@Autowired
|
||||
public BrokerRunning brokerRunning;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("dmfClient")
|
||||
private RabbitTemplate dmfClient;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
dmfClient.setExchange(getExchange());
|
||||
}
|
||||
|
||||
protected abstract String getExchange();
|
||||
|
||||
protected RabbitTemplate getDmfClient() {
|
||||
return dmfClient;
|
||||
}
|
||||
|
||||
protected ConditionFactory createConditionFactory() {
|
||||
return Awaitility.await().atMost(2, SECONDS);
|
||||
}
|
||||
|
||||
protected Message createMessage(final Object payload, final MessageProperties messageProperties) {
|
||||
if (payload == null) {
|
||||
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
|
||||
return new Message(null, messageProperties);
|
||||
}
|
||||
return getDmfClient().getMessageConverter().toMessage(payload, messageProperties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,443 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.integration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DownloadResponse;
|
||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
|
||||
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
|
||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.context.annotation.Description;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
import ru.yandex.qatools.allure.annotations.Stories;
|
||||
|
||||
@Features("Component Tests - Device Management Federation API")
|
||||
@Stories("Amqp Authentication Message Handler")
|
||||
public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmqpIntegrationTest {
|
||||
|
||||
private static final String TARGET_SECRUITY_TOKEN = "12345";
|
||||
private static final String TARGET_TOKEN_HEADER = "TargetToken " + TARGET_SECRUITY_TOKEN;
|
||||
private static final String TENANT_EXIST = "DEFAULT";
|
||||
private static final String TARGET = "NewDmfTarget";
|
||||
|
||||
@Before
|
||||
public void testSetup() {
|
||||
enableTargetTokenAuthentification();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests wrong content type. This message is invalid and should not be requeued. Additional the receive message is null")
|
||||
public void wrongContentType() {
|
||||
final Message createAndSendMessage = getDmfClient()
|
||||
.sendAndReceive(new Message("".getBytes(), new MessageProperties()));
|
||||
assertThat(createAndSendMessage).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests a null message. This message is invalid and should not be requeued. Additional the receive message is null")
|
||||
public void securityTokenIsNull() {
|
||||
final Message createAndSendMessage = sendAndReceiveAuthenticationMessage(null);
|
||||
assertThat(createAndSendMessage).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tenant in the message is null. This message is invalid and should not be requeued. Additional the receive message is null")
|
||||
public void securityTokenTenantIsNull() {
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(null, TARGET,
|
||||
FileResource.createFileResourceBySha1(TARGET_SECRUITY_TOKEN));
|
||||
final Message createAndSendMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
assertThat(createAndSendMessage).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Target in the message is null.This message is invalid and should not requeued. Additional the receive message is null")
|
||||
public void securityTokenFileResourceIsNull() {
|
||||
enableAnonymousAuthentification();
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, null);
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyResult(returnMessage, HttpStatus.NOT_FOUND, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that login fails if the given credential not match.")
|
||||
public void loginFailedBadCredentials() {
|
||||
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED,
|
||||
false);
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
|
||||
FileResource.createFileResourceBySha1(TARGET_SECRUITY_TOKEN));
|
||||
final Message createAndSendMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
|
||||
verifyResult(createAndSendMessage, HttpStatus.FORBIDDEN, "Login failed");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that the receive message contains a 404 code,if the artifact could not found")
|
||||
public void fileResourceGetSha1InSecurityTokenIsNull() {
|
||||
enableAnonymousAuthentification();
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
|
||||
FileResource.createFileResourceBySha1(null));
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
|
||||
"Artifact for resource FileResource [sha1=null, artifactId=null, filename=null]not found ");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that the receive message contains a 404 code , if the distributionSet is not assigned to the target")
|
||||
public void artifactForFileResourceSHA1FoundByTargetIdTargetExistsButIsNotAssigned() {
|
||||
final Target target = createTarget(TARGET);
|
||||
|
||||
final DistributionSet distributionSet = createDistributionSet();
|
||||
final List<Artifact> artifacts = createArtifacts(distributionSet);
|
||||
final String sha1Hash = artifacts.get(0).getSha1Hash();
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(target.getTenant(), target.getId(), null,
|
||||
FileResource.createFileResourceBySha1(sha1Hash));
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
|
||||
"Artifact for resource FileResource [sha1=" + sha1Hash + ", artifactId=null, filename=null]not found ");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that the receive message contains a 404 code, if there is no artifact for the given sha1")
|
||||
public void artifactForFileResourceSHA1NotFound() {
|
||||
enableAnonymousAuthentification();
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
|
||||
FileResource.createFileResourceBySha1(TARGET_SECRUITY_TOKEN));
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
|
||||
"Artifact for resource FileResource [sha1=12345, artifactId=null, filename=null]not found ");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that the receive message contains a 404 code, if there is no existing target for the given controller id")
|
||||
public void artifactForFileResourceSHA1FoundTargetNotExists() {
|
||||
enableAnonymousAuthentification();
|
||||
final DistributionSet distributionSet = createDistributionSet();
|
||||
final List<Artifact> artifacts = createArtifacts(distributionSet);
|
||||
final String sha1Hash = artifacts.get(0).getSha1Hash();
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
|
||||
FileResource.createFileResourceBySha1(sha1Hash));
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
|
||||
"Artifact for resource FileResource [sha1=" + sha1Hash + ", artifactId=null, filename=null]not found ");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that the receive message contains a 404 code, if there is no existing artifact for the target")
|
||||
public void artifactForFileResourceSHA1FoundTargetExistsButNotAssigned() {
|
||||
createTarget(TARGET);
|
||||
|
||||
final DistributionSet distributionSet = createDistributionSet();
|
||||
final List<Artifact> artifacts = createArtifacts(distributionSet);
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
|
||||
FileResource.createFileResourceBySha1(artifacts.get(0).getSha1Hash()));
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
|
||||
"Artifact for resource FileResource [sha1=2f532b93ed23b4341a81dc9b1ee8a1c44b5526ab, artifactId=null, filename=null]not found ");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that the receive message contains a 200 code and a artifact for the existing controller id ")
|
||||
public void artifactForFileResourceSHA1FoundTargetExistsIsAssigned() {
|
||||
createTarget(TARGET);
|
||||
|
||||
final DistributionSet distributionSet = createDistributionSet();
|
||||
final List<Artifact> artifacts = createArtifacts(distributionSet);
|
||||
final Artifact artifact = artifacts.get(0);
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
|
||||
FileResource.createFileResourceBySha1(artifact.getSha1Hash()));
|
||||
|
||||
deploymentManagement.assignDistributionSet(distributionSet.getId(),
|
||||
Arrays.asList(new TargetWithActionType(TARGET)));
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyOkResult(returnMessage, artifact);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that the receive message contains a 200 code and a artifact for the existing target id ")
|
||||
public void artifactForFileResourceSHA1FoundByTargetIdTargetExistsIsAssigned() {
|
||||
final Target target = createTarget(TARGET);
|
||||
|
||||
final DistributionSet distributionSet = createDistributionSet();
|
||||
final List<Artifact> artifacts = createArtifacts(distributionSet);
|
||||
final Artifact artifact = artifacts.get(0);
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, target.getId(), null,
|
||||
FileResource.createFileResourceBySha1(artifact.getSha1Hash()));
|
||||
|
||||
deploymentManagement.assignDistributionSet(distributionSet.getId(),
|
||||
Arrays.asList(new TargetWithActionType(TARGET)));
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyOkResult(returnMessage, artifact);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that the receive message contains a 200 code and a artifact without a controller id (anonymous enabled)")
|
||||
public void anonymousAuthentification() {
|
||||
enableAnonymousAuthentification();
|
||||
final DistributionSet distributionSet = createDistributionSet();
|
||||
final List<Artifact> artifacts = createArtifacts(distributionSet);
|
||||
final Artifact artifact = artifacts.get(0);
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, null, null,
|
||||
FileResource.createFileResourceBySha1(artifact.getSha1Hash()));
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyOkResult(returnMessage, artifact);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Artifact is downloaded anonymous as there is not target id or controller id assigned to the target.")
|
||||
public void targetTokenAuthentification() {
|
||||
createTarget(TARGET);
|
||||
|
||||
final DistributionSet distributionSet = createDistributionSet();
|
||||
final List<Artifact> artifacts = createArtifacts(distributionSet);
|
||||
final Artifact artifact = artifacts.get(0);
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
|
||||
FileResource.createFileResourceBySha1(artifact.getSha1Hash()));
|
||||
|
||||
deploymentManagement.assignDistributionSet(distributionSet.getId(),
|
||||
Arrays.asList(new TargetWithActionType(TARGET)));
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyOkResult(returnMessage, artifact);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that the receive message contains a 404, if there is no artifact to the given filename")
|
||||
public void artifactForFileResourceFileNameNotFound() {
|
||||
enableAnonymousAuthentification();
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
|
||||
FileResource.createFileResourceByFilename("Test.txt"));
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
|
||||
"Artifact for resource FileResource [sha1=null, artifactId=null, filename=Test.txt]not found ");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that the receive message contains a 404, if there is no distribution set assigned to the target")
|
||||
public void artifactForFileResourceFileNameFoundTargetExistsButNotAssigned() {
|
||||
createTarget(TARGET);
|
||||
|
||||
final DistributionSet distributionSet = createDistributionSet();
|
||||
final List<Artifact> artifacts = createArtifacts(distributionSet);
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
|
||||
FileResource.createFileResourceByFilename(artifacts.get(0).getFilename()));
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
|
||||
"Artifact for resource FileResource [sha1=null, artifactId=null, filename=filename0]not found ");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that the receive message contains a 404, if there is no exisiting target")
|
||||
public void artifactForFileResourceArtifactIdFoundTargetNotExists() {
|
||||
enableAnonymousAuthentification();
|
||||
final DistributionSet distributionSet = createDistributionSet();
|
||||
final List<Artifact> artifacts = createArtifacts(distributionSet);
|
||||
final FileResource fileResource = FileResource.createFileResourceByArtifactId(artifacts.get(0).getId());
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, fileResource);
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyResult(returnMessage, HttpStatus.NOT_FOUND, "Artifact for resource FileResource [sha1=null, artifactId="
|
||||
+ artifacts.get(0).getId() + ", filename=null]not found ");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that the receive message contains a 200 and a artifact for the given artifact id")
|
||||
public void artifactForFileResourceArtifactIdFoundTargetExistsIsAssigned() {
|
||||
createTarget(TARGET);
|
||||
|
||||
final DistributionSet distributionSet = createDistributionSet();
|
||||
final List<Artifact> artifacts = createArtifacts(distributionSet);
|
||||
final Artifact artifact = artifacts.get(0);
|
||||
final FileResource fileResource = FileResource.createFileResourceByArtifactId(artifact.getId());
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, fileResource);
|
||||
|
||||
deploymentManagement.assignDistributionSet(distributionSet.getId(),
|
||||
Arrays.asList(new TargetWithActionType(TARGET)));
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyOkResult(returnMessage, artifact);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that the receive message contains a 404, if there is no artifact to the given softwareModuleFilename")
|
||||
public void artifactForFileResourceSoftwareModuleFilenameNotFound() {
|
||||
enableAnonymousAuthentification();
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
|
||||
FileResource.softwareModuleFilename(1L, "Test.txt"));
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
|
||||
"Artifact for resource FileResource [sha1=null, artifactId=null, filename=null]not found ");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that the receive message contains a 404, if there is no existing target for the file resource")
|
||||
public void artifactForFileResourceSoftwareModuleFilenameFoundTargetNotExists() {
|
||||
enableAnonymousAuthentification();
|
||||
final DistributionSet distributionSet = createDistributionSet();
|
||||
final List<Artifact> artifacts = createArtifacts(distributionSet);
|
||||
|
||||
final SoftwareModule softwareModule = distributionSet.getModules().stream().findFirst().get();
|
||||
final Artifact artifact = findArtifactOfSoftwareModule(artifacts, softwareModule);
|
||||
|
||||
final FileResource fileResource = FileResource.softwareModuleFilename(softwareModule.getId(),
|
||||
softwareModule.getArtifact(artifact.getId()).get().getFilename());
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, fileResource);
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
|
||||
"Artifact for resource FileResource [sha1=null, artifactId=null, filename=null]not found ");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that the receive message contains a 200 and a artifact, if there is a existing artifct fpt the for the given softwareModuleFilename")
|
||||
public void artifactForFileResourceSoftwareModuleFilenameFoundTargetExistsIsAssigned() {
|
||||
createTarget(TARGET);
|
||||
|
||||
final DistributionSet distributionSet = createDistributionSet();
|
||||
final List<Artifact> artifacts = createArtifacts(distributionSet);
|
||||
|
||||
final SoftwareModule softwareModule = distributionSet.getModules().stream().findFirst().get();
|
||||
final Artifact artifact = findArtifactOfSoftwareModule(artifacts, softwareModule);
|
||||
|
||||
final FileResource fileResource = FileResource.softwareModuleFilename(softwareModule.getId(),
|
||||
softwareModule.getArtifact(artifact.getId()).get().getFilename());
|
||||
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, fileResource);
|
||||
|
||||
deploymentManagement.assignDistributionSet(distributionSet.getId(),
|
||||
Arrays.asList(new TargetWithActionType(TARGET)));
|
||||
|
||||
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
|
||||
verifyOkResult(returnMessage, artifact);
|
||||
|
||||
}
|
||||
|
||||
private void verifyOkResult(Message returnMessage, Artifact artifact) {
|
||||
final DownloadResponse convertedMessage = verifyResult(returnMessage, HttpStatus.OK, null);
|
||||
assertThat(convertedMessage.getDownloadUrl()).isNotNull();
|
||||
assertThat(convertedMessage.getArtifact()).isNotNull();
|
||||
assertThat(convertedMessage.getArtifact().getHashes().getSha1()).isEqualTo(artifact.getSha1Hash());
|
||||
|
||||
}
|
||||
|
||||
private void enableAnonymousAuthentification() {
|
||||
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED,
|
||||
true);
|
||||
tenantConfigurationManagement.addOrUpdateConfiguration(
|
||||
TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED, false);
|
||||
}
|
||||
|
||||
private void enableTargetTokenAuthentification() {
|
||||
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED,
|
||||
false);
|
||||
tenantConfigurationManagement.addOrUpdateConfiguration(
|
||||
TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED, true);
|
||||
}
|
||||
|
||||
private Target createTarget(final String controllerId) {
|
||||
return targetManagement.createTarget(
|
||||
entityFactory.target().create().controllerId(controllerId).securityToken(TARGET_SECRUITY_TOKEN));
|
||||
}
|
||||
|
||||
private TenantSecurityToken createTenantSecurityToken(final String tenant, final String controllerId,
|
||||
final FileResource fileResource) {
|
||||
final TenantSecurityToken tenantSecurityToken = new TenantSecurityToken(tenant, controllerId, fileResource);
|
||||
tenantSecurityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, TARGET_TOKEN_HEADER);
|
||||
return tenantSecurityToken;
|
||||
}
|
||||
|
||||
private TenantSecurityToken createTenantSecurityToken(final String tenant, final Long targetId,
|
||||
final String controllerId, final FileResource fileResource) {
|
||||
final TenantSecurityToken tenantSecurityToken = new TenantSecurityToken(tenant, null, controllerId, targetId,
|
||||
fileResource);
|
||||
tenantSecurityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, TARGET_TOKEN_HEADER);
|
||||
return tenantSecurityToken;
|
||||
}
|
||||
|
||||
private DistributionSet createDistributionSet() {
|
||||
return testdataFactory.createDistributionSet("one");
|
||||
}
|
||||
|
||||
private List<Artifact> createArtifacts(final DistributionSet distributionSet) {
|
||||
final List<Artifact> artifacts = new ArrayList<>();
|
||||
for (final SoftwareModule module : distributionSet.getModules()) {
|
||||
artifacts.addAll(testdataFactory.createArtifacts(module.getId()));
|
||||
}
|
||||
return artifacts;
|
||||
}
|
||||
|
||||
private DownloadResponse verifyResult(final Message returnMessage, final HttpStatus expectedStatus,
|
||||
final String expectedMessage) {
|
||||
final DownloadResponse convertedMessage = (DownloadResponse) getDmfClient().getMessageConverter()
|
||||
.fromMessage(returnMessage);
|
||||
assertThat(convertedMessage.getResponseCode()).isEqualTo(expectedStatus.value());
|
||||
|
||||
if (!StringUtils.isEmpty(expectedMessage)) {
|
||||
assertThat(convertedMessage.getMessage()).isEqualTo(expectedMessage);
|
||||
}
|
||||
return convertedMessage;
|
||||
}
|
||||
|
||||
private Message sendAndReceiveAuthenticationMessage(final TenantSecurityToken securityToken) {
|
||||
return getDmfClient().sendAndReceive(createMessage(securityToken, new MessageProperties()));
|
||||
}
|
||||
|
||||
private Artifact findArtifactOfSoftwareModule(final List<Artifact> artifacts, final SoftwareModule softwareModule) {
|
||||
return artifacts.stream().filter(space -> space.getSoftwareModule().getId().equals(softwareModule.getId()))
|
||||
.findFirst().get();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getExchange() {
|
||||
return AmqpSettings.AUTHENTICATION_EXCHANGE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.integration;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
||||
import org.junit.Test;
|
||||
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
import ru.yandex.qatools.allure.annotations.Stories;
|
||||
|
||||
@Features("Component Tests - Device Management Federation API")
|
||||
@Stories("Amqp Message Dispatcher Service")
|
||||
public class AmqpMessageDispatcherServiceIntegrationTest extends AmqpServiceIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Verify that a distribution assignment send a download and install message.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 2) })
|
||||
public void sendDownloadAndInstallStatus() {
|
||||
registerTargetAndAssignDistributionSet();
|
||||
|
||||
createAndSendTarget(TENANT_EXIST);
|
||||
waitUntilTargetStatusIsPending();
|
||||
assertDownloadAndInstallMessage(getDistributionSet().getModules());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that a distribution assignment multiple times send cancel and assign events with right softwaremodules")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
|
||||
@Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 1),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 3) })
|
||||
public void assignDistributionSetMultipleTimes() {
|
||||
final DistributionSetAssignmentResult assignmentResult = registerTargetAndAssignDistributionSet();
|
||||
|
||||
final DistributionSet distributionSet2 = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
|
||||
registerTargetAndAssignDistributionSet(distributionSet2.getId(), TargetUpdateStatus.PENDING,
|
||||
getDistributionSet().getModules());
|
||||
assertCancelActionMessage(assignmentResult.getActions().get(0));
|
||||
|
||||
createAndSendTarget(TENANT_EXIST);
|
||||
waitUntilTargetStatusIsPending();
|
||||
assertCancelActionMessage(assignmentResult.getActions().get(0));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that a cancel assignment send a cancel message.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
|
||||
@Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 2) })
|
||||
public void sendCancelStatus() {
|
||||
final Long actionId = registerTargetAndCancelActionId();
|
||||
|
||||
createAndSendTarget(TENANT_EXIST);
|
||||
waitUntilTargetStatusIsPending();
|
||||
assertCancelActionMessage(actionId);
|
||||
|
||||
}
|
||||
|
||||
private void waitUntilTargetStatusIsPending() {
|
||||
waitUntil(() -> {
|
||||
final Optional<Target> findTargetByControllerID = targetManagement
|
||||
.findTargetByControllerID(REGISTER_TARGET);
|
||||
return findTargetByControllerID.isPresent() && TargetUpdateStatus.PENDING
|
||||
.equals(findTargetByControllerID.get().getUpdateStatus());
|
||||
});
|
||||
}
|
||||
|
||||
private void waitUntil(final Callable<Boolean> callable) {
|
||||
createConditionFactory().until(() -> {
|
||||
return securityRule.runAsPrivileged(callable);
|
||||
});
|
||||
}
|
||||
|
||||
private void createAndSendTarget(final String tenant) {
|
||||
createAndSendTarget(REGISTER_TARGET, tenant);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,618 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.integration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
|
||||
import org.eclipse.hawkbit.dmf.json.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.dmf.json.model.ActionUpdateStatus;
|
||||
import org.eclipse.hawkbit.dmf.json.model.AttributeUpdate;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
import ru.yandex.qatools.allure.annotations.Stories;
|
||||
|
||||
@Features("Component Tests - Device Management Federation API")
|
||||
@Stories("Amqp Message Handler Service")
|
||||
public class AmqpMessageHandlerServiceIntegrationTest extends AmqpServiceIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Tests register target")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 2),
|
||||
@Expect(type = TargetPollEvent.class, count = 3) })
|
||||
public void registerTargets() {
|
||||
registerAndAssertTargetWithExistingTenant(REGISTER_TARGET, 1);
|
||||
|
||||
final String target2 = "Target2";
|
||||
registerAndAssertTargetWithExistingTenant(target2, 2);
|
||||
final Long pollingTimeTarget2 = controllerManagement.findByControllerId(target2).get().getLastTargetQuery();
|
||||
registerSameTargetAndAssertBasedOnLastPolling(target2, 2, TargetUpdateStatus.REGISTERED, pollingTimeTarget2);
|
||||
Mockito.verifyZeroInteractions(getDeadletterListener());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests register invalid target withy empty controller id. Tests register invalid target with null controller id")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void registerEmptyTarget() {
|
||||
createAndSendTarget("", TENANT_EXIST);
|
||||
assertAllTargetsCount(0);
|
||||
verifyDeadLetterMessages(1);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests register invalid target with whitspace controller id. Tests register invalid target with null controller id")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void registerWhitespaceTarget() {
|
||||
createAndSendTarget("Invalid Invalid", TENANT_EXIST);
|
||||
assertAllTargetsCount(0);
|
||||
verifyDeadLetterMessages(1);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests register invalid target with null controller id. Tests register invalid target with null controller id")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void registerInvalidNullTargets() {
|
||||
createAndSendTarget(null, TENANT_EXIST);
|
||||
assertAllTargetsCount(0);
|
||||
verifyDeadLetterMessages(1);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests not allowed content-type in message. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void wrongContentType() {
|
||||
final Message createTargetMessage = createTargetMessage(REGISTER_TARGET, TENANT_EXIST);
|
||||
createTargetMessage.getMessageProperties().setContentType("WrongContentType");
|
||||
getDmfClient().send(createTargetMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
assertAllTargetsCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests null reply to property in message header. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void missingReplyToProperty() {
|
||||
final Message createTargetMessage = createTargetMessage(REGISTER_TARGET, TENANT_EXIST);
|
||||
createTargetMessage.getMessageProperties().setReplyTo(null);
|
||||
getDmfClient().send(createTargetMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
assertAllTargetsCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests missing reply to property in message header. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void emptyReplyToProperty() {
|
||||
final Message createTargetMessage = createTargetMessage(REGISTER_TARGET, TENANT_EXIST);
|
||||
createTargetMessage.getMessageProperties().setReplyTo("");
|
||||
getDmfClient().send(createTargetMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
assertAllTargetsCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests missing thing id property in message. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void missingThingIdProperty() {
|
||||
final Message createTargetMessage = createTargetMessage(null, TENANT_EXIST);
|
||||
createTargetMessage.getMessageProperties().getHeaders().remove(MessageHeaderKey.THING_ID);
|
||||
getDmfClient().send(createTargetMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
assertAllTargetsCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests null thing id property in message. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void nullThingIdProperty() {
|
||||
final Message createTargetMessage = createTargetMessage(null, TENANT_EXIST);
|
||||
getDmfClient().send(createTargetMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
assertAllTargetsCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests missing tenant message header. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void missingTenantHeader() {
|
||||
final Message createTargetMessage = createTargetMessage(REGISTER_TARGET, TENANT_EXIST);
|
||||
createTargetMessage.getMessageProperties().getHeaders().remove(MessageHeaderKey.TENANT);
|
||||
getDmfClient().send(createTargetMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
assertAllTargetsCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests null tenant message header. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void nullTenantHeader() {
|
||||
final Message createTargetMessage = createTargetMessage(REGISTER_TARGET, null);
|
||||
getDmfClient().send(createTargetMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
assertAllTargetsCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests empty tenant message header. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void emptyTenantHeader() {
|
||||
final Message createTargetMessage = createTargetMessage(REGISTER_TARGET, "");
|
||||
getDmfClient().send(createTargetMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
assertAllTargetsCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests tenant not exist. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void tenantNotExist() {
|
||||
final Message createTargetMessage = createTargetMessage(REGISTER_TARGET, "TenantNotExist");
|
||||
getDmfClient().send(createTargetMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
assertThat(systemManagement.findTenants()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests missing type message header. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void missingTypeHeader() {
|
||||
final Message createTargetMessage = createTargetMessage(null, TENANT_EXIST);
|
||||
createTargetMessage.getMessageProperties().getHeaders().remove(MessageHeaderKey.TYPE);
|
||||
getDmfClient().send(createTargetMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
assertAllTargetsCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests null type message header. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void nullTypeHeader() {
|
||||
final Message createTargetMessage = createTargetMessage(null, TENANT_EXIST);
|
||||
createTargetMessage.getMessageProperties().getHeaders().put(MessageHeaderKey.TYPE, null);
|
||||
getDmfClient().send(createTargetMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
assertAllTargetsCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests empty type message header. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void emptyTypeHeader() {
|
||||
final Message createTargetMessage = createTargetMessage(null, TENANT_EXIST);
|
||||
createTargetMessage.getMessageProperties().getHeaders().put(MessageHeaderKey.TYPE, "");
|
||||
getDmfClient().send(createTargetMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
assertAllTargetsCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests invalid type message header. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void invalidTypeHeader() {
|
||||
final Message createTargetMessage = createTargetMessage(null, TENANT_EXIST);
|
||||
createTargetMessage.getMessageProperties().getHeaders().put(MessageHeaderKey.TYPE, "NotExist");
|
||||
getDmfClient().send(createTargetMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
assertAllTargetsCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests null topic message header. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void nullTopicHeader() {
|
||||
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS, "");
|
||||
eventMessage.getMessageProperties().getHeaders().put(MessageHeaderKey.TOPIC, null);
|
||||
getDmfClient().send(eventMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests null topic message header. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void emptyTopicHeader() {
|
||||
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS, "");
|
||||
eventMessage.getMessageProperties().getHeaders().put(MessageHeaderKey.TOPIC, "");
|
||||
getDmfClient().send(eventMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests null topic message header. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void invalidTopicHeader() {
|
||||
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS, "");
|
||||
eventMessage.getMessageProperties().getHeaders().put(MessageHeaderKey.TOPIC, "NotExist");
|
||||
getDmfClient().send(eventMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests missing topic message header. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void missingTopicHeader() {
|
||||
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS, "");
|
||||
eventMessage.getMessageProperties().getHeaders().remove(MessageHeaderKey.TOPIC);
|
||||
getDmfClient().send(eventMessage);
|
||||
|
||||
verifyDeadLetterMessages(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests invalid null message content. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void updateActionStatusWithNullContent() {
|
||||
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS, null);
|
||||
getDmfClient().send(eventMessage);
|
||||
verifyDeadLetterMessages(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests invalid empty message content. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void updateActionStatusWithEmptyContent() {
|
||||
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS, "");
|
||||
getDmfClient().send(eventMessage);
|
||||
verifyDeadLetterMessages(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests invalid json message content. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void updateActionStatusWithInvalidJsonContent() {
|
||||
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS,
|
||||
"Invalid Content");
|
||||
getDmfClient().send(eventMessage);
|
||||
verifyDeadLetterMessages(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests invalid topic message header. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
public void updateActionStatusWithInvalidActionId() {
|
||||
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(1L, ActionStatus.RUNNING);
|
||||
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS,
|
||||
actionUpdateStatus);
|
||||
getDmfClient().send(eventMessage);
|
||||
verifyDeadLetterMessages(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests register target and cancel a assignment")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1) })
|
||||
public void finishActionStatus() {
|
||||
registerTargetAndSendAndAssertUpdateActionStatus(ActionStatus.FINISHED, Status.FINISHED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Register a target and send a update action status (running). Verfiy if the updated action status is correct.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = ActionUpdatedEvent.class, count = 0), @Expect(type = ActionCreatedEvent.class, count = 1),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
|
||||
public void runningActionStatus() {
|
||||
registerTargetAndSendAndAssertUpdateActionStatus(ActionStatus.RUNNING, Status.RUNNING);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Register a target and send a update action status (download). Verfiy if the updated action status is correct.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
|
||||
public void downloadActionStatus() {
|
||||
registerTargetAndSendAndAssertUpdateActionStatus(ActionStatus.DOWNLOAD, Status.DOWNLOAD);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Register a target and send a update action status (error). Verfiy if the updated action status is correct.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1) })
|
||||
public void errorActionStatus() {
|
||||
registerTargetAndSendAndAssertUpdateActionStatus(ActionStatus.ERROR, Status.ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Register a target and send a update action status (warning). Verfiy if the updated action status is correct.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
|
||||
public void warningActionStatus() {
|
||||
registerTargetAndSendAndAssertUpdateActionStatus(ActionStatus.WARNING, Status.WARNING);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Register a target and send a update action status (retrieved). Verfiy if the updated action status is correct.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
|
||||
public void retrievedActionStatus() {
|
||||
registerTargetAndSendAndAssertUpdateActionStatus(ActionStatus.RETRIEVED, Status.RETRIEVED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Register a target and send a invalid update action status (cancel). This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
|
||||
public void cancelNotAllowActionStatus() {
|
||||
registerTargetAndSendActionStatus(ActionStatus.CANCELED);
|
||||
verifyDeadLetterMessages(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verfiy receiving a download and install message if a deployment is done before the target has polled the first time.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
|
||||
public void receiveDownLoadAndInstallMessageAfterAssignment() {
|
||||
|
||||
// setup
|
||||
controllerManagement.findOrRegisterTargetIfItDoesNotexist(REGISTER_TARGET, null);
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
|
||||
assignDistributionSet(distributionSet.getId(), REGISTER_TARGET);
|
||||
|
||||
// test
|
||||
registerAndAssertTargetWithExistingTenant(REGISTER_TARGET, 1, TargetUpdateStatus.PENDING, "bumlux");
|
||||
|
||||
// verify
|
||||
assertDownloadAndInstallMessage(distributionSet.getModules());
|
||||
Mockito.verifyZeroInteractions(getDeadletterListener());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verfiy receiving a cancel update message if a deployment is canceled before the target has polled the first time.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
|
||||
@Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1),
|
||||
@Expect(type = TargetPollEvent.class, count = 1) })
|
||||
public void receiveCancelUpdateMessageAfterAssignmentWasCanceled() {
|
||||
|
||||
// Setup
|
||||
controllerManagement.findOrRegisterTargetIfItDoesNotexist(REGISTER_TARGET, null);
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
|
||||
final DistributionSetAssignmentResult distributionSetAssignmentResult = assignDistributionSet(
|
||||
distributionSet.getId(), REGISTER_TARGET);
|
||||
deploymentManagement.cancelAction(distributionSetAssignmentResult.getActions().get(0));
|
||||
|
||||
// test
|
||||
registerAndAssertTargetWithExistingTenant(REGISTER_TARGET, 1, TargetUpdateStatus.PENDING, "bumlux");
|
||||
|
||||
// verify
|
||||
assertCancelActionMessage(distributionSetAssignmentResult.getActions().get(0));
|
||||
Mockito.verifyZeroInteractions(getDeadletterListener());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Register a target and send a invalid update action status (canceled). The current status (pending) is not a canceling state. This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
|
||||
public void actionNotExists() {
|
||||
final Long actionId = registerTargetAndCancelActionId();
|
||||
final Long actionNotExist = actionId + 1;
|
||||
|
||||
sendActionUpdateStatus(new ActionUpdateStatus(actionNotExist, ActionStatus.CANCELED));
|
||||
verifyDeadLetterMessages(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Register a target and send a invalid update action status (cancel_rejected). This message should forwarded to the deadletter queue")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = ActionCreatedEvent.class, count = 1),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
|
||||
public void canceledRejectedNotAllowActionStatus() {
|
||||
registerTargetAndSendActionStatus(ActionStatus.CANCEL_REJECTED);
|
||||
verifyDeadLetterMessages(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Register a target and send a valid update action status (cancel_rejected). Verfiy if the updated action status is correct.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
|
||||
@Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
|
||||
public void canceledRejectedActionStatus() {
|
||||
final Long actionId = registerTargetAndCancelActionId();
|
||||
|
||||
sendActionUpdateStatus(new ActionUpdateStatus(actionId, ActionStatus.CANCEL_REJECTED));
|
||||
assertAction(actionId, Status.RUNNING, Status.CANCELING, Status.CANCEL_REJECTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that sending an update controller attribute message to an existing target works.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
|
||||
public void updateAttributes() {
|
||||
|
||||
// setup
|
||||
final String target = "ControllerAttributeTestTarget";
|
||||
registerAndAssertTargetWithExistingTenant(target, 1);
|
||||
final AttributeUpdate controllerAttribute = new AttributeUpdate();
|
||||
controllerAttribute.getAttributes().put("test1", "testA");
|
||||
controllerAttribute.getAttributes().put("test2", "testB");
|
||||
|
||||
// test
|
||||
sendUpdateAttributeMessage(target, TENANT_EXIST, controllerAttribute);
|
||||
|
||||
// validate
|
||||
assertUpdateAttributes(target, controllerAttribute.getAttributes());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that sending an update controller attribute message with no thingid header to an existing target does not work.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 0), @Expect(type = TargetPollEvent.class, count = 1) })
|
||||
public void updateAttributesWithNoThingId() {
|
||||
|
||||
// setup
|
||||
final String target = "ControllerAttributeTestTarget";
|
||||
registerAndAssertTargetWithExistingTenant(target, 1);
|
||||
final AttributeUpdate controllerAttribute = new AttributeUpdate();
|
||||
controllerAttribute.getAttributes().put("test1", "testA");
|
||||
controllerAttribute.getAttributes().put("test2", "testB");
|
||||
final Message createUpdateAttributesMessage = createUpdateAttributesMessage(null, TENANT_EXIST,
|
||||
controllerAttribute);
|
||||
createUpdateAttributesMessage.getMessageProperties().getHeaders().remove(MessageHeaderKey.THING_ID);
|
||||
|
||||
// test
|
||||
getDmfClient().send(createUpdateAttributesMessage);
|
||||
|
||||
// verify
|
||||
verifyDeadLetterMessages(1);
|
||||
final AttributeUpdate controllerAttributeEmpty = new AttributeUpdate();
|
||||
assertUpdateAttributes(target, controllerAttributeEmpty.getAttributes());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that sending an update controller attribute message with invalid body to an existing target does not work.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 0), @Expect(type = TargetPollEvent.class, count = 1) })
|
||||
public void updateAttributesWithWrongBody() {
|
||||
|
||||
// setup
|
||||
final String target = "ControllerAttributeTestTarget";
|
||||
registerAndAssertTargetWithExistingTenant(target, 1);
|
||||
final AttributeUpdate controllerAttribute = new AttributeUpdate();
|
||||
controllerAttribute.getAttributes().put("test1", "testA");
|
||||
controllerAttribute.getAttributes().put("test2", "testB");
|
||||
final Message createUpdateAttributesMessageWrongBody = createUpdateAttributesMessageWrongBody(target,
|
||||
TENANT_EXIST);
|
||||
|
||||
// test
|
||||
getDmfClient().send(createUpdateAttributesMessageWrongBody);
|
||||
|
||||
// verify
|
||||
verifyDeadLetterMessages(1);
|
||||
}
|
||||
|
||||
private Long registerTargetAndSendActionStatus(final ActionStatus sendActionStatus) {
|
||||
final DistributionSetAssignmentResult assignmentResult = registerTargetAndAssignDistributionSet();
|
||||
final Long actionId = assignmentResult.getActions().get(0);
|
||||
sendActionUpdateStatus(new ActionUpdateStatus(actionId, sendActionStatus));
|
||||
return actionId;
|
||||
}
|
||||
|
||||
private void sendActionUpdateStatus(final ActionUpdateStatus actionStatus) {
|
||||
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS, actionStatus);
|
||||
getDmfClient().send(eventMessage);
|
||||
}
|
||||
|
||||
private void registerTargetAndSendAndAssertUpdateActionStatus(final ActionStatus sendActionStatus,
|
||||
final Status expectedActionStatus) {
|
||||
final Long actionId = registerTargetAndSendActionStatus(sendActionStatus);
|
||||
assertAction(actionId, Status.RUNNING, expectedActionStatus);
|
||||
}
|
||||
|
||||
private void assertAction(final Long actionId, final Status... expectedActionStates) {
|
||||
final Action action = waitUntilIsPresent(() -> controllerManagement.findActionWithDetails(actionId));
|
||||
final List<Status> status = action.getActionStatus().stream().map(actionStatus -> actionStatus.getStatus())
|
||||
.collect(Collectors.toList());
|
||||
assertThat(status).containsOnly(expectedActionStates);
|
||||
}
|
||||
|
||||
private Message createEventMessage(final String tenant, final EventTopic eventTopic, final Object payload) {
|
||||
final MessageProperties messageProperties = createMessagePropertiesWithTenant(tenant);
|
||||
messageProperties.getHeaders().put(MessageHeaderKey.TYPE, MessageType.EVENT.toString());
|
||||
messageProperties.getHeaders().put(MessageHeaderKey.TOPIC, eventTopic.toString());
|
||||
|
||||
return createMessage(payload, messageProperties);
|
||||
}
|
||||
|
||||
private void sendUpdateAttributeMessage(final String target, final String tenant,
|
||||
final AttributeUpdate attributeUpdate) {
|
||||
final Message updateMessage = createUpdateAttributesMessage(target, tenant, attributeUpdate);
|
||||
getDmfClient().send(updateMessage);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.integration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.eclipse.hawkbit.AmqpTestConfiguration;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
|
||||
import org.eclipse.hawkbit.dmf.json.model.AttributeUpdate;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DownloadAndUpdateRequest;
|
||||
import org.eclipse.hawkbit.integration.listener.DeadletterListener;
|
||||
import org.eclipse.hawkbit.integration.listener.ReplyToListener;
|
||||
import org.eclipse.hawkbit.matcher.SoftwareModuleJsonMatcher;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.util.IpUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.test.RabbitListenerTestHarness;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
*
|
||||
* Common class for {@link AmqpMessageHandlerServiceIntegrationTest} and
|
||||
* {@link AmqpMessageDispatcherServiceIntegrationTest}.
|
||||
*/
|
||||
public abstract class AmqpServiceIntegrationTest extends AbstractAmqpIntegrationTest {
|
||||
|
||||
protected static final String TENANT_EXIST = "DEFAULT";
|
||||
protected static final String REGISTER_TARGET = "NewDmfTarget";
|
||||
protected static final String CREATED_BY = "CONTROLLER_PLUG_AND_PLAY";
|
||||
|
||||
private DeadletterListener deadletterListener;
|
||||
private ReplyToListener replyToListener;
|
||||
private DistributionSet distributionSet;
|
||||
|
||||
@Autowired
|
||||
private RabbitListenerTestHarness harness;
|
||||
|
||||
@Autowired
|
||||
private ConnectionFactory connectionFactory;
|
||||
|
||||
@Before
|
||||
public void initListener() {
|
||||
deadletterListener = harness.getSpy(DeadletterListener.LISTENER_ID);
|
||||
assertThat(deadletterListener).isNotNull();
|
||||
Mockito.reset(deadletterListener);
|
||||
replyToListener = harness.getSpy(ReplyToListener.LISTENER_ID);
|
||||
assertThat(replyToListener).isNotNull();
|
||||
Mockito.reset(replyToListener);
|
||||
getDmfClient().setExchange(AmqpSettings.DMF_EXCHANGE);
|
||||
}
|
||||
|
||||
protected <T> T waitUntilIsPresent(final Callable<Optional<T>> callable) {
|
||||
createConditionFactory().until(() -> {
|
||||
return securityRule.runAsPrivileged(() -> callable.call().isPresent());
|
||||
});
|
||||
|
||||
try {
|
||||
return callable.call().get();
|
||||
} catch (final Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected void verifyDeadLetterMessages(final int expectedMessages) {
|
||||
createConditionFactory().until(() -> {
|
||||
Mockito.verify(getDeadletterListener(), Mockito.times(expectedMessages)).handleMessage(Mockito.any());
|
||||
});
|
||||
}
|
||||
|
||||
protected DeadletterListener getDeadletterListener() {
|
||||
return deadletterListener;
|
||||
}
|
||||
|
||||
protected DistributionSet getDistributionSet() {
|
||||
return distributionSet;
|
||||
}
|
||||
|
||||
protected DistributionSetAssignmentResult registerTargetAndAssignDistributionSet() {
|
||||
distributionSet = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
|
||||
return registerTargetAndAssignDistributionSet(distributionSet.getId(), TargetUpdateStatus.REGISTERED,
|
||||
distributionSet.getModules());
|
||||
}
|
||||
|
||||
protected DistributionSetAssignmentResult registerTargetAndAssignDistributionSet(final Long assignDs,
|
||||
final TargetUpdateStatus expectedStatus,
|
||||
final Set<org.eclipse.hawkbit.repository.model.SoftwareModule> expectedSoftwareModulesInMessage) {
|
||||
registerAndAssertTargetWithExistingTenant(REGISTER_TARGET, 1, expectedStatus, CREATED_BY);
|
||||
|
||||
final DistributionSetAssignmentResult assignmentResult = assignDistributionSet(assignDs, REGISTER_TARGET);
|
||||
assertDownloadAndInstallMessage(expectedSoftwareModulesInMessage);
|
||||
return assignmentResult;
|
||||
}
|
||||
|
||||
protected void assertCancelActionMessage(final Long actionId) {
|
||||
final Message replyMessage = assertReplyMessageHeader(EventTopic.CANCEL_DOWNLOAD);
|
||||
|
||||
final Long actionUpdateStatus = (Long) getDmfClient().getMessageConverter().fromMessage(replyMessage);
|
||||
assertThat(actionUpdateStatus).isEqualTo(actionId);
|
||||
}
|
||||
|
||||
protected void assertDownloadAndInstallMessage(
|
||||
final Set<org.eclipse.hawkbit.repository.model.SoftwareModule> dsModules) {
|
||||
final Message replyMessage = assertReplyMessageHeader(EventTopic.DOWNLOAD_AND_INSTALL);
|
||||
assertAllTargetsCount(1);
|
||||
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = (DownloadAndUpdateRequest) getDmfClient()
|
||||
.getMessageConverter().fromMessage(replyMessage);
|
||||
|
||||
Assert.assertThat(dsModules,
|
||||
SoftwareModuleJsonMatcher.containsExactly(downloadAndUpdateRequest.getSoftwareModules()));
|
||||
|
||||
final Target updatedTarget = waitUntilIsPresent(
|
||||
() -> targetManagement.findTargetByControllerID(REGISTER_TARGET));
|
||||
|
||||
assertThat(updatedTarget.getSecurityToken()).isEqualTo(downloadAndUpdateRequest.getTargetSecurityToken());
|
||||
}
|
||||
|
||||
protected void createAndSendTarget(final String target, final String tenant) {
|
||||
final Message message = createTargetMessage(target, tenant);
|
||||
getDmfClient().send(message);
|
||||
}
|
||||
|
||||
protected void verifyReplyToListener() {
|
||||
createConditionFactory().until(() -> {
|
||||
Mockito.verify(replyToListener, Mockito.atLeast(1)).handleMessage(Mockito.any());
|
||||
});
|
||||
}
|
||||
|
||||
protected Long cancelAction(final Long actionId) {
|
||||
deploymentManagement.cancelAction(actionId);
|
||||
assertCancelActionMessage(actionId);
|
||||
return actionId;
|
||||
}
|
||||
|
||||
protected Long registerTargetAndCancelActionId() {
|
||||
final DistributionSetAssignmentResult assignmentResult = registerTargetAndAssignDistributionSet();
|
||||
return cancelAction(assignmentResult.getActions().get(0));
|
||||
}
|
||||
|
||||
protected void assertAllTargetsCount(final long expectedTargetsCount) {
|
||||
assertThat(targetManagement.countTargetsAll()).isEqualTo(expectedTargetsCount);
|
||||
}
|
||||
|
||||
private Message assertReplyMessageHeader(final EventTopic eventTopic) {
|
||||
verifyReplyToListener();
|
||||
final Message replyMessage = replyToListener.getMessages().get(eventTopic);
|
||||
assertAllTargetsCount(1);
|
||||
final Map<String, Object> headers = replyMessage.getMessageProperties().getHeaders();
|
||||
assertThat(headers.get(MessageHeaderKey.TOPIC)).isEqualTo(eventTopic.toString());
|
||||
assertThat(headers.get(MessageHeaderKey.THING_ID)).isEqualTo(REGISTER_TARGET);
|
||||
assertThat(headers.get(MessageHeaderKey.TENANT)).isEqualTo(TENANT_EXIST);
|
||||
assertThat(headers.get(MessageHeaderKey.TYPE)).isEqualTo(MessageType.EVENT.toString());
|
||||
return replyMessage;
|
||||
}
|
||||
|
||||
protected void registerAndAssertTargetWithExistingTenant(final String target,
|
||||
final int existingTargetsAfterCreation) {
|
||||
|
||||
registerAndAssertTargetWithExistingTenant(target, existingTargetsAfterCreation, TargetUpdateStatus.REGISTERED,
|
||||
CREATED_BY);
|
||||
|
||||
}
|
||||
|
||||
protected void registerAndAssertTargetWithExistingTenant(final String target,
|
||||
final int existingTargetsAfterCreation, final TargetUpdateStatus expectedTargetStatus,
|
||||
final String createdBy) {
|
||||
createAndSendTarget(target, TENANT_EXIST);
|
||||
final Target registerdTarget = waitUntilIsPresent(() -> targetManagement.findTargetByControllerID(target));
|
||||
assertAllTargetsCount(existingTargetsAfterCreation);
|
||||
assertTarget(registerdTarget, expectedTargetStatus, createdBy);
|
||||
}
|
||||
|
||||
protected void registerSameTargetAndAssertBasedOnLastPolling(final String target,
|
||||
final int existingTargetsAfterCreation, final TargetUpdateStatus expectedTargetStatus,
|
||||
final Long pollingTimeTargetOld) {
|
||||
createAndSendTarget(target, TENANT_EXIST);
|
||||
final Target registerdTarget = waitUntilIsPresent(
|
||||
() -> findTargetBasedOnNewPolltime(target, pollingTimeTargetOld));
|
||||
assertAllTargetsCount(existingTargetsAfterCreation);
|
||||
assertThat(registerdTarget.getUpdateStatus()).isEqualTo(expectedTargetStatus);
|
||||
}
|
||||
|
||||
private Optional<Target> findTargetBasedOnNewPolltime(final String controllerId, final Long pollingTimeTargetOld) {
|
||||
final Optional<Target> target2 = controllerManagement.findByControllerId(controllerId);
|
||||
final Long pollingTimeTargetNew = target2.get().getLastTargetQuery();
|
||||
if (pollingTimeTargetOld < pollingTimeTargetNew) {
|
||||
return target2;
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private void assertTarget(final Target target, final TargetUpdateStatus updateStatus, final String createdBy) {
|
||||
assertThat(target.getTenant()).isEqualTo(TENANT_EXIST);
|
||||
assertThat(target.getDescription()).contains("Plug and Play");
|
||||
assertThat(target.getDescription()).contains(target.getControllerId());
|
||||
assertThat(target.getCreatedBy()).isEqualTo(createdBy);
|
||||
assertThat(target.getUpdateStatus()).isEqualTo(updateStatus);
|
||||
assertThat(target.getAddress()).isEqualTo(
|
||||
IpUtil.createAmqpUri(connectionFactory.getVirtualHost(), AmqpTestConfiguration.REPLY_TO_EXCHANGE));
|
||||
}
|
||||
|
||||
protected Message createTargetMessage(final String target, final String tenant) {
|
||||
final MessageProperties messageProperties = createMessagePropertiesWithTenant(tenant);
|
||||
messageProperties.getHeaders().put(MessageHeaderKey.THING_ID, target);
|
||||
messageProperties.getHeaders().put(MessageHeaderKey.TYPE, MessageType.THING_CREATED.toString());
|
||||
messageProperties.setReplyTo(AmqpTestConfiguration.REPLY_TO_EXCHANGE);
|
||||
|
||||
return createMessage("", messageProperties);
|
||||
}
|
||||
|
||||
protected MessageProperties createMessagePropertiesWithTenant(final String tenant) {
|
||||
final MessageProperties messageProperties = new MessageProperties();
|
||||
messageProperties.getHeaders().put(MessageHeaderKey.TENANT, tenant);
|
||||
return messageProperties;
|
||||
}
|
||||
|
||||
protected Message createUpdateAttributesMessage(final String target, final String tenant,
|
||||
final AttributeUpdate attributeUpdate) {
|
||||
final MessageProperties messageProperties = createMessagePropertiesWithTenant(tenant);
|
||||
messageProperties.getHeaders().put(MessageHeaderKey.THING_ID, target);
|
||||
messageProperties.getHeaders().put(MessageHeaderKey.TYPE, MessageType.EVENT.toString());
|
||||
messageProperties.getHeaders().put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ATTRIBUTES.toString());
|
||||
|
||||
return createMessage(attributeUpdate, messageProperties);
|
||||
|
||||
}
|
||||
|
||||
protected Message createUpdateAttributesMessageWrongBody(final String target, final String tenant) {
|
||||
final MessageProperties messageProperties = createMessagePropertiesWithTenant(tenant);
|
||||
messageProperties.getHeaders().put(MessageHeaderKey.THING_ID, target);
|
||||
messageProperties.getHeaders().put(MessageHeaderKey.TYPE, MessageType.EVENT.toString());
|
||||
messageProperties.getHeaders().put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ATTRIBUTES.toString());
|
||||
|
||||
return createMessage("wrongbody", messageProperties);
|
||||
|
||||
}
|
||||
|
||||
protected void assertUpdateAttributes(final String controllerId, final Map<String, String> attributes) {
|
||||
final Target findByControllerId = waitUntilIsPresent(
|
||||
() -> controllerManagement.findByControllerId(controllerId));
|
||||
final Map<String, String> controllerAttributes = targetManagement
|
||||
.getControllerAttributes(findByControllerId.getControllerId());
|
||||
assertThat(controllerAttributes.size()).isEqualTo(attributes.size());
|
||||
attributes.forEach((k, v) -> assertKeyValueInMap(k, v, controllerAttributes));
|
||||
}
|
||||
|
||||
private void assertKeyValueInMap(final String key, final String value,
|
||||
final Map<String, String> controllerAttributes) {
|
||||
assertThat(controllerAttributes.containsKey(key)).isTrue();
|
||||
assertThat(controllerAttributes.get(key)).isEqualTo(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getExchange() {
|
||||
return AmqpSettings.DMF_EXCHANGE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.integration.listener;
|
||||
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
|
||||
public class DeadletterListener implements TestRabbitListener {
|
||||
|
||||
public static final String LISTENER_ID = "deadletter";
|
||||
|
||||
@Override
|
||||
@RabbitListener(id = "deadletter", queues = "dmf_connector_deadletter_ttl")
|
||||
public void handleMessage(Message message) {
|
||||
// currently the message is not needed
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.integration.listener;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.hawkbit.AmqpTestConfiguration;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
|
||||
public class ReplyToListener implements TestRabbitListener {
|
||||
|
||||
public static final String LISTENER_ID = "replyto";
|
||||
|
||||
private final Map<EventTopic, Message> messages = new HashMap<>();
|
||||
|
||||
@Override
|
||||
@RabbitListener(id = LISTENER_ID, queues = AmqpTestConfiguration.REPLY_TO_QUEUE)
|
||||
public void handleMessage(Message message) {
|
||||
final EventTopic eventTopic = EventTopic
|
||||
.valueOf(message.getMessageProperties().getHeaders().get(MessageHeaderKey.TOPIC).toString());
|
||||
messages.put(eventTopic, message);
|
||||
}
|
||||
|
||||
public Map<EventTopic, Message> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.integration.listener;
|
||||
|
||||
import org.springframework.amqp.core.Message;
|
||||
|
||||
public interface TestRabbitListener {
|
||||
|
||||
void handleMessage(Message message);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.matcher;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Factory;
|
||||
|
||||
/**
|
||||
* Set matcher for {@link SoftwareModule} and a list of
|
||||
* {@link org.eclipse.hawkbit.dmf.json.model.SoftwareModule}.
|
||||
*/
|
||||
public final class SoftwareModuleJsonMatcher {
|
||||
|
||||
/**
|
||||
* Creates a matcher that matches when the list of repository software
|
||||
* modules arelogically equal to the specified JSON software modules.
|
||||
* <p>
|
||||
* If the specified repository software modules are <code>null</code> then
|
||||
* the created matcher will only match if the JSON software modules are
|
||||
* <code>null</code>
|
||||
* <p>
|
||||
* For example:
|
||||
*
|
||||
* <pre>
|
||||
* List<SoftwareModule> modules;
|
||||
* List<org.eclipse.hawkbit.dmf.json.model.SoftwareModule> expectedModules;
|
||||
*
|
||||
* assertThat(modules, containsExactly(expectedModules));
|
||||
* </pre>
|
||||
*
|
||||
* @param expectedModules
|
||||
* the json sofware modules.
|
||||
*/
|
||||
@Factory
|
||||
public static SoftwareModulesMatcher containsExactly(
|
||||
final List<org.eclipse.hawkbit.dmf.json.model.SoftwareModule> expectedModules) {
|
||||
return new SoftwareModulesMatcher(expectedModules);
|
||||
}
|
||||
|
||||
private static class SoftwareModulesMatcher extends BaseMatcher<Set<SoftwareModule>> {
|
||||
|
||||
private final List<org.eclipse.hawkbit.dmf.json.model.SoftwareModule> expectedModules;
|
||||
|
||||
public SoftwareModulesMatcher(List<org.eclipse.hawkbit.dmf.json.model.SoftwareModule> expectedModules) {
|
||||
this.expectedModules = expectedModules;
|
||||
}
|
||||
|
||||
static boolean containsExactly(Object actual,
|
||||
List<org.eclipse.hawkbit.dmf.json.model.SoftwareModule> expected) {
|
||||
if (actual == null) {
|
||||
return expected == null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final Collection<SoftwareModule> modules = (Collection<SoftwareModule>) actual;
|
||||
|
||||
if (modules.size() != expected.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (final SoftwareModule repoSoftwareModule : modules) {
|
||||
boolean containsElement = false;
|
||||
|
||||
for (final org.eclipse.hawkbit.dmf.json.model.SoftwareModule jsonSoftwareModule : expected) {
|
||||
if (!jsonSoftwareModule.getModuleId().equals(repoSoftwareModule.getId())) {
|
||||
continue;
|
||||
}
|
||||
containsElement = true;
|
||||
|
||||
if (!jsonSoftwareModule.getModuleType().equals(repoSoftwareModule.getType().getKey())) {
|
||||
return false;
|
||||
}
|
||||
if (!jsonSoftwareModule.getModuleVersion().equals(repoSoftwareModule.getVersion())) {
|
||||
return false;
|
||||
}
|
||||
if (jsonSoftwareModule.getArtifacts().size() != repoSoftwareModule.getArtifacts().size()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!containsElement) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object actualValue) {
|
||||
return containsExactly(actualValue, expectedModules);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendValue(expectedModules);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,9 @@
|
||||
# http://www.eclipse.org/legal/epl-v10.html
|
||||
#
|
||||
|
||||
# RabbitMQ
|
||||
spring.rabbitmq.host=localhost
|
||||
|
||||
# supported: H2, MYSQL
|
||||
hawkbit.server.database=H2
|
||||
|
||||
@@ -33,4 +36,32 @@ H2.spring.datasource.password=sa
|
||||
MYSQL.spring.datasource.url=jdbc:mysql://localhost:3306/sp_test
|
||||
MYSQL.spring.datasource.driverClassName=org.mariadb.jdbc.Driver
|
||||
MYSQL.spring.datasource.username=root
|
||||
MYSQL.spring.datasource.password=
|
||||
MYSQL.spring.datasource.password=
|
||||
|
||||
# Default tenant configuration properties
|
||||
hawkbit.server.tenant.configuration.authentication-header-enabled.keyName=authentication.header.enabled
|
||||
hawkbit.server.tenant.configuration.authentication-header-enabled.defaultValue=false
|
||||
hawkbit.server.tenant.configuration.authentication-header-enabled.dataType=java.lang.Boolean
|
||||
hawkbit.server.tenant.configuration.authentication-header-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
|
||||
|
||||
hawkbit.server.tenant.configuration.authentication-header-authority.keyName=authentication.header.authority
|
||||
hawkbit.server.tenant.configuration.authentication-header-authority.defaultValue=false
|
||||
|
||||
hawkbit.server.tenant.configuration.authentication-targettoken-enabled.keyName=authentication.targettoken.enabled
|
||||
hawkbit.server.tenant.configuration.authentication-targettoken-enabled.defaultValue=false
|
||||
hawkbit.server.tenant.configuration.authentication-targettoken-enabled.dataType=java.lang.Boolean
|
||||
hawkbit.server.tenant.configuration.authentication-targettoken-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
|
||||
|
||||
hawkbit.server.tenant.configuration.authentication-gatewaytoken-enabled.keyName=authentication.gatewaytoken.enabled
|
||||
hawkbit.server.tenant.configuration.authentication-gatewaytoken-enabled.defaultValue=false
|
||||
hawkbit.server.tenant.configuration.authentication-gatewaytoken-enabled.dataType=java.lang.Boolean
|
||||
hawkbit.server.tenant.configuration.authentication-gatewaytoken-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
|
||||
|
||||
hawkbit.server.tenant.configuration.authentication-gatewaytoken-key.keyName=authentication.gatewaytoken.key
|
||||
hawkbit.server.tenant.configuration.authentication-gatewaytoken-key.defaultValue=false
|
||||
|
||||
hawkbit.server.tenant.configuration.anonymous-download-enabled.keyName=anonymous.download.enabled
|
||||
hawkbit.server.tenant.configuration.anonymous-download-enabled.defaultValue=false
|
||||
hawkbit.server.tenant.configuration.anonymous-download-enabled.dataType=java.lang.Boolean
|
||||
hawkbit.server.tenant.configuration.anonymous-download-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
|
||||
|
||||
|
||||
Reference in New Issue
Block a user