View Javadoc
1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.actions;
17  
18  import org.junit.Test;
19  import org.kuali.rice.kew.actiontaken.ActionTakenValue;
20  import org.kuali.rice.kew.api.KewApiConstants;
21  import org.kuali.rice.kew.api.KewApiServiceLocator;
22  import org.kuali.rice.kew.api.WorkflowDocument;
23  import org.kuali.rice.kew.api.WorkflowDocumentFactory;
24  import org.kuali.rice.kew.api.action.ActionRequest;
25  import org.kuali.rice.kew.api.action.ActionRequestType;
26  import org.kuali.rice.kew.api.action.AdHocRevoke;
27  import org.kuali.rice.kew.api.action.InvalidActionTakenException;
28  import org.kuali.rice.kew.api.exception.WorkflowException;
29  import org.kuali.rice.kew.service.KEWServiceLocator;
30  import org.kuali.rice.kew.test.KEWTestCase;
31  import org.kuali.rice.kew.test.TestUtilities;
32  import org.kuali.rice.kim.api.KimConstants;
33  
34  import java.util.Collection;
35  import java.util.List;
36  
37  import static org.junit.Assert.*;
38  
39  public class RevokeAdHocActionTest extends KEWTestCase {
40  
41  	private static final String ADH0C_DOC = "AdhocRouteTest";
42  	private String docId;
43  
44      protected void loadTestData() throws Exception {
45          loadXmlFile("ActionsConfig.xml");
46      }
47  
48      /**
49       * Tests revoking by action request id.
50       */
51      @Test public void testRevokeByActionRequestId() throws Exception {
52      	WorkflowDocument doc = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("rkirkend"), ADH0C_DOC);
53      	docId = doc.getDocumentId();
54      	doc.adHocToPrincipal(ActionRequestType.APPROVE, "AdHoc", "annotation1", getPrincipalIdForName("dewey"), "respDesc1", false);
55  
56      	// check the action requests
57      	TestUtilities.assertNumberOfPendingRequests(docId, 1);
58      	TestUtilities.assertUserHasPendingRequest(docId, "dewey");
59  
60      	// now revoke by a bad action request id, we should recieve a WorkflowException
61      	try {
62      		doc.revokeAdHocRequestById("123456789", "");
63      		fail("Revoking by a bad action request id should have thrown an exception!");
64      	} catch (InvalidActionTakenException e) {}
65  
66      	// revoke by the real action request id
67      	List<ActionRequest> actionRequestVOs = KewApiServiceLocator.getWorkflowDocumentService().getRootActionRequests(docId);
68      	assertEquals(1, actionRequestVOs.size());
69      	String actionRequestId = actionRequestVOs.get(0).getId();
70      	doc.revokeAdHocRequestById(actionRequestId, "");
71  
72      	// there should now be no pending requests
73      	TestUtilities.assertNumberOfPendingRequests(docId, 0);
74  
75      	// route the document, this doc type is configured to route to user1
76      	doc.route("");
77      	doc = getDocument("user1");
78      	assertTrue(doc.isApprovalRequested());
79  
80      	// now attempt to revoke this non-adhoc request by id, it should throw an error
81      	actionRequestVOs = KewApiServiceLocator.getWorkflowDocumentService().getRootActionRequests(docId);
82      	for (int index = 0; index < actionRequestVOs.size(); index++) {
83      		if (actionRequestVOs.get(index).isPending()) {
84      			try {
85      				doc.revokeAdHocRequestById(actionRequestVOs.get(index).getId().toString(), "");
86      				fail("Attempted to revoke by an invalid action request id, should have thrown an error!");
87      			} catch (InvalidActionTakenException e) {}
88      		}
89      	}
90  
91      }
92  
93      /**
94       * Tests revoking by user and workgroup.
95       */
96      @Test public void testRevokeByUserAndGroup() throws Exception {
97      	// ad hoc the document to dewey (twice) and the workgroup WorkflowAdmin
98      	WorkflowDocument doc =WorkflowDocumentFactory.createDocument(getPrincipalIdForName("rkirkend"), ADH0C_DOC);
99      	docId = doc.getDocumentId();
100     	String principalId = getPrincipalIdForName("dewey");
101     	doc.adHocToPrincipal(ActionRequestType.APPROVE, "AdHoc", "annotationDewey1", principalId, "respDesc1", false);
102     	doc.adHocToPrincipal(ActionRequestType.APPROVE, "AdHoc", "annotationDewey2", principalId, "respDesc1", false);
103     	String groupId = getGroupIdForName(KimConstants.KIM_GROUP_WORKFLOW_NAMESPACE_CODE, "WorkflowAdmin");
104     	doc.adHocToGroup(ActionRequestType.APPROVE, "AdHoc", "Annotation WorkflowAdmin",  groupId, "respDesc2", true);
105 
106     	TestUtilities.assertNumberOfPendingRequests(docId, 3);
107     	TestUtilities.assertUserHasPendingRequest(docId, "dewey");
108     	TestUtilities.assertUserHasPendingRequest(docId, "quickstart");
109 
110     	// route the document, this should activate the ad hoc requests
111     	doc.route("");
112     	assertTrue(doc.isEnroute());
113     	TestUtilities.assertAtNodeNew(doc, "AdHoc");
114     	TestUtilities.assertNumberOfPendingRequests(docId, 3);
115 
116     	String testGroupId = getGroupIdForName(KimConstants.KIM_GROUP_WORKFLOW_NAMESPACE_CODE, "TestWorkgroup");
117     	// try revoking by a user and workgroup without adhoc requests, it should effectively be a no-op
118     	AdHocRevoke revoke = AdHocRevoke.createRevokeFromPrincipal(getPrincipalIdForName("ewestfal"));
119     	doc.revokeAdHocRequests(revoke, "This should be a no-op");
120     	revoke = AdHocRevoke.createRevokeFromGroup(testGroupId);
121     	doc.revokeAdHocRequests(revoke, "This should be a no-op");
122     	doc = getDocument("rkirkend");
123     	TestUtilities.assertNumberOfPendingRequests(docId, 3);
124     	TestUtilities.assertUserHasPendingRequest(docId, "dewey");
125     	TestUtilities.assertUserHasPendingRequest(docId, "quickstart");
126 
127     	// now revoke each request in turn, after the second one is invoked the document should transition to it's next route level
128     	// and route to user1
129     	revoke = AdHocRevoke.createRevokeFromPrincipal(getPrincipalIdForName("dewey"));
130     	doc.revokeAdHocRequests(revoke, "revokeUser");
131     	TestUtilities.assertNumberOfPendingRequests(docId, 1);
132     	doc = getDocument("dewey");
133     	assertFalse("dewey should no longer have an approve request.", doc.isApprovalRequested());
134     	revoke = AdHocRevoke.createRevokeFromGroup(groupId);
135     	doc.revokeAdHocRequests(revoke, "revokeWorkgroup");
136 
137     	// the doc should now transition to the next node
138     	doc = getDocument("user1");
139     	TestUtilities.assertAtNodeNew(doc, "One");
140     	assertTrue("user1 should have an approve request.", doc.isApprovalRequested());
141     	doc.approve("");
142 
143     	// doc should now be final
144     	assertTrue("doc should be final", doc.isFinal());
145     }
146 
147     /**
148      * Tests revoking by node name.
149      */
150     @Test public void testRevokeByNodeName() throws Exception {
151     	// ad hoc requests at the AdHoc node and then revoke the entire node
152     	WorkflowDocument doc = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("rkirkend"), ADH0C_DOC);
153     	docId = doc.getDocumentId();
154     	doc.adHocToPrincipal(ActionRequestType.APPROVE, "AdHoc", "annotationDewey1", getPrincipalIdForName("dewey"), "respDesc1", false);
155     	String groupId = getGroupIdForName(KimConstants.KIM_GROUP_WORKFLOW_NAMESPACE_CODE, "WorkflowAdmin");
156     	doc.adHocToGroup(ActionRequestType.APPROVE, "AdHoc", "Annotation WorkflowAdmin", groupId, "respDesc2", true);
157     	TestUtilities.assertNumberOfPendingRequests(docId, 2);
158 
159     	// now revoke the node
160     	doc.revokeAdHocRequests(AdHocRevoke.createRevokeAtNode("AdHoc"), "");
161     	TestUtilities.assertNumberOfPendingRequests(docId, 0);
162     	// send an Acknowledge to the AdHoc node prior to routing
163     	doc.adHocToPrincipal(ActionRequestType.ACKNOWLEDGE, "AdHoc", "annotationEwestfal1", getPrincipalIdForName("ewestfal"), "respDesc1", false);
164 
165     	// route the document
166     	doc = getDocument("rkirkend");
167     	doc.route("");
168     	TestUtilities.assertAtNodeNew(doc, "One");
169 
170     	// ewestfal should have an acknowledge request
171     	doc = getDocument("ewestfal");
172     	assertTrue(doc.isAcknowledgeRequested());
173 
174     	// approve the document, it should go PROCESSED
175     	doc = getDocument("user1");
176     	assertTrue(doc.isApprovalRequested());
177     	doc.approve("");
178     	assertTrue(doc.isProcessed());
179 
180     	// revoke at the "One" node where there are no requests, it should be a no-op (the document should stay processed)
181     	doc.revokeAdHocRequests(AdHocRevoke.createRevokeAtNode("One"), "");
182     	doc = getDocument("ewestfal");
183     	assertTrue(doc.isProcessed());
184 
185     	// now revoke the ACKNOWLEDGE to ewestfal by revoking at the "AdHoc" node, the document should go FINAL
186     	doc.revokeAdHocRequests(AdHocRevoke.createRevokeAtNode("AdHoc"), "");
187     	doc = getDocument("ewestfal");
188     	assertTrue(doc.isFinal());
189     }
190 
191     /**
192      * Tests the behavior revocation of ad hoc requests prior to the routing of the document.
193      *
194      * @throws Exception
195      */
196     @Test public void testRevokePriorToRouting() throws Exception {
197     	// ad hoc the document to dewey and the workgroup WorkflowAdmin
198     	WorkflowDocument doc = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("rkirkend"), ADH0C_DOC);
199     	docId = doc.getDocumentId();
200     	doc.adHocToPrincipal(ActionRequestType.APPROVE, "AdHoc", "annotation1", getPrincipalIdForName("dewey"), "respDesc1", false);
201 
202     	doc = getDocument("dewey");
203     	assertFalse("User andlee should not have an approve request yet.  Document not yet routed.", doc.isApprovalRequested());
204     	String groupId = getGroupIdForName(KimConstants.KIM_GROUP_WORKFLOW_NAMESPACE_CODE, "WorkflowAdmin");
205     	doc.adHocToGroup(ActionRequestType.APPROVE, "AdHoc", "annotation2", groupId , "respDesc2", true);
206     	doc = getDocument("quickstart");
207     	assertFalse("User should not have approve request yet.  Document not yet routed.", doc.isApprovalRequested());
208 
209     	// the document should be initiated at this point
210     	assertTrue("Document should still be intitiated.", doc.isInitiated());
211 
212     	// check and revoke the actual ActionRequestVOs
213     	// reaquire the document as the initiator
214     	doc = getDocument("rkirkend");
215     	List<ActionRequest> actionRequestVOs = KewApiServiceLocator.getWorkflowDocumentService().getRootActionRequests(doc.getDocumentId());
216     	assertEquals("There should be 2 ad hoc requests.", 2, actionRequestVOs.size());
217     	for (ActionRequest requestVO : actionRequestVOs) {
218     		assertTrue("Should be an ad hoc request.", requestVO.isAdHocRequest());
219     		// revoke by id
220     		doc.revokeAdHocRequestById(requestVO.getId().toString(), "");
221     	}
222 
223     	// now the document should have no pending action requests on it
224     	List actionRequests = KEWServiceLocator.getActionRequestService().findPendingByDoc(docId);
225     	assertEquals("There should be no pending requests.", 0, actionRequests.size());
226 
227     	// check that the "ActionTakens" have been properly recorded
228     	Collection<ActionTakenValue> actionTakens = KEWServiceLocator.getActionTakenService().findByDocumentId(docId);
229         // count how many are AdhocRevoked
230         int numAdhocRevoked = 0;
231         for (ActionTakenValue actionTaken : actionTakens) {
232             if (actionTaken.getActionTaken().equals(KewApiConstants.ACTION_TAKEN_ADHOC_REVOKED_CD)) {
233                 numAdhocRevoked++;
234             }
235         }
236     	assertEquals("There should be 2 'AdHocRevoked' action takens", 2, numAdhocRevoked);
237 
238     	// now check that the document is still intiated
239     	doc = getDocument("rkirkend");
240     	assertTrue("Document should still be intitiated.", doc.isInitiated());
241     }
242 
243     /**
244      * Tests the revocation of ad hoc requests after a blanket approve.  The goal of this test is to verify that revocation of
245      * ad hoc requests doesn't have any adverse effects on the notification requests
246      */
247     @Test public void testRevokeAfterBlanketApprove() throws Exception {
248     	WorkflowDocument doc = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("rkirkend"), ADH0C_DOC);
249     	docId = doc.getDocumentId();
250     	// send an FYI to the AdHoc node prior to blanket approving
251     	doc.adHocToPrincipal(ActionRequestType.FYI, "AdHoc", "annotationEwestfal1", getPrincipalIdForName("ewestfal"), "respDesc1", false);
252 
253     	// blanket approve the document
254     	doc.blanketApprove("");
255     	assertTrue(doc.isProcessed());
256 
257     	// ewestfal should have his ad hoc FYI and user1 should have an ack from the blanket approve
258     	doc = getDocument("ewestfal");
259     	assertTrue(doc.isFYIRequested());
260     	doc = getDocument("user1");
261     	assertTrue(doc.isAcknowledgeRequested());
262 
263     	// revoke all ad hoc requests
264     	doc.revokeAllAdHocRequests("revoking all adhocs");
265     	assertTrue(doc.isProcessed());
266     	TestUtilities.assertNumberOfPendingRequests(docId, 1);
267 
268     	// user1 should still have acknowledge request
269     	assertTrue(doc.isAcknowledgeRequested());
270 
271     	// ewestfal should no longer have an fyi
272     	doc = getDocument("ewestfal");
273     	assertFalse(doc.isFYIRequested());
274     }
275 
276     private WorkflowDocument getDocument(String netid) throws WorkflowException {
277     	return WorkflowDocumentFactory.loadDocument(getPrincipalIdForName(netid), docId);
278     }
279 
280 }