View Javadoc

1   /**
2    * Copyright 2005-2011 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.docsearch;
17  
18  import org.joda.time.DateTime;
19  import org.joda.time.Days;
20  import org.joda.time.Years;
21  import org.junit.Test;
22  import org.kuali.rice.core.api.config.property.ConfigContext;
23  import org.kuali.rice.kew.api.WorkflowDocument;
24  import org.kuali.rice.kew.api.WorkflowDocumentFactory;
25  import org.kuali.rice.kew.api.action.RequestedActions;
26  import org.kuali.rice.kew.api.document.DocumentStatus;
27  import org.kuali.rice.kew.api.document.DocumentStatusCategory;
28  import org.kuali.rice.kew.api.document.search.DocumentSearchCriteria;
29  import org.kuali.rice.kew.api.document.search.DocumentSearchResult;
30  import org.kuali.rice.kew.api.document.search.DocumentSearchResults;
31  import org.kuali.rice.kew.api.document.search.RouteNodeLookupLogic;
32  import org.kuali.rice.kew.docsearch.service.DocumentSearchService;
33  import org.kuali.rice.kew.doctype.bo.DocumentType;
34  import org.kuali.rice.kew.doctype.service.DocumentTypeService;
35  import org.kuali.rice.kew.engine.node.RouteNode;
36  import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
37  import org.kuali.rice.kew.service.KEWServiceLocator;
38  import org.kuali.rice.kew.test.KEWTestCase;
39  import org.kuali.rice.kew.useroptions.UserOptions;
40  import org.kuali.rice.kew.useroptions.UserOptionsService;
41  import org.kuali.rice.kew.api.KewApiConstants;
42  import org.kuali.rice.kim.api.identity.Person;
43  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
44  import org.kuali.rice.ksb.util.KSBConstants;
45  import org.kuali.rice.test.BaselineTestCase;
46  import org.kuali.rice.test.TestHarnessServiceLocator;
47  import org.springframework.jdbc.core.JdbcTemplate;
48  
49  import java.util.Arrays;
50  import java.util.Collection;
51  import java.util.HashMap;
52  import java.util.Iterator;
53  import java.util.List;
54  import java.util.Map;
55  import java.util.Set;
56  
57  import static org.junit.Assert.*;
58  import static org.junit.Assert.assertEquals;
59  
60  @BaselineTestCase.BaselineMode(BaselineTestCase.Mode.CLEAR_DB)
61  public class DocumentSearchTest extends KEWTestCase {
62      private static final String KREW_DOC_HDR_T = "KREW_DOC_HDR_T";
63      private static final String INITIATOR_COL = "INITR_PRNCPL_ID";
64  
65      DocumentSearchService docSearchService;
66      UserOptionsService userOptionsService;
67  
68      @Override
69      protected void loadTestData() throws Exception {
70          loadXmlFile("SearchAttributeConfig.xml");
71      }
72  
73      @Override
74      protected void setUpAfterDataLoad() throws Exception {
75          docSearchService = (DocumentSearchService)KEWServiceLocator.getDocumentSearchService();
76          userOptionsService = (UserOptionsService)KEWServiceLocator.getUserOptionsService();
77      }
78  
79  
80      @Test public void testDocSearch() throws Exception {
81          Person user = KimApiServiceLocator.getPersonService().getPersonByPrincipalName("bmcgough");
82          DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
83          DocumentSearchResults results = null;
84          criteria.setTitle("*IN");
85          criteria.setSaveName("bytitle");
86          results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build());
87          criteria = DocumentSearchCriteria.Builder.create();
88          criteria.setTitle("*IN-CFSG");
89          criteria.setSaveName("for in accounts");
90          results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build());
91          criteria = DocumentSearchCriteria.Builder.create();
92          criteria.setDateApprovedFrom(new DateTime(2004, 9, 16, 0, 0));
93          results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build());
94          criteria = DocumentSearchCriteria.Builder.create();
95          user = KimApiServiceLocator.getPersonService().getPersonByPrincipalName("bmcgough");
96          DocumentSearchCriteria savedCriteria = docSearchService.getNamedSearchCriteria(user.getPrincipalId(), "bytitle");
97          assertNotNull(savedCriteria);
98          assertEquals("bytitle", savedCriteria.getSaveName());
99          savedCriteria = docSearchService.getNamedSearchCriteria(user.getPrincipalId(), "for in accounts");
100         assertNotNull(savedCriteria);
101         assertEquals("for in accounts", savedCriteria.getSaveName());
102     }
103 
104     @Test public void testDocSearch_maxResults() throws Exception {
105         String[] docIds = routeTestDocs();
106 
107         String principalId = getPrincipalId("bmcgough");
108 
109         DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
110         criteria.setDocumentTypeName("SearchDocType");
111         criteria.setMaxResults(5);
112         DocumentSearchResults results = docSearchService.lookupDocuments(principalId, criteria.build());
113         assertEquals(3, results.getSearchResults().size());
114         criteria.setMaxResults(2);
115         results = docSearchService.lookupDocuments(principalId, criteria.build());
116         assertEquals(2, results.getSearchResults().size());
117 
118         // test search result document population
119         // break out into separate test if/when we have more fields to test
120         assertEquals("_blank", results.getSearchResults().get(0).getDocument().getDocumentHandlerUrl());
121     }
122 
123     @Test public void testDocSearch_maxResultsIsNull() throws Exception {
124         String[] docIds = routeTestDocs();
125 
126         String principalId = getPrincipalId("bmcgough");
127 
128         DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
129         criteria.setDocumentTypeName("SearchDocType");
130         criteria.setMaxResults(5);
131         DocumentSearchResults results = docSearchService.lookupDocuments(principalId, criteria.build());
132         assertEquals(3, results.getSearchResults().size());
133         criteria.setMaxResults(null);
134         results = docSearchService.lookupDocuments(principalId, criteria.build());
135         assertEquals(3, results.getSearchResults().size());
136     }
137 
138     @Test public void testDocSearch_maxResultsIsZero() throws Exception {
139         String[] docIds = routeTestDocs();
140 
141         String principalId = getPrincipalId("bmcgough");
142 
143         DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
144         criteria.setDocumentTypeName("SearchDocType");
145         criteria.setMaxResults(5);
146         DocumentSearchResults results = docSearchService.lookupDocuments(principalId, criteria.build());
147         assertEquals(3, results.getSearchResults().size());
148         criteria.setMaxResults(0);
149         results = docSearchService.lookupDocuments(principalId, criteria.build());
150         assertEquals(0, results.getSearchResults().size());
151     }
152 
153     @Test public void testDocSearch_startAtIndex() throws Exception {
154         String[] docIds = routeTestDocs();
155 
156         String principalId = getPrincipalId("bmcgough");
157 
158         DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
159         criteria.setDocumentTypeName("SearchDocType");
160         criteria.setMaxResults(5);
161         DocumentSearchResults results = docSearchService.lookupDocuments(principalId, criteria.build());
162         assertEquals(3, results.getSearchResults().size());
163         criteria.setStartAtIndex(1);
164         results = docSearchService.lookupDocuments(principalId, criteria.build());
165         assertEquals(2, results.getSearchResults().size());
166     }
167 
168     @Test public void testDocSearch_startAtIndexMoreThanResuls() throws Exception {
169         String[] docIds = routeTestDocs();
170 
171         String principalId = getPrincipalId("bmcgough");
172 
173         DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
174         criteria.setDocumentTypeName("SearchDocType");
175         criteria.setMaxResults(5);
176         DocumentSearchResults results = docSearchService.lookupDocuments(principalId, criteria.build());
177         assertEquals(3, results.getSearchResults().size());
178         criteria.setStartAtIndex(5);
179         results = docSearchService.lookupDocuments(principalId, criteria.build());
180         assertEquals(0, results.getSearchResults().size());
181 
182     }
183 
184     @Test public void testDocSearch_startAtIndexNegative() throws Exception {
185         String[] docIds = routeTestDocs();
186 
187         String principalId = getPrincipalId("bmcgough");
188 
189         DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
190         criteria.setDocumentTypeName("SearchDocType");
191         criteria.setMaxResults(5);
192         DocumentSearchResults results = docSearchService.lookupDocuments(principalId, criteria.build());
193         assertEquals(3, results.getSearchResults().size());
194         criteria.setStartAtIndex(-1);
195         results = docSearchService.lookupDocuments(principalId, criteria.build());
196         assertEquals(0, results.getSearchResults().size());
197 
198     }
199 
200     @Test public void testDocSearch_startAtIndexZero() throws Exception {
201         String[] docIds = routeTestDocs();
202 
203         String principalId = getPrincipalId("bmcgough");
204 
205         DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
206         criteria.setDocumentTypeName("SearchDocType");
207         criteria.setMaxResults(5);
208         DocumentSearchResults results = docSearchService.lookupDocuments(principalId, criteria.build());
209         assertEquals(3, results.getSearchResults().size());
210         criteria.setStartAtIndex(0);
211         results = docSearchService.lookupDocuments(principalId, criteria.build());
212         assertEquals(3, results.getSearchResults().size());
213 
214     }
215 
216     /**
217      * Tests that performing a search automatically saves the last search criteria
218      */
219     @Test public void testUnnamedDocSearchPersistence() throws Exception {
220         Person user = KimApiServiceLocator.getPersonService().getPersonByPrincipalName("bmcgough");
221         Collection<UserOptions> allUserOptions_before = userOptionsService.findByWorkflowUser(user.getPrincipalId());
222         List<UserOptions> namedSearches_before = userOptionsService.findByUserQualified(user.getPrincipalId(), "DocSearch.NamedSearch.%");
223 
224         assertEquals(0, namedSearches_before.size());
225         assertEquals(0, allUserOptions_before.size());
226 
227         DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
228         criteria.setTitle("*IN");
229         criteria.setDateCreatedFrom(DateTime.now().minus(Years.ONE)); // otherwise one is set for us
230         DocumentSearchCriteria c1 = criteria.build();
231         DocumentSearchResults results = docSearchService.lookupDocuments(user.getPrincipalId(), c1);
232 
233         Collection<UserOptions> allUserOptions_after = userOptionsService.findByWorkflowUser(user.getPrincipalId());
234         List<UserOptions> namedSearches_after = userOptionsService.findByUserQualified(user.getPrincipalId(), "DocSearch.NamedSearch.%");
235 
236         // saves the "last doc search criteria"
237         // and a pointer to the "last doc search criteria"
238         assertEquals(allUserOptions_before.size() + 2, allUserOptions_after.size());
239         assertEquals(namedSearches_before.size(), namedSearches_after.size());
240 
241         assertEquals("DocSearch.LastSearch.Holding0", userOptionsService.findByOptionId("DocSearch.LastSearch.Order".toString(), user.getPrincipalId()).getOptionVal());
242         assertEquals(marshall(c1), userOptionsService.findByOptionId("DocSearch.LastSearch.Holding0", user.getPrincipalId()).getOptionVal());
243 
244         // 2nd search
245 
246         criteria = DocumentSearchCriteria.Builder.create();
247         criteria.setTitle("*IN-CFSG*");
248         criteria.setDateCreatedFrom(DateTime.now().minus(Years.ONE)); // otherwise one is set for us
249         DocumentSearchCriteria c2 = criteria.build();
250         results = docSearchService.lookupDocuments(user.getPrincipalId(), c2);
251 
252         // still only 2 more user options
253         assertEquals(allUserOptions_before.size() + 2, allUserOptions_after.size());
254         assertEquals(namedSearches_before.size(), namedSearches_after.size());
255 
256         assertEquals("DocSearch.LastSearch.Holding1,DocSearch.LastSearch.Holding0", userOptionsService.findByOptionId("DocSearch.LastSearch.Order", user.getPrincipalId()).getOptionVal());
257         assertEquals(marshall(c1), userOptionsService.findByOptionId("DocSearch.LastSearch.Holding0", user.getPrincipalId()).getOptionVal());
258         assertEquals(marshall(c2), userOptionsService.findByOptionId("DocSearch.LastSearch.Holding1", user.getPrincipalId()).getOptionVal());
259     }
260 
261      /**
262      * Tests that performing a named search automatically saves the last search criteria as well as named search
263      */
264     @Test public void testNamedDocSearchPersistence() throws Exception {
265         Person user = KimApiServiceLocator.getPersonService().getPersonByPrincipalName("bmcgough");
266         Collection<UserOptions> allUserOptions_before = userOptionsService.findByWorkflowUser(user.getPrincipalId());
267         List<UserOptions> namedSearches_before = userOptionsService.findByUserQualified(user.getPrincipalId(), "DocSearch.NamedSearch.%");
268 
269         DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
270         criteria.setTitle("*IN");
271         criteria.setSaveName("bytitle");
272         criteria.setDateCreatedFrom(DateTime.now().minus(Years.ONE)); // otherwise one is set for us
273         DocumentSearchCriteria c1 = criteria.build();
274         DocumentSearchResults results = docSearchService.lookupDocuments(user.getPrincipalId(), c1);
275 
276         Collection<UserOptions> allUserOptions_after = userOptionsService.findByWorkflowUser(user.getPrincipalId());
277         List<UserOptions> namedSearches_after = userOptionsService.findByUserQualified(user.getPrincipalId(), "DocSearch.NamedSearch.%");
278 
279         assertEquals(allUserOptions_before.size() + 1, allUserOptions_after.size());
280         assertEquals(namedSearches_before.size() + 1, namedSearches_after.size());
281 
282         assertEquals(marshall(c1), userOptionsService.findByOptionId("DocSearch.NamedSearch." + criteria.getSaveName(), user.getPrincipalId()).getOptionVal());
283 
284         // second search
285         criteria = DocumentSearchCriteria.Builder.create();
286         criteria.setTitle("*IN");
287         criteria.setSaveName("bytitle2");
288         criteria.setDateCreatedFrom(DateTime.now().minus(Years.ONE)); // otherwise one is set for us
289         DocumentSearchCriteria c2 = criteria.build();
290         results = docSearchService.lookupDocuments(user.getPrincipalId(), c2);
291 
292         allUserOptions_after = userOptionsService.findByWorkflowUser(user.getPrincipalId());
293         namedSearches_after = userOptionsService.findByUserQualified(user.getPrincipalId(), "DocSearch.NamedSearch.%");
294 
295         // saves a second named search
296         assertEquals(allUserOptions_before.size() + 2, allUserOptions_after.size());
297         assertEquals(namedSearches_before.size() + 2, namedSearches_after.size());
298 
299         assertEquals(marshall(c2), userOptionsService.findByOptionId("DocSearch.NamedSearch." + criteria.getSaveName(), user.getPrincipalId()).getOptionVal());
300 
301     }
302 
303     protected static String marshall(DocumentSearchCriteria criteria) throws Exception {
304         return DocumentSearchInternalUtils.marshalDocumentSearchCriteria(criteria);
305     }
306 
307     @Test
308     public void testDocSearch_criteriaModified() throws Exception {
309         String principalId = getPrincipalId("ewestfal");
310 
311         // if no criteria is specified, the dateCreatedFrom is defaulted to today
312         DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
313         DocumentSearchResults results = docSearchService.lookupDocuments(principalId, criteria.build());
314         assertTrue("criteria should have been modified", results.isCriteriaModified());
315         assertNull("original date created from should have been null", criteria.getDateCreatedFrom());
316         assertNotNull("modified date created from should be non-null", results.getCriteria().getDateCreatedFrom());
317         assertEquals("Criteria date minus today's date should equal the constant value",
318                 KewApiConstants.DOCUMENT_SEARCH_NO_CRITERIA_CREATE_DATE_DAYS_AGO.intValue(),
319                 getDifferenceInDays(results.getCriteria().getDateCreatedFrom()));
320 
321         // now set some attributes which should still result in modified criteria since they don't count toward
322         // determining if the criteria is empty or not
323         criteria.setMaxResults(new Integer(50));
324         criteria.setSaveName("myRadSearch");
325         results = docSearchService.lookupDocuments(principalId, criteria.build());
326         assertTrue("criteria should have been modified", results.isCriteriaModified());
327         assertNotNull("modified date created from should be non-null", results.getCriteria().getDateCreatedFrom());
328 
329         // now set the title, when only title is specified, date created from is defaulted
330         criteria.setTitle("My rad title search!");
331         results = docSearchService.lookupDocuments(principalId, criteria.build());
332         assertTrue("criteria should have been modified", results.isCriteriaModified());
333         assertNotNull("modified date created from should be non-null", results.getCriteria().getDateCreatedFrom());
334         assertEquals("Criteria date minus today's date should equal the constant value",
335                 Math.abs(KewApiConstants.DOCUMENT_SEARCH_DOC_TITLE_CREATE_DATE_DAYS_AGO.intValue()),
336                 getDifferenceInDays(results.getCriteria().getDateCreatedFrom()));
337 
338         // now set another field on the criteria, modification should *not* occur
339         criteria.setApplicationDocumentId("12345");
340         results = docSearchService.lookupDocuments(principalId, criteria.build());
341         assertFalse("criteria should *not* have been modified", results.isCriteriaModified());
342         assertNull("modified date created from should still be null", results.getCriteria().getDateCreatedFrom());
343         assertEquals("both criterias should be equal", criteria.build(), results.getCriteria());
344     }
345 
346     /**
347      * Test for https://test.kuali.org/jira/browse/KULRICE-1968 - Document search fails when users are missing
348      * Tests that we can safely search on docs whose initiator no longer exists in the identity management system
349      * This test searches by doc type name criteria.
350      * @throws Exception
351      */
352     @Test public void testDocSearch_MissingInitiator() throws Exception {
353         String documentTypeName = "SearchDocType";
354         DocumentType docType = ((DocumentTypeService)KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE)).findByName(documentTypeName);
355         String userNetworkId = "arh14";
356         // route a document to enroute and route one to final
357         WorkflowDocument workflowDocument = WorkflowDocumentFactory.createDocument(getPrincipalId(userNetworkId), documentTypeName);
358         workflowDocument.setTitle("testDocSearch_MissingInitiator");
359         workflowDocument.route("routing this document.");
360 
361         // verify the document is enroute for jhopf
362         workflowDocument = WorkflowDocumentFactory.loadDocument(getPrincipalId("jhopf"),workflowDocument.getDocumentId());
363         assertTrue(workflowDocument.isEnroute());
364         assertTrue(workflowDocument.isApprovalRequested());
365 
366         // now nuke the initiator...
367         new JdbcTemplate(TestHarnessServiceLocator.getDataSource()).execute("update " + KREW_DOC_HDR_T + " set " + INITIATOR_COL + " = 'bogus user' where DOC_HDR_ID = " + workflowDocument.getDocumentId());
368 
369 
370         Person user = KimApiServiceLocator.getPersonService().getPersonByPrincipalName("jhopf");
371         DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
372         criteria.setDocumentTypeName(documentTypeName);
373         DocumentSearchResults results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build());
374         assertEquals("Search returned invalid number of documents", 1, results.getSearchResults().size());
375     }
376 
377     /**
378      * Test for https://test.kuali.org/jira/browse/KULRICE-1968 - Tests that we get an error if we try and search on an initiator that doesn't exist in the IDM system
379      * @throws Exception
380      */
381     @Test public void testDocSearch_SearchOnMissingInitiator() throws Exception {
382         String documentTypeName = "SearchDocType";
383         DocumentType docType = ((DocumentTypeService)KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE)).findByName(documentTypeName);
384         String userNetworkId = "arh14";
385         // route a document to enroute and route one to final
386         WorkflowDocument workflowDocument = WorkflowDocumentFactory.createDocument(getPrincipalId(userNetworkId), documentTypeName);
387         workflowDocument.setTitle("testDocSearch_MissingInitiator");
388         workflowDocument.route("routing this document.");
389 
390         // verify the document is enroute for jhopf
391         workflowDocument = WorkflowDocumentFactory.loadDocument(getPrincipalId("jhopf"),workflowDocument.getDocumentId());
392         assertTrue(workflowDocument.isEnroute());
393         assertTrue(workflowDocument.isApprovalRequested());
394 
395         // now nuke the initiator...
396         new JdbcTemplate(TestHarnessServiceLocator.getDataSource()).execute("update " + KREW_DOC_HDR_T + " set " + INITIATOR_COL + " = 'bogus user' where DOC_HDR_ID = " + workflowDocument.getDocumentId());
397 
398 
399         Person user = KimApiServiceLocator.getPersonService().getPersonByPrincipalName("jhopf");
400         DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
401         criteria.setInitiatorPrincipalName("bogus user");
402 
403         DocumentSearchResults results = docSearchService.lookupDocuments(user.getPrincipalId(),
404                 criteria.build());
405         int size = results.getSearchResults().size();
406         assertTrue("Searching by an invalid initiator should return nothing", size == 0);
407 
408     }
409 
410     @Test public void testDocSearch_RouteNodeName() throws Exception {
411         loadXmlFile("DocSearchTest_RouteNode.xml");
412         String documentTypeName = "SearchDocType_RouteNodeTest";
413         DocumentType docType = ((DocumentTypeService)KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE)).findByName(documentTypeName);
414         String userNetworkId = "rkirkend";
415 
416         // route a document to enroute and route one to final
417         WorkflowDocument workflowDocument = WorkflowDocumentFactory.createDocument(getPrincipalId(userNetworkId), documentTypeName);
418         workflowDocument.setTitle("Routing style");
419         workflowDocument.route("routing this document.");
420         // verify the document is enroute for jhopf
421         workflowDocument = WorkflowDocumentFactory.loadDocument(getPrincipalId("jhopf"),workflowDocument.getDocumentId());
422         assertTrue(workflowDocument.isEnroute());
423         assertTrue(workflowDocument.isApprovalRequested());
424         workflowDocument.approve("");
425         workflowDocument = WorkflowDocumentFactory.loadDocument(getPrincipalId("jhopf"),workflowDocument.getDocumentId());
426         assertTrue(workflowDocument.isFinal());
427         workflowDocument = WorkflowDocumentFactory.createDocument(getPrincipalId(userNetworkId), documentTypeName);
428         workflowDocument.setTitle("Routing style");
429         workflowDocument.route("routing this document.");
430         // verify the document is enroute for jhopf
431         workflowDocument = WorkflowDocumentFactory.loadDocument(getPrincipalId("jhopf"),workflowDocument.getDocumentId());
432         assertTrue(workflowDocument.isEnroute());
433         assertTrue(workflowDocument.isApprovalRequested());
434 
435 
436         Person user = KimApiServiceLocator.getPersonService().getPersonByPrincipalName(userNetworkId);
437         DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
438         criteria.setDocumentTypeName(documentTypeName);
439         DocumentSearchResults results = docSearchService.lookupDocuments(user.getPrincipalId(),
440                 criteria.build());
441         assertEquals("Search returned invalid number of documents", 2, results.getSearchResults().size());
442 
443         criteria.setRouteNodeName(getRouteNodeForSearch(documentTypeName,workflowDocument.getNodeNames()));
444         criteria.setRouteNodeLookupLogic(RouteNodeLookupLogic.EXACTLY);
445         results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build());
446         assertEquals("Search returned invalid number of documents", 1, results.getSearchResults().size());
447 
448         // load the document type again to change the route node ids
449         loadXmlFile("DocSearchTest_RouteNode.xml");
450 
451         workflowDocument = WorkflowDocumentFactory.loadDocument(getPrincipalId("jhopf"),workflowDocument.getDocumentId());
452         assertTrue(workflowDocument.isEnroute());
453         assertTrue(workflowDocument.isApprovalRequested());
454         criteria.setRouteNodeName(getRouteNodeForSearch(documentTypeName, workflowDocument.getNodeNames()));
455         results = docSearchService.lookupDocuments(user.getPrincipalId(), criteria.build());
456         assertEquals("Search returned invalid number of documents", 1, results.getSearchResults().size());
457 
458     }
459 
460     private String getRouteNodeForSearch(String documentTypeName, Set<String> nodeNames) {
461         assertEquals(1,	nodeNames.size());
462     String expectedNodeName = nodeNames.iterator().next();
463         List routeNodes = KEWServiceLocator.getRouteNodeService().getFlattenedNodes(KEWServiceLocator.getDocumentTypeService().findByName(documentTypeName), true);
464         for (Iterator iterator = routeNodes.iterator(); iterator.hasNext();) {
465         RouteNode node = (RouteNode) iterator.next();
466         if (expectedNodeName.equals(node.getRouteNodeName())) {
467         return node.getRouteNodeName();
468         }
469     }
470         return null;
471     }
472 
473     @Test public void testGetNamedDocSearches() throws Exception {
474         List namedSearches = docSearchService.getNamedSearches(getPrincipalId("bmcgough"));
475         assertNotNull(namedSearches);
476     }
477 
478     private static int getDifferenceInDays(DateTime compareDate) {
479         return Days.daysBetween(compareDate, new DateTime()).getDays();
480     }
481 
482     /**
483      * Tests searching against document search attrs
484      * @throws Exception
485      */
486     @Test public void testDocSearchWithAttributes() throws Exception {
487         String[] docIds = routeTestDocs();
488 
489         String principalId = getPrincipalId("bmcgough");
490         DocumentSearchCriteria.Builder builder = DocumentSearchCriteria.Builder.create();
491         builder.setDocumentTypeName("SearchDocType");
492         builder.setSaveName("testDocSearchWithAttributes");
493         Map<String, List<String>> docAttrs = new HashMap<String, List<String>>();
494         docAttrs.put(TestXMLSearchableAttributeString.SEARCH_STORAGE_KEY, Arrays.asList(new String[]{TestXMLSearchableAttributeString.SEARCH_STORAGE_VALUE}));
495         builder.setDocumentAttributeValues(docAttrs);
496 
497         DocumentSearchResults results = docSearchService.lookupDocuments(principalId, builder.build());
498         assertEquals(docIds.length, results.getSearchResults().size());
499 
500         DocumentSearchCriteria loaded = docSearchService.getNamedSearchCriteria(principalId, builder.getSaveName());
501         assertNotNull(loaded);
502         assertEquals(docAttrs, loaded.getDocumentAttributeValues());
503 
504         // re-run saved search
505         results = docSearchService.lookupDocuments(principalId, loaded);
506         assertEquals(docIds.length, results.getSearchResults().size());
507     }
508 
509     /**
510      * Tests the usage of wildcards on the regular document search attributes.
511      * @throws Exception
512      */
513     @Test public void testDocSearch_WildcardsOnRegularAttributes() throws Exception {
514         // TODO: Add some wildcard testing for the document type attribute once wildcards are usable with it.
515 
516         // Route some test documents.
517         String[] docIds = routeTestDocs();
518 
519         String principalId = getPrincipalId("bmcgough");
520         DocumentSearchCriteria.Builder criteria = null;
521         DocumentSearchResults results = null;
522 
523         /**
524          * BEGIN - commenting out until we can resolve issues with person service not returning proper persons based on wildcards and various things
525          */
526         // Test the wildcards on the initiator attribute.
527         String[] searchStrings = {"!quickstart", "!quickstart&&!rkirkend", "!admin", "user1", "quickstart|bmcgough",
528         		"admin|rkirkend", ">bmcgough", ">=rkirkend", "<bmcgough", "<=quickstart", ">bmcgough&&<=rkirkend", "<rkirkend&&!bmcgough",
529         		"?mc?oug?", "*t", "*i?k*", "*", "!quick*", "!*g*&&!*k*", "quickstart..rkirkend"};
530         int[] expectedResults = {2, 1, 3, 0, 2, 1, 2, 1, 0, 2, 2, 1, 1, 1, 2, 3, 2, 0, 2/*1*/};
531         for (int i = 0; i < searchStrings.length; i++) {
532         	criteria = DocumentSearchCriteria.Builder.create();
533         	criteria.setInitiatorPrincipalName(searchStrings[i]);
534         	results = docSearchService.lookupDocuments(principalId, criteria.build());
535         	assertEquals("Initiator search at index " + i + " retrieved the wrong number of documents.", expectedResults[i], results.getSearchResults().size());
536         }
537 
538         // Test the wildcards on the approver attribute.
539         searchStrings = new String[] {"jhopf","!jhopf", ">jhopf", "<jjopf", ">=quickstart", "<=jhopf", "jhope..jhopg", "?hopf", "*i*", "!*f", "j*"};
540         expectedResults = new int[] {1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1};
541         for (int i = 0; i < searchStrings.length; i++) {
542         	criteria = DocumentSearchCriteria.Builder.create();
543         	criteria.setApproverPrincipalName(searchStrings[i]);
544         	results = docSearchService.lookupDocuments(principalId, criteria.build());
545         	assertEquals("Approver search at index " + i + " retrieved the wrong number of documents.", expectedResults[i], results.getSearchResults().size());
546         }
547 
548         // Test the wildcards on the viewer attribute.
549         searchStrings = new String[] {"jhopf","!jhopf", ">jhopf", "<jjopf", ">=quickstart", "<=jhopf", "jhope..jhopg", "?hopf", "*i*", "!*f", "j*"};
550         expectedResults = new int[] {3, 0, 0, 3, 0, 3, 3, 3, 0, 0, 3};
551         for (int i = 0; i < searchStrings.length; i++) {
552         	criteria = DocumentSearchCriteria.Builder.create();
553         	criteria.setViewerPrincipalName(searchStrings[i]);
554         	results = docSearchService.lookupDocuments(principalId, criteria.build());
555         	if(expectedResults[i] !=  results.getSearchResults().size()){
556         		assertEquals("Viewer search at index " + i + " retrieved the wrong number of documents.", expectedResults[i], results.getSearchResults().size());
557         	}
558         }
559 
560         /**
561          * END
562          */
563 
564         // Test the wildcards on the document/notification ID attribute. The string wildcards should work, since the doc ID is not a string.
565         searchStrings = new String[] {"!"+docIds[0], docIds[1]+"|"+docIds[2], "<="+docIds[1], ">="+docIds[2], "<"+docIds[0]+"&&>"+docIds[2],
566                 ">"+docIds[1], "<"+docIds[2]+"&&!"+docIds[0], docIds[0]+".."+docIds[2], "?"+docIds[1]+"*", "?"+docIds[1].substring(1)+"*", "?9*7"};
567         expectedResults = new int[] {2, 2, 2, 1, 0, 1, 1, 3, 0, 1, 0};
568         for (int i = 0; i < searchStrings.length; i++) {
569             criteria = DocumentSearchCriteria.Builder.create();
570             criteria.setDocumentId(searchStrings[i]);
571             results = docSearchService.lookupDocuments(principalId, criteria.build());
572             assertEquals("Doc ID search at index " + i + " retrieved the wrong number of documents.", expectedResults[i], results.getSearchResults().size());
573         }
574 
575         // Test the wildcards on the application document/notification ID attribute. The string wildcards should work, since the app doc ID is a string.
576         searchStrings = new String[] {"6543", "5432|4321", ">4321", "<=5432", ">=6543", "<3210", "!3210", "!5432", "!4321!5432", ">4321&&!6543",
577                 "*5?3*", "*", "?3?1", "!*43*", "!???2", ">43*1", "<=5432&&!?32?", "5432..6543"};
578         expectedResults = new int[] {1, 2, 2, 2, 1, 0, 3, 2, 1, 1, 2, 3, 1, 0, 2, 3, 1, 2/*1*/};
579         for (int i = 0; i < searchStrings.length; i++) {
580             criteria = DocumentSearchCriteria.Builder.create();
581             criteria.setApplicationDocumentId(searchStrings[i]);
582             results = docSearchService.lookupDocuments(principalId, criteria.build());
583             if(expectedResults[i] !=  results.getSearchResults().size()){
584                 assertEquals("App doc ID search at index " + i + " retrieved the wrong number of documents.", expectedResults[i], results.getSearchResults().size());
585             }
586         }
587 
588         // Test the wildcards on the title attribute.
589         searchStrings = new String[] {"Some New Document", "Document Number 2|The New Doc", "!The New Doc", "!Some New Document!Document Number 2",
590                 "!The New Doc&&!Some New Document", ">Document Number 2", "<=Some New Document", ">=The New Doc&&<Some New Document", ">A New Doc",
591                 "<Some New Document|The New Doc", ">=Document Number 2&&!Some New Document", "*Docu??nt*", "*New*", "The ??? Doc", "*Doc*", "*Number*",
592                 "Some New Document..The New Doc", "Document..The", "*New*&&!*Some*", "!The ??? Doc|!*New*"};
593         expectedResults = new int[] {1, 2, 2, 1, 1, 2, 2, 0, 3, 2, 2, 2, 2, 1, 3, 1, 2/*1*/, 2, 1, 2};
594         for (int i = 0; i < searchStrings.length; i++) {
595             criteria = DocumentSearchCriteria.Builder.create();
596             criteria.setTitle(searchStrings[i]);
597             results = docSearchService.lookupDocuments(principalId, criteria.build());
598             if(expectedResults[i] !=  results.getSearchResults().size()){
599                 assertEquals("Doc title search at index " + i + " retrieved the wrong number of documents.", expectedResults[i], results.getSearchResults().size());
600             }
601         }
602 
603     }
604 
605     @Test public void testAdditionalDocumentTypesCriteria() throws Exception {
606         String[] docIds = routeTestDocs();
607         String docId2 = routeTestDoc2();
608 
609         String principalId = getPrincipalId("bmcgough");
610 
611         DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
612         criteria.setDocumentTypeName("SearchDocType");
613 
614         // TODO finish this test
615         DocumentSearchResults results = docSearchService.lookupDocuments(principalId, criteria.build());
616         assertEquals(3, results.getSearchResults().size());
617 
618         criteria.setDocumentTypeName("SearchDocType2");
619         results = docSearchService.lookupDocuments(principalId, criteria.build());
620         assertEquals(1, results.getSearchResults().size());
621 
622         criteria.getAdditionalDocumentTypeNames().add("SearchDocType");
623         results = docSearchService.lookupDocuments(principalId, criteria.build());
624         assertEquals(4, results.getSearchResults().size());
625     }
626 
627     /**
628      * Tests searching on document status and document status category
629      */
630     @Test public void testDocumentStatusSearching() {
631         String dt = "SearchDocType";
632         String pid = getPrincipalIdForName("quickstart");
633         WorkflowDocument initiated = WorkflowDocumentFactory.createDocument(pid, dt);
634         WorkflowDocument saved = WorkflowDocumentFactory.createDocument(pid, dt);
635         saved.saveDocument("saved");
636         assertEquals(DocumentStatus.SAVED, saved.getStatus());
637 
638         WorkflowDocument enroute = WorkflowDocumentFactory.createDocument(pid, dt);
639         enroute.route("routed");
640         assertEquals(DocumentStatus.ENROUTE, enroute.getStatus());
641 
642         WorkflowDocument exception = WorkflowDocumentFactory.createDocument(pid, dt);
643         exception.route("routed");
644         exception.placeInExceptionRouting("placed in exception routing");
645         assertEquals(DocumentStatus.EXCEPTION, exception.getStatus());
646 
647         // no acks on this doc, can't test?
648         //WorkflowDocument processed = WorkflowDocumentFactory.createDocument(pid, dt);
649         //processed.route("routed");
650 
651         WorkflowDocument finl = WorkflowDocumentFactory.createDocument(pid, dt);
652         finl.route("routed");
653         finl.switchPrincipal(getPrincipalId("jhopf"));
654         finl.approve("approved");
655         assertEquals(DocumentStatus.FINAL, finl.getStatus());
656 
657         WorkflowDocument canceled = WorkflowDocumentFactory.createDocument(pid, dt);
658         canceled.cancel("canceled");
659         assertEquals(DocumentStatus.CANCELED, canceled.getStatus());
660 
661         WorkflowDocument disapproved = WorkflowDocumentFactory.createDocument(pid, dt);
662         disapproved.route("routed");
663         disapproved.switchPrincipal(getPrincipalId("jhopf"));
664         RequestedActions ra = disapproved.getRequestedActions();
665         disapproved.disapprove("disapproved");
666         assertEquals(DocumentStatus.DISAPPROVED, disapproved.getStatus());
667 
668         assertDocumentStatuses(dt, pid, 1, 1, 1, 1, 0, 1, 1, 1);
669     }
670 
671     /**
672      * Asserts that documents are present in the given statuses, including document status categories (this requires that
673      * no docs are in the system prior to routing of the test docs)
674      */
675     protected void assertDocumentStatuses(String documentType, String principalId, int initiated, int saved, int enroute, int exception,
676                                                           int processed, int finl, int canceled, int disapproved) {
677         assertDocumentStatus(documentType, principalId, DocumentStatus.INITIATED, initiated);
678         assertDocumentStatus(documentType, principalId, DocumentStatus.SAVED, saved);
679         assertDocumentStatus(documentType, principalId, DocumentStatus.ENROUTE, enroute);
680         assertDocumentStatus(documentType, principalId, DocumentStatus.EXCEPTION, exception);
681 
682         assertDocumentStatusCategory(documentType, principalId, DocumentStatusCategory.PENDING,
683                 initiated + saved + enroute + exception);
684 
685         assertDocumentStatus(documentType, principalId, DocumentStatus.PROCESSED, processed);
686         assertDocumentStatus(documentType, principalId, DocumentStatus.FINAL, finl);
687 
688         assertDocumentStatusCategory(documentType, principalId, DocumentStatusCategory.SUCCESSFUL, processed + finl);
689 
690         assertDocumentStatus(documentType, principalId, DocumentStatus.CANCELED, canceled);
691         assertDocumentStatus(documentType, principalId, DocumentStatus.DISAPPROVED, finl);
692 
693         assertDocumentStatusCategory(documentType, principalId, DocumentStatusCategory.UNSUCCESSFUL,
694                 canceled + disapproved);
695     }
696 
697     /**
698      * Asserts that there are a certain number of docs in the given status
699      */
700     protected void assertDocumentStatus(String documentType, String principalId, DocumentStatus status, int num) {
701         DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
702         criteria.setDocumentTypeName(documentType);
703         criteria.setDocumentStatuses(Arrays.asList(new DocumentStatus[] { status }));
704         DocumentSearchResults result = docSearchService.lookupDocuments(principalId, criteria.build());
705         assertEquals("Expected " + num + " documents in status " + status, num, result.getSearchResults().size());
706     }
707 
708     /**
709      * Asserts that there are a certain number of docs in the given document status category
710      */
711     protected void assertDocumentStatusCategory(String documentType, String principalId, DocumentStatusCategory status, int num) {
712         DocumentSearchCriteria.Builder criteria = DocumentSearchCriteria.Builder.create();
713         criteria.setDocumentTypeName(documentType);
714         criteria.setDocumentStatusCategories(Arrays.asList(new DocumentStatusCategory[]{status}));
715         DocumentSearchResults result = docSearchService.lookupDocuments(principalId, criteria.build());
716         assertEquals("Expected " + num + " documents in status category " + status, num,
717                 result.getSearchResults().size());
718     }
719 
720     /**
721      * Routes some test docs for searching
722      * @return String[] of doc ids
723      */
724     protected String[] routeTestDocs() {
725         // Route some test documents.
726         String docTypeName = "SearchDocType";
727         String[] principalNames = {"bmcgough", "quickstart", "rkirkend"};
728         String[] titles = {"The New Doc", "Document Number 2", "Some New Document"};
729         String[] docIds = new String[titles.length];
730         String[] appDocIds = {"6543", "5432", "4321"};
731         String[] approverNames = {null, "jhopf", null};
732         for (int i = 0; i < titles.length; i++) {
733             WorkflowDocument workflowDocument = WorkflowDocumentFactory.createDocument(getPrincipalId(principalNames[i]), docTypeName);
734             workflowDocument.setTitle(titles[i]);
735             workflowDocument.setApplicationDocumentId(appDocIds[i]);
736             workflowDocument.route("routing this document.");
737             docIds[i] = workflowDocument.getDocumentId();
738             if (approverNames[i] != null) {
739                 workflowDocument.switchPrincipal(getPrincipalId(approverNames[i]));
740                 workflowDocument.approve("approving this document.");
741             }
742         }
743 
744         return docIds;
745     }
746 
747     /**
748      * "Saves" a single instance of a "SearchDocType2" document and returns it's id.
749      */
750     protected String routeTestDoc2() {
751         // Route some test documents.
752         String docTypeName = "SearchDocType2";
753         WorkflowDocument workflowDocument = WorkflowDocumentFactory.createDocument(getPrincipalId("ewestfal"), docTypeName);
754         workflowDocument.setTitle("Search Doc Type 2!");
755         workflowDocument.saveDocument("saving the document");
756         return workflowDocument.getDocumentId();
757     }
758 
759     private String getPrincipalId(String principalName) {
760         return KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(principalName).getPrincipalId();
761     }
762 }