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