diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set3/pom.xml b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d2a664c1b33c3a7d8fe5485897de17045ec3264d
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/pom.xml
@@ -0,0 +1,74 @@
+
+
+ 4.0.0
+
+
+ org.activiti
+ activiti-spring-conformance-tests
+ 7.0.0-SNAPSHOT
+
+
+ activiti-spring-conformance-set3
+ jar
+ Activiti Spring :: Conformance Set 3
+ Activiti Spring :: Conformance Set 3
+
+
+
+ org.activiti
+ activiti-spring-conformance-util
+
+
+ org.activiti.api
+ activiti-api-process-runtime
+
+
+ org.activiti.api
+ activiti-api-task-runtime
+
+
+ org.activiti.api
+ activiti-api-runtime-shared
+
+
+ org.activiti.api
+ activiti-api-task-model
+
+
+ org.activiti.api
+ activiti-api-process-model
+
+
+ org.activiti.api
+ activiti-api-model-shared
+
+
+ org.activiti
+ activiti-engine
+
+
+ org.activiti
+ activiti-spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.slf4j
+ slf4j-api
+
+
+ com.h2database
+ h2
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/Application.java b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/Application.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea045a9fc71bdeb09541ad0953ebf8da9c9e449d
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/Application.java
@@ -0,0 +1,13 @@
+package org.activiti.spring.conformance.set3;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class);
+ }
+
+}
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/ConformanceBasicProcessRuntimeTest.java b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/ConformanceBasicProcessRuntimeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..222d4347dc73a510369e44fd66d7a4fb3c7d7ace
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/ConformanceBasicProcessRuntimeTest.java
@@ -0,0 +1,85 @@
+package org.activiti.spring.conformance.set3;
+
+import org.activiti.api.process.model.ProcessDefinition;
+import org.activiti.api.process.model.ProcessDefinitionMeta;
+import org.activiti.api.process.runtime.ProcessRuntime;
+import org.activiti.api.process.runtime.conf.ProcessRuntimeConfiguration;
+import org.activiti.api.process.runtime.events.listener.ProcessRuntimeEventListener;
+import org.activiti.api.runtime.shared.events.VariableEventListener;
+import org.activiti.api.runtime.shared.query.Page;
+import org.activiti.api.runtime.shared.query.Pageable;
+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.test.context.junit4.SpringRunner;
+import org.activiti.spring.conformance.util.security.SecurityUtil;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
+public class ConformanceBasicProcessRuntimeTest {
+
+
+ @Autowired
+ private ProcessRuntime processRuntime;
+
+ @Autowired
+ private SecurityUtil securityUtil;
+
+ @Test
+ public void shouldGetConfiguration() {
+ securityUtil.logInAs("user1");
+ //when
+ ProcessRuntimeConfiguration configuration = processRuntime.configuration();
+ //then
+ assertThat(configuration).isNotNull();
+ //when
+ List> processRuntimeEventListeners = configuration.processEventListeners();
+ List> variableEventListeners = configuration.variableEventListeners();
+ //then
+ assertThat(processRuntimeEventListeners).hasSize(10);
+ assertThat(variableEventListeners).hasSize(3);
+
+ }
+
+ @Test
+ public void shouldProcessDefinitions() {
+ securityUtil.logInAs("user1");
+
+ Page processDefinitionPage = processRuntime.processDefinitions(Pageable.of(0, 50));
+
+ List processDefinitions = processDefinitionPage.getContent();
+ assertThat(processDefinitions).extracting(ProcessDefinition::getName).contains(
+ "UserTask Assignee Followed By Group1",
+ "UserTask Candidate Group",
+ "UserTask Candidate Group1 Followed by Group2"
+
+ );
+
+ }
+
+ @Test
+ public void shouldProcessDefinitionsMetaData() {
+ securityUtil.logInAs("user1");
+
+ Page processDefinitionPage = processRuntime.processDefinitions(Pageable.of(0, 50));
+
+ List processDefinitions = processDefinitionPage.getContent();
+ assertThat(processDefinitions).extracting(ProcessDefinition::getName).contains(
+ "UserTask Assignee Followed By Group1",
+ "UserTask Candidate Group",
+ "UserTask Candidate Group1 Followed by Group2"
+
+ );
+
+
+ }
+
+
+
+
+}
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/Set3RuntimeTestConfiguration.java b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/Set3RuntimeTestConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..6266ba4f4c834eff9300237fa87dce935110e6fa
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/Set3RuntimeTestConfiguration.java
@@ -0,0 +1,123 @@
+package org.activiti.spring.conformance.set3;
+
+import org.activiti.api.model.shared.event.RuntimeEvent;
+import org.activiti.api.model.shared.event.VariableCreatedEvent;
+import org.activiti.api.model.shared.event.VariableDeletedEvent;
+import org.activiti.api.model.shared.event.VariableUpdatedEvent;
+import org.activiti.api.process.model.events.BPMNActivityCancelledEvent;
+import org.activiti.api.process.model.events.BPMNActivityCompletedEvent;
+import org.activiti.api.process.model.events.BPMNActivityStartedEvent;
+import org.activiti.api.process.model.events.BPMNSequenceFlowTakenEvent;
+import org.activiti.api.process.runtime.events.*;
+import org.activiti.api.process.runtime.events.listener.BPMNElementEventListener;
+import org.activiti.api.process.runtime.events.listener.ProcessRuntimeEventListener;
+import org.activiti.api.runtime.shared.events.VariableEventListener;
+import org.activiti.api.task.runtime.events.*;
+import org.activiti.api.task.runtime.events.listener.TaskEventListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Configuration
+public class Set3RuntimeTestConfiguration {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(Set3RuntimeTestConfiguration.class);
+
+ public static List collectedEvents = new ArrayList<>();
+
+
+ @Bean
+ public BPMNElementEventListener bpmnActivityStartedListener() {
+ return bpmnActivityStartedEvent -> collectedEvents.add(bpmnActivityStartedEvent);
+ }
+
+ @Bean
+ public BPMNElementEventListener bpmnActivityCompletedListener() {
+ return bpmnActivityCompletedEvent -> collectedEvents.add(bpmnActivityCompletedEvent);
+ }
+
+ @Bean
+ public BPMNElementEventListener bpmnActivityCancelledListener() {
+ return bpmnActivityCancelledEvent -> collectedEvents.add(bpmnActivityCancelledEvent);
+ }
+
+ @Bean
+ public BPMNElementEventListener bpmnSequenceFlowTakenListener() {
+ return bpmnSequenceFlowTakenEvent -> collectedEvents.add(bpmnSequenceFlowTakenEvent);
+ }
+
+ @Bean
+ public ProcessRuntimeEventListener processCreatedListener() {
+ return processCreatedEvent -> collectedEvents.add(processCreatedEvent);
+ }
+
+ @Bean
+ public ProcessRuntimeEventListener processStartedListener() {
+ return processStartedEvent -> collectedEvents.add(processStartedEvent);
+ }
+
+ @Bean
+ public ProcessRuntimeEventListener processCompletedListener() {
+ return processCompletedEvent -> collectedEvents.add(processCompletedEvent);
+ }
+
+ @Bean
+ public ProcessRuntimeEventListener processResumedListener() {
+ return processResumedEvent -> collectedEvents.add(processResumedEvent);
+ }
+
+ @Bean
+ public ProcessRuntimeEventListener processSuspendedListener() {
+ return processSuspendedEvent -> collectedEvents.add(processSuspendedEvent);
+ }
+
+ @Bean
+ public ProcessRuntimeEventListener processCancelledListener() {
+ return processCancelledEvent -> collectedEvents.add(processCancelledEvent);
+ }
+
+ @Bean
+ public VariableEventListener variableCreatedEventListener() {
+ return variableCreatedEvent -> collectedEvents.add(variableCreatedEvent);
+ }
+
+ @Bean
+ public VariableEventListener variableDeletedEventListener() {
+ return variableDeletedEvent -> collectedEvents.add(variableDeletedEvent);
+ }
+
+ @Bean
+ public VariableEventListener variableUpdatedEventListener() {
+ return variableUpdatedEvent -> collectedEvents.add(variableUpdatedEvent);
+ }
+
+ @Bean
+ public TaskEventListener taskCreatedEventListener() {
+ return taskCreatedEvent -> collectedEvents.add(taskCreatedEvent);
+ }
+
+ @Bean
+ public TaskEventListener taskUpdatedEventListener() {
+ return taskUpdatedEvent -> collectedEvents.add(taskUpdatedEvent);
+ }
+
+ @Bean
+ public TaskEventListener taskCompletedEventListener() {
+ return taskCompletedEvent -> collectedEvents.add(taskCompletedEvent);
+ }
+
+ @Bean
+ public TaskEventListener taskSuspendedEventListener() {
+ return taskSuspendedEvent -> collectedEvents.add(taskSuspendedEvent);
+ }
+
+ @Bean
+ public TaskEventListener taskAssignedEventListener() {
+ return taskAssignedEvent -> collectedEvents.add(taskAssignedEvent);
+ }
+
+}
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/UserTaskCandidateGroupAndAssigneeTest.java b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/UserTaskCandidateGroupAndAssigneeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc33944b2353f4f717d151869974b26165039e7a
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/UserTaskCandidateGroupAndAssigneeTest.java
@@ -0,0 +1,170 @@
+package org.activiti.spring.conformance.set3;
+
+import org.activiti.api.model.shared.event.RuntimeEvent;
+import org.activiti.api.process.model.ProcessInstance;
+import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
+import org.activiti.api.process.model.events.BPMNActivityEvent;
+import org.activiti.api.process.model.events.BPMNSequenceFlowTakenEvent;
+import org.activiti.api.process.model.events.ProcessRuntimeEvent;
+import org.activiti.api.process.runtime.ProcessAdminRuntime;
+import org.activiti.api.process.runtime.ProcessRuntime;
+import org.activiti.api.runtime.shared.query.Page;
+import org.activiti.api.runtime.shared.query.Pageable;
+import org.activiti.api.task.model.Task;
+import org.activiti.api.task.model.builders.TaskPayloadBuilder;
+import org.activiti.api.task.model.events.TaskRuntimeEvent;
+import org.activiti.api.task.runtime.TaskRuntime;
+import org.activiti.spring.conformance.util.security.SecurityUtil;
+import org.junit.After;
+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.test.context.junit4.SpringRunner;
+
+import java.util.List;
+
+import static org.activiti.spring.conformance.set3.Set3RuntimeTestConfiguration.collectedEvents;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
+public class UserTaskCandidateGroupAndAssigneeTest {
+
+ private final String processKey = "usertaskas-b5300a4b-8950-4486-ba20-a8d775a3d75d";
+
+ @Autowired
+ private ProcessRuntime processRuntime;
+
+ @Autowired
+ private TaskRuntime taskRuntime;
+
+ @Autowired
+ private SecurityUtil securityUtil;
+
+ @Autowired
+ private ProcessAdminRuntime processAdminRuntime;
+
+ @Before
+ public void cleanUp() {
+ collectedEvents.clear();
+ }
+
+
+ @Test
+ public void shouldCreateAndCompleteATaskAndDontSeeNext() {
+
+ securityUtil.logInAs("user1");
+
+ ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder
+ .start()
+ .withProcessDefinitionKey(processKey)
+ .withBusinessKey("my-business-key")
+ .withProcessInstanceName("my-process-instance-name")
+ .build());
+
+ //then
+ assertThat(processInstance).isNotNull();
+ assertThat(processInstance.getStatus()).isEqualTo(ProcessInstance.ProcessInstanceStatus.RUNNING);
+ assertThat(processInstance.getBusinessKey()).isEqualTo("my-business-key");
+ assertThat(processInstance.getName()).isEqualTo("my-process-instance-name");
+
+ // I should be able to get the process instance from the Runtime because it is still running
+ ProcessInstance processInstanceById = processRuntime.processInstance(processInstance.getId());
+
+ assertThat(processInstanceById).isEqualTo(processInstance);
+
+ // I should get a task for User1
+ Page tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ Task task = tasks.getContent().get(0);
+
+ Task taskById = taskRuntime.task(task.getId());
+
+ assertThat(taskById.getStatus()).isEqualTo(Task.TaskStatus.ASSIGNED);
+
+ assertThat(task).isEqualTo(taskById);
+
+ assertThat(task.getAssignee()).isEqualTo("user1");
+
+
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .containsExactly(
+ ProcessRuntimeEvent.ProcessEvents.PROCESS_CREATED,
+ ProcessRuntimeEvent.ProcessEvents.PROCESS_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_COMPLETED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ TaskRuntimeEvent.TaskEvents.TASK_CREATED,
+ TaskRuntimeEvent.TaskEvents.TASK_ASSIGNED);
+
+
+ collectedEvents.clear();
+
+ taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
+
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .containsExactly(
+ TaskRuntimeEvent.TaskEvents.TASK_COMPLETED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_COMPLETED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ TaskRuntimeEvent.TaskEvents.TASK_CREATED);
+
+ // Check with user1 as he is a candidate
+
+ tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ task = tasks.getContent().get(0);
+
+ taskById = taskRuntime.task(task.getId());
+
+ assertThat(taskById.getStatus()).isEqualTo(Task.TaskStatus.CREATED);
+
+ List candidateUsers = taskRuntime.userCandidates(task.getId());
+
+ assertThat(candidateUsers.size()).isEqualTo(0);
+
+ List candidateGroups = taskRuntime.groupCandidates(task.getId());
+ assertThat(candidateGroups).contains("group1");
+
+ // Check with user2 candidates which is not a candidate
+ securityUtil.logInAs("user2");
+
+ tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(0);
+
+ // Check with user2 candidates which is not a candidate
+ securityUtil.logInAs("user3");
+
+ tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ task = tasks.getContent().get(0);
+
+ taskById = taskRuntime.task(task.getId());
+
+ assertThat(taskById.getStatus()).isEqualTo(Task.TaskStatus.CREATED);
+ }
+
+
+ @After
+ public void cleanup() {
+ securityUtil.logInAs("admin");
+ Page processInstancePage = processAdminRuntime.processInstances(Pageable.of(0, 50));
+ for (ProcessInstance pi : processInstancePage.getContent()) {
+ processAdminRuntime.delete(ProcessPayloadBuilder.delete(pi.getId()));
+ }
+ }
+
+}
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/UserTaskCandidateGroupsTest.java b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/UserTaskCandidateGroupsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b766b34c7b823ed9b60faa662511d9742590e7ca
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/UserTaskCandidateGroupsTest.java
@@ -0,0 +1,176 @@
+package org.activiti.spring.conformance.set3;
+
+import org.activiti.api.model.shared.event.RuntimeEvent;
+import org.activiti.api.process.model.ProcessInstance;
+import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
+import org.activiti.api.process.model.events.BPMNActivityEvent;
+import org.activiti.api.process.model.events.BPMNSequenceFlowTakenEvent;
+import org.activiti.api.process.model.events.ProcessRuntimeEvent;
+import org.activiti.api.process.runtime.ProcessAdminRuntime;
+import org.activiti.api.process.runtime.ProcessRuntime;
+import org.activiti.api.runtime.shared.query.Page;
+import org.activiti.api.runtime.shared.query.Pageable;
+import org.activiti.api.task.model.Task;
+import org.activiti.api.task.model.builders.TaskPayloadBuilder;
+import org.activiti.api.task.model.events.TaskRuntimeEvent;
+import org.activiti.api.task.runtime.TaskRuntime;
+import org.activiti.spring.conformance.util.security.SecurityUtil;
+import org.junit.After;
+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.test.context.junit4.SpringRunner;
+
+import java.util.List;
+
+import static org.activiti.spring.conformance.set3.Set3RuntimeTestConfiguration.collectedEvents;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
+public class UserTaskCandidateGroupsTest {
+
+ private final String processKey = "usertaskgr-1a8cdf77-0981-45d4-8080-7cf1a80c973b";
+
+ @Autowired
+ private ProcessRuntime processRuntime;
+
+ @Autowired
+ private TaskRuntime taskRuntime;
+
+ @Autowired
+ private SecurityUtil securityUtil;
+
+ @Autowired
+ private ProcessAdminRuntime processAdminRuntime;
+
+ @Before
+ public void cleanUp() {
+ collectedEvents.clear();
+ }
+
+
+ @Test
+ public void shouldCreateAndCompleteATaskAndDontSeeNext() {
+
+ securityUtil.logInAs("user1");
+
+ ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder
+ .start()
+ .withProcessDefinitionKey(processKey)
+ .withBusinessKey("my-business-key")
+ .withProcessInstanceName("my-process-instance-name")
+ .build());
+
+ //then
+ assertThat(processInstance).isNotNull();
+ assertThat(processInstance.getStatus()).isEqualTo(ProcessInstance.ProcessInstanceStatus.RUNNING);
+ assertThat(processInstance.getBusinessKey()).isEqualTo("my-business-key");
+ assertThat(processInstance.getName()).isEqualTo("my-process-instance-name");
+
+ // I should be able to get the process instance from the Runtime because it is still running
+ ProcessInstance processInstanceById = processRuntime.processInstance(processInstance.getId());
+
+ assertThat(processInstanceById).isEqualTo(processInstance);
+
+ // I should get a task for User1
+ Page tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ Task task = tasks.getContent().get(0);
+
+ Task taskById = taskRuntime.task(task.getId());
+
+ assertThat(taskById.getStatus()).isEqualTo(Task.TaskStatus.CREATED);
+
+ assertThat(task).isEqualTo(taskById);
+
+ assertThat(task.getAssignee()).isNull();
+
+
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .containsExactly(
+ ProcessRuntimeEvent.ProcessEvents.PROCESS_CREATED,
+ ProcessRuntimeEvent.ProcessEvents.PROCESS_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_COMPLETED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ TaskRuntimeEvent.TaskEvents.TASK_CREATED);
+
+
+ collectedEvents.clear();
+
+ taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());
+
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .containsExactly(
+ TaskRuntimeEvent.TaskEvents.TASK_ASSIGNED,
+ TaskRuntimeEvent.TaskEvents.TASK_UPDATED);
+
+ collectedEvents.clear();
+
+ taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
+
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .containsExactly(
+ TaskRuntimeEvent.TaskEvents.TASK_COMPLETED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_COMPLETED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ TaskRuntimeEvent.TaskEvents.TASK_CREATED);
+
+ // Check with user2 as he is a candidate
+ securityUtil.logInAs("user2");
+
+ tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ task = tasks.getContent().get(0);
+
+ taskById = taskRuntime.task(task.getId());
+
+ assertThat(taskById.getStatus()).isEqualTo(Task.TaskStatus.CREATED);
+
+ List candidateUsers = taskRuntime.userCandidates(task.getId());
+
+ assertThat(candidateUsers.size()).isEqualTo(0);
+
+ List candidateGroups = taskRuntime.groupCandidates(task.getId());
+ assertThat(candidateGroups).contains("group2");
+
+ // Check with user1 candidates which is not a candidate
+ securityUtil.logInAs("user1");
+
+ tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(0);
+
+ // Check with user3 candidates which is a candidate
+ securityUtil.logInAs("user3");
+
+ tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+
+ }
+
+
+ @After
+ public void cleanup() {
+ securityUtil.logInAs("admin");
+ Page processInstancePage = processAdminRuntime.processInstances(Pageable.of(0, 50));
+ for (ProcessInstance pi : processInstancePage.getContent()) {
+ processAdminRuntime.delete(ProcessPayloadBuilder.delete(pi.getId()));
+ }
+ }
+
+}
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/UserTaskCandidateVisibilityTest.java b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/UserTaskCandidateVisibilityTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..dd22ab70eef66095f664f26df81c34fb2027b20e
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/java/org/activiti/spring/conformance/set3/UserTaskCandidateVisibilityTest.java
@@ -0,0 +1,313 @@
+package org.activiti.spring.conformance.set3;
+
+import org.activiti.api.model.shared.event.RuntimeEvent;
+import org.activiti.api.process.model.ProcessInstance;
+import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
+import org.activiti.api.process.model.events.BPMNActivityEvent;
+import org.activiti.api.process.model.events.BPMNSequenceFlowTakenEvent;
+import org.activiti.api.process.model.events.ProcessRuntimeEvent;
+import org.activiti.api.process.runtime.ProcessAdminRuntime;
+import org.activiti.api.process.runtime.ProcessRuntime;
+import org.activiti.api.runtime.shared.NotFoundException;
+import org.activiti.api.runtime.shared.query.Page;
+import org.activiti.api.runtime.shared.query.Pageable;
+import org.activiti.api.task.model.Task;
+import org.activiti.api.task.model.builders.TaskPayloadBuilder;
+import org.activiti.api.task.model.events.TaskRuntimeEvent;
+import org.activiti.api.task.runtime.TaskRuntime;
+import org.activiti.spring.conformance.util.security.SecurityUtil;
+import org.junit.After;
+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.test.context.junit4.SpringRunner;
+
+import java.util.List;
+
+import static org.activiti.spring.conformance.set3.Set3RuntimeTestConfiguration.collectedEvents;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.catchThrowable;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
+public class UserTaskCandidateVisibilityTest {
+
+ private final String processKey = "usertaskca-1e577517-7404-4645-b650-4fbde528f612";
+
+ @Autowired
+ private ProcessRuntime processRuntime;
+
+ @Autowired
+ private TaskRuntime taskRuntime;
+
+ @Autowired
+ private SecurityUtil securityUtil;
+
+ @Autowired
+ private ProcessAdminRuntime processAdminRuntime;
+
+ @Before
+ public void cleanUp() {
+ collectedEvents.clear();
+ }
+
+
+ @Test
+ public void shouldCreateATaskAndAddNewCandidateUser() {
+
+ securityUtil.logInAs("user1");
+
+ ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder
+ .start()
+ .withProcessDefinitionKey(processKey)
+ .withBusinessKey("my-business-key")
+ .withProcessInstanceName("my-process-instance-name")
+ .build());
+
+ //then
+ assertThat(processInstance).isNotNull();
+ assertThat(processInstance.getStatus()).isEqualTo(ProcessInstance.ProcessInstanceStatus.RUNNING);
+ assertThat(processInstance.getBusinessKey()).isEqualTo("my-business-key");
+ assertThat(processInstance.getName()).isEqualTo("my-process-instance-name");
+
+ // I should be able to get the process instance from the Runtime because it is still running
+ ProcessInstance processInstanceById = processRuntime.processInstance(processInstance.getId());
+
+ assertThat(processInstanceById).isEqualTo(processInstance);
+
+ // I should get a task for User1
+ Page tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ final Task task = tasks.getContent().get(0);
+
+ Task taskById = taskRuntime.task(task.getId());
+
+ assertThat(taskById.getStatus()).isEqualTo(Task.TaskStatus.CREATED);
+
+
+ assertThat(task).isEqualTo(taskById);
+
+ assertThat(task.getAssignee()).isNull();
+
+
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .containsExactly(
+ ProcessRuntimeEvent.ProcessEvents.PROCESS_CREATED,
+ ProcessRuntimeEvent.ProcessEvents.PROCESS_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_COMPLETED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ TaskRuntimeEvent.TaskEvents.TASK_CREATED);
+
+ collectedEvents.clear();
+
+ // Check with user2
+ securityUtil.logInAs("user2");
+
+ tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(0);
+
+ Throwable throwable = catchThrowable(() -> taskRuntime.task(task.getId()));
+
+ assertThat(throwable)
+ .isInstanceOf(NotFoundException.class);
+
+ // Check with user1 candidates
+ securityUtil.logInAs("user1");
+
+ taskById = taskRuntime.task(task.getId());
+
+ List candidateUsers = taskRuntime.userCandidates(task.getId());
+ assertThat(candidateUsers).isEmpty();
+
+ List candidateGroups = taskRuntime.groupCandidates(task.getId());
+ assertThat(candidateGroups).contains("group1");
+
+ // This should fail because user1 is not the assignee
+ throwable = catchThrowable(() -> taskRuntime.addCandidateUsers(TaskPayloadBuilder
+ .addCandidateUsers()
+ .withTaskId(task.getId())
+ .withCandidateUser("user2")
+ .build()));
+
+ assertThat(throwable)
+ .isInstanceOf(IllegalStateException.class);
+
+ taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());
+
+ // Now it should work
+ taskRuntime.addCandidateUsers(TaskPayloadBuilder
+ .addCandidateUsers()
+ .withTaskId(task.getId())
+ .withCandidateUser("user2")
+ .build());
+
+ candidateUsers = taskRuntime.userCandidates(task.getId());
+ assertThat(candidateUsers).contains("user2");
+
+ // User 1 needs to release the task in order for User 2 see it as candidate
+
+ taskRuntime.release(TaskPayloadBuilder.release().withTaskId(task.getId()).build());
+
+ // Check with user2
+ securityUtil.logInAs("user2");
+
+ tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ }
+
+
+ @Test
+ public void shouldCreateATaskAndAddNewCandidateGroup() {
+
+ securityUtil.logInAs("user1");
+
+ ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder
+ .start()
+ .withProcessDefinitionKey(processKey)
+ .withBusinessKey("my-business-key")
+ .withProcessInstanceName("my-process-instance-name")
+ .build());
+
+ //then
+ assertThat(processInstance).isNotNull();
+ assertThat(processInstance.getStatus()).isEqualTo(ProcessInstance.ProcessInstanceStatus.RUNNING);
+ assertThat(processInstance.getBusinessKey()).isEqualTo("my-business-key");
+ assertThat(processInstance.getName()).isEqualTo("my-process-instance-name");
+
+ // I should be able to get the process instance from the Runtime because it is still running
+ ProcessInstance processInstanceById = processRuntime.processInstance(processInstance.getId());
+
+ assertThat(processInstanceById).isEqualTo(processInstance);
+
+ // I should get a task for User1
+ Page tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ final Task task = tasks.getContent().get(0);
+
+ Task taskById = taskRuntime.task(task.getId());
+
+ assertThat(taskById.getStatus()).isEqualTo(Task.TaskStatus.CREATED);
+
+
+ assertThat(task).isEqualTo(taskById);
+
+ assertThat(task.getAssignee()).isNull();
+
+
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .containsExactly(
+ ProcessRuntimeEvent.ProcessEvents.PROCESS_CREATED,
+ ProcessRuntimeEvent.ProcessEvents.PROCESS_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_COMPLETED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ TaskRuntimeEvent.TaskEvents.TASK_CREATED);
+
+ collectedEvents.clear();
+
+ // Check with user2
+ securityUtil.logInAs("user2");
+
+ tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(0);
+
+ Throwable throwable = catchThrowable(() -> taskRuntime.task(task.getId()));
+
+ assertThat(throwable)
+ .isInstanceOf(NotFoundException.class);
+
+ // Check with user1 candidates
+ securityUtil.logInAs("user1");
+
+ taskById = taskRuntime.task(task.getId());
+
+ List candidateUsers = taskRuntime.userCandidates(task.getId());
+ assertThat(candidateUsers).isEmpty();
+
+ List candidateGroups = taskRuntime.groupCandidates(task.getId());
+ assertThat(candidateGroups).contains("group1");
+
+ // This should fail because user1 is not the assignee
+ throwable = catchThrowable(() -> taskRuntime.addCandidateUsers(TaskPayloadBuilder
+ .addCandidateUsers()
+ .withTaskId(task.getId())
+ .withCandidateUser("user2")
+ .build()));
+
+ assertThat(throwable)
+ .isInstanceOf(IllegalStateException.class);
+
+
+
+ taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());
+
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .containsExactly(TaskRuntimeEvent.TaskEvents.TASK_ASSIGNED,
+ TaskRuntimeEvent.TaskEvents.TASK_UPDATED);
+
+ collectedEvents.clear();
+
+ // Now it should work
+ taskRuntime.addCandidateGroups(TaskPayloadBuilder
+ .addCandidateGroups()
+ .withTaskId(task.getId())
+ .withCandidateGroup("group2")
+ .build());
+
+ //@TODO: operations should cause events
+ // https://github.com/Activiti/Activiti/issues/2330
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .containsExactly();
+
+ collectedEvents.clear();
+
+ candidateGroups = taskRuntime.groupCandidates(task.getId());
+ assertThat(candidateGroups).contains("group1", "group2");
+
+ // User 1 needs to release the task in order for User 2 see it as candidate
+
+ taskRuntime.release(TaskPayloadBuilder.release().withTaskId(task.getId()).build());
+
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .containsExactly(TaskRuntimeEvent.TaskEvents.TASK_ASSIGNED,
+ TaskRuntimeEvent.TaskEvents.TASK_UPDATED);
+
+ collectedEvents.clear();
+
+ // Check with user2
+ securityUtil.logInAs("user2");
+
+ tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ }
+
+ @After
+ public void cleanup(){
+ securityUtil.logInAs("admin");
+ Page processInstancePage = processAdminRuntime.processInstances(Pageable.of(0, 50));
+ for(ProcessInstance pi : processInstancePage.getContent()){
+ processAdminRuntime.delete(ProcessPayloadBuilder.delete(pi.getId()));
+ }
+ }
+
+}
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/resources/processes/user-task-assignee-followed-group1.bpmn20.xml b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/resources/processes/user-task-assignee-followed-group1.bpmn20.xml
new file mode 100755
index 0000000000000000000000000000000000000000..54b8158562c5d8a0961c7af2d29e6814d5f38c4a
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/resources/processes/user-task-assignee-followed-group1.bpmn20.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+ SequenceFlow_1uccvwa
+
+
+
+
+ SequenceFlow_0le1m49
+
+
+
+ SequenceFlow_1uccvwa
+ SequenceFlow_151v2cg
+
+
+ SequenceFlow_151v2cg
+ SequenceFlow_0le1m49
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/resources/processes/user-task-candidate-group.bpmn20.xml b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/resources/processes/user-task-candidate-group.bpmn20.xml
new file mode 100755
index 0000000000000000000000000000000000000000..01895a7a2d0ecfb22a7671d0dcaace1fd6258f0e
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/resources/processes/user-task-candidate-group.bpmn20.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+ SequenceFlow_10vblge
+
+
+
+ SequenceFlow_0vf8cap
+
+
+
+ SequenceFlow_10vblge
+ SequenceFlow_0vf8cap
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/resources/processes/user-task-group1-followed-group2.bpmn20.xml b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/resources/processes/user-task-group1-followed-group2.bpmn20.xml
new file mode 100755
index 0000000000000000000000000000000000000000..2ede89311b7b69aa01e6ce1d78535624b7a843f2
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set3/src/test/resources/processes/user-task-group1-followed-group2.bpmn20.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+ SequenceFlow_052072h
+
+
+
+
+ SequenceFlow_16g2n05
+
+
+
+ SequenceFlow_052072h
+ SequenceFlow_0cyfmaw
+
+
+ SequenceFlow_0cyfmaw
+ SequenceFlow_16g2n05
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set4/pom.xml b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f2265310482939027c225af2e8f5e8c602230006
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/pom.xml
@@ -0,0 +1,74 @@
+
+
+ 4.0.0
+
+
+ org.activiti
+ activiti-spring-conformance-tests
+ 7.0.0-SNAPSHOT
+
+
+ activiti-spring-conformance-set4
+ jar
+ Activiti Spring :: Conformance Set 4
+ Activiti Spring :: Conformance Set 4
+
+
+
+ org.activiti
+ activiti-spring-conformance-util
+
+
+ org.activiti.api
+ activiti-api-process-runtime
+
+
+ org.activiti.api
+ activiti-api-task-runtime
+
+
+ org.activiti.api
+ activiti-api-runtime-shared
+
+
+ org.activiti.api
+ activiti-api-task-model
+
+
+ org.activiti.api
+ activiti-api-process-model
+
+
+ org.activiti.api
+ activiti-api-model-shared
+
+
+ org.activiti
+ activiti-engine
+
+
+ org.activiti
+ activiti-spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.slf4j
+ slf4j-api
+
+
+ com.h2database
+ h2
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/Application.java b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/Application.java
new file mode 100644
index 0000000000000000000000000000000000000000..8a7710b54796ce4524b738928e519b8238ec8a8f
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/Application.java
@@ -0,0 +1,13 @@
+package org.activiti.spring.conformance.set4;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class);
+ }
+
+}
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/BasicExclusiveGatewayErrorTest.java b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/BasicExclusiveGatewayErrorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b156b0dcaca93b22d4ec3472a6351047dfe2a9f
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/BasicExclusiveGatewayErrorTest.java
@@ -0,0 +1,135 @@
+package org.activiti.spring.conformance.set4;
+
+import org.activiti.api.model.shared.event.RuntimeEvent;
+import org.activiti.api.process.model.ProcessInstance;
+import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
+import org.activiti.api.process.model.events.BPMNActivityEvent;
+import org.activiti.api.process.model.events.BPMNSequenceFlowTakenEvent;
+import org.activiti.api.process.model.events.ProcessRuntimeEvent;
+import org.activiti.api.process.runtime.ProcessAdminRuntime;
+import org.activiti.api.process.runtime.ProcessRuntime;
+import org.activiti.api.runtime.shared.NotFoundException;
+import org.activiti.api.runtime.shared.query.Page;
+import org.activiti.api.runtime.shared.query.Pageable;
+import org.activiti.api.task.model.Task;
+import org.activiti.api.task.model.builders.TaskPayloadBuilder;
+import org.activiti.api.task.model.events.TaskRuntimeEvent;
+import org.activiti.api.task.runtime.TaskRuntime;
+import org.activiti.engine.ActivitiException;
+import org.activiti.spring.conformance.util.security.SecurityUtil;
+import org.junit.After;
+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.test.context.junit4.SpringRunner;
+
+import static org.activiti.spring.conformance.set4.Set4RuntimeTestConfiguration.collectedEvents;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.catchThrowable;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
+public class BasicExclusiveGatewayErrorTest {
+
+ private final String processKey = "basicexclu-15cdd4ac-ff4d-4925-9b4e-err";
+
+ @Autowired
+ private ProcessRuntime processRuntime;
+
+ @Autowired
+ private TaskRuntime taskRuntime;
+
+ @Autowired
+ private SecurityUtil securityUtil;
+
+ @Autowired
+ private ProcessAdminRuntime processAdminRuntime;
+
+ @Before
+ public void cleanUp() {
+ collectedEvents.clear();
+ }
+
+
+ @Test
+ public void shouldFailOnExpressionError() {
+
+ securityUtil.logInAs("user1");
+
+ ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder
+ .start()
+ .withProcessDefinitionKey(processKey)
+ .withBusinessKey("my-business-key")
+ .withProcessInstanceName("my-process-instance-name")
+ .build());
+
+ //then
+ assertThat(processInstance).isNotNull();
+ assertThat(processInstance.getStatus()).isEqualTo(ProcessInstance.ProcessInstanceStatus.RUNNING);
+ assertThat(processInstance.getBusinessKey()).isEqualTo("my-business-key");
+ assertThat(processInstance.getName()).isEqualTo("my-process-instance-name");
+
+ // I should be able to get the process instance from the Runtime because it is still running
+ ProcessInstance processInstanceById = processRuntime.processInstance(processInstance.getId());
+
+ assertThat(processInstanceById).isEqualTo(processInstance);
+
+ // I should get a task for User1
+ Page tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ Task task = tasks.getContent().get(0);
+
+ Task taskById = taskRuntime.task(task.getId());
+
+ assertThat(taskById.getStatus()).isEqualTo(Task.TaskStatus.ASSIGNED);
+
+ assertThat(task).isEqualTo(taskById);
+
+ assertThat(task.getAssignee()).isEqualTo("user1");
+
+
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .containsExactly(
+ ProcessRuntimeEvent.ProcessEvents.PROCESS_CREATED,
+ ProcessRuntimeEvent.ProcessEvents.PROCESS_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_COMPLETED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ TaskRuntimeEvent.TaskEvents.TASK_CREATED,
+ TaskRuntimeEvent.TaskEvents.TASK_ASSIGNED);
+
+
+ collectedEvents.clear();
+
+
+
+ Throwable throwable = catchThrowable(() -> taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build()));
+
+ //@TODO: this is leaking ActivitiException.class we should validate expressions before running the process
+ // https://github.com/Activiti/Activiti/issues/2328
+ assertThat(throwable)
+ .isInstanceOf(ActivitiException.class);
+ assertThat(throwable).
+ hasMessageContaining("condition expression returns non-Boolean");
+
+
+
+ }
+
+
+ @After
+ public void cleanup() {
+ securityUtil.logInAs("admin");
+ Page processInstancePage = processAdminRuntime.processInstances(Pageable.of(0, 50));
+ for (ProcessInstance pi : processInstancePage.getContent()) {
+ processAdminRuntime.delete(ProcessPayloadBuilder.delete(pi.getId()));
+ }
+ }
+
+}
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/BasicExclusiveGatewayTest.java b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/BasicExclusiveGatewayTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..350a34a8514be0583638725029374a7d75a4dbc9
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/BasicExclusiveGatewayTest.java
@@ -0,0 +1,151 @@
+package org.activiti.spring.conformance.set4;
+
+import org.activiti.api.model.shared.event.RuntimeEvent;
+import org.activiti.api.process.model.ProcessInstance;
+import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
+import org.activiti.api.process.model.events.BPMNActivityEvent;
+import org.activiti.api.process.model.events.BPMNSequenceFlowTakenEvent;
+import org.activiti.api.process.model.events.ProcessRuntimeEvent;
+import org.activiti.api.process.runtime.ProcessAdminRuntime;
+import org.activiti.api.process.runtime.ProcessRuntime;
+import org.activiti.api.runtime.shared.query.Page;
+import org.activiti.api.runtime.shared.query.Pageable;
+import org.activiti.api.task.model.Task;
+import org.activiti.api.task.model.builders.TaskPayloadBuilder;
+import org.activiti.api.task.model.events.TaskRuntimeEvent;
+import org.activiti.api.task.runtime.TaskRuntime;
+import org.activiti.spring.conformance.util.security.SecurityUtil;
+import org.junit.After;
+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.test.context.junit4.SpringRunner;
+
+import static org.activiti.spring.conformance.set4.Set4RuntimeTestConfiguration.collectedEvents;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
+public class BasicExclusiveGatewayTest {
+
+ private final String processKey = "basicexclu-15cdd4ac-ff4d-4925-9b4e-87ea77528613";
+
+ @Autowired
+ private ProcessRuntime processRuntime;
+
+ @Autowired
+ private TaskRuntime taskRuntime;
+
+ @Autowired
+ private SecurityUtil securityUtil;
+
+ @Autowired
+ private ProcessAdminRuntime processAdminRuntime;
+
+ @Before
+ public void cleanUp() {
+ collectedEvents.clear();
+ }
+
+
+ @Test
+ public void shouldCreateAndCompleteATaskAndDontSeeNext() {
+
+ securityUtil.logInAs("user1");
+
+ ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder
+ .start()
+ .withProcessDefinitionKey(processKey)
+ .withBusinessKey("my-business-key")
+ .withProcessInstanceName("my-process-instance-name")
+ .build());
+
+ //then
+ assertThat(processInstance).isNotNull();
+ assertThat(processInstance.getStatus()).isEqualTo(ProcessInstance.ProcessInstanceStatus.RUNNING);
+ assertThat(processInstance.getBusinessKey()).isEqualTo("my-business-key");
+ assertThat(processInstance.getName()).isEqualTo("my-process-instance-name");
+
+ // I should be able to get the process instance from the Runtime because it is still running
+ ProcessInstance processInstanceById = processRuntime.processInstance(processInstance.getId());
+
+ assertThat(processInstanceById).isEqualTo(processInstance);
+
+ // I should get a task for User1
+ Page tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ Task task = tasks.getContent().get(0);
+
+ Task taskById = taskRuntime.task(task.getId());
+
+ assertThat(taskById.getStatus()).isEqualTo(Task.TaskStatus.ASSIGNED);
+
+ assertThat(task).isEqualTo(taskById);
+
+ assertThat(task.getAssignee()).isEqualTo("user1");
+
+
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .containsExactly(
+ ProcessRuntimeEvent.ProcessEvents.PROCESS_CREATED,
+ ProcessRuntimeEvent.ProcessEvents.PROCESS_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_COMPLETED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ TaskRuntimeEvent.TaskEvents.TASK_CREATED,
+ TaskRuntimeEvent.TaskEvents.TASK_ASSIGNED);
+
+
+ collectedEvents.clear();
+
+ taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
+
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .containsExactly(
+ TaskRuntimeEvent.TaskEvents.TASK_COMPLETED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_COMPLETED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_COMPLETED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ TaskRuntimeEvent.TaskEvents.TASK_CREATED,
+ TaskRuntimeEvent.TaskEvents.TASK_ASSIGNED);
+
+ collectedEvents.clear();
+
+ tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ task = tasks.getContent().get(0);
+
+ taskById = taskRuntime.task(task.getId());
+
+ assertThat(taskById.getStatus()).isEqualTo(Task.TaskStatus.ASSIGNED);
+
+ assertThat(task).isEqualTo(taskById);
+
+ assertThat(task.getAssignee()).isEqualTo("user1");
+
+
+ }
+
+
+ @After
+ public void cleanup() {
+ securityUtil.logInAs("admin");
+ Page processInstancePage = processAdminRuntime.processInstances(Pageable.of(0, 50));
+ for (ProcessInstance pi : processInstancePage.getContent()) {
+ processAdminRuntime.delete(ProcessPayloadBuilder.delete(pi.getId()));
+ }
+ }
+
+}
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/BasicParallelGatewayGroupAssignmentsTest.java b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/BasicParallelGatewayGroupAssignmentsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..bfd07eae396b3c7b3b729cf15967b25b5079c413
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/BasicParallelGatewayGroupAssignmentsTest.java
@@ -0,0 +1,181 @@
+package org.activiti.spring.conformance.set4;
+
+import org.activiti.api.model.shared.event.RuntimeEvent;
+import org.activiti.api.process.model.ProcessInstance;
+import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
+import org.activiti.api.process.model.events.BPMNActivityEvent;
+import org.activiti.api.process.model.events.BPMNSequenceFlowTakenEvent;
+import org.activiti.api.process.model.events.ProcessRuntimeEvent;
+import org.activiti.api.process.runtime.ProcessAdminRuntime;
+import org.activiti.api.process.runtime.ProcessRuntime;
+import org.activiti.api.runtime.shared.query.Page;
+import org.activiti.api.runtime.shared.query.Pageable;
+import org.activiti.api.task.model.Task;
+import org.activiti.api.task.model.builders.TaskPayloadBuilder;
+import org.activiti.api.task.model.events.TaskRuntimeEvent;
+import org.activiti.api.task.runtime.TaskRuntime;
+import org.activiti.spring.conformance.util.security.SecurityUtil;
+import org.junit.After;
+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.test.context.junit4.SpringRunner;
+
+import static org.activiti.spring.conformance.set4.Set4RuntimeTestConfiguration.collectedEvents;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
+public class BasicParallelGatewayGroupAssignmentsTest {
+
+ private final String processKey = "basicparal-41c130f7-0b06-4bbb-aee4-73667298e369";
+
+ @Autowired
+ private ProcessRuntime processRuntime;
+
+ @Autowired
+ private TaskRuntime taskRuntime;
+
+ @Autowired
+ private SecurityUtil securityUtil;
+
+ @Autowired
+ private ProcessAdminRuntime processAdminRuntime;
+
+ @Before
+ public void cleanUp() {
+ collectedEvents.clear();
+ }
+
+
+ @Test
+ public void shouldCheckThatParallelGatewayCreateBothTasksForGroups() {
+
+ securityUtil.logInAs("user1");
+
+ ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder
+ .start()
+ .withProcessDefinitionKey(processKey)
+ .withBusinessKey("my-business-key")
+ .withProcessInstanceName("my-process-instance-name")
+ .build());
+
+ //then
+ assertThat(processInstance).isNotNull();
+ assertThat(processInstance.getStatus()).isEqualTo(ProcessInstance.ProcessInstanceStatus.RUNNING);
+ assertThat(processInstance.getBusinessKey()).isEqualTo("my-business-key");
+ assertThat(processInstance.getName()).isEqualTo("my-process-instance-name");
+
+ // I should be able to get the process instance from the Runtime because it is still running
+ ProcessInstance processInstanceById = processRuntime.processInstance(processInstance.getId());
+
+ assertThat(processInstanceById).isEqualTo(processInstance);
+
+ // I should get a task for User1
+ Page tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ Task task = tasks.getContent().get(0);
+
+ Task taskById = taskRuntime.task(task.getId());
+
+ assertThat(taskById.getStatus()).isEqualTo(Task.TaskStatus.ASSIGNED);
+
+ assertThat(task).isEqualTo(taskById);
+
+ assertThat(task.getAssignee()).isEqualTo("user1");
+
+
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .containsExactly(
+ ProcessRuntimeEvent.ProcessEvents.PROCESS_CREATED,
+ ProcessRuntimeEvent.ProcessEvents.PROCESS_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_COMPLETED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ TaskRuntimeEvent.TaskEvents.TASK_CREATED,
+ TaskRuntimeEvent.TaskEvents.TASK_ASSIGNED);
+
+
+ collectedEvents.clear();
+
+ taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
+
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .contains(
+ TaskRuntimeEvent.TaskEvents.TASK_COMPLETED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_COMPLETED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_COMPLETED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ TaskRuntimeEvent.TaskEvents.TASK_CREATED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ TaskRuntimeEvent.TaskEvents.TASK_CREATED);
+
+ collectedEvents.clear();
+
+
+ // User 1 is a candidate for a task
+ securityUtil.logInAs("user1");
+
+ tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ task = tasks.getContent().get(0);
+
+ taskById = taskRuntime.task(task.getId());
+
+ assertThat(taskById.getStatus()).isEqualTo(Task.TaskStatus.CREATED);
+
+ assertThat(task).isEqualTo(taskById);
+
+ assertThat(task.getAssignee()).isNull();
+
+
+ // User 2 is a candidate for a task
+ securityUtil.logInAs("user2");
+
+ tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ task = tasks.getContent().get(0);
+
+ taskById = taskRuntime.task(task.getId());
+
+ assertThat(taskById.getStatus()).isEqualTo(Task.TaskStatus.CREATED);
+
+ assertThat(task).isEqualTo(taskById);
+
+ assertThat(task.getAssignee()).isNull();
+
+ // User 3 is a candidate for both tasks
+ securityUtil.logInAs("user3");
+
+ tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(2);
+
+ }
+
+
+ @After
+ public void cleanup() {
+ securityUtil.logInAs("admin");
+ Page processInstancePage = processAdminRuntime.processInstances(Pageable.of(0, 50));
+ for (ProcessInstance pi : processInstancePage.getContent()) {
+ processAdminRuntime.delete(ProcessPayloadBuilder.delete(pi.getId()));
+ }
+ }
+
+}
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/BasicParallelGatewayTest.java b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/BasicParallelGatewayTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c7dee2ed6dd11220353f6c85d07874c7138e075
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/BasicParallelGatewayTest.java
@@ -0,0 +1,176 @@
+package org.activiti.spring.conformance.set4;
+
+import org.activiti.api.model.shared.event.RuntimeEvent;
+import org.activiti.api.process.model.ProcessInstance;
+import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
+import org.activiti.api.process.model.events.BPMNActivityEvent;
+import org.activiti.api.process.model.events.BPMNSequenceFlowTakenEvent;
+import org.activiti.api.process.model.events.ProcessRuntimeEvent;
+import org.activiti.api.process.runtime.ProcessAdminRuntime;
+import org.activiti.api.process.runtime.ProcessRuntime;
+import org.activiti.api.runtime.shared.query.Page;
+import org.activiti.api.runtime.shared.query.Pageable;
+import org.activiti.api.task.model.Task;
+import org.activiti.api.task.model.builders.TaskPayloadBuilder;
+import org.activiti.api.task.model.events.TaskRuntimeEvent;
+import org.activiti.api.task.runtime.TaskRuntime;
+import org.activiti.spring.conformance.util.security.SecurityUtil;
+import org.junit.After;
+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.test.context.junit4.SpringRunner;
+
+import static org.activiti.spring.conformance.set4.Set4RuntimeTestConfiguration.collectedEvents;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
+public class BasicParallelGatewayTest {
+
+ private final String processKey = "basicparal-b1db86dd-4a15-4c0e-9168-25d9c42d53ee";
+
+ @Autowired
+ private ProcessRuntime processRuntime;
+
+ @Autowired
+ private TaskRuntime taskRuntime;
+
+ @Autowired
+ private SecurityUtil securityUtil;
+
+ @Autowired
+ private ProcessAdminRuntime processAdminRuntime;
+
+ @Before
+ public void cleanUp() {
+ collectedEvents.clear();
+ }
+
+
+ @Test
+ public void shouldCheckThatParallelGatewayCreateTwoAssignedTasks() {
+
+ securityUtil.logInAs("user1");
+
+ ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder
+ .start()
+ .withProcessDefinitionKey(processKey)
+ .withBusinessKey("my-business-key")
+ .withProcessInstanceName("my-process-instance-name")
+ .build());
+
+ //then
+ assertThat(processInstance).isNotNull();
+ assertThat(processInstance.getStatus()).isEqualTo(ProcessInstance.ProcessInstanceStatus.RUNNING);
+ assertThat(processInstance.getBusinessKey()).isEqualTo("my-business-key");
+ assertThat(processInstance.getName()).isEqualTo("my-process-instance-name");
+
+ // I should be able to get the process instance from the Runtime because it is still running
+ ProcessInstance processInstanceById = processRuntime.processInstance(processInstance.getId());
+
+ assertThat(processInstanceById).isEqualTo(processInstance);
+
+ // I should get a task for User1
+ Page tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ Task task = tasks.getContent().get(0);
+
+ Task taskById = taskRuntime.task(task.getId());
+
+ assertThat(taskById.getStatus()).isEqualTo(Task.TaskStatus.ASSIGNED);
+
+ assertThat(task).isEqualTo(taskById);
+
+ assertThat(task.getAssignee()).isEqualTo("user1");
+
+
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .containsExactly(
+ ProcessRuntimeEvent.ProcessEvents.PROCESS_CREATED,
+ ProcessRuntimeEvent.ProcessEvents.PROCESS_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_COMPLETED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ TaskRuntimeEvent.TaskEvents.TASK_CREATED,
+ TaskRuntimeEvent.TaskEvents.TASK_ASSIGNED);
+
+
+ collectedEvents.clear();
+
+ taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
+
+ assertThat(collectedEvents)
+ .extracting(RuntimeEvent::getEventType)
+ .contains(
+ TaskRuntimeEvent.TaskEvents.TASK_COMPLETED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_COMPLETED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_COMPLETED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ TaskRuntimeEvent.TaskEvents.TASK_CREATED,
+ TaskRuntimeEvent.TaskEvents.TASK_ASSIGNED,
+ BPMNSequenceFlowTakenEvent.SequenceFlowEvents.SEQUENCE_FLOW_TAKEN,
+ BPMNActivityEvent.ActivityEvents.ACTIVITY_STARTED,
+ TaskRuntimeEvent.TaskEvents.TASK_CREATED,
+ TaskRuntimeEvent.TaskEvents.TASK_ASSIGNED);
+
+ collectedEvents.clear();
+
+
+ // User 1 has his/her task
+ securityUtil.logInAs("user1");
+
+ tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ task = tasks.getContent().get(0);
+
+ taskById = taskRuntime.task(task.getId());
+
+ assertThat(taskById.getStatus()).isEqualTo(Task.TaskStatus.ASSIGNED);
+
+ assertThat(task).isEqualTo(taskById);
+
+ assertThat(task.getAssignee()).isEqualTo("user1");
+
+
+ // User 2 has his/her task
+ securityUtil.logInAs("user2");
+
+ tasks = taskRuntime.tasks(Pageable.of(0, 50));
+
+ assertThat(tasks.getTotalItems()).isEqualTo(1);
+
+ task = tasks.getContent().get(0);
+
+ taskById = taskRuntime.task(task.getId());
+
+ assertThat(taskById.getStatus()).isEqualTo(Task.TaskStatus.ASSIGNED);
+
+ assertThat(task).isEqualTo(taskById);
+
+ assertThat(task.getAssignee()).isEqualTo("user2");
+
+ }
+
+
+ @After
+ public void cleanup() {
+ securityUtil.logInAs("admin");
+ Page processInstancePage = processAdminRuntime.processInstances(Pageable.of(0, 50));
+ for (ProcessInstance pi : processInstancePage.getContent()) {
+ processAdminRuntime.delete(ProcessPayloadBuilder.delete(pi.getId()));
+ }
+ }
+
+}
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/ConformanceBasicProcessRuntimeTest.java b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/ConformanceBasicProcessRuntimeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..92f677be5e28911c3f769567fae5d2e16e4354df
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/ConformanceBasicProcessRuntimeTest.java
@@ -0,0 +1,87 @@
+package org.activiti.spring.conformance.set4;
+
+import org.activiti.api.process.model.ProcessDefinition;
+import org.activiti.api.process.model.ProcessDefinitionMeta;
+import org.activiti.api.process.runtime.ProcessRuntime;
+import org.activiti.api.process.runtime.conf.ProcessRuntimeConfiguration;
+import org.activiti.api.process.runtime.events.listener.ProcessRuntimeEventListener;
+import org.activiti.api.runtime.shared.events.VariableEventListener;
+import org.activiti.api.runtime.shared.query.Page;
+import org.activiti.api.runtime.shared.query.Pageable;
+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.test.context.junit4.SpringRunner;
+import org.activiti.spring.conformance.util.security.SecurityUtil;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
+public class ConformanceBasicProcessRuntimeTest {
+
+
+ @Autowired
+ private ProcessRuntime processRuntime;
+
+ @Autowired
+ private SecurityUtil securityUtil;
+
+ @Test
+ public void shouldGetConfiguration() {
+ securityUtil.logInAs("user1");
+ //when
+ ProcessRuntimeConfiguration configuration = processRuntime.configuration();
+ //then
+ assertThat(configuration).isNotNull();
+ //when
+ List> processRuntimeEventListeners = configuration.processEventListeners();
+ List> variableEventListeners = configuration.variableEventListeners();
+ //then
+ assertThat(processRuntimeEventListeners).hasSize(10);
+ assertThat(variableEventListeners).hasSize(3);
+
+ }
+
+ @Test
+ public void shouldProcessDefinitions() {
+ securityUtil.logInAs("user1");
+
+ Page processDefinitionPage = processRuntime.processDefinitions(Pageable.of(0, 50));
+
+ List processDefinitions = processDefinitionPage.getContent();
+ assertThat(processDefinitions).extracting(ProcessDefinition::getName).contains(
+ "Basic Exclusive Gateway",
+ "Basic Exclusive Gateway Expr Error",
+ "Basic Parallel Gateway",
+ "Basic Parallel Gateway Groups"
+
+ );
+
+ }
+
+ @Test
+ public void shouldProcessDefinitionsMetaData() {
+ securityUtil.logInAs("user1");
+
+ Page processDefinitionPage = processRuntime.processDefinitions(Pageable.of(0, 50));
+
+ List processDefinitions = processDefinitionPage.getContent();
+ assertThat(processDefinitions).extracting(ProcessDefinition::getName).contains(
+ "Basic Exclusive Gateway",
+ "Basic Exclusive Gateway Expr Error",
+ "Basic Parallel Gateway",
+ "Basic Parallel Gateway Groups"
+
+ );
+
+
+ }
+
+
+
+
+}
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/Set4RuntimeTestConfiguration.java b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/Set4RuntimeTestConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f332088de461f0f8da5e2658240dc87998c974b
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/java/org/activiti/spring/conformance/set4/Set4RuntimeTestConfiguration.java
@@ -0,0 +1,123 @@
+package org.activiti.spring.conformance.set4;
+
+import org.activiti.api.model.shared.event.RuntimeEvent;
+import org.activiti.api.model.shared.event.VariableCreatedEvent;
+import org.activiti.api.model.shared.event.VariableDeletedEvent;
+import org.activiti.api.model.shared.event.VariableUpdatedEvent;
+import org.activiti.api.process.model.events.BPMNActivityCancelledEvent;
+import org.activiti.api.process.model.events.BPMNActivityCompletedEvent;
+import org.activiti.api.process.model.events.BPMNActivityStartedEvent;
+import org.activiti.api.process.model.events.BPMNSequenceFlowTakenEvent;
+import org.activiti.api.process.runtime.events.*;
+import org.activiti.api.process.runtime.events.listener.BPMNElementEventListener;
+import org.activiti.api.process.runtime.events.listener.ProcessRuntimeEventListener;
+import org.activiti.api.runtime.shared.events.VariableEventListener;
+import org.activiti.api.task.runtime.events.*;
+import org.activiti.api.task.runtime.events.listener.TaskEventListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Configuration
+public class Set4RuntimeTestConfiguration {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(Set4RuntimeTestConfiguration.class);
+
+ public static List collectedEvents = new ArrayList<>();
+
+
+ @Bean
+ public BPMNElementEventListener bpmnActivityStartedListener() {
+ return bpmnActivityStartedEvent -> collectedEvents.add(bpmnActivityStartedEvent);
+ }
+
+ @Bean
+ public BPMNElementEventListener bpmnActivityCompletedListener() {
+ return bpmnActivityCompletedEvent -> collectedEvents.add(bpmnActivityCompletedEvent);
+ }
+
+ @Bean
+ public BPMNElementEventListener bpmnActivityCancelledListener() {
+ return bpmnActivityCancelledEvent -> collectedEvents.add(bpmnActivityCancelledEvent);
+ }
+
+ @Bean
+ public BPMNElementEventListener bpmnSequenceFlowTakenListener() {
+ return bpmnSequenceFlowTakenEvent -> collectedEvents.add(bpmnSequenceFlowTakenEvent);
+ }
+
+ @Bean
+ public ProcessRuntimeEventListener processCreatedListener() {
+ return processCreatedEvent -> collectedEvents.add(processCreatedEvent);
+ }
+
+ @Bean
+ public ProcessRuntimeEventListener processStartedListener() {
+ return processStartedEvent -> collectedEvents.add(processStartedEvent);
+ }
+
+ @Bean
+ public ProcessRuntimeEventListener processCompletedListener() {
+ return processCompletedEvent -> collectedEvents.add(processCompletedEvent);
+ }
+
+ @Bean
+ public ProcessRuntimeEventListener processResumedListener() {
+ return processResumedEvent -> collectedEvents.add(processResumedEvent);
+ }
+
+ @Bean
+ public ProcessRuntimeEventListener processSuspendedListener() {
+ return processSuspendedEvent -> collectedEvents.add(processSuspendedEvent);
+ }
+
+ @Bean
+ public ProcessRuntimeEventListener processCancelledListener() {
+ return processCancelledEvent -> collectedEvents.add(processCancelledEvent);
+ }
+
+ @Bean
+ public VariableEventListener variableCreatedEventListener() {
+ return variableCreatedEvent -> collectedEvents.add(variableCreatedEvent);
+ }
+
+ @Bean
+ public VariableEventListener variableDeletedEventListener() {
+ return variableDeletedEvent -> collectedEvents.add(variableDeletedEvent);
+ }
+
+ @Bean
+ public VariableEventListener variableUpdatedEventListener() {
+ return variableUpdatedEvent -> collectedEvents.add(variableUpdatedEvent);
+ }
+
+ @Bean
+ public TaskEventListener taskCreatedEventListener() {
+ return taskCreatedEvent -> collectedEvents.add(taskCreatedEvent);
+ }
+
+ @Bean
+ public TaskEventListener taskUpdatedEventListener() {
+ return taskUpdatedEvent -> collectedEvents.add(taskUpdatedEvent);
+ }
+
+ @Bean
+ public TaskEventListener taskCompletedEventListener() {
+ return taskCompletedEvent -> collectedEvents.add(taskCompletedEvent);
+ }
+
+ @Bean
+ public TaskEventListener taskSuspendedEventListener() {
+ return taskSuspendedEvent -> collectedEvents.add(taskSuspendedEvent);
+ }
+
+ @Bean
+ public TaskEventListener taskAssignedEventListener() {
+ return taskAssignedEvent -> collectedEvents.add(taskAssignedEvent);
+ }
+
+}
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/resources/processes/basic-exclusive-gateway-expr-error.bpmn20.xml b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/resources/processes/basic-exclusive-gateway-expr-error.bpmn20.xml
new file mode 100755
index 0000000000000000000000000000000000000000..68a202b850ba1292feb011cc78b59277e3e43c0a
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/resources/processes/basic-exclusive-gateway-expr-error.bpmn20.xml
@@ -0,0 +1,122 @@
+
+
+
+
+
+ SequenceFlow_1035s34
+
+
+
+ SequenceFlow_0pdm5j0
+ SequenceFlow_1tut9mk
+ SequenceFlow_1betql9
+
+
+
+ err
+
+
+ err
+
+
+ SequenceFlow_15iqzbb
+
+
+
+ SequenceFlow_1na0oew
+
+
+
+ SequenceFlow_1035s34
+ SequenceFlow_0pdm5j0
+
+
+ SequenceFlow_1tut9mk
+ SequenceFlow_1na0oew
+
+
+ SequenceFlow_1betql9
+ SequenceFlow_15iqzbb
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/resources/processes/basic-exclusive-gateway.bpmn20.xml b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/resources/processes/basic-exclusive-gateway.bpmn20.xml
new file mode 100755
index 0000000000000000000000000000000000000000..11031d8b374a1d0a3cbd772bda2ddad7998d0e04
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/resources/processes/basic-exclusive-gateway.bpmn20.xml
@@ -0,0 +1,122 @@
+
+
+
+
+
+ SequenceFlow_1035s34
+
+
+
+ SequenceFlow_0pdm5j0
+ SequenceFlow_1tut9mk
+ SequenceFlow_1betql9
+
+
+
+ ${true}
+
+
+ ${false}
+
+
+ SequenceFlow_15iqzbb
+
+
+
+ SequenceFlow_1na0oew
+
+
+
+ SequenceFlow_1035s34
+ SequenceFlow_0pdm5j0
+
+
+ SequenceFlow_1tut9mk
+ SequenceFlow_1na0oew
+
+
+ SequenceFlow_1betql9
+ SequenceFlow_15iqzbb
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/resources/processes/basic-parallel-gateway-groups.bpmn20.xml b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/resources/processes/basic-parallel-gateway-groups.bpmn20.xml
new file mode 100755
index 0000000000000000000000000000000000000000..8aaf624fb4f5b4433986111e02811f4edd02eb4e
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/resources/processes/basic-parallel-gateway-groups.bpmn20.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+ SequenceFlow_1deq42g
+
+
+ SequenceFlow_0uqnr3f
+ SequenceFlow_1bnuihc
+ SequenceFlow_1w72n38
+
+
+ SequenceFlow_1lqk19w
+
+
+ SequenceFlow_0yybgo4
+
+
+ SequenceFlow_1deq42g
+ SequenceFlow_0uqnr3f
+
+
+ SequenceFlow_1bnuihc
+ SequenceFlow_0yybgo4
+
+
+ SequenceFlow_1w72n38
+ SequenceFlow_1lqk19w
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/resources/processes/basic-parallel-gateway.bpmn20.xml b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/resources/processes/basic-parallel-gateway.bpmn20.xml
new file mode 100755
index 0000000000000000000000000000000000000000..06f322efc0f301f55b0ea239e3c151a8b04468ad
--- /dev/null
+++ b/activiti-spring-conformance-tests/activiti-spring-conformance-set4/src/test/resources/processes/basic-parallel-gateway.bpmn20.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+ SequenceFlow_0zyqlmv
+
+
+
+
+ SequenceFlow_193b2u1
+ SequenceFlow_0l4z512
+ SequenceFlow_1xq89lk
+
+
+
+
+ SequenceFlow_19475d9
+
+
+
+ SequenceFlow_08i9pa2
+
+
+
+ SequenceFlow_0zyqlmv
+ SequenceFlow_193b2u1
+
+
+ SequenceFlow_0l4z512
+ SequenceFlow_08i9pa2
+
+
+ SequenceFlow_1xq89lk
+ SequenceFlow_19475d9
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/activiti-spring-conformance-tests/pom.xml b/activiti-spring-conformance-tests/pom.xml
index d1fd25499b277ed95090b001a18ab9f696bf27a5..13f1f18b86a22f468699916cc09d11a59fa5e312 100644
--- a/activiti-spring-conformance-tests/pom.xml
+++ b/activiti-spring-conformance-tests/pom.xml
@@ -20,5 +20,7 @@
activiti-spring-conformance-set0
activiti-spring-conformance-set1
activiti-spring-conformance-set2
+ activiti-spring-conformance-set3
+ activiti-spring-conformance-set4