001 /** 002 * Copyright 2005-2011 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.rice.kew.engine; 017 018 019 import static org.junit.Assert.assertEquals; 020 import static org.junit.Assert.assertNotNull; 021 import static org.junit.Assert.assertTrue; 022 023 import java.util.Collection; 024 import java.util.Iterator; 025 import java.util.List; 026 import java.util.Set; 027 028 import org.junit.Test; 029 import org.kuali.rice.kew.actionrequest.ActionRequestValue; 030 import org.kuali.rice.kew.api.WorkflowDocument; 031 import org.kuali.rice.kew.api.WorkflowDocumentFactory; 032 import org.kuali.rice.kew.api.action.ActionRequestStatus; 033 import org.kuali.rice.kew.engine.node.RouteNodeInstance; 034 import org.kuali.rice.kew.service.KEWServiceLocator; 035 import org.kuali.rice.kew.test.KEWTestCase; 036 import org.kuali.rice.kew.api.KewApiConstants; 037 038 public class ParallelRoutingTest extends KEWTestCase { 039 040 private static final String DOCUMENT_TYPE_NAME = "ParallelDocType"; 041 private static final String PARALLEL_EMPTY_DOCUMENT_TYPE_NAME = "ParallelEmptyDocType"; 042 private static final String PARALLEL_EMPTY_DOCUMENT_TYPE_2_NAME = "ParallelEmptyDocType2"; 043 private static final String ACKNOWLEDGE_1_NODE = "Acknowledge1"; 044 private static final String WORKFLOW_DOCUMENT_2_NODE = "WorkflowDocument2"; 045 private static final String WORKFLOW_DOCUMENT_3_NODE = "WorkflowDocument3"; 046 private static final String JOIN_NODE = "Join"; 047 private static final String WORKFLOW_DOCUMENT_FINAL_NODE = "WorkflowDocumentFinal"; 048 049 protected void loadTestData() throws Exception { 050 loadXmlFile("EngineConfig.xml"); 051 } 052 053 @Test public void testParallelRoute() throws Exception { 054 WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), DOCUMENT_TYPE_NAME); 055 document.saveDocumentData(); 056 assertTrue("Document should be initiated", document.isInitiated()); 057 assertEquals("Should be no action requests.", 0, document.getRootActionRequests().size()); 058 Collection nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId()); 059 assertEquals("Wrong number of active nodes.", 1, nodeInstances.size()); 060 document.route("Routing for parallel"); 061 062 // should have generated a request to "bmcgough" 063 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("bmcgough"), document.getDocumentId()); 064 assertTrue("Document should be enroute", document.isEnroute()); 065 List actionRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(document.getDocumentId()); 066 assertEquals("Incorrect pending action requests.", 1, actionRequests.size()); 067 ActionRequestValue bRequest = (ActionRequestValue)actionRequests.get(0); 068 assertNotNull("Should have been routed through node instance.", bRequest.getNodeInstance()); 069 assertTrue(document.isApprovalRequested()); 070 071 document.approve("Approving test"); 072 073 // document should split at this point and generate an ack to temay and approves to rkirkend and pmckown 074 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("rkirkend"), document.getDocumentId()); 075 assertTrue("Document should be enroute", document.isEnroute()); 076 actionRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(document.getDocumentId()); 077 assertEquals("Incorrect pending action requests.", 3, actionRequests.size()); 078 boolean isToTemay = false; 079 boolean isToPmckown = false; 080 boolean isToRkirkend = false; 081 for (Iterator iterator = actionRequests.iterator(); iterator.hasNext();) { 082 ActionRequestValue actionRequest = (ActionRequestValue) iterator.next(); 083 if (actionRequest.getPrincipalId().equals(getPrincipalIdForName("temay"))) { 084 isToTemay = true; 085 assertEquals("Request should be activated.", ActionRequestStatus.ACTIVATED.getCode(), actionRequest.getStatus()); 086 assertEquals("Wrong action requested.", KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ, actionRequest.getActionRequested()); 087 assertNotNull("Should have been routed through node instance.", actionRequest.getNodeInstance()); 088 assertEquals("Invalid node.", ACKNOWLEDGE_1_NODE, actionRequest.getNodeInstance().getRouteNode().getRouteNodeName()); 089 } 090 if (actionRequest.getPrincipalId().equals(getPrincipalIdForName("rkirkend"))) { 091 isToRkirkend = true; 092 assertEquals("Request should be activated.", ActionRequestStatus.ACTIVATED.getCode(), actionRequest.getStatus()); 093 assertEquals("Wrong action requested.", KewApiConstants.ACTION_REQUEST_APPROVE_REQ, actionRequest.getActionRequested()); 094 assertNotNull("Should have been routed through node instance.", actionRequest.getNodeInstance()); 095 assertEquals("Invalid node.", WORKFLOW_DOCUMENT_2_NODE, actionRequest.getNodeInstance().getRouteNode().getRouteNodeName()); 096 } 097 if (actionRequest.getPrincipalId().equals(getPrincipalIdForName("pmckown"))) { 098 isToPmckown = true; 099 assertEquals("Request should be activated.", ActionRequestStatus.ACTIVATED.getCode(), actionRequest.getStatus()); 100 assertEquals("Wrong action requested.", KewApiConstants.ACTION_REQUEST_APPROVE_REQ, actionRequest.getActionRequested()); 101 assertNotNull("Should have been routed through node instance.", actionRequest.getNodeInstance()); 102 assertEquals("Invalid node.", WORKFLOW_DOCUMENT_3_NODE, actionRequest.getNodeInstance().getRouteNode().getRouteNodeName()); 103 } 104 } 105 assertTrue("No request to temay.", isToTemay); 106 assertTrue("No request to pmckown.", isToPmckown); 107 assertTrue("No request to rkirkend.", isToRkirkend); 108 109 // check that we are at both nodes, one in each branch 110 Set<String> nodeNames = document.getNodeNames(); 111 assertEquals("Wrong number of node names.", 2, nodeNames.size() ); 112 boolean isNode2 = false; 113 boolean isNode3 = false; 114 for (String name : nodeNames) { 115 if (name.equals(WORKFLOW_DOCUMENT_2_NODE)) { 116 isNode2 = true; 117 } 118 if (name.equals(WORKFLOW_DOCUMENT_3_NODE)) { 119 isNode3 = true; 120 } 121 } 122 assertTrue("Not at node2.", isNode2); 123 assertTrue("Not at node3.", isNode3); 124 nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId()); 125 assertEquals("Wrong number of active nodes.", 2, nodeInstances.size()); 126 Iterator iterator = nodeInstances.iterator(); 127 RouteNodeInstance instance1 = (RouteNodeInstance)iterator.next(); 128 RouteNodeInstance instance2 = (RouteNodeInstance)iterator.next(); 129 assertNotNull("Node should be in branch.", instance1.getBranch()); 130 assertNotNull("Node should be in branch.", instance2.getBranch()); 131 assertTrue("Branches should be different.", !instance1.getBranch().getBranchId().equals(instance2.getBranch().getBranchId())); 132 133 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("rkirkend"), document.getDocumentId()); 134 assertTrue("Should have request.", document.isApprovalRequested()); 135 document.approve("Git-r-dun"); 136 137 nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId()); 138 assertEquals("Wrong number of active nodes.", 2, nodeInstances.size()); 139 boolean isAtJoin = false; 140 boolean isAtWD3 = false; 141 for (Iterator iter = nodeInstances.iterator(); iter.hasNext();) { 142 RouteNodeInstance nodeInstance = (RouteNodeInstance) iter.next(); 143 if (nodeInstance.getRouteNode().getRouteNodeName().equals(JOIN_NODE)) { 144 assertEquals("Join branch should be split branch.", instance1.getBranch().getParentBranch().getBranchId(), nodeInstance.getBranch().getBranchId()); 145 isAtJoin = true; 146 } 147 if (nodeInstance.getRouteNode().getRouteNodeName().equals(WORKFLOW_DOCUMENT_3_NODE)) { 148 isAtWD3 = true; 149 } 150 } 151 assertTrue("Not at join", isAtJoin); 152 assertTrue("Not at WD3", isAtWD3); 153 154 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("pmckown"), document.getDocumentId()); 155 assertTrue("Should have request.", document.isApprovalRequested()); 156 document.approve("Do it."); 157 158 nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId()); 159 assertEquals("Wrong number of active nodes.", 1, nodeInstances.size()); 160 boolean isAtWDF = false; 161 for (Iterator iter = nodeInstances.iterator(); iter.hasNext();) { 162 RouteNodeInstance nodeInstance = (RouteNodeInstance) iter.next(); 163 if (nodeInstance.getRouteNode().getRouteNodeName().equals(WORKFLOW_DOCUMENT_FINAL_NODE)) { 164 isAtWDF = true; 165 } 166 } 167 assertTrue("Not at WDF", isAtWDF); 168 169 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("xqi"), document.getDocumentId()); 170 assertTrue("Should still be enroute.", document.isEnroute()); 171 assertTrue("Should have request.", document.isApprovalRequested()); 172 document.approve("I'm the last approver"); 173 174 assertTrue("Document should be processed.", document.isProcessed()); 175 nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId()); 176 //commented out because the final RouteNodeInstance is now not active when the doc goes final 177 // assertEquals("Wrong number of active nodes.", 1, nodeInstances.size()); 178 // isAtWDF = false; 179 // for (Iterator iter = nodeInstances.iterator(); iter.hasNext();) { 180 // RouteNodeInstance nodeInstance = (RouteNodeInstance) iter.next(); 181 // if (nodeInstance.getRouteNode().getRouteNodeName().equals(WORKFLOW_DOCUMENT_FINAL_NODE)) { 182 // isAtWDF = true; 183 // } 184 // } 185 // assertTrue("Not at WDF", isAtWDF); 186 187 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("temay"), document.getDocumentId()); 188 assertTrue("Should have request.", document.isAcknowledgeRequested()); 189 document.acknowledge(""); 190 assertTrue(document.isFinal()); 191 } 192 193 /** 194 * Tests that the document route past the join properly when there are parallel branches that don't generate requests. 195 * This was coded in response to a bug found while testing with ERA in order to track it down and fix it. 196 */ 197 @Test public void testEmptyParallelBranches() throws Exception { 198 199 WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), PARALLEL_EMPTY_DOCUMENT_TYPE_NAME); 200 document.saveDocumentData(); 201 assertTrue("Document should be initiated", document.isInitiated()); 202 assertEquals("Should be no action requests.", 0, document.getRootActionRequests().size()); 203 Collection<? extends Object> nodeInstances = KEWServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId()); 204 assertEquals("Wrong number of active nodes.", 1, nodeInstances.size()); 205 document.route(""); 206 207 // should have generated a request to "bmcgough" 208 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("bmcgough"), document.getDocumentId()); 209 assertTrue("Document should be enroute", document.isEnroute()); 210 List actionRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(document.getDocumentId()); 211 assertEquals("Incorrect pending action requests.", 1, actionRequests.size()); 212 ActionRequestValue bRequest = (ActionRequestValue)actionRequests.get(0); 213 assertNotNull("Should have been routed through node instance.", bRequest.getNodeInstance()); 214 assertTrue(document.isApprovalRequested()); 215 216 document.approve(""); 217 218 // now the document should have split, passed through nodes in each branch which didn't generate requests, 219 // and then passed the join node and generated requests at WorkflowDocumentFinal 220 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("xqi"), document.getDocumentId()); 221 assertTrue("Document should be enroute", document.isEnroute()); 222 assertTrue(document.isApprovalRequested()); 223 224 } 225 226 /** 227 * This runs the test with the adhoc approvers branch second instead of first 228 *//* 229 public void testEmptyParallelBranchesSwitched() throws Exception { 230 231 WorkflowDocument document = WorkflowDocumentFactory.createDocument(new NetworkIdVO("ewestfal"), PARALLEL_EMPTY_DOCUMENT_TYPE_2_NAME); 232 document.saveDocumentData(); 233 assertTrue("Document should be initiated", document.isInitiated()); 234 assertEquals("Should be no action requests.", 0, document.getActionRequests().length); 235 assertEquals("Invalid route level.", new Integer(0), document.getRouteHeader().getDocRouteLevel()); 236 Collection nodeInstances = SpringServiceLocator.getRouteNodeService().getActiveNodeInstances(document.getDocumentId()); 237 assertEquals("Wrong number of active nodes.", 1, nodeInstances.size()); 238 document.route(""); 239 240 // should have generated a request to "bmcgough" 241 document = WorkflowDocumentFactory.loadDocument(new NetworkIdVO("bmcgough"), document.getDocumentId()); 242 assertTrue("Document should be enroute", document.isEnroute()); 243 List actionRequests = TestUtilities.getActionRequestService().findPendingByDoc(document.getDocumentId()); 244 assertEquals("Incorrect pending action requests.", 1, actionRequests.size()); 245 ActionRequestValue bRequest = (ActionRequestValue)actionRequests.get(0); 246 assertNotNull("Should have been routed through node instance.", bRequest.getRouteNodeInstance()); 247 assertTrue(document.isApprovalRequested()); 248 249 document.approve(""); 250 251 // now the document should have split, passed through nodes in each branch which didn't generate requests, 252 // and then passed the join node and generated requests at WorkflowDocumentFinal 253 document = WorkflowDocumentFactory.loadDocument(new NetworkIdVO("xqi"), document.getDocumentId()); 254 assertTrue("Document should be enroute", document.isEnroute()); 255 assertTrue(document.isApprovalRequested()); 256 257 }*/ 258 259 @Test public void testAdhocApproversJoinScenario() throws Exception { 260 WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), "AdHocApproversDocType"); 261 document.route(""); 262 263 // should send an approve to bmcgough 264 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("bmcgough"), document.getDocumentId()); 265 assertTrue("Bmcgough should have approve request.", document.isApprovalRequested()); 266 document.approve(""); 267 268 // at this point the document should pass the split, and end up at the WorkflowDocument2 node and the AdHocApproversJoin node 269 // after bypassing the AdHocJoinPoint 270 Set<String> nodeNames = document.getNodeNames(); 271 assertEquals("There should be two node names.", 2, nodeNames.size()); 272 assertTrue("Should be at WorkflowDocument2 node.", nodeNames.contains("WorkflowDocument2")); 273 assertTrue("Should be at WorkflowDocument2 node.", nodeNames.contains("AdHocApproversJoin")); 274 275 // pmckown has the request at the adhoc approvers node, if we approve as him then the document should _not_ transition out 276 // of it's current nodes 277 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("pmckown"), document.getDocumentId()); 278 assertTrue("Pmckown should have approve request.", document.isApprovalRequested()); 279 document.approve(""); 280 281 // the document should still be at the same nodes 282 nodeNames = document.getNodeNames(); 283 assertEquals("There should be two node names.", 2, nodeNames.size()); 284 assertTrue("Should be at WorkflowDocument2 node.", nodeNames.contains("WorkflowDocument2")); 285 assertTrue("Should be at WorkflowDocument2 node.", nodeNames.contains("AdHocApproversJoin")); 286 287 // at WorkflowDocument2, rkirkend is the approver, if we approve as him we should end up at the WorkflowDocumentFinal node 288 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("rkirkend"), document.getDocumentId()); 289 assertTrue("Rkirkend should have approve request.", document.isApprovalRequested()); 290 document.approve(""); 291 292 // the document should now be at WorkflowDocumentFinal with a request to xqi 293 nodeNames = document.getNodeNames(); 294 assertEquals("There should be one node name.", 1, nodeNames.size()); 295 assertTrue("Should be at WorkflowDocumentFinal node.", nodeNames.contains("WorkflowDocumentFinal")); 296 297 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("xqi"), document.getDocumentId()); 298 assertTrue("Document should still be enroute.", document.isEnroute()); 299 document.approve(""); 300 assertTrue("Document should now be final.", document.isFinal()); 301 302 } 303 304 }