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 }