001 /* 002 * Copyright 2006-2012 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.actions; 017 018 import org.apache.commons.lang.ArrayUtils; 019 import org.junit.Test; 020 import org.kuali.rice.kew.actionitem.ActionItem; 021 import org.kuali.rice.kew.api.KewApiConstants; 022 import org.kuali.rice.kew.api.WorkflowDocument; 023 import org.kuali.rice.kew.api.WorkflowDocumentFactory; 024 import org.kuali.rice.kew.api.action.ActionRequest; 025 import org.kuali.rice.kew.api.action.ActionType; 026 import org.kuali.rice.kew.api.action.InvalidActionTakenException; 027 import org.kuali.rice.kew.framework.postprocessor.*; 028 import org.kuali.rice.kew.framework.postprocessor.ActionTakenEvent; 029 import org.kuali.rice.kew.postprocessor.DefaultPostProcessor; 030 import org.kuali.rice.kew.service.KEWServiceLocator; 031 import org.kuali.rice.kew.test.KEWTestCase; 032 import org.kuali.rice.kim.api.KimConstants; 033 import org.kuali.rice.kim.api.common.template.Template; 034 import org.kuali.rice.kim.api.permission.Permission; 035 import org.kuali.rice.kim.api.role.Role; 036 import org.kuali.rice.kim.api.services.KimApiServiceLocator; 037 038 import java.util.Collection; 039 import java.util.HashMap; 040 import java.util.List; 041 import java.util.Map; 042 043 import static org.junit.Assert.*; 044 045 public class RecallActionTest extends KEWTestCase { 046 /** 047 * test postprocessor for testing afterActionTaken hook 048 */ 049 public static class RecallTestPostProcessor extends DefaultPostProcessor { 050 public static ActionType afterActionTakenType; 051 public static ActionTakenEvent afterActionTakenEvent; 052 @Override 053 public ProcessDocReport afterActionTaken(ActionType performed, ActionTakenEvent event) throws Exception { 054 afterActionTakenType = performed; 055 afterActionTakenEvent = event; 056 return super.afterActionTaken(performed, event); 057 } 058 } 059 060 private static final String RECALL_TEST_DOC = "RecallTest"; 061 private static final String RECALL_NOTIFY_TEST_DOC = "RecallWithPrevNotifyTest"; 062 private static final String RECALL_NO_PENDING_NOTIFY_TEST_DOC = "RecallWithoutPendingNotifyTest"; 063 private static final String RECALL_NOTIFY_THIRDPARTY_TEST_DOC = "RecallWithThirdPartyNotifyTest"; 064 065 private String EWESTFAL = null; 066 private String JHOPF = null; 067 private String RKIRKEND = null; 068 private String NATJOHNS = null; 069 private String BMCGOUGH = null; 070 071 protected void loadTestData() throws Exception { 072 loadXmlFile("ActionsConfig.xml"); 073 } 074 075 @Override 076 protected void setUpAfterDataLoad() throws Exception { 077 super.setUpAfterDataLoad(); 078 EWESTFAL = getPrincipalIdForName("ewestfal"); 079 JHOPF = getPrincipalIdForName("jhopf"); 080 RKIRKEND = getPrincipalIdForName("rkirkend"); 081 NATJOHNS = getPrincipalIdForName("natjohns"); 082 BMCGOUGH = getPrincipalIdForName("bmcgough"); 083 084 RecallTestPostProcessor.afterActionTakenType = null; 085 RecallTestPostProcessor.afterActionTakenEvent = null; 086 } 087 088 protected void assertAfterActionTakenCalled(ActionType performed, ActionType taken) { 089 assertEquals(performed, RecallTestPostProcessor.afterActionTakenType); 090 assertNotNull(RecallTestPostProcessor.afterActionTakenEvent); 091 assertEquals(taken, RecallTestPostProcessor.afterActionTakenEvent.getActionTaken().getActionTaken()); 092 } 093 094 @Test(expected=InvalidActionTakenException.class) public void testCantRecallUnroutedDoc() { 095 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC); 096 document.recall("recalling", true); 097 } 098 099 @Test public void testRecallAsInitiatorBeforeAnyApprovals() throws Exception { 100 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC); 101 document.route(""); 102 103 document.recall("recalling", true); 104 105 assertTrue("Document should be recalled", document.isRecalled()); 106 assertAfterActionTakenCalled(ActionType.RECALL, ActionType.RECALL); 107 108 //verify that the document is truly dead - no more action requests or action items. 109 110 List requests = KEWServiceLocator.getActionRequestService().findPendingByDoc(document.getDocumentId()); 111 assertEquals("Should not have any active requests", 0, requests.size()); 112 113 Collection<ActionItem> actionItems = KEWServiceLocator.getActionListService().findByDocumentId(document.getDocumentId()); 114 assertEquals("Should not have any action items", 0, actionItems.size()); 115 } 116 117 @Test public void testRecallAsInitiatorAfterSingleApproval() throws Exception { 118 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC); 119 document.route(""); 120 121 document = WorkflowDocumentFactory.loadDocument(JHOPF, document.getDocumentId()); 122 document.approve(""); 123 124 document = WorkflowDocumentFactory.loadDocument(EWESTFAL, document.getDocumentId()); 125 document.recall("recalling", true); 126 127 assertTrue("Document should be recalled", document.isRecalled()); 128 assertAfterActionTakenCalled(ActionType.RECALL, ActionType.RECALL); 129 130 //verify that the document is truly dead - no more action requests or action items. 131 132 List requests = KEWServiceLocator.getActionRequestService().findPendingByDoc(document.getDocumentId()); 133 assertEquals("Should not have any active requests", 0, requests.size()); 134 135 Collection<ActionItem> actionItems = KEWServiceLocator.getActionListService().findByDocumentId(document.getDocumentId()); 136 assertEquals("Should not have any action items", 0, actionItems.size()); 137 138 // can't recall recalled doc 139 assertFalse(document.getValidActions().getValidActions().contains(ActionType.RECALL)); 140 } 141 142 @Test(expected=InvalidActionTakenException.class) 143 public void testRecallInvalidWhenProcessed() throws Exception { 144 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC); 145 document.route(""); 146 147 for (String user: new String[] { JHOPF, EWESTFAL, RKIRKEND, NATJOHNS, BMCGOUGH }) { 148 document = WorkflowDocumentFactory.loadDocument(user, document.getDocumentId()); 149 document.approve(""); 150 } 151 152 document.refresh(); 153 assertTrue("Document should be processed", document.isProcessed()); 154 assertTrue("Document should be approved", document.isApproved()); 155 assertFalse("Document should not be final", document.isFinal()); 156 157 document = WorkflowDocumentFactory.loadDocument(EWESTFAL, document.getDocumentId()); 158 document.recall("recalling when processed should fail", true); 159 } 160 161 @Test(expected=InvalidActionTakenException.class) 162 public void testRecallInvalidWhenFinal() throws Exception { 163 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC); 164 document.route(""); 165 166 for (String user: new String[] { JHOPF, EWESTFAL, RKIRKEND, NATJOHNS, BMCGOUGH }) { 167 document = WorkflowDocumentFactory.loadDocument(user, document.getDocumentId()); 168 document.approve(""); 169 } 170 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("xqi"), document.getDocumentId()); 171 document.acknowledge(""); 172 173 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("jthomas"), document.getDocumentId()); 174 document.fyi(); 175 176 for (ActionRequest a: document.getRootActionRequests()) { 177 System.err.println(a); 178 if (a.isAcknowledgeRequest() || a.isFyiRequest()) { 179 System.err.println(a.getPrincipalId()); 180 System.err.println(KimApiServiceLocator.getIdentityService().getPrincipal(a.getPrincipalId()).getPrincipalName()); 181 } 182 } 183 184 assertFalse("Document should not be processed", document.isProcessed()); 185 assertTrue("Document should be approved", document.isApproved()); 186 assertTrue("Document should be final", document.isFinal()); 187 188 document = WorkflowDocumentFactory.loadDocument(EWESTFAL, document.getDocumentId()); 189 document.recall("recalling when processed should fail", true); 190 } 191 192 @Test public void testRecallToActionListAsInitiatorBeforeAnyApprovals() throws Exception { 193 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC); 194 document.route(""); 195 196 document.recall("recalling", false); 197 198 assertTrue("Document should be saved", document.isSaved()); 199 assertEquals(1, document.getCurrentNodeNames().size()); 200 assertTrue(document.getCurrentNodeNames().contains("AdHoc")); 201 assertAfterActionTakenCalled(ActionType.RECALL, ActionType.COMPLETE); 202 203 // initiator has completion request 204 assertTrue(document.isCompletionRequested()); 205 // can't recall saved doc 206 assertFalse(document.getValidActions().getValidActions().contains(ActionType.RECALL)); 207 208 // first approver has FYI 209 assertTrue(WorkflowDocumentFactory.loadDocument(JHOPF, document.getDocumentId()).isFYIRequested()); 210 211 document.complete("completing"); 212 213 assertTrue("Document should be enroute", document.isEnroute()); 214 215 assertTrue(WorkflowDocumentFactory.loadDocument(JHOPF, document.getDocumentId()).isApprovalRequested()); 216 } 217 218 private static final String PERM_APP_DOC_STATUS = "recallable by admins"; 219 private static final String ROUTE_NODE = "NotifyFirst"; 220 private static final String ROUTE_STATUS = "R"; 221 222 protected Permission createRecallPermission(String docType, String appDocStatus, String routeNode, String routeStatus) { 223 return createPermissionForTemplate(KewApiConstants.KEW_NAMESPACE, KewApiConstants.RECALL_PERMISSION, KewApiConstants.KEW_NAMESPACE, KewApiConstants.RECALL_PERMISSION + " for test case", docType, appDocStatus, routeNode, routeStatus); 224 } 225 226 protected Permission createRouteDocumentPermission(String docType, String appDocStatus, String routeNode, String routeStatus) { 227 return createPermissionForTemplate(KewApiConstants.KEW_NAMESPACE, KewApiConstants.ROUTE_PERMISSION, KewApiConstants.KEW_NAMESPACE, KewApiConstants.ROUTE_PERMISSION + " for test case", docType, appDocStatus, routeNode, routeStatus); 228 } 229 230 protected Permission createPermissionForTemplate(String template_ns, String template_name, String permission_ns, String permission_name, String docType, String appDocStatus, String routeNode, String routeStatus) { 231 Template permTmpl = KimApiServiceLocator.getPermissionService().findPermTemplateByNamespaceCodeAndName(template_ns, template_name); 232 assertNotNull(permTmpl); 233 Permission.Builder permission = Permission.Builder.create(permission_ns, permission_name); 234 permission.setDescription(permission_name); 235 permission.setTemplate(Template.Builder.create(permTmpl)); 236 Map<String, String> attrs = new HashMap<String, String>(); 237 attrs.put(KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME, docType); 238 attrs.put(KimConstants.AttributeConstants.APP_DOC_STATUS, appDocStatus); 239 attrs.put(KimConstants.AttributeConstants.ROUTE_NODE_NAME, routeNode); 240 attrs.put(KimConstants.AttributeConstants.ROUTE_STATUS_CODE, routeStatus); 241 permission.setActive(true); 242 permission.setAttributes(attrs); 243 244 // save the permission and check that's it's wired up correctly 245 Permission perm = KimApiServiceLocator.getPermissionService().createPermission(permission.build()); 246 assertEquals(perm.getTemplate().getId(), permTmpl.getId()); 247 int num = 1; 248 if (appDocStatus != null) num++; 249 if (routeNode != null) num++; 250 if (routeStatus != null) num++; 251 assertEquals(num, perm.getAttributes().size()); 252 assertEquals(docType, perm.getAttributes().get(KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME)); 253 assertEquals(appDocStatus, perm.getAttributes().get(KimConstants.AttributeConstants.APP_DOC_STATUS)); 254 assertEquals(routeNode, perm.getAttributes().get(KimConstants.AttributeConstants.ROUTE_NODE_NAME)); 255 assertEquals(routeStatus, perm.getAttributes().get(KimConstants.AttributeConstants.ROUTE_STATUS_CODE)); 256 257 return perm; 258 } 259 260 // disable the existing Recall Permission assigned to Initiator Role for test purposes 261 protected void disableInitiatorRecallPermission() { 262 Permission p = KimApiServiceLocator.getPermissionService().findPermByNamespaceCodeAndName("KR-WKFLW", "Recall Document"); 263 Permission.Builder pb = Permission.Builder.create(p); 264 pb.setActive(false); 265 KimApiServiceLocator.getPermissionService().updatePermission(pb.build()); 266 } 267 268 /** 269 * Tests that a new permission can be configured with the Recall Permission template and that matching works correctly 270 * against the new permission 271 */ 272 @Test public void testRecallPermissionMatching() { 273 disableInitiatorRecallPermission(); 274 createRecallPermission(RECALL_TEST_DOC, PERM_APP_DOC_STATUS, ROUTE_NODE, ROUTE_STATUS); 275 276 Map<String, String> details = new HashMap<String, String>(); 277 details.put(KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME, RECALL_TEST_DOC); 278 details.put(KimConstants.AttributeConstants.APP_DOC_STATUS, PERM_APP_DOC_STATUS); 279 details.put(KimConstants.AttributeConstants.ROUTE_NODE_NAME, ROUTE_NODE); 280 details.put(KimConstants.AttributeConstants.ROUTE_STATUS_CODE, ROUTE_STATUS); 281 282 // test all single field mismatches 283 for (Map.Entry<String, String> entry: details.entrySet()) { 284 Map<String, String> testDetails = new HashMap<String, String>(details); 285 // change a single detail to a non-matching value 286 testDetails.put(entry.getKey(), entry.getValue() + " BOGUS "); 287 assertFalse("non-matching " + entry.getKey() + " detail should cause template to not match", KimApiServiceLocator.getPermissionService().isPermissionDefinedByTemplate(KewApiConstants.KEW_NAMESPACE, KewApiConstants.RECALL_PERMISSION, testDetails)); 288 } 289 290 assertTrue("template should match details", KimApiServiceLocator.getPermissionService().isPermissionDefinedByTemplate(KewApiConstants.KEW_NAMESPACE, KewApiConstants.RECALL_PERMISSION, details)); 291 } 292 293 @Test public void testRecallPermissionTemplate() throws Exception { 294 WorkflowDocument document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC); 295 document.route(""); 296 297 // nope, technical admins can't recall 298 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("admin"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL)); 299 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("quickstart"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL)); 300 301 // create a recall permission for the RECALL_TEST_DOC doctype 302 Permission perm = createRecallPermission(RECALL_TEST_DOC, PERM_APP_DOC_STATUS, ROUTE_NODE, ROUTE_STATUS); 303 304 // assign the permission to Technical Administrator role 305 Role techadmin = KimApiServiceLocator.getRoleService().getRoleByNamespaceCodeAndName("KR-SYS", "Technical Administrator"); 306 KimApiServiceLocator.getRoleService().assignPermissionToRole(perm.getId(), techadmin.getId()); 307 308 // our recall permission is assigned to the technical admin role 309 310 // but the doc will not match... 311 document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_NOTIFY_TEST_DOC); 312 document.route(PERM_APP_DOC_STATUS); 313 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("admin"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL)); 314 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("quickstart"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL)); 315 316 // .. the app doc status will not match... 317 document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC); 318 document.route(""); 319 // technical admins can't recall since the app doc status is not correct 320 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("admin"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL)); 321 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("quickstart"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL)); 322 323 // ... the node will not match ... 324 document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC); 325 document.route(""); 326 WorkflowDocumentFactory.loadDocument(JHOPF, document.getDocumentId()).approve(""); // approve past notifyfirstnode 327 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("admin"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL)); 328 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("quickstart"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL)); 329 330 // ... the doc status will not match (not recallable anyway) ... 331 document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC); 332 document.route(""); 333 document.cancel("cancelled"); 334 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("admin"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL)); 335 assertFalse(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("quickstart"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL)); 336 337 // everything should match 338 document = WorkflowDocumentFactory.createDocument(EWESTFAL, RECALL_TEST_DOC); 339 document.setApplicationDocumentStatus(PERM_APP_DOC_STATUS); 340 document.route(""); 341 // now technical admins can recall by virtue of having the recall permission on this doc 342 assertTrue(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("admin"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL)); 343 assertTrue(WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("quickstart"), document.getDocumentId()).getValidActions().getValidActions().contains(ActionType.RECALL)); 344 } 345 346 @Test public void testRecallToActionListAsInitiatorAfterApprovals() throws Exception { 347 this.testRecallToActionListAsInitiatorAfterApprovals(RECALL_TEST_DOC); 348 } 349 350 @Test public void testRecallToActionListAsInitiatorWithNotificationAfterApprovals() throws Exception { 351 this.testRecallToActionListAsInitiatorAfterApprovals(RECALL_NOTIFY_TEST_DOC); 352 } 353 354 @Test public void testRecallToActionListAsInitiatorWithoutPendingNotificationAfterApprovals() throws Exception { 355 this.testRecallToActionListAsInitiatorAfterApprovals(RECALL_NO_PENDING_NOTIFY_TEST_DOC); 356 } 357 358 @Test public void testRecallToActionListAsInitiatorWithThirdPartyNotificationAfterApprovals() throws Exception { 359 this.testRecallToActionListAsInitiatorAfterApprovals(RECALL_NOTIFY_THIRDPARTY_TEST_DOC); 360 } 361 362 /** 363 * Tests that the document is returned to the *recaller*'s action list, not the original initiator 364 * @throws Exception 365 */ 366 @Test public void testRecallToActionListAsThirdParty() throws Exception { 367 Permission perm = createRecallPermission(RECALL_TEST_DOC, null, null, null); 368 // assign the permission to Technical Administrator role 369 Role techadmin = KimApiServiceLocator.getRoleService().getRoleByNamespaceCodeAndName("KR-SYS", "Technical Administrator"); 370 KimApiServiceLocator.getRoleService().assignPermissionToRole(perm.getId(), techadmin.getId()); 371 // recall as 'admin' user 372 testRecallToActionListAfterApprovals(EWESTFAL, getPrincipalIdForName("admin"), RECALL_TEST_DOC); 373 } 374 375 // the three tests below test permutations of recall permission and derived role assignment 376 protected void assignRoutePermissionToTechAdmin() { 377 // assign Route Document permission to the Technical Administrator role 378 Permission routePerm = createRouteDocumentPermission(RECALL_TEST_DOC, null, null, null); 379 Role techadmin = KimApiServiceLocator.getRoleService().getRoleByNamespaceCodeAndName("KR-SYS", "Technical Administrator"); 380 KimApiServiceLocator.getRoleService().assignPermissionToRole(routePerm.getId(), techadmin.getId()); 381 } 382 protected void assignRecallPermissionToDocumentRouters() { 383 // assign Recall permission to the Document Router derived role 384 Permission recallPerm = createRecallPermission(RECALL_TEST_DOC, null, null, null); 385 Role documentRouterDerivedRole = KimApiServiceLocator.getRoleService().getRoleByNamespaceCodeAndName("KR-WKFLW", "Document Router"); 386 KimApiServiceLocator.getRoleService().assignPermissionToRole(recallPerm.getId(), documentRouterDerivedRole.getId()); 387 } 388 /** 389 * Tests that simply assigning the Route Document permission to the Technical Admin role *without* assigning the 390 * Recall permission to the Document Router derived role, is NOT sufficient to enable recall. 391 */ 392 @Test public void testRoutePermissionAssignmentInsufficientForRouterToRecallDoc() throws Exception { 393 assignRoutePermissionToTechAdmin(); 394 // recall as 'admin' (Tech Admin) user 395 testRecallToActionListAfterApprovals(EWESTFAL, getPrincipalIdForName("admin"), RECALL_TEST_DOC, false); 396 } 397 /** 398 * Tests that simply assigning the recall permission to the Document Router derived role *without* assigning the 399 * Route Document permission to the Technical Admin role, is NOT sufficient to enable recall. 400 */ 401 @Test public void testRecallPermissionAssignmentInsufficientForRouterToRecallDoc() throws Exception { 402 assignRecallPermissionToDocumentRouters(); 403 // recall as 'admin' (Tech Admin) user 404 testRecallToActionListAfterApprovals(EWESTFAL, getPrincipalIdForName("admin"), RECALL_TEST_DOC, false); 405 } 406 /** 407 * Tests that we can use the Route Document derived role to assign Recall permission to document routers. 408 */ 409 @Test public void testRecallToActionListAsRouterDerivedRole() throws Exception { 410 // assign both! derived role works its magic 411 assignRoutePermissionToTechAdmin(); 412 assignRecallPermissionToDocumentRouters(); 413 // recall as 'admin' user (Tech Admin) user 414 testRecallToActionListAfterApprovals(EWESTFAL, getPrincipalIdForName("admin"), RECALL_TEST_DOC); 415 } 416 417 protected void testRecallToActionListAsInitiatorAfterApprovals(String doctype) { 418 testRecallToActionListAfterApprovals(EWESTFAL, EWESTFAL, doctype); 419 } 420 421 // Implements various permutations of recalls - with and without doctype policies/notifications of various sorts 422 // and as initiator or a third party recaller 423 protected void testRecallToActionListAfterApprovals(String initiator, String recaller, String doctype) { 424 testRecallToActionListAfterApprovals(initiator, recaller, doctype, true); 425 } 426 protected void testRecallToActionListAfterApprovals(String initiator, String recaller, String doctype, boolean expect_recall_success) { 427 boolean notifyPreviousRecipients = !RECALL_TEST_DOC.equals(doctype); 428 boolean notifyPendingRecipients = !RECALL_NO_PENDING_NOTIFY_TEST_DOC.equals(doctype); 429 String[] thirdPartiesNotified = RECALL_NOTIFY_THIRDPARTY_TEST_DOC.equals(doctype) ? new String[] { "quickstart", "admin" } : new String[] {}; 430 431 WorkflowDocument document = WorkflowDocumentFactory.createDocument(initiator, doctype); 432 document.route(""); 433 434 WorkflowDocumentFactory.loadDocument(JHOPF, document.getDocumentId()).approve(""); 435 WorkflowDocumentFactory.loadDocument(initiator, document.getDocumentId()).approve(""); 436 WorkflowDocumentFactory.loadDocument(RKIRKEND, document.getDocumentId()).approve(""); 437 438 document = WorkflowDocumentFactory.loadDocument(recaller, document.getDocumentId()); 439 System.err.println(document.getValidActions().getValidActions()); 440 if (expect_recall_success) { 441 assertTrue("recaller '" + recaller + "' should be able to RECALL", document.getValidActions().getValidActions().contains(ActionType.RECALL)); 442 } else { 443 assertFalse("recaller '" + recaller + "' should NOT be able to RECALL", document.getValidActions().getValidActions().contains(ActionType.RECALL)); 444 return; 445 } 446 document.recall("recalling", false); 447 448 assertTrue("Document should be saved", document.isSaved()); 449 assertAfterActionTakenCalled(ActionType.RECALL, ActionType.COMPLETE); 450 451 // the recaller has a completion request 452 assertTrue(document.isCompletionRequested()); 453 454 // pending approver has FYI 455 assertEquals(notifyPendingRecipients, WorkflowDocumentFactory.loadDocument(NATJOHNS, document.getDocumentId()).isFYIRequested()); 456 // third approver has FYI 457 assertEquals(notifyPreviousRecipients, WorkflowDocumentFactory.loadDocument(RKIRKEND, document.getDocumentId()).isFYIRequested()); 458 // second approver does not have FYI - approver is initiator, FYI is skipped 459 assertFalse(WorkflowDocumentFactory.loadDocument(initiator, document.getDocumentId()).isFYIRequested()); 460 // first approver has FYI 461 assertEquals(notifyPreviousRecipients, WorkflowDocumentFactory.loadDocument(JHOPF, document.getDocumentId()).isFYIRequested()); 462 463 if (!ArrayUtils.isEmpty(thirdPartiesNotified)) { 464 for (String recipient: thirdPartiesNotified) { 465 assertTrue("Expected FYI to be sent to: " + recipient, WorkflowDocumentFactory.loadDocument(getPrincipalIdForName(recipient), document.getDocumentId()).isFYIRequested()); 466 } 467 } 468 469 // omit JHOPF, and see if FYI is subsumed by approval request 470 for (String user: new String[] { RKIRKEND, NATJOHNS }) { 471 WorkflowDocumentFactory.loadDocument(user, document.getDocumentId()).fyi(); 472 } 473 474 document.complete("completing"); 475 476 assertTrue("Document should be enroute", document.isEnroute()); 477 478 // generation of approval requests nullify FYIs (?) 479 // if JHOPF had an FYI, he doesn't any longer 480 for (String user: new String[] { JHOPF, RKIRKEND, NATJOHNS }) { 481 document = WorkflowDocumentFactory.loadDocument(user, document.getDocumentId()); 482 assertFalse(getPrincipalNameForId(user) + " should not have an FYI", document.isFYIRequested()); 483 } 484 485 // submit all approvals 486 for (String user: new String[] { JHOPF, initiator, RKIRKEND, NATJOHNS, BMCGOUGH }) { 487 document = WorkflowDocumentFactory.loadDocument(user, document.getDocumentId()); 488 assertTrue(getPrincipalNameForId(user) + " should have approval request", document.isApprovalRequested()); 489 document.approve("approving"); 490 } 491 492 // 2 acks outstanding, we're PROCESSED 493 assertTrue("Document should be processed", document.isProcessed()); 494 assertTrue("Document should be approved", document.isApproved()); 495 496 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("xqi"), document.getDocumentId()); 497 document.acknowledge(""); 498 499 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("jthomas"), document.getDocumentId()); 500 document.fyi(); 501 502 assertTrue("Document should be approved", document.isApproved()); 503 assertTrue("Document should be final", document.isFinal()); 504 } 505 }