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.text.Collator;
26 import java.util.ArrayList;
27 import java.util.Comparator;
28 import java.util.List;
29 import java.util.Locale;
30 import javax.ws.rs.core.MediaType;
31 import javax.ws.rs.core.Response;
33 import org.collectionspace.services.MovementJAXBSchema;
34 import org.collectionspace.services.client.CollectionSpaceClient;
35 import org.collectionspace.services.client.MovementClient;
36 import org.collectionspace.services.movement.MovementsCommon;
37 import org.collectionspace.services.movement.MovementsCommonList;
38 import org.collectionspace.services.jaxb.AbstractCommonList;
40 import org.jboss.resteasy.client.ClientResponse;
41 import org.jboss.resteasy.plugins.providers.multipart.MultipartInput;
42 import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput;
43 import org.jboss.resteasy.plugins.providers.multipart.OutputPart;
45 import org.testng.Assert;
46 import org.testng.annotations.AfterClass;
47 import org.testng.annotations.DataProvider;
48 import org.testng.annotations.Test;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
54 * MovementSortByTest, tests sorting of summary lists by fields
55 * of various datatypes.
57 * $LastChangedRevision: 2562 $
58 * $LastChangedDate: 2010-06-22 23:26:51 -0700 (Tue, 22 Jun 2010) $
60 public class MovementSortByTest extends BaseServiceTest {
62 private final String CLASS_NAME = MovementSortByTest.class.getName();
63 private final Logger logger = LoggerFactory.getLogger(CLASS_NAME);
64 final String DELIMITER_SCHEMA_AND_FIELD = ":";
65 final String KEYWORD_DESCENDING_SEARCH = "DESC";
66 // Instance variables specific to this test.
67 final String SERVICE_PATH_COMPONENT = "movements";
68 private List<String> movementIdsCreated = new ArrayList<String>();
71 * @see org.collectionspace.services.client.test.BaseServiceTest#getClientInstance()
74 protected CollectionSpaceClient getClientInstance() {
75 throw new UnsupportedOperationException(); //method not supported (or needed) in this test class
79 * @see org.collectionspace.services.client.test.BaseServiceTest#getAbstractCommonList(org.jboss.resteasy.client.ClientResponse)
82 protected AbstractCommonList getAbstractCommonList(
83 ClientResponse<AbstractCommonList> response) {
84 throw new UnsupportedOperationException(); //method not supported (or needed) in this test class
87 // ---------------------------------------------------------------
89 // ---------------------------------------------------------------
94 * Tests whether a list of records, sorted by a String field in
95 * ascending order, is returned in the expected order.
97 @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class,
98 dependsOnMethods = {"createList"})
99 public void sortByStringFieldAscending(String testName) throws Exception {
101 if (logger.isDebugEnabled()) {
102 logger.debug(testBanner(testName, CLASS_NAME));
105 String sortFieldName = qualifySortFieldName(MovementJAXBSchema.MOVEMENT_NOTE);
106 if (logger.isDebugEnabled()) {
107 logger.debug("Sorting on field name=" + sortFieldName);
109 MovementsCommonList list = readSortedList(sortFieldName);
110 List<MovementsCommonList.MovementListItem> items =
111 list.getMovementListItem();
113 String[] values = new String[100];
114 Collator usEnglishCollator = Collator.getInstance(Locale.US);
116 for (MovementsCommonList.MovementListItem item : items) {
117 // Because movementNote is not currently a summary field
118 // (returned in summary list items), we will need to verify
119 // sort order by retrieving full records, using the
120 // IDs provided in the summary list items. amd then retriving
121 // the value of that field from each of those records.
122 MovementsCommon movement = read(item.getCsid());
123 values[i] = movement.getMovementNote();
124 if (logger.isDebugEnabled()) {
125 logger.debug("list-item[" + i + "] movementNote=" + values[i]);
127 // Verify that the value of the specified field in the current record
128 // is equal to or greater than its value in the previous record,
129 // using a locale-specific collator.
131 // (Note: when used with certain text, this test case could potentially
132 // reflect inconsistencies, if any, between Java's collator and the
133 // collator used for ordering by the database. To help avoid this,
134 // it might be useful to keep test strings fairly generic.)
136 Assert.assertTrue(usEnglishCollator.compare(values[i], values[i - 1]) >= 0);
144 * Tests whether a list of records, sorted by a String field in
145 * descending order, is returned in the expected order.
147 @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class,
148 dependsOnMethods = {"createList"})
149 public void sortByStringFieldDescending(String testName) throws Exception {
151 if (logger.isDebugEnabled()) {
152 logger.debug(testBanner(testName, CLASS_NAME));
155 String sortFieldName =
156 asDescendingSort(qualifySortFieldName(MovementJAXBSchema.MOVEMENT_NOTE));
157 if (logger.isDebugEnabled()) {
158 logger.debug("Sorting on field name=" + sortFieldName);
160 MovementsCommonList list = readSortedList(sortFieldName);
161 List<MovementsCommonList.MovementListItem> items =
162 list.getMovementListItem();
164 String[] values = new String[100];
165 Collator usEnglishCollator = Collator.getInstance(Locale.US);
167 for (MovementsCommonList.MovementListItem item : items) {
168 // Because movementNote is not currently a summary field
169 // (returned in summary list items), we will need to verify
170 // sort order by retrieving full records, using the
171 // IDs provided in the summary list items. amd then retriving
172 // the value of that field from each of those records.
173 MovementsCommon movement = read(item.getCsid());
174 values[i] = movement.getMovementNote();
175 if (logger.isDebugEnabled()) {
176 logger.debug("list-item[" + i + "] movementNote=" + values[i]);
178 // Verify that the value of the specified field in the current record
179 // is less than or equal to than its value in the previous record,
180 // using a locale-specific collator.
182 // (Note: when used with certain text, this test case could potentially
183 // reflect inconsistencies, if any, between Java's collator and the
184 // collator used for ordering by the database. To help avoid this,
185 // it might be useful to keep test strings fairly generic.)
187 Assert.assertTrue(usEnglishCollator.compare(values[i], values[i - 1]) <= 0);
195 * Tests whether a list of records, sorted by a dateTime field in
196 * ascending order, is returned in the expected order.
198 @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class,
199 dependsOnMethods = {"createList"})
200 public void sortByDateTimeFieldAscending(String testName) throws Exception {
202 if (logger.isDebugEnabled()) {
203 logger.debug(testBanner(testName, CLASS_NAME));
206 String sortFieldName = qualifySortFieldName(MovementJAXBSchema.LOCATION_DATE);
207 if (logger.isDebugEnabled()) {
208 logger.debug("Sorting on field name=" + sortFieldName);
210 MovementsCommonList list = readSortedList(sortFieldName);
211 List<MovementsCommonList.MovementListItem> items =
212 list.getMovementListItem();
214 String[] values = new String[100];
215 Comparator<String> comparator = String.CASE_INSENSITIVE_ORDER;
217 for (MovementsCommonList.MovementListItem item : items) {
218 values[i] = item.getLocationDate();
219 if (logger.isDebugEnabled()) {
220 logger.debug("list-item[" + i + "] locationDate=" + values[i]);
222 // Verify that the value of the specified field in the current record
223 // is equal to or greater than its value in the previous record.
225 Assert.assertTrue(comparator.compare(values[i], values[i - 1]) >= 0);
232 * Tests whether a list of records, sorted by a dateTime field in
233 * descending order, is returned in the expected order.
235 @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class,
236 dependsOnMethods = {"createList"})
237 public void sortByDateTimeFieldDescending(String testName) throws Exception {
239 if (logger.isDebugEnabled()) {
240 logger.debug(testBanner(testName, CLASS_NAME));
243 String sortFieldName =
244 asDescendingSort(qualifySortFieldName(MovementJAXBSchema.LOCATION_DATE));
245 if (logger.isDebugEnabled()) {
246 logger.debug("Sorting on field name=" + sortFieldName);
248 MovementsCommonList list = readSortedList(sortFieldName);
249 List<MovementsCommonList.MovementListItem> items =
250 list.getMovementListItem();
252 String[] values = new String[100];
253 Comparator<String> comparator = String.CASE_INSENSITIVE_ORDER;
255 for (MovementsCommonList.MovementListItem item : items) {
256 values[i] = item.getLocationDate();
257 if (logger.isDebugEnabled()) {
258 logger.debug("list-item[" + i + "] locationDate=" + values[i]);
260 // Verify that the value of the specified field in the current record
261 // is less than or equal to its value in the previous record.
263 Assert.assertTrue(comparator.compare(values[i], values[i - 1]) <= 1);
270 * Tests whether a request to sort by an empty field name is handled
271 * as expected: the query parameter is simply ignored, and a list
272 * of records is returned, unsorted, with a success result.
274 @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class)
275 public void sortWithEmptySortFieldName(String testName) throws Exception {
277 if (logger.isDebugEnabled()) {
278 logger.debug(testBanner(testName, CLASS_NAME));
280 testSetup(STATUS_OK, ServiceRequestType.READ);
282 // Submit the request to the service and store the response.
283 MovementClient client = new MovementClient();
284 final String EMPTY_SORT_FIELD_NAME = "";
285 ClientResponse<MovementsCommonList> res =
286 client.readListSortedBy(EMPTY_SORT_FIELD_NAME);
287 int statusCode = res.getStatus();
289 // Check the status code of the response: does it match
290 // the expected response(s)?
291 if (logger.isDebugEnabled()) {
292 logger.debug(testName + ": status = " + statusCode);
294 Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
295 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
296 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
303 * Tests whether a request to sort by an unqualified field name is
304 * handled as expected. The field name provided in this test is valid,
305 * but has not been qualified by being prefixed by a schema name and delimiter.
307 @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class)
308 public void sortWithUnqualifiedFieldName(String testName) throws Exception {
310 if (logger.isDebugEnabled()) {
311 logger.debug(testBanner(testName, CLASS_NAME));
313 // FIXME: Ultimately, this should return a BAD_REQUEST status.
314 testSetup(STATUS_INTERNAL_SERVER_ERROR, ServiceRequestType.READ);
316 // Submit the request to the service and store the response.
317 MovementClient client = new MovementClient();
318 ClientResponse<MovementsCommonList> res =
319 client.readListSortedBy(MovementJAXBSchema.LOCATION_DATE);
320 int statusCode = res.getStatus();
322 // Check the status code of the response: does it match
323 // the expected response(s)?
324 if (logger.isDebugEnabled()) {
325 logger.debug(testName + ": status = " + statusCode);
327 Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
328 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
329 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
334 * Tests whether a request to sort by a malformed field name is
335 * handled as expected.
338 @Test(dataProvider="testName", dataProviderClass=AbstractServiceTestImpl.class)
339 public void sortWithMalformedFieldName(String testName) throws Exception {
341 // FIXME: Implement this stub method.
343 // FIXME: Consider splitting this test into various tests, with
344 // different malformed field name formats that might confuse parsers
345 // and/or validation code.
347 // FIXME: Consider fixing DocumentFilter.setSortOrder() to return
348 // an error response to this test case, then revise this test case
349 // to expect that response.
354 // ---------------------------------------------------------------
355 // Cleanup of resources created during testing
356 // ---------------------------------------------------------------
358 * Deletes all resources created by tests, after all tests have been run.
360 * This cleanup method will always be run, even if one or more tests fail.
361 * For this reason, it attempts to remove all resources created
362 * at any point during testing, even if some of those resources
363 * may be expected to be deleted by certain tests.
365 @AfterClass(alwaysRun = true)
366 public void cleanUp() {
367 String noTest = System.getProperty("noTestCleanup");
368 if (Boolean.TRUE.toString().equalsIgnoreCase(noTest)) {
369 if (logger.isDebugEnabled()) {
370 logger.debug("Skipping Cleanup phase ...");
374 if (logger.isDebugEnabled()) {
375 logger.debug("Cleaning up temporary resources created for testing ...");
377 // Delete all Movement resource(s) created during this test.
378 MovementClient movementClient = new MovementClient();
379 for (String resourceId : movementIdsCreated) {
380 // Note: Any non-success responses are ignored and not reported.
381 movementClient.delete(resourceId);
385 // ---------------------------------------------------------------
386 // Utility methods used by tests above
387 // ---------------------------------------------------------------
390 public String getServicePathComponent() {
391 return SERVICE_PATH_COMPONENT;
394 private String getCommonSchemaName() {
395 // FIXME: While this convention - appending a suffix to the name of
396 // the service's first unique URL path component - works, it would
397 // be preferable to get the common schema name from configuration.
398 return getServicePathComponent() + "_" + "common";
401 public String qualifySortFieldName(String fieldName) {
402 return getCommonSchemaName() + DELIMITER_SCHEMA_AND_FIELD + fieldName;
405 public String asDescendingSort(String qualifiedFieldName) {
406 return qualifiedFieldName + " " + KEYWORD_DESCENDING_SEARCH;
410 * A data provider that provides a set of unsorted values, which are
411 * to be used in populating (seeding) values in test records.
413 * Data elements provided for each test record consist of:
414 * * An integer, reflecting expected sort order.
415 * * US English text, to populate the value of a free text (String) field.
416 * * An ISO 8601 timestamp, to populate the value of a calendar date (dateTime) field.
418 @DataProvider(name = "unsortedValues")
419 public Object[][] unsortedValues() {
420 // Add a test record-specific string so we have the option of
421 // constraining tests to only test records, in list or search results.
422 final String TEST_RECORD_SPECIFIC_STRING = CLASS_NAME + " " + "jlmbsoq";
423 return new Object[][]{
424 {1, "Aardvark and plumeria. " + TEST_RECORD_SPECIFIC_STRING, "2009-01-29T00:00:05Z"},
425 {4, "Bat fling off wall. " + TEST_RECORD_SPECIFIC_STRING, "2010-08-30T00:00:00Z"},
426 {2, "Aardvarks and plumeria. " + TEST_RECORD_SPECIFIC_STRING, "2009-01-29T08:00:00Z"},
427 {5, "Zounds! " + TEST_RECORD_SPECIFIC_STRING, "2010-08-31T00:00:00Z"},
428 {3, "Bat flies off ball. " + TEST_RECORD_SPECIFIC_STRING, "2009-05-29T00:00:00Z"}
432 @Test(dataProvider = "unsortedValues")
433 public void createList(int expectedSortOrder, String movementNote,
434 String locationDate) throws Exception {
436 String testName = "createList";
437 if (logger.isDebugEnabled()) {
438 logger.debug(testBanner(testName, CLASS_NAME));
440 testSetup(STATUS_CREATED, ServiceRequestType.CREATE);
442 // Create each unsorted record provided by the data provider.
443 create(movementNote, locationDate);
446 private void create(String movementNote, String locationDate) throws Exception {
448 String testName = "create";
449 testSetup(STATUS_CREATED, ServiceRequestType.CREATE);
451 // Submit the request to the service and store the response.
452 MovementClient client = new MovementClient();
453 MultipartOutput multipart = createMovementInstance(createIdentifier(),
454 movementNote, locationDate);
455 ClientResponse<Response> res = client.create(multipart);
456 int statusCode = res.getStatus();
458 // Check the status code of the response: does it match
459 // the expected response(s)?
462 // Does it fall within the set of valid status codes?
463 // Does it exactly match the expected status code?
464 if (logger.isDebugEnabled()) {
465 logger.debug(testName + ": status = " + statusCode);
467 Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
468 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
469 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
471 // Store the IDs from every resource created by tests,
472 // so they can be deleted after tests have been run.
473 movementIdsCreated.add(extractId(res));
476 private MovementsCommon read(String csid) throws Exception {
478 String testName = "read";
479 testSetup(STATUS_OK, ServiceRequestType.READ);
481 // Submit the request to the service and store the response.
482 MovementClient client = new MovementClient();
483 ClientResponse<MultipartInput> res = client.read(csid);
484 int statusCode = res.getStatus();
486 // Check the status code of the response: does it match
487 // the expected response(s)?
488 if (logger.isDebugEnabled()) {
489 logger.debug(testName + ": status = " + statusCode);
491 Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
492 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
493 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
495 // Extract and return the common part of the record.
496 MultipartInput input = (MultipartInput) res.getEntity();
497 MovementsCommon movement = (MovementsCommon) extractPart(input,
498 client.getCommonPartName(), MovementsCommon.class);
503 private MultipartOutput createMovementInstance(
504 String movementReferenceNumber,
506 String locationDate) {
507 MovementsCommon movement = new MovementsCommon();
508 movement.setMovementReferenceNumber(movementReferenceNumber);
509 movement.setMovementNote(movementNote);
510 movement.setLocationDate(locationDate);
511 MultipartOutput multipart = new MultipartOutput();
512 OutputPart commonPart =
513 multipart.addPart(movement, MediaType.APPLICATION_XML_TYPE);
514 commonPart.getHeaders().add("label", new MovementClient().getCommonPartName());
516 if (logger.isDebugEnabled()) {
517 logger.debug("to be created, movement common");
518 logger.debug(objectAsXmlString(movement, MovementsCommon.class));
524 private MovementsCommonList readSortedList(String sortFieldName) throws Exception {
526 String testName = "readSortedList";
527 testSetup(STATUS_OK, ServiceRequestType.READ);
529 // Submit the request to the service and store the response.
530 MovementClient client = new MovementClient();
532 ClientResponse<MovementsCommonList> res =
533 client.readListSortedBy(sortFieldName);
534 MovementsCommonList list = res.getEntity();
535 int statusCode = res.getStatus();
537 // Check the status code of the response: does it match
538 // the expected response(s)?
539 if (logger.isDebugEnabled()) {
540 logger.debug(testName + ": status = " + statusCode);
542 Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
543 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
544 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);