]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
acd7b03ee8f1f40ead320d30c3a305ec00b704b9
[tmp/jakarta-migration.git] /
1 /**
2  * This document is a part of the source code and related artifacts
3  * for CollectionSpace, an open source collections management system
4  * for museums and related institutions:
5  *
6  * http://www.collectionspace.org
7  * http://wiki.collectionspace.org
8  *
9  * Copyright © 2009 Regents of the University of California
10  *
11  * Licensed under the Educational Community License (ECL), Version 2.0.
12  * You may not use this file except in compliance with this License.
13  *
14  * You may obtain a copy of the ECL 2.0 License at
15  * https://source.collectionspace.org/collection-space/LICENSE.txt
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 package org.collectionspace.services.client.test;
24
25 import java.util.Arrays;
26 import java.util.ArrayList;
27 import java.util.List;
28 import javax.ws.rs.core.MediaType;
29 import javax.ws.rs.core.Response;
30 import org.collectionspace.services.client.CollectionObjectClient;
31 import org.collectionspace.services.client.CollectionSpaceClient;
32 import org.collectionspace.services.collectionobject.BriefDescriptionList;
33 import org.collectionspace.services.collectionobject.CollectionobjectsCommon;
34 import org.collectionspace.services.collectionobject.CollectionobjectsCommonList;
35 import org.collectionspace.services.jaxb.AbstractCommonList;
36 import org.jboss.resteasy.client.ClientResponse;
37 import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput;
38 import org.jboss.resteasy.plugins.providers.multipart.OutputPart;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41 import org.testng.Assert;
42 import org.testng.annotations.AfterClass;
43 import org.testng.annotations.BeforeClass;
44 import org.testng.annotations.Test;
45
46 /**
47  * CollectionObjectSearchTest, carries out tests of keyword
48  * search functionality against a deployed and running
49  * CollectionObject Service.
50  *
51  * $LastChangedRevision: 1327 $
52  * $LastChangedDate: 2010-02-12 10:35:11 -0800 (Fri, 12 Feb 2010) $
53  */
54 public class CollectionObjectSearchTest extends BaseServiceTest {
55
56    /** The logger. */
57     private final String CLASS_NAME = CollectionObjectSearchTest.class.getName();
58     private final Logger logger = LoggerFactory.getLogger(CLASS_NAME);
59
60     final static String IDENTIFIER = getSystemTimeIdentifier();
61
62     final static String KEYWORD = "Tsolyani" + IDENTIFIER;
63     final static List<String> TWO_KEYWORDS =
64             Arrays.asList(new String[]{"Cheggarra" + IDENTIFIER, "Ahoggya" + IDENTIFIER});
65     final static List<String> TWO_MORE_KEYWORDS =
66             Arrays.asList(new String[]{"Karihaya" + IDENTIFIER, "Hlikku" + IDENTIFIER});
67     final static String NOISE_WORD = "Mihalli + IDENTIFIER";
68     // Test Unicode UTF-8 term for keyword searching: the last name of Lech
69     // Wałęsa, which contains two non-USASCII range Unicode UTF-8 characters.
70     //
71     // For details regarding the łę characters in the last name, see:
72     // http://en.wikipedia.org/wiki/L_with_stroke
73     // http://en.wikipedia.org/wiki/%C4%98
74     final String UTF8_KEYWORD = "Wa" + '\u0142' + '\u0119' + "sa";
75     // Non-existent term, consisting of two back-to-back sets of the first letters
76     // of each of the words in a short pangram for the English alphabet.
77     final static String NON_EXISTENT_KEYWORD = "jlmbsoqjlmbsoq";
78
79     final static String KEYWORD_SEPARATOR = " ";
80     final long numNoiseWordResources = 10;
81     final double pctNonNoiseWordResources = 0.5;
82
83     /* Use this to keep track of resources to delete */
84     private List<String> allResourceIdsCreated = new ArrayList<String>();
85
86     /* (non-Javadoc)
87      * @see org.collectionspace.services.client.test.BaseServiceTest#getServicePathComponent()
88      */
89     @Override
90     protected String getServicePathComponent() {
91         return new CollectionObjectClient().getServicePathComponent();
92     }
93
94     /* (non-Javadoc)
95      * @see org.collectionspace.services.client.test.BaseServiceTest#getClientInstance()
96      */
97     @Override
98     protected CollectionSpaceClient getClientInstance() {
99         return new CollectionObjectClient();
100     }
101
102     /* (non-Javadoc)
103      * @see org.collectionspace.services.client.test.BaseServiceTest#getAbstractCommonList(org.jboss.resteasy.client.ClientResponse)
104      */
105     @Override
106     protected AbstractCommonList getAbstractCommonList(ClientResponse<AbstractCommonList> response) {
107         return response.getEntity(CollectionobjectsCommonList.class);
108     }
109
110     /**
111      * Creates one or more resources containing a "noise" keyword,
112      * which should NOT be retrieved by keyword searches.
113      *
114      * This also helps ensure that searches will not fail, due
115      * to a database-specific constraint or otherwise, if the
116      * number of records containing a particular keyword represent
117      * too high a proportion of the total number of records.
118      */
119     @BeforeClass(alwaysRun=true)
120     public void setup() {
121         if (logger.isDebugEnabled()) {
122             logger.debug("Creating " + numNoiseWordResources +
123                 " 'noise word' resources ...");
124         }
125         createCollectionObjects(numNoiseWordResources, NOISE_WORD);
126     }
127
128
129     // ---------------------------------------------------------------
130     // Search tests
131     // ---------------------------------------------------------------
132
133     // Success outcomes
134
135     @Test(dataProvider="testName", dataProviderClass=AbstractServiceTestImpl.class,
136         groups = {"oneKeyword"})
137     public void searchWithOneKeyword(String testName) throws Exception {
138
139         if (logger.isDebugEnabled()) {
140             logger.debug(testBanner(testName, CLASS_NAME));
141         }
142
143         // Create one or more keyword retrievable resources, each containing
144         // a specified keyword.
145         long numKeywordRetrievableResources =
146                 (long) (numNoiseWordResources * pctNonNoiseWordResources);
147         if (logger.isDebugEnabled()) {
148             logger.debug("Creating " + numKeywordRetrievableResources +
149                 " keyword-retrievable resources ...");
150         }
151         createCollectionObjects(numKeywordRetrievableResources, KEYWORD);
152         
153         // Set the expected status code and group of valid status codes
154         testSetup(STATUS_OK, ServiceRequestType.SEARCH);
155
156         // Send the search request and receive a response
157         ClientResponse<CollectionobjectsCommonList> res = doSearch(KEYWORD);
158         int statusCode = res.getStatus();
159
160         // Check the status code of the response: does it match
161         // the expected response(s)?
162         if (logger.isDebugEnabled()) {
163             logger.debug(testName + ": status = " + statusCode);
164         }
165         Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
166                 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
167         Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
168
169         // Verify that the number of resources matched by the search
170         // is identical to the expected result
171         long NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
172         long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED);
173         Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
174     }
175
176     @Test(dataProvider="testName", dataProviderClass=AbstractServiceTestImpl.class)
177     public void searchWithTwoKeywordsInSameField(String testName) throws Exception {
178
179         if (logger.isDebugEnabled()) {
180             logger.debug(testBanner(testName, CLASS_NAME));
181         }
182
183         // Create one or more keyword retrievable resources, each containing
184         // two specified keywords.
185         long numKeywordRetrievableResources =
186                 (long) (numNoiseWordResources * pctNonNoiseWordResources);
187         if (logger.isDebugEnabled()) {
188             logger.debug("Creating " + numKeywordRetrievableResources +
189                 " keyword-retrievable resources ...");
190         }
191         boolean keywordsInSameField = true;
192         createCollectionObjects(numKeywordRetrievableResources, TWO_KEYWORDS, keywordsInSameField);
193
194         // Set the expected status code and group of valid status codes
195         testSetup(STATUS_OK, ServiceRequestType.SEARCH);
196
197         // Search using both terms
198
199         // Send the search request and receive a response
200         ClientResponse<CollectionobjectsCommonList> res = doSearch(TWO_KEYWORDS);
201         int statusCode = res.getStatus();
202
203         // Check the status code of the response: does it match
204         // the expected response(s)?
205         if (logger.isDebugEnabled()) {
206             logger.debug(testName + ": status = " + statusCode);
207         }
208         Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
209                 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
210         Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
211
212         // Verify that the number of resources matched by the search
213         // is identical to the expected result
214         long NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
215         long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED);
216         Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
217
218         // Search using a single term
219
220         // Send the search request and receive a response
221         res = doSearch(TWO_KEYWORDS.get(0));
222         statusCode = res.getStatus();
223
224         // Check the status code of the response: does it match
225         // the expected response(s)?
226         if (logger.isDebugEnabled()) {
227             logger.debug(testName + ": status = " + statusCode);
228         }
229         Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
230                 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
231         Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
232
233         // Verify that the number of resources matched by the search
234         // is identical to the expected result
235         NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
236         numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED);
237         Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
238
239     }
240
241     @Test(dataProvider="testName", dataProviderClass=AbstractServiceTestImpl.class)
242     public void searchWithTwoKeywordsAcrossTwoFields(String testName) throws Exception {
243
244         if (logger.isDebugEnabled()) {
245             logger.debug(testBanner(testName, CLASS_NAME));
246         }
247
248         // Create one or more keyword retrievable resources, each containing
249         // two specified keywords.
250         long numKeywordRetrievableResources = 5;
251         if (logger.isDebugEnabled()) {
252             logger.debug("Creating " + numKeywordRetrievableResources +
253                 " keyword-retrievable resources ...");
254         }
255         boolean keywordsInSameField = false;
256         createCollectionObjects(numKeywordRetrievableResources, TWO_MORE_KEYWORDS, keywordsInSameField);
257
258         // Set the expected status code and group of valid status codes
259         testSetup(STATUS_OK, ServiceRequestType.SEARCH);
260
261         // Search using both terms
262
263         // Send the search request and receive a response
264         ClientResponse<CollectionobjectsCommonList> res = doSearch(TWO_MORE_KEYWORDS);
265         int statusCode = res.getStatus();
266
267         // Check the status code of the response: does it match
268         // the expected response(s)?
269         if (logger.isDebugEnabled()) {
270             logger.debug(testName + ": status = " + statusCode);
271         }
272         Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
273                 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
274         Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
275
276         // Verify that the number of resources matched by the search
277         // is identical to the expected result
278         long NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
279         long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED);
280         Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
281
282         // Search using a single term
283
284         // Send the search request and receive a response
285         res = doSearch(TWO_MORE_KEYWORDS.get(0));
286         statusCode = res.getStatus();
287
288         // Check the status code of the response: does it match
289         // the expected response(s)?
290         if (logger.isDebugEnabled()) {
291             logger.debug(testName + ": status = " + statusCode);
292         }
293         Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
294                 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
295         Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
296
297         // Verify that the number of resources matched by the search
298         // is identical to the expected result
299         NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
300         numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED);
301         Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
302
303     }
304
305 //    @Test(dataProvider="testName", dataProviderClass=AbstractServiceTestImpl.class)
306 //    public void searchWithOneKeywordInRepeatableScalarField(String testName) throws Exception {
307 //        BriefDescriptionList descriptionList = new BriefDescriptionList();
308 //        List<String> descriptions = descriptionList.getBriefDescription();
309 //        if (TWO_KEYWORDS.size() >= 2) {
310 //            descriptions.add(TWO_KEYWORDS.get(0));
311 //            descriptions.add(TWO_KEYWORDS.get(1));
312 //        }
313 //    }
314
315
316     // FIXME: Test currently fails with a true UTF-8 String - need to investigate why.
317     // Will be commented out for now until we get this working ...
318 /*
319     @Test(dataProvider="testName", dataProviderClass=AbstractServiceTestImpl.class,
320         groups = {"utf8"})
321     public void searchWithUTF8Keyword(String testName) {
322
323         if (logger.isDebugEnabled()) {
324             logger.debug(testBanner(testName, CLASS_NAME));
325         }
326
327         // Create one or more keyword retrievable resources, each containing
328         // two specified keywords.
329         long numKeywordRetrievableResources = 2;
330         if (logger.isDebugEnabled()) {
331             logger.debug("Creating " + numKeywordRetrievableResources +
332                 " keyword-retrievable resources ...");
333         }
334         createCollectionObjects(numKeywordRetrievableResources, UTF8_KEYWORD);
335
336         // Set the expected status code and group of valid status codes
337         testSetup(STATUS_OK, ServiceRequestType.SEARCH);
338
339         // Send the search request and receive a response
340         ClientResponse<CollectionobjectsCommonList> res = doSearch(UTF8_KEYWORD);
341         int statusCode = res.getStatus();
342
343         // Check the status code of the response: does it match
344         // the expected response(s)?
345         if (logger.isDebugEnabled()) {
346             logger.debug(testName + ": status = " + statusCode);
347         }
348         Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
349                 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
350         Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
351
352         // Verify that the number of resources matched by the search
353         // is identical to the expected result
354         long NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
355         long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED);
356         Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
357     }
358 */
359     
360     // Failure outcomes
361
362     // FIXME: Rename to searchWithNonExistentKeyword
363     @Test(dataProvider="testName", dataProviderClass=AbstractServiceTestImpl.class)
364     public void keywordSearchNonExistentKeyword(String testName) throws Exception {
365
366         if (logger.isDebugEnabled()) {
367             logger.debug(testBanner(testName, CLASS_NAME));
368         }
369
370         // Set the expected status code and group of valid status codes
371         testSetup(STATUS_OK, ServiceRequestType.SEARCH);
372
373         // Send the search request and receive a response
374         ClientResponse<CollectionobjectsCommonList> res = doSearch(NON_EXISTENT_KEYWORD);
375         int statusCode = res.getStatus();
376
377         // Check the status code of the response: does it match
378         // the expected response(s)?
379         if (logger.isDebugEnabled()) {
380             logger.debug(testName + ": status = " + statusCode);
381         }
382         Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
383                 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
384         Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
385
386         // Verify that the number of resources matched by the search
387         // is identical to the expected result
388         long NUM_MATCHES_EXPECTED = 0;
389         long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED);
390         Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
391
392     }
393
394     // ---------------------------------------------------------------
395     // Cleanup of resources created during testing
396     // ---------------------------------------------------------------
397
398     /**
399      * Deletes all resources created by setup and tests, after all tests have been run.
400      *
401      * This cleanup method will always be run, even if one or more tests fail.
402      * For this reason, it attempts to remove all resources created
403      * at any point during testing, even if some of those resources
404      * may be expected to be deleted by certain tests.
405      */
406     @AfterClass(alwaysRun=true)
407     public void cleanUp() {
408         String noTest = System.getProperty("noTestCleanup");
409         if(Boolean.TRUE.toString().equalsIgnoreCase(noTest)) {
410             if (logger.isDebugEnabled()) {
411                 logger.debug("Skipping Cleanup phase ...");
412             }
413             return;
414         }
415         if (logger.isDebugEnabled()) {
416             logger.debug("Cleaning up temporary resources created for testing ...");
417         }
418         CollectionObjectClient collectionObjectClient = new CollectionObjectClient();
419         for (String resourceId : allResourceIdsCreated) {
420             // Note: Any non-success responses are ignored and not reported.
421             collectionObjectClient.delete(resourceId).releaseConnection();
422         }
423     }
424
425     // ---------------------------------------------------------------
426     // Utility methods used by tests above
427     // ---------------------------------------------------------------
428     
429     private void createCollectionObjects(long numToCreate, String keyword) {
430         List keywords = new ArrayList<String>();
431         keywords.add(keyword);
432         boolean keywordsInSameField = true;
433         createCollectionObjects(numToCreate, keywords, keywordsInSameField);
434     }
435     
436     private void createCollectionObjects(long numToCreate, List<String> keywords,
437             boolean keywordsInSameField) {
438         testSetup(STATUS_CREATED, ServiceRequestType.CREATE);
439         CollectionObjectClient client = new CollectionObjectClient();
440         for (long i = 0; i < numToCreate; i++) {
441             MultipartOutput multipart =
442                     createCollectionObjectInstance(i, keywords, keywordsInSameField);
443             ClientResponse<Response> res = client.create(multipart);
444             try {
445                 int statusCode = res.getStatus();
446                 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
447                 String id = extractId(res);
448                 allResourceIdsCreated.add(id);
449                 if (logger.isDebugEnabled()) {
450                     logger.debug("Created new resource [" + i + "] with ID " + id);
451                 }
452             } finally {
453                 res.releaseConnection();
454             }
455         }
456     }
457     
458     private MultipartOutput createCollectionObjectInstance(long i, List<String> keywords,
459             boolean keywordsInSameField) {
460         CollectionobjectsCommon collectionObject = new CollectionobjectsCommon();
461         collectionObject.setObjectNumber(createIdentifier());
462         if (keywordsInSameField) {
463             collectionObject.setDistinguishingFeatures(listToString(keywords, KEYWORD_SEPARATOR));
464         } else {
465             if (keywords.size() == 1) {
466                 collectionObject.setDistinguishingFeatures(keywords.get(0));
467             } else if (keywords.size() == 2) {
468                 collectionObject.setDistinguishingFeatures(keywords.get(0));
469                 collectionObject.setPhysicalDescription(keywords.get(1));
470             } else {
471                 Assert.fail("List of keywords must have exactly one or two members.");
472             }
473         }
474         MultipartOutput multipart = new MultipartOutput();
475         OutputPart commonPart = multipart.addPart(collectionObject,
476                 MediaType.APPLICATION_XML_TYPE);
477         commonPart.getHeaders().add("label", new CollectionObjectClient().getCommonPartName());
478         return multipart;
479     }
480
481     private static String listToString(List<String> list, String separator) {
482         StringBuffer sb = new StringBuffer();
483         if (list.size() > 0) {
484             sb.append(list.get(0));
485             for (int i=1; i < list.size(); i++) {
486                 sb.append(separator);
487                 sb.append(list.get(i));
488             }
489         }
490         return sb.toString();
491     }
492
493     private ClientResponse<CollectionobjectsCommonList> doSearch(List<String> keywords) {
494         String searchParamValue = listToString(keywords, KEYWORD_SEPARATOR);
495         return doSearch(searchParamValue);
496     }
497
498     private ClientResponse<CollectionobjectsCommonList> doSearch(String keyword) {
499        String searchParamValue = keyword;
500         if (logger.isDebugEnabled()) {
501             logger.debug("Searching on keyword(s): " + searchParamValue + " ...");
502         }
503         CollectionObjectClient client = new CollectionObjectClient();
504         ClientResponse<CollectionobjectsCommonList> res =
505             client.keywordSearch(searchParamValue);
506         return res;
507     }
508
509     private long getNumMatched(ClientResponse<CollectionobjectsCommonList> res,
510             long numExpectedMatches) {
511         CollectionobjectsCommonList list = (CollectionobjectsCommonList)
512             res.getEntity(CollectionobjectsCommonList.class);
513         long numMatched = list.getTotalItems();
514
515         if (logger.isDebugEnabled()) {
516             logger.debug("Keyword search matched " + numMatched +
517                 " resources, expected to match " + numExpectedMatches);
518         }
519
520         // Optionally output additional data about list members for debugging.
521         boolean iterateThroughList = false;
522         if (iterateThroughList && logger.isDebugEnabled()) {
523             itemizeListItems(list);
524         }
525         return numMatched;
526     }
527
528     private void itemizeListItems(CollectionobjectsCommonList list) {
529         List<CollectionobjectsCommonList.CollectionObjectListItem> items =
530             list.getCollectionObjectListItem();
531         int i = 0;
532         for (CollectionobjectsCommonList.CollectionObjectListItem item : items) {
533             logger.debug("list-item[" + i + "] title="
534                     + item.getTitle());
535             logger.debug("list-item[" + i + "] URI="
536                     + item.getUri());
537             i++;
538         }
539     }
540
541     public static String getSystemTimeIdentifier() {
542       return Long.toString(System.currentTimeMillis());
543     }
544
545 }