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.actionitem; 017 018 import org.junit.Test; 019 import org.kuali.rice.kew.actionlist.service.ActionListService; 020 import org.kuali.rice.kew.api.WorkflowDocument; 021 import org.kuali.rice.kew.api.WorkflowDocumentFactory; 022 import org.kuali.rice.kew.api.action.ActionRequest; 023 import org.kuali.rice.kew.api.action.ActionRequestType; 024 import org.kuali.rice.kew.service.KEWServiceLocator; 025 import org.kuali.rice.kew.test.KEWTestCase; 026 import org.kuali.rice.kim.api.KimConstants; 027 import org.kuali.rice.kim.api.group.Group; 028 import org.kuali.rice.kim.api.group.GroupMember; 029 import org.kuali.rice.kim.api.group.GroupService; 030 import org.kuali.rice.kim.api.identity.principal.Principal; 031 import org.kuali.rice.kim.api.services.KimApiServiceLocator; 032 import org.kuali.rice.test.BaselineTestCase; 033 034 import java.util.ArrayList; 035 import java.util.Collection; 036 import java.util.Iterator; 037 import java.util.List; 038 039 import static org.junit.Assert.*; 040 @BaselineTestCase.BaselineMode(BaselineTestCase.Mode.NONE) 041 public class ActionItemServiceTest extends KEWTestCase { 042 043 private ActionListService actionListService; 044 045 protected void loadTestData() throws Exception { 046 loadXmlFile("ActionItemConfig.xml"); 047 } 048 049 protected void setUpAfterDataLoad() throws Exception { 050 super.setUpAfterDataLoad(); 051 actionListService = KEWServiceLocator.getActionListService(); 052 } 053 054 /** 055 * When workgroup membership changes all action items to that workgroup need to reflect 056 * the new membership 057 * 058 * @throws Exception 059 */ 060 @Test public void testUpdateActionItemsForWorkgroupChange() throws Exception { 061 062 WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("user1"), "ActionItemDocumentType"); 063 document.setTitle(""); 064 document.route(""); 065 066 Group oldWorkgroup = KimApiServiceLocator.getGroupService().getGroupByNamespaceCodeAndName("KR-WKFLW", 067 "AIWG-Admin"); 068 //Group oldWorkgroup = this.getGroupImpl(oldGroup.getId()); 069 070 071 GroupService groupService = KimApiServiceLocator.getGroupService(); 072 assertEquals("Workgroup should have 6 members.", 6, groupService.getMembersOfGroup(oldWorkgroup.getId()).size()); 073 074 Principal user1 = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName("user1"); 075 Principal user2 = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName("user2"); 076 077 Principal rkirkend = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName("rkirkend"); 078 Principal shenl = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName("shenl"); 079 080 List<GroupMember> usersToRemove = new ArrayList<GroupMember>(); 081 //remove 'rkirkend' and 'shenl' from the workgroup 082 Collection<GroupMember> members = groupService.getMembersOfGroup(oldWorkgroup.getId()); 083 for (GroupMember recipient : members) { 084 if (recipient.getMemberId().equals(rkirkend.getPrincipalId()) || recipient.getMemberId().equals(shenl.getPrincipalId())) { 085 KimApiServiceLocator.getGroupService().removePrincipalFromGroup(recipient.getMemberId(), oldWorkgroup.getId()); 086 } 087 088 } 089 090 //add user1 and user2 091 KimApiServiceLocator.getGroupService().addPrincipalToGroup(user1.getPrincipalId(), oldWorkgroup.getId()); 092 KimApiServiceLocator.getGroupService().addPrincipalToGroup(user2.getPrincipalId(), oldWorkgroup.getId()); 093 094 095 // verify that the new workgroup is sane... 096 Group loadedNewWorkgroup = this.getGroup(oldWorkgroup.getId()); 097 098 boolean foundUser1 = false; 099 boolean foundUser2 = false; 100 Collection<GroupMember> loadedNewGroupMembers = groupService.getMembersOfGroup(loadedNewWorkgroup.getId()); 101 assertEquals("Workgroup should have 6 members.", 6, loadedNewGroupMembers.size()); 102 103 104 for (GroupMember recipient : loadedNewGroupMembers) { 105 if (recipient.getMemberId().equals(user1.getPrincipalId())){ 106 foundUser1 = true; 107 } else if (recipient.getMemberId().equals(user2.getPrincipalId())){ 108 foundUser2 = true; 109 } 110 } 111 112 assertTrue("Did not find user 1 on workgroup.", foundUser1); 113 assertTrue("Did not find user 2 on workgroup.", foundUser2); 114 115 Collection actionItems = KEWServiceLocator.getActionListService().findByDocumentId(document.getDocumentId()); 116 boolean foundrkirkend = false; 117 boolean foundlshen = false; 118 boolean founduser1 = false; 119 boolean founduser2 = false; 120 121 for (Iterator iter = actionItems.iterator(); iter.hasNext();) { 122 ActionItem actionItem = (ActionItem) iter.next(); 123 String authId = actionItem.getPrincipal().getPrincipalName(); 124 if (authId.equals("rkirkend")) { 125 foundrkirkend = true; 126 } else if (authId.equals("user1")) { 127 founduser1 = true; 128 } else if (authId.equals("lshen")) { 129 foundlshen = true; 130 } else if (authId.equals("user2")) { 131 founduser2 = true; 132 } 133 } 134 135 assertTrue("rkirkend should still have an AI because he is in 2 workgroups that are routed to.", foundrkirkend); 136 assertTrue("user1 should have an AI because they were added to 'AIWG-Admin'", founduser1); 137 assertTrue("user2 should have an AI because they were added to 'AIWG-Admin'", founduser2); 138 assertFalse("lshen should not have an AI because they were removed from 'AIWG-Admin'", foundlshen); 139 140 } 141 142 /** 143 * When workgroup membership changes all action items to that workgroup need to reflect 144 * the new membership even in the case of nested workgroups. 145 * 146 * @throws Exception 147 */ 148 149 @Test public void testUpdateActionItemsForNestedGroupChange() throws Exception { 150 151 WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("user1"), "ActionItemDocumentType"); 152 document.setTitle(""); 153 154 Group workgroup1 = KimApiServiceLocator.getGroupService().getGroupByNamespaceCodeAndName("KR-WKFLW", 155 "AIWG-Admin"); 156 document.adHocToGroup(ActionRequestType.APPROVE, "",workgroup1.getId(), "", true); 157 document.route(""); 158 159 Principal ewestfal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName("ewestfal"); 160 161 assertEquals("User should have 1 action item", 1, KEWServiceLocator.getActionListService().findByPrincipalId(ewestfal.getPrincipalId()).size()); 162 assertEquals("Workgroup should have 6 members.", 6, KimApiServiceLocator.getGroupService().getMemberPrincipalIds(workgroup1.getId()).size()); 163 KimApiServiceLocator.getGroupService().removePrincipalFromGroup(ewestfal.getPrincipalId(), workgroup1.getId()); 164 165 assertEquals("Workgroup should have 5 members.", 5, KimApiServiceLocator.getGroupService().getMemberPrincipalIds(workgroup1.getId()).size()); 166 assertEquals("User should have 0 action item", 0, KEWServiceLocator.getActionListService().findByPrincipalId(ewestfal.getPrincipalId()).size()); 167 168 KimApiServiceLocator.getGroupService().addPrincipalToGroup(ewestfal.getPrincipalId(), workgroup1.getId()); 169 assertEquals("Workgroup should have 6 members.", 6, KimApiServiceLocator.getGroupService().getMemberPrincipalIds(workgroup1.getId()).size()); 170 assertEquals("User should have 1 action item", 1, KEWServiceLocator.getActionListService().findByPrincipalId(ewestfal.getPrincipalId()).size()); 171 172 173 // test the save group 174 Group workgroup1Impl = this.getGroup(workgroup1.getId()); 175 Principal dewey = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName("dewey"); 176 KimApiServiceLocator.getGroupService().addPrincipalToGroup(dewey.getPrincipalId(), workgroup1Impl.getId()); 177 //GroupMember groupMember = GroupMember.Builder.create(workgroup1Impl.getId(), dewey.getPrincipalId(), KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE).build(); 178 //GroupBo workgroup1Bo = GroupBo.from(workgroup1Impl); 179 //workgroup1Bo.getMembersOfGroup().add(GroupMemberBo.from(groupMember)); 180 //workgroup1Impl.getMembersOfGroup().add(groupMember); 181 182 //KimImplServiceLocator.getGroupInternalService().saveWorkgroup(workgroup1Bo); 183 184 assertEquals("Workgroup should have 7 members.", 7, KimApiServiceLocator.getGroupService().getMemberPrincipalIds(workgroup1.getId()).size()); 185 assertEquals("User should have 1 action item", 1, KEWServiceLocator.getActionListService().findByPrincipalId(dewey.getPrincipalId()).size()); 186 187 // test nested 188 Principal user1 = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName("user1"); 189 190 document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("jhopf"), "ActionItemDocumentType"); 191 document.setTitle(""); 192 193 workgroup1 = KimApiServiceLocator.getGroupService().getGroupByNamespaceCodeAndName("KR-WKFLW", "AIWG-Nested1"); 194 document.adHocToGroup(ActionRequestType.APPROVE, "",workgroup1.getId(), "", true); 195 document.route(""); 196 197 assertEquals("User should have 1 action item", 1, KEWServiceLocator.getActionListService().findByPrincipalId(user1.getPrincipalId()).size()); 198 assertEquals("Workgroup should have 6 members.", 6, KimApiServiceLocator.getGroupService().getMemberPrincipalIds(workgroup1.getId()).size()); 199 200 //get the subgroup so we can remove the member. 201 Group workgroupSub = KimApiServiceLocator.getGroupService().getGroupByNamespaceCodeAndName("KR-WKFLW", 202 "AIWG-Nested2"); 203 KimApiServiceLocator.getGroupService().removePrincipalFromGroup(user1.getPrincipalId(), workgroupSub.getId()); 204 205 assertEquals("Workgroup should have 5 members.", 5, KimApiServiceLocator.getGroupService().getMemberPrincipalIds(workgroup1.getId()).size()); 206 assertEquals("User should have 0 action item", 0, KEWServiceLocator.getActionListService().findByPrincipalId(user1.getPrincipalId()).size()); 207 208 KimApiServiceLocator.getGroupService().addPrincipalToGroup(user1.getPrincipalId(), workgroupSub.getId()); 209 assertEquals("Workgroup should have 6 members.", 6, KimApiServiceLocator.getGroupService().getMemberPrincipalIds(workgroup1.getId()).size()); 210 assertEquals("User should have 1 action item", 1, KEWServiceLocator.getActionListService().findByPrincipalId(user1.getPrincipalId()).size()); 211 212 } 213 214 /** 215 * addresses the following bug http://fms.dfa.cornell.edu:8080/browse/KULWF-428 216 * 217 * @throws Exception 218 */ 219 @Test public void testWorkgroupActionItemGenerationWhenMultipleWorkgroupRequests() throws Exception { 220 WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("user1"), "ActionItemDocumentType"); 221 document.setTitle(""); 222 document.route(""); 223 224 document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("jitrue"), document.getDocumentId()); 225 226 Group testGroup = KimApiServiceLocator.getGroupService().getGroupByNamespaceCodeAndName( 227 KimConstants.KIM_GROUP_WORKFLOW_NAMESPACE_CODE, "AIWG-Test"); 228 Group adminGroup = KimApiServiceLocator.getGroupService().getGroupByNamespaceCodeAndName( 229 KimConstants.KIM_GROUP_WORKFLOW_NAMESPACE_CODE, "AIWG-Admin"); 230 231 List<ActionRequest> ars = document.getRootActionRequests(); 232 boolean routedWorkflowAdmin = false; 233 boolean routedTestWorkgroup = false; 234 for (ActionRequest request : ars) { 235 if (request.isGroupRequest() && testGroup.getId().equals(request.getGroupId())) { 236 routedTestWorkgroup = true; 237 } else if (request.isGroupRequest() && adminGroup.getId().equals(request.getGroupId())) { 238 routedWorkflowAdmin = true; 239 } 240 } 241 242 //verify that our test is sane 243 assertTrue("Should have routed to 'AIWG-Test'", routedTestWorkgroup); 244 assertTrue("Should have routed to 'AIWG-Admin'", routedWorkflowAdmin); 245 assertTrue("Approve should be requested to member of 'AIWG-Test'", document.isApprovalRequested()); 246 247 document.approve(""); 248 249 Collection actionItems = KEWServiceLocator.getActionListService().findByDocumentId(document.getDocumentId()); 250 251 assertEquals("There should be 6 action items to the AIWG-Admin.", 6, actionItems.size()); 252 253 for (Iterator iter = actionItems.iterator(); iter.hasNext();) { 254 ActionItem actionItem = (ActionItem)iter.next(); 255 //don't worry about which workgroup - they can get activated in any order 256 assertNotNull("this should be a workgroup request", actionItem.getGroup()); 257 } 258 } 259 260 /** 261 * This test verifies that if someone gets more than one request routed to them then they will get 262 * multiple Action Items but only one of them will show up in their Action List. 263 */ 264 @Test public void testMultipleActionItemGeneration() throws Exception { 265 WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("user1"), "ActionItemDocumentType"); 266 document.setTitle(""); 267 document.route(""); 268 269 // now the document should be at both the AIWG-Admin workgroup and the AIWG-Test 270 // ewestfal is a member of both Workgroups so verify that he has two action items 271 String ewestfalPrincipalId = getPrincipalIdForName("ewestfal"); 272 String jitruePrincipalId = getPrincipalIdForName("jitrue"); 273 274 Collection actionItems = KEWServiceLocator.getActionListService().findByWorkflowUserDocumentId(ewestfalPrincipalId, document.getDocumentId()); 275 assertEquals("Ewestfal should have two action items.", 2, actionItems.size()); 276 277 // now check the action list, there should be only one entry 278 actionItems = KEWServiceLocator.getActionListService().getActionList(ewestfalPrincipalId, null); 279 assertEquals("Ewestfal should have one action item in his action list.", 1, actionItems.size()); 280 document = WorkflowDocumentFactory.loadDocument(ewestfalPrincipalId, document.getDocumentId()); 281 assertTrue("Ewestfal should have an approval requested.", document.isApprovalRequested()); 282 283 // approve as a member from the first workgroup 284 document = WorkflowDocumentFactory.loadDocument(jitruePrincipalId, document.getDocumentId()); 285 assertTrue("Jitrue should have an approval requested.", document.isApprovalRequested()); 286 document.approve(""); 287 288 // now ewestfal should have only one action item in both his action items and his action list 289 actionItems = KEWServiceLocator.getActionListService().findByWorkflowUserDocumentId(ewestfalPrincipalId, document.getDocumentId()); 290 assertEquals("Ewestfal should have one action item.", 1, actionItems.size()); 291 String actionItemId = ((ActionItem)actionItems.iterator().next()).getId(); 292 actionItems = KEWServiceLocator.getActionListService().getActionList(ewestfalPrincipalId, null); 293 assertEquals("Ewestfal should have one action item in his action list.", 1, actionItems.size()); 294 assertEquals("The two action items should be the same.", actionItemId, ((ActionItem)actionItems.iterator().next()).getId()); 295 } 296 297 /** 298 * This tests verifies that bug KULWF-507 has been fixed: 299 * 300 * https://test.kuali.org/jira/browse/KULWF-507 301 * 302 * To fix this, we implemented the system so that multiple action items are generated rather then just 303 * one which gets reassigned across multiple requests as needed. 304 * 305 * This test verifies that after the blanket approval, there should no longer be an orphaned Acknowledge 306 * request. The workgroup used here is the TestWorkgroup and "user1" is ewestfal with "user2" as rkirkend. 307 * 308 * The routing is configured in the BAOrphanedRequestDocumentType. 309 */ 310 @Test public void testOrphanedAcknowledgeFromBlanketApprovalFix() throws Exception { 311 WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), "BAOrphanedRequestDocumentType"); 312 document.blanketApprove(""); 313 assertTrue("Document should be processed.", document.isProcessed()); 314 315 // after the document has blanket approved there should be 2 action items since the blanket approver 316 // is in the final workgroup. These action items should be the acknowledges generated to both 317 // rkirkend and user1 318 int numActionItems = actionListService.findByDocumentId(document.getDocumentId()).size(); 319 assertEquals("Incorrect number of action items.", 2, numActionItems); 320 321 String user1PrincipalId = getPrincipalIdForName("user1"); 322 String rkirkendPrincipalId = getPrincipalIdForName("rkirkend"); 323 324 // check that user1 has 1 action item 325 Collection actionItems = actionListService.findByWorkflowUserDocumentId(user1PrincipalId, document.getDocumentId()); 326 assertEquals("user1 should have one action item.", 1, actionItems.size()); 327 328 // check that rkirkend still has 1, the is where the bug would have manifested itself before, rkirkend would have had 329 // no action item (hence the orphaned request) 330 actionItems = actionListService.findByWorkflowUserDocumentId(rkirkendPrincipalId, document.getDocumentId()); 331 assertEquals("rkirkend should have one action item.", 1, actionItems.size()); 332 333 // lets go ahead and take it to final for funsies 334 document = WorkflowDocumentFactory.loadDocument(rkirkendPrincipalId, document.getDocumentId()); 335 assertTrue("Should have ack request.", document.isAcknowledgeRequested()); 336 document.acknowledge(""); 337 assertTrue("Should still be PROCESSED.", document.isProcessed()); 338 339 document = WorkflowDocumentFactory.loadDocument(user1PrincipalId, document.getDocumentId()); 340 assertTrue("Should have ack request.", document.isAcknowledgeRequested()); 341 document.acknowledge(""); 342 assertTrue("Should now be FINAL.", document.isFinal()); 343 } 344 345 /** 346 * Executes a deep copy of the BaseWorkgroup 347 */ 348 /* private BaseWorkgroup copy(BaseWorkgroup workgroup) throws Exception { 349 BaseWorkgroup workgroupCopy = (BaseWorkgroup)KEWServiceLocator.getWorkgroupService().copy(workgroup); 350 // copy above does a shallow copy so we need to deep copy members 351 List<BaseWorkgroupMember> members = workgroupCopy.getWorkgroupMembers(); 352 List<BaseWorkgroupMember> membersCopy = new ArrayList<BaseWorkgroupMember>(); 353 for (BaseWorkgroupMember member : members) { 354 membersCopy.add(copy(member)); 355 } 356 workgroupCopy.setWorkgroupMembers(membersCopy); 357 workgroupCopy.setMembers(new ArrayList<Recipient>()); 358 workgroupCopy.materializeMembers(); 359 return workgroupCopy; 360 } 361 362 private BaseWorkgroupMember copy(BaseWorkgroupMember member) throws Exception { 363 return (BaseWorkgroupMember)BeanUtils.cloneBean(member); 364 } 365 */ 366 367 protected Group getGroup(String groupId) { 368 return KimApiServiceLocator.getGroupService().getGroup(groupId); 369 } 370 }