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:
6 * http://www.collectionspace.org
7 * http://wiki.collectionspace.org
9 * Copyright © 2009 Regents of the University of California
11 * Licensed under the Educational Community License (ECL), Version 2.0.
12 * You may not use this file except in compliance with this License.
14 * You may obtain a copy of the ECL 2.0 License at
15 * https://source.collectionspace.org/collection-space/LICENSE.txt
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.
23 package org.collectionspace.services.client.test;
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;
47 * CollectionObjectAuthRefsTest, carries out tests against a
48 * deployed and running CollectionObject Service.
50 * $LastChangedRevision: 1327 $
51 * $LastChangedDate: 2010-02-12 10:35:11 -0800 (Fri, 12 Feb 2010) $
53 public class CollectionObjectSearchTest extends BaseServiceTest {
56 private final String CLASS_NAME = CollectionObjectSearchTest.class.getName();
57 private final Logger logger = LoggerFactory.getLogger(CLASS_NAME);
59 final static String IDENTIFIER = getSystemTimeIdentifier();
60 final static String KEYWORD = "Tsolyani" + IDENTIFIER;
61 final static List<String> TWO_KEYWORDS =
62 Arrays.asList(new String[]{"Cheggarra" + IDENTIFIER, "Ahoggya" + IDENTIFIER});
63 final static List<String> TWO_MORE_KEYWORDS =
64 Arrays.asList(new String[]{"Karihaya" + IDENTIFIER, "Hlikku" + IDENTIFIER});
65 final static String NOISE_WORD = "Mihalli + IDENTIFIER";
66 final static String NON_EXISTENT_KEYWORD = "jlmbsoqjlmbsoq";
67 final static String KEYWORD_SEPARATOR = " ";
68 final long numNoiseWordResources = 10;
69 final double pctNonNoiseWordResources = 0.5;
71 /* Use this to keep track of resources to delete */
72 private List<String> allResourceIdsCreated = new ArrayList<String>();
75 * @see org.collectionspace.services.client.test.BaseServiceTest#getServicePathComponent()
78 protected String getServicePathComponent() {
79 return new CollectionObjectClient().getServicePathComponent();
83 * @see org.collectionspace.services.client.test.BaseServiceTest#getClientInstance()
86 protected CollectionSpaceClient getClientInstance() {
87 return new CollectionObjectClient();
91 * @see org.collectionspace.services.client.test.BaseServiceTest#getAbstractCommonList(org.jboss.resteasy.client.ClientResponse)
94 protected AbstractCommonList getAbstractCommonList(ClientResponse<AbstractCommonList> response) {
95 return response.getEntity(CollectionobjectsCommonList.class);
99 * Creates one or more resources containing a "noise" keyword,
100 * which should NOT be retrieved by keyword searches.
102 * This also helps ensure that searches will not fail, due
103 * to a database-specific constraint or otherwise, if the
104 * number of records retrieved by a particular keyword represent
105 * too high a proportion of the total records retrieved.
107 @BeforeClass(alwaysRun=true)
108 public void setup() {
109 if (logger.isDebugEnabled()) {
110 logger.debug("Creating " + numNoiseWordResources +
111 " 'noise word' resources ...");
113 createCollectionObjects(numNoiseWordResources, NOISE_WORD);
117 // ---------------------------------------------------------------
119 // ---------------------------------------------------------------
123 @Test(dataProvider="testName", dataProviderClass=AbstractServiceTestImpl.class,
124 groups = {"oneKeyword"})
125 public void searchWithOneKeyword(String testName) throws Exception {
127 if (logger.isDebugEnabled()) {
128 logger.debug(testBanner(testName, CLASS_NAME));
131 // Create one or more keyword retrievable resources, each containing
132 // a specified keyword.
133 long numKeywordRetrievableResources =
134 (long) (numNoiseWordResources * pctNonNoiseWordResources);
135 if (logger.isDebugEnabled()) {
136 logger.debug("Creating " + numKeywordRetrievableResources +
137 " keyword-retrievable resources ...");
139 createCollectionObjects(numKeywordRetrievableResources, KEYWORD);
141 // Set the expected status code and group of valid status codes
142 testSetup(STATUS_OK, ServiceRequestType.SEARCH);
144 // Send the search request and receive a response
145 ClientResponse<CollectionobjectsCommonList> res = doSearch(KEYWORD);
146 int statusCode = res.getStatus();
148 // Check the status code of the response: does it match
149 // the expected response(s)?
150 if (logger.isDebugEnabled()) {
151 logger.debug(testName + ": status = " + statusCode);
153 Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
154 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
155 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
157 // Verify that the number of resources matched by the search
158 // is identical to the expected result
159 long NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
160 long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED);
161 Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
164 @Test(dataProvider="testName", dataProviderClass=AbstractServiceTestImpl.class)
165 public void searchWithTwoKeywordsInSameField(String testName) throws Exception {
167 if (logger.isDebugEnabled()) {
168 logger.debug(testBanner(testName, CLASS_NAME));
171 // Create one or more keyword retrievable resources, each containing
172 // two specified keywords.
173 long numKeywordRetrievableResources =
174 (long) (numNoiseWordResources * pctNonNoiseWordResources);
175 if (logger.isDebugEnabled()) {
176 logger.debug("Creating " + numKeywordRetrievableResources +
177 " keyword-retrievable resources ...");
179 boolean keywordsInSameField = true;
180 createCollectionObjects(numKeywordRetrievableResources, TWO_KEYWORDS, keywordsInSameField);
182 // Set the expected status code and group of valid status codes
183 testSetup(STATUS_OK, ServiceRequestType.SEARCH);
185 // Search using both terms
187 // Send the search request and receive a response
188 ClientResponse<CollectionobjectsCommonList> res = doSearch(TWO_KEYWORDS);
189 int statusCode = res.getStatus();
191 // Check the status code of the response: does it match
192 // the expected response(s)?
193 if (logger.isDebugEnabled()) {
194 logger.debug(testName + ": status = " + statusCode);
196 Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
197 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
198 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
200 // Verify that the number of resources matched by the search
201 // is identical to the expected result
202 long NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
203 long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED);
204 Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
206 // Search using a single term
208 // Send the search request and receive a response
209 res = doSearch(TWO_KEYWORDS.get(0));
210 statusCode = res.getStatus();
212 // Check the status code of the response: does it match
213 // the expected response(s)?
214 if (logger.isDebugEnabled()) {
215 logger.debug(testName + ": status = " + statusCode);
217 Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
218 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
219 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
221 // Verify that the number of resources matched by the search
222 // is identical to the expected result
223 NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
224 numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED);
225 Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
229 @Test(dataProvider="testName", dataProviderClass=AbstractServiceTestImpl.class)
230 public void searchWithTwoKeywordsAcrossTwoFields(String testName) throws Exception {
232 if (logger.isDebugEnabled()) {
233 logger.debug(testBanner(testName, CLASS_NAME));
236 // Create one or more keyword retrievable resources, each containing
237 // two specified keywords.
238 long numKeywordRetrievableResources = 5;
239 if (logger.isDebugEnabled()) {
240 logger.debug("Creating " + numKeywordRetrievableResources +
241 " keyword-retrievable resources ...");
243 boolean keywordsInSameField = false;
244 createCollectionObjects(numKeywordRetrievableResources, TWO_MORE_KEYWORDS, keywordsInSameField);
246 // Set the expected status code and group of valid status codes
247 testSetup(STATUS_OK, ServiceRequestType.SEARCH);
249 // Search using both terms
251 // Send the search request and receive a response
252 ClientResponse<CollectionobjectsCommonList> res = doSearch(TWO_MORE_KEYWORDS);
253 int statusCode = res.getStatus();
255 // Check the status code of the response: does it match
256 // the expected response(s)?
257 if (logger.isDebugEnabled()) {
258 logger.debug(testName + ": status = " + statusCode);
260 Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
261 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
262 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
264 // Verify that the number of resources matched by the search
265 // is identical to the expected result
266 long NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
267 long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED);
268 Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
270 // Search using a single term
272 // Send the search request and receive a response
273 res = doSearch(TWO_MORE_KEYWORDS.get(0));
274 statusCode = res.getStatus();
276 // Check the status code of the response: does it match
277 // the expected response(s)?
278 if (logger.isDebugEnabled()) {
279 logger.debug(testName + ": status = " + statusCode);
281 Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
282 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
283 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
285 // Verify that the number of resources matched by the search
286 // is identical to the expected result
287 NUM_MATCHES_EXPECTED = numKeywordRetrievableResources;
288 numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED);
289 Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
293 // @Test(dataProvider="testName", dataProviderClass=AbstractServiceTestImpl.class)
294 // public void searchWithOneKeywordInRepeatableScalarField(String testName) throws Exception {
299 // FIXME: Rename to searchWithNonExistentKeyword
300 @Test(dataProvider="testName", dataProviderClass=AbstractServiceTestImpl.class)
301 public void keywordSearchNonExistentKeyword(String testName) throws Exception {
303 if (logger.isDebugEnabled()) {
304 logger.debug(testBanner(testName, CLASS_NAME));
307 // Set the expected status code and group of valid status codes
308 testSetup(STATUS_OK, ServiceRequestType.SEARCH);
310 // Send the search request and receive a response
311 ClientResponse<CollectionobjectsCommonList> res = doSearch(NON_EXISTENT_KEYWORD);
312 int statusCode = res.getStatus();
314 // Check the status code of the response: does it match
315 // the expected response(s)?
316 if (logger.isDebugEnabled()) {
317 logger.debug(testName + ": status = " + statusCode);
319 Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
320 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
321 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
323 // Verify that the number of resources matched by the search
324 // is identical to the expected result
325 long NUM_MATCHES_EXPECTED = 0;
326 long numMatched = getNumMatched(res, NUM_MATCHES_EXPECTED);
327 Assert.assertEquals(numMatched, NUM_MATCHES_EXPECTED);
331 // ---------------------------------------------------------------
332 // Cleanup of resources created during testing
333 // ---------------------------------------------------------------
336 * Deletes all resources created by setup and tests, after all tests have been run.
338 * This cleanup method will always be run, even if one or more tests fail.
339 * For this reason, it attempts to remove all resources created
340 * at any point during testing, even if some of those resources
341 * may be expected to be deleted by certain tests.
343 @AfterClass(alwaysRun=true)
344 public void cleanUp() {
345 String noTest = System.getProperty("noTestCleanup");
346 if(Boolean.TRUE.toString().equalsIgnoreCase(noTest)) {
347 if (logger.isDebugEnabled()) {
348 logger.debug("Skipping Cleanup phase ...");
352 if (logger.isDebugEnabled()) {
353 logger.debug("Cleaning up temporary resources created for testing ...");
355 CollectionObjectClient collectionObjectClient = new CollectionObjectClient();
356 for (String resourceId : allResourceIdsCreated) {
357 // Note: Any non-success responses are ignored and not reported.
358 collectionObjectClient.delete(resourceId).releaseConnection();
362 // ---------------------------------------------------------------
363 // Utility methods used by tests above
364 // ---------------------------------------------------------------
366 private void createCollectionObjects(long numToCreate, String keyword) {
367 List keywords = new ArrayList<String>();
368 keywords.add(keyword);
369 boolean keywordsInSameField = true;
370 createCollectionObjects(numToCreate, keywords, keywordsInSameField);
373 private void createCollectionObjects(long numToCreate, List<String> keywords,
374 boolean keywordsInSameField) {
375 testSetup(STATUS_CREATED, ServiceRequestType.CREATE);
376 CollectionObjectClient client = new CollectionObjectClient();
377 for (long i = 0; i < numToCreate; i++) {
378 MultipartOutput multipart =
379 createCollectionObjectInstance(i, keywords, keywordsInSameField);
380 ClientResponse<Response> res = client.create(multipart);
382 int statusCode = res.getStatus();
383 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
384 String id = extractId(res);
385 allResourceIdsCreated.add(id);
386 if (logger.isDebugEnabled()) {
387 logger.debug("Created new resource [" + i + "] with ID " + id);
390 res.releaseConnection();
395 private MultipartOutput createCollectionObjectInstance(long i, List<String> keywords,
396 boolean keywordsInSameField) {
397 CollectionobjectsCommon collectionObject = new CollectionobjectsCommon();
398 collectionObject.setObjectNumber(createIdentifier());
399 if (keywordsInSameField) {
400 collectionObject.setTitle(listToString(keywords, KEYWORD_SEPARATOR));
402 if (keywords.size() == 1) {
403 collectionObject.setTitle(keywords.get(0));
404 } else if (keywords.size() == 2) {
405 collectionObject.setTitle(keywords.get(0));
406 collectionObject.setDistinguishingFeatures(keywords.get(1));
408 Assert.fail("List of keywords must have exactly one or two members.");
411 MultipartOutput multipart = new MultipartOutput();
412 OutputPart commonPart = multipart.addPart(collectionObject,
413 MediaType.APPLICATION_XML_TYPE);
414 commonPart.getHeaders().add("label", new CollectionObjectClient().getCommonPartName());
418 private static String listToString(List<String> list, String separator) {
419 StringBuffer sb = new StringBuffer();
420 if (list.size() > 0) {
421 sb.append(list.get(0));
422 for (int i=1; i < list.size(); i++) {
423 sb.append(separator);
424 sb.append(list.get(i));
427 return sb.toString();
430 private ClientResponse<CollectionobjectsCommonList> doSearch(List<String> keywords) {
431 String searchParamValue = listToString(keywords, KEYWORD_SEPARATOR);
432 return doSearch(searchParamValue);
435 private ClientResponse<CollectionobjectsCommonList> doSearch(String keyword) {
436 String searchParamValue = keyword;
437 if (logger.isDebugEnabled()) {
438 logger.debug("Searching on keyword(s): " + searchParamValue + " ...");
440 CollectionObjectClient client = new CollectionObjectClient();
441 ClientResponse<CollectionobjectsCommonList> res =
442 client.keywordSearch(searchParamValue);
446 private long getNumMatched(ClientResponse<CollectionobjectsCommonList> res,
447 long numExpectedMatches) {
448 CollectionobjectsCommonList list = (CollectionobjectsCommonList)
449 res.getEntity(CollectionobjectsCommonList.class);
450 long numMatched = list.getTotalItems();
452 if (logger.isDebugEnabled()) {
453 logger.debug("Keyword search matched " + numMatched +
454 " resources, expected to match " + numExpectedMatches);
457 // Optionally output additional data about list members for debugging.
458 boolean iterateThroughList = false;
459 if (iterateThroughList && logger.isDebugEnabled()) {
460 itemizeListItems(list);
465 private void itemizeListItems(CollectionobjectsCommonList list) {
466 List<CollectionobjectsCommonList.CollectionObjectListItem> items =
467 list.getCollectionObjectListItem();
469 for (CollectionobjectsCommonList.CollectionObjectListItem item : items) {
470 logger.debug("list-item[" + i + "] title="
472 logger.debug("list-item[" + i + "] URI="
478 public static String getSystemTimeIdentifier() {
479 return Long.toString(System.currentTimeMillis());