001    /**
002     * Copyright 2005-2014 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.junit.Test;
019    import org.kuali.rice.kew.actiontaken.ActionTakenValue;
020    import org.kuali.rice.kew.api.KewApiConstants;
021    import org.kuali.rice.kew.api.KewApiServiceLocator;
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.ActionRequestType;
026    import org.kuali.rice.kew.api.action.AdHocRevoke;
027    import org.kuali.rice.kew.api.action.InvalidActionTakenException;
028    import org.kuali.rice.kew.api.exception.WorkflowException;
029    import org.kuali.rice.kew.service.KEWServiceLocator;
030    import org.kuali.rice.kew.test.KEWTestCase;
031    import org.kuali.rice.kew.test.TestUtilities;
032    import org.kuali.rice.kim.api.KimConstants;
033    
034    import java.util.Collection;
035    import java.util.List;
036    
037    import static org.junit.Assert.*;
038    
039    public class RevokeAdHocActionTest extends KEWTestCase {
040    
041            private static final String ADH0C_DOC = "AdhocRouteTest";
042            private String docId;
043    
044        protected void loadTestData() throws Exception {
045            loadXmlFile("ActionsConfig.xml");
046        }
047    
048        /**
049         * Tests revoking by action request id.
050         */
051        @Test public void testRevokeByActionRequestId() throws Exception {
052            WorkflowDocument doc = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("rkirkend"), ADH0C_DOC);
053            docId = doc.getDocumentId();
054            doc.adHocToPrincipal(ActionRequestType.APPROVE, "AdHoc", "annotation1", getPrincipalIdForName("dewey"), "respDesc1", false);
055    
056            // check the action requests
057            TestUtilities.assertNumberOfPendingRequests(docId, 1);
058            TestUtilities.assertUserHasPendingRequest(docId, "dewey");
059    
060            // now revoke by a bad action request id, we should recieve a WorkflowException
061            try {
062                    doc.revokeAdHocRequestById("123456789", "");
063                    fail("Revoking by a bad action request id should have thrown an exception!");
064            } catch (InvalidActionTakenException e) {}
065    
066            // revoke by the real action request id
067            List<ActionRequest> actionRequestVOs = KewApiServiceLocator.getWorkflowDocumentService().getRootActionRequests(docId);
068            assertEquals(1, actionRequestVOs.size());
069            String actionRequestId = actionRequestVOs.get(0).getId();
070            doc.revokeAdHocRequestById(actionRequestId, "");
071    
072            // there should now be no pending requests
073            TestUtilities.assertNumberOfPendingRequests(docId, 0);
074    
075            // route the document, this doc type is configured to route to user1
076            doc.route("");
077            doc = getDocument("user1");
078            assertTrue(doc.isApprovalRequested());
079    
080            // now attempt to revoke this non-adhoc request by id, it should throw an error
081            actionRequestVOs = KewApiServiceLocator.getWorkflowDocumentService().getRootActionRequests(docId);
082            for (int index = 0; index < actionRequestVOs.size(); index++) {
083                    if (actionRequestVOs.get(index).isPending()) {
084                            try {
085                                    doc.revokeAdHocRequestById(actionRequestVOs.get(index).getId().toString(), "");
086                                    fail("Attempted to revoke by an invalid action request id, should have thrown an error!");
087                            } catch (InvalidActionTakenException e) {}
088                    }
089            }
090    
091        }
092    
093        /**
094         * Tests revoking by user and workgroup.
095         */
096        @Test public void testRevokeByUserAndGroup() throws Exception {
097            // ad hoc the document to dewey (twice) and the workgroup WorkflowAdmin
098            WorkflowDocument doc =WorkflowDocumentFactory.createDocument(getPrincipalIdForName("rkirkend"), ADH0C_DOC);
099            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    }