From bb9768db08f2a31dd73f0f439a5d87f558823924 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Sat, 17 Aug 2019 00:37:37 -0700 Subject: [PATCH 1/5] fix: isolate process-destinatino-location property test --- .../activiti/cloud/runtime/RuntimeBundleApplicationIT.java | 5 ++--- src/test/resources/application.properties | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationIT.java b/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationIT.java index 3a62bbb0..585b7b70 100644 --- a/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationIT.java +++ b/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationIT.java @@ -1,15 +1,14 @@ package org.activiti.cloud.runtime; -import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) -@SpringBootTest(classes = RuntimeBundleApplication.class) +@SpringBootTest(classes = RuntimeBundleApplication.class, + properties = "spring.activiti.process-definition-location-prefix=file:/processes/") @DirtiesContext public class RuntimeBundleApplicationIT { diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index 00466c75..16878725 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -26,4 +26,4 @@ activiti.keycloak.admin-client-app=${ACT_KEYCLOAK_CLIENT_APP:admin-cli} activiti.keycloak.client-user=${ACT_KEYCLOAK_CLIENT_USER:client} activiti.keycloak.client-password=${ACT_KEYCLOAK_CLIENT_PASSWORD:client} -spring.activiti.process-definition-location-prefix=file:${ACT_RB_PROCESSES_PATH:/processes/} + From 69bc9d3be34d4c5486f2c857b7448fb7880996b3 Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Sat, 17 Aug 2019 00:46:07 -0700 Subject: [PATCH 2/5] feat: add Example Connector unit test support template --- pom.xml | 32 +++- .../runtime/RuntimeBundleApplicationTest.java | 153 ++++++++++++++++++ src/test/resources/application.properties | 4 + 3 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationTest.java diff --git a/pom.xml b/pom.xml index 99a5f701..adfe86c6 100755 --- a/pom.xml +++ b/pom.xml @@ -73,17 +73,43 @@ org.springframework.boot - spring-boot-test + spring-boot-starter-test test + - org.springframework - spring-test + org.springframework.cloud + spring-cloud-stream + test-jar + 2.1.0.RELEASE test + test-binder + + org.awaitility + awaitility + test + + + org.apache.maven.plugins + maven-jar-plugin + + + + + **/integration/* + + test-binder + + + test-jar + + + + org.apache.maven.plugins maven-compiler-plugin diff --git a/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationTest.java b/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationTest.java new file mode 100644 index 00000000..af72299a --- /dev/null +++ b/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationTest.java @@ -0,0 +1,153 @@ +package org.activiti.cloud.runtime; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.activiti.cloud.api.process.model.IntegrationRequest; +import org.activiti.cloud.api.process.model.IntegrationResult; +import org.activiti.cloud.api.process.model.impl.IntegrationResultImpl; +import org.activiti.engine.RuntimeService; +import org.activiti.engine.runtime.ProcessInstance; +import org.awaitility.Awaitility; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.annotation.Input; +import org.springframework.cloud.stream.annotation.StreamListener; +import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration; +import org.springframework.cloud.stream.binding.BinderAwareChannelResolver; +import org.springframework.context.annotation.Bean; +import org.springframework.messaging.Message; +import org.springframework.messaging.SubscribableChannel; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +/** + * See: https://cloud.spring.io/spring-cloud-static/spring-cloud-stream/2.1.0.RELEASE/multi/multi__testing.html#spring_integration_test_binder + */ +@SpringBootTest(classes = {RuntimeBundleApplication.class, TestChannelBinderConfiguration.class}) +public class RuntimeBundleApplicationTest { + + private static final String CONNECTOR_PROCESS = "ConnectorProcess"; + private static final String BUSINESS_KEY = "businessKey"; + + @Autowired + private RuntimeService runtimeService; + + private static CountDownLatch exampleConnectorConsumer; + + @Before + public void setUp() { + exampleConnectorConsumer = new CountDownLatch(1); + } + + @Test + public void testExampleConnectorProcess() throws InterruptedException { + // given + String businessKey = BUSINESS_KEY; + + // when + ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder() + .processDefinitionKey(CONNECTOR_PROCESS) + .businessKey(businessKey) + .start(); + // then + assertThat(processInstance).as("Should start process instance") + .isNotNull(); + // and + assertThat(exampleConnectorConsumer.await(1, TimeUnit.SECONDS)) + .as("Should execute cloud connector task") + .isTrue(); + // and + Awaitility.await().untilAsserted(() -> { + assertThat(runtimeService.createProcessInstanceQuery() + .processDefinitionKey(CONNECTOR_PROCESS) + .active() + .list()) + .as("Should complete process") + .isEmpty(); + }); + } + + /** + * Define Connector Mock Channel + */ + public static interface ExampleConnectorChannels { + String IMPLEMENTATION = "exampleConnectorConsumer"; + + @Input(IMPLEMENTATION) + SubscribableChannel input(); + } + + /** + * Define Mock Connector Consumer + */ + @EnableBinding(ExampleConnectorChannels.class) + public static class ExampleConnectorConsumer { + + @Autowired + TestIntegrationResultSender integrationResultSender; + + @StreamListener(ExampleConnectorChannels.IMPLEMENTATION) + public void perfromTask(IntegrationRequest event) throws JsonParseException, JsonMappingException, IOException { + + Map result = Collections.singletonMap("result", + event.getIntegrationContext() + .getBusinessKey()); + event.getIntegrationContext() + .addOutBoundVariables(result); + + IntegrationResult integrationResult = new IntegrationResultImpl(event, + event.getIntegrationContext()); + + integrationResultSender.send(integrationResult, ExampleConnectorChannels.IMPLEMENTATION); + + assertThat(result).containsEntry("result", BUSINESS_KEY); + + exampleConnectorConsumer.countDown(); + } + } + + @TestConfiguration + public static class TestSupportConfig { + + @Bean + public TestIntegrationResultSender testIntegrationResultSender() { + return new TestIntegrationResultSender(); + } + + } + + public static class TestIntegrationResultSender { + + @Autowired + private BinderAwareChannelResolver resolver; + + @Autowired + private ObjectMapper objectMapper; + + public void send(IntegrationResult integrationResult, String targetService) throws JsonProcessingException { + + Message message = MessageBuilder.withPayload(objectMapper.writeValueAsString(integrationResult)) + .setHeader("targetService", targetService) + .build(); + + resolver.resolveDestination("integrationResultsConsumer") + .send(message); + } + } +} \ No newline at end of file diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index 16878725..278a3f4d 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -26,4 +26,8 @@ activiti.keycloak.admin-client-app=${ACT_KEYCLOAK_CLIENT_APP:admin-cli} activiti.keycloak.client-user=${ACT_KEYCLOAK_CLIENT_USER:client} activiti.keycloak.client-password=${ACT_KEYCLOAK_CLIENT_PASSWORD:client} +# Unit test support for Example Connector +spring.cloud.stream.bindings.exampleConnectorConsumer.destination=Example Connector +spring.cloud.stream.bindings.exampleConnectorConsumer.content-type=application/json +spring.cloud.stream.bindings.exampleConnectorConsumer.group=${spring.application.name} From 231c35237a6012502d9b97d8cfa09c524d2c7d2c Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Sat, 17 Aug 2019 01:49:58 -0700 Subject: [PATCH 3/5] fix: correct indentation --- .../activiti/cloud/runtime/RuntimeBundleApplicationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationTest.java b/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationTest.java index af72299a..951e687e 100644 --- a/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationTest.java +++ b/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationTest.java @@ -107,7 +107,7 @@ public void perfromTask(IntegrationRequest event) throws JsonParseException, Jso Map result = Collections.singletonMap("result", event.getIntegrationContext() - .getBusinessKey()); + .getBusinessKey()); event.getIntegrationContext() .addOutBoundVariables(result); From aeb48cb87aaf446d8adc91c21530ca2cfa824d9b Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Mon, 26 Aug 2019 00:16:50 -0700 Subject: [PATCH 4/5] fix: use ProcessRuntime wrapper to start process instance --- .../runtime/RuntimeBundleApplicationTest.java | 134 +++++++++++++++++- 1 file changed, 129 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationTest.java b/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationTest.java index 951e687e..2442aee5 100644 --- a/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationTest.java +++ b/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationTest.java @@ -3,7 +3,10 @@ import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -12,15 +15,22 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; +import org.activiti.api.process.model.ProcessInstance; +import org.activiti.api.process.model.builders.StartProcessPayloadBuilder; +import org.activiti.api.process.model.payloads.StartProcessPayload; +import org.activiti.api.process.runtime.ProcessRuntime; +import org.activiti.api.runtime.shared.security.SecurityManager; import org.activiti.cloud.api.process.model.IntegrationRequest; import org.activiti.cloud.api.process.model.IntegrationResult; import org.activiti.cloud.api.process.model.impl.IntegrationResultImpl; +import org.activiti.core.common.spring.identity.ExtendedInMemoryUserDetailsManager; import org.activiti.engine.RuntimeService; -import org.activiti.engine.runtime.ProcessInstance; import org.awaitility.Awaitility; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; @@ -33,6 +43,14 @@ import org.springframework.messaging.Message; import org.springframework.messaging.SubscribableChannel; import org.springframework.messaging.support.MessageBuilder; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @@ -45,9 +63,15 @@ public class RuntimeBundleApplicationTest { private static final String CONNECTOR_PROCESS = "ConnectorProcess"; private static final String BUSINESS_KEY = "businessKey"; + @Autowired + private ProcessRuntime processRuntime; + @Autowired private RuntimeService runtimeService; + @Autowired + private TestSecurityUtil securityUtil; + private static CountDownLatch exampleConnectorConsumer; @Before @@ -58,13 +82,15 @@ public void setUp() { @Test public void testExampleConnectorProcess() throws InterruptedException { // given + securityUtil.logInAs("user"); + String businessKey = BUSINESS_KEY; + StartProcessPayload payload = new StartProcessPayloadBuilder().withProcessDefinitionKey(CONNECTOR_PROCESS) + .withBusinessKey(businessKey) + .build(); // when - ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder() - .processDefinitionKey(CONNECTOR_PROCESS) - .businessKey(businessKey) - .start(); + ProcessInstance processInstance = processRuntime.start(payload); // then assertThat(processInstance).as("Should start process instance") .isNotNull(); @@ -130,8 +156,106 @@ public TestIntegrationResultSender testIntegrationResultSender() { return new TestIntegrationResultSender(); } + @Bean + public UserDetailsService myUserDetailsService() { + ExtendedInMemoryUserDetailsManager extendedInMemoryUserDetailsManager = new ExtendedInMemoryUserDetailsManager(); + + List userAuthorities = new ArrayList<>(); + userAuthorities.add(new SimpleGrantedAuthority("ROLE_ACTIVITI_USER")); + userAuthorities.add(new SimpleGrantedAuthority("GROUP_activitiTeam")); + + extendedInMemoryUserDetailsManager.createUser(new User("user", + "password", + userAuthorities)); + + + List adminAuthorities = new ArrayList<>(); + adminAuthorities.add(new SimpleGrantedAuthority("ROLE_ACTIVITI_ADMIN")); + + extendedInMemoryUserDetailsManager.createUser(new User("admin", + "password", + adminAuthorities)); + + List garthAuthorities = new ArrayList<>(); + garthAuthorities.add(new SimpleGrantedAuthority("ROLE_ACTIVITI_USER")); + garthAuthorities.add(new SimpleGrantedAuthority("GROUP_doctor")); + + extendedInMemoryUserDetailsManager.createUser(new User("garth", + "password", + garthAuthorities)); + + //dean has role but no group + List deanAuthorities = new ArrayList<>(); + deanAuthorities.add(new SimpleGrantedAuthority("ROLE_ACTIVITI_USER")); + extendedInMemoryUserDetailsManager.createUser(new User("dean", + "password", + deanAuthorities)); + + return extendedInMemoryUserDetailsManager; + } + } + + @TestConfiguration + public static class TestSecurityUtil { + + private Logger logger = LoggerFactory.getLogger(TestSecurityUtil.class); + + @Autowired + private UserDetailsService userDetailsService; + + @Autowired + private SecurityManager securityManager; + + public void logInAs(String username) { + + UserDetails user = userDetailsService.loadUserByUsername(username); + if (user == null) { + throw new IllegalStateException("User " + username + " doesn't exist, please provide a valid user"); + } + logger.info("> Logged in as: " + username); + SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() { + @Override + public Collection getAuthorities() { + return user.getAuthorities(); + } + + @Override + public Object getCredentials() { + return user.getPassword(); + } + + @Override + public Object getDetails() { + return user; + } + + @Override + public Object getPrincipal() { + return user; + } + + @Override + public boolean isAuthenticated() { + return true; + } + + @Override + public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { + + } + + @Override + public String getName() { + return user.getUsername(); + } + })); + org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username); + + assertThat(securityManager.getAuthenticatedUserId()).isEqualTo(username); + } } + public static class TestIntegrationResultSender { @Autowired From 80fbca05a27732a574ac3201fe62a7c4fd9ff70c Mon Sep 17 00:00:00 2001 From: Igor Dianov Date: Mon, 9 Sep 2019 18:59:57 -0700 Subject: [PATCH 5/5] fix: add activiti-cloud-starter-connector test module dependency --- pom.xml | 6 ++ .../runtime/RuntimeBundleApplicationTest.java | 58 +++++-------------- 2 files changed, 22 insertions(+), 42 deletions(-) diff --git a/pom.xml b/pom.xml index adfe86c6..86b2aa7b 100755 --- a/pom.xml +++ b/pom.xml @@ -90,6 +90,12 @@ awaitility test + + org.activiti.cloud.connector + activiti-cloud-starter-connector + test + + diff --git a/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationTest.java b/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationTest.java index 2442aee5..7ecce922 100644 --- a/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationTest.java +++ b/src/test/java/org/activiti/cloud/runtime/RuntimeBundleApplicationTest.java @@ -12,9 +12,7 @@ import java.util.concurrent.TimeUnit; import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectMapper; import org.activiti.api.process.model.ProcessInstance; import org.activiti.api.process.model.builders.StartProcessPayloadBuilder; import org.activiti.api.process.model.payloads.StartProcessPayload; @@ -22,7 +20,9 @@ import org.activiti.api.runtime.shared.security.SecurityManager; import org.activiti.cloud.api.process.model.IntegrationRequest; import org.activiti.cloud.api.process.model.IntegrationResult; -import org.activiti.cloud.api.process.model.impl.IntegrationResultImpl; +import org.activiti.cloud.connectors.starter.channels.IntegrationResultSender; +import org.activiti.cloud.connectors.starter.configuration.ConnectorProperties; +import org.activiti.cloud.connectors.starter.model.IntegrationResultBuilder; import org.activiti.core.common.spring.identity.ExtendedInMemoryUserDetailsManager; import org.activiti.engine.RuntimeService; import org.awaitility.Awaitility; @@ -38,11 +38,9 @@ import org.springframework.cloud.stream.annotation.Input; import org.springframework.cloud.stream.annotation.StreamListener; import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration; -import org.springframework.cloud.stream.binding.BinderAwareChannelResolver; import org.springframework.context.annotation.Bean; import org.springframework.messaging.Message; import org.springframework.messaging.SubscribableChannel; -import org.springframework.messaging.support.MessageBuilder; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; @@ -126,23 +124,24 @@ public static interface ExampleConnectorChannels { public static class ExampleConnectorConsumer { @Autowired - TestIntegrationResultSender integrationResultSender; + IntegrationResultSender integrationResultSender; + + @Autowired + private ConnectorProperties connectorProperties; @StreamListener(ExampleConnectorChannels.IMPLEMENTATION) public void perfromTask(IntegrationRequest event) throws JsonParseException, JsonMappingException, IOException { - Map result = Collections.singletonMap("result", - event.getIntegrationContext() - .getBusinessKey()); - event.getIntegrationContext() - .addOutBoundVariables(result); - - IntegrationResult integrationResult = new IntegrationResultImpl(event, - event.getIntegrationContext()); - - integrationResultSender.send(integrationResult, ExampleConnectorChannels.IMPLEMENTATION); + Map results = Collections.singletonMap("result", + event.getIntegrationContext() + .getBusinessKey()); - assertThat(result).containsEntry("result", BUSINESS_KEY); + Message message = IntegrationResultBuilder.resultFor(event, connectorProperties) + .withOutboundVariables(results) + .buildMessage(); + integrationResultSender.send(message); + + assertThat(results).containsEntry("result", BUSINESS_KEY); exampleConnectorConsumer.countDown(); } @@ -151,11 +150,6 @@ public void perfromTask(IntegrationRequest event) throws JsonParseException, Jso @TestConfiguration public static class TestSupportConfig { - @Bean - public TestIntegrationResultSender testIntegrationResultSender() { - return new TestIntegrationResultSender(); - } - @Bean public UserDetailsService myUserDetailsService() { ExtendedInMemoryUserDetailsManager extendedInMemoryUserDetailsManager = new ExtendedInMemoryUserDetailsManager(); @@ -254,24 +248,4 @@ public String getName() { assertThat(securityManager.getAuthenticatedUserId()).isEqualTo(username); } } - - - public static class TestIntegrationResultSender { - - @Autowired - private BinderAwareChannelResolver resolver; - - @Autowired - private ObjectMapper objectMapper; - - public void send(IntegrationResult integrationResult, String targetService) throws JsonProcessingException { - - Message message = MessageBuilder.withPayload(objectMapper.writeValueAsString(integrationResult)) - .setHeader("targetService", targetService) - .build(); - - resolver.resolveDestination("integrationResultsConsumer") - .send(message); - } - } } \ No newline at end of file