]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
cb2836c022a5885f3cb809e6eebab452a869cea1
[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.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;
32
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.client.PayloadInputPart;
37 import org.collectionspace.services.client.PayloadOutputPart;
38 import org.collectionspace.services.client.PoxPayloadIn;
39 import org.collectionspace.services.client.PoxPayloadOut;
40 import org.collectionspace.services.movement.MovementsCommon;
41 import org.collectionspace.services.movement.MovementsCommonList;
42 import org.collectionspace.services.jaxb.AbstractCommonList;
43
44 import org.jboss.resteasy.client.ClientResponse;
45
46 import org.testng.Assert;
47 import org.testng.annotations.AfterClass;
48 import org.testng.annotations.DataProvider;
49 import org.testng.annotations.Test;
50
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 /**
55  * MovementSortByTest, tests sorting of summary lists by fields
56  * of various datatypes.
57  *
58  * $LastChangedRevision: 2562 $
59  * $LastChangedDate: 2010-06-22 23:26:51 -0700 (Tue, 22 Jun 2010) $
60  */
61 public class MovementSortByTest extends BaseServiceTest {
62
63     private final String CLASS_NAME = MovementSortByTest.class.getName();
64     private final Logger logger = LoggerFactory.getLogger(CLASS_NAME);
65     final String SERVICE_NAME = "movements";
66
67     // Instance variables specific to this test.
68     private final String DELIMITER_SCHEMA_AND_FIELD = ":";
69     private final String KEYWORD_DESCENDING_SEARCH = "DESC";
70     private final String SERVICE_PATH_COMPONENT = "movements";
71     private final String TEST_SPECIFIC_KEYWORD = "msotebstpfscn";
72     private List<String> movementIdsCreated = new ArrayList<String>();
73     private final String SORT_FIELD_SEPARATOR = ", ";
74     private final Locale LOCALE = Locale.US;
75
76     /* (non-Javadoc)
77      * @see org.collectionspace.services.client.test.BaseServiceTest#getClientInstance()
78      */
79     @Override
80     protected CollectionSpaceClient getClientInstance() {
81         throw new UnsupportedOperationException(); //method not supported (or needed) in this test class
82     }
83
84     /* (non-Javadoc)
85      * @see org.collectionspace.services.client.test.BaseServiceTest#getAbstractCommonList(org.jboss.resteasy.client.ClientResponse)
86      */
87     @Override
88     protected AbstractCommonList getAbstractCommonList(
89             ClientResponse<AbstractCommonList> response) {
90         throw new UnsupportedOperationException(); //method not supported (or needed) in this test class
91     }
92
93     // ---------------------------------------------------------------
94     // Sort tests
95     // ---------------------------------------------------------------
96
97     // Success outcomes
98
99     /*
100      * Tests whether a list of records, sorted by a String field in
101      * ascending order, is returned in the expected order.
102      */
103     @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class,
104     dependsOnMethods = {"createList"})
105     public void sortByStringFieldAscending(String testName) throws Exception {
106
107         if (logger.isDebugEnabled()) {
108             logger.debug(testBanner(testName, CLASS_NAME));
109         }
110
111         String sortFieldName = qualifySortFieldName(MovementJAXBSchema.MOVEMENT_NOTE);
112         if (logger.isDebugEnabled()) {
113             logger.debug("Sorting on field name=" + sortFieldName);
114         }
115         MovementsCommonList list = readSortedList(sortFieldName);
116         List<MovementsCommonList.MovementListItem> items =
117                 list.getMovementListItem();
118
119         ArrayList<String> values = new ArrayList<String>();
120         Collator localeSpecificCollator = Collator.getInstance(LOCALE);
121         int i = 0;
122         for (MovementsCommonList.MovementListItem item : items) {
123             // Because movementNote is not currently a summary field
124             // (returned in summary list items), we will need to verify
125             // sort order by retrieving full records, using the
126             // IDs provided in the summary list items. amd then retriving
127             // the value of that field from each of those records.
128             MovementsCommon movement = read(item.getCsid());
129             values.add(i, movement.getMovementNote());
130             if (logger.isDebugEnabled()) {
131                 logger.debug("list-item[" + i + "] movementNote=" + values.get(i));
132             }
133             // Verify that the value of the specified field in the current record
134             // is equal to or greater than its value in the previous record,
135             // using a locale-specific collator.
136             //
137             // (Note: when used with certain text, this test case could potentially
138             // reflect inconsistencies, if any, between Java's collator and the
139             // collator used for ordering by the database.  To help avoid this,
140             // it might be useful to keep test strings fairly generic.)
141             if (i > 0 && values.get(i) != null && values.get(i - 1) != null) {
142                 Assert.assertTrue(localeSpecificCollator.compare(values.get(i), values.get(i - 1)) >= 0);
143             }
144             i++;
145         }
146
147     }
148
149     /*
150      * Tests whether a list of records, obtained by a keyword search, and
151      * sorted by a String field in ascending order, is returned in the expected order.
152      *
153      * This verifies that summary list results from keyword searches, in
154      * addition to 'read list' requests, can be returned in sorted order.
155      */
156     @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class,
157     dependsOnMethods = {"createList"})
158     public void sortKeywordSearchResultsByStringFieldAscending(String testName) throws Exception {
159
160         if (logger.isDebugEnabled()) {
161             logger.debug(testBanner(testName, CLASS_NAME));
162         }
163
164         String sortFieldName = qualifySortFieldName(MovementJAXBSchema.MOVEMENT_NOTE);
165         if (logger.isDebugEnabled()) {
166             logger.debug("Sorting on field name=" + sortFieldName);
167         }
168         MovementsCommonList list = keywordSearchSortedBy(TEST_SPECIFIC_KEYWORD, sortFieldName);
169         List<MovementsCommonList.MovementListItem> items =
170                 list.getMovementListItem();
171
172         ArrayList<String> values = new ArrayList<String>();
173         Collator localeSpecificCollator = Collator.getInstance(LOCALE);
174         int i = 0;
175         for (MovementsCommonList.MovementListItem item : items) {
176             // Because movementNote is not currently a summary field
177             // (returned in summary list items), we will need to verify
178             // sort order by retrieving full records, using the
179             // IDs provided in the summary list items. amd then retriving
180             // the value of that field from each of those records.
181             MovementsCommon movement = read(item.getCsid());
182             values.add(i, movement.getMovementNote());
183             if (logger.isDebugEnabled()) {
184                 logger.debug("list-item[" + i + "] movementNote=" + values.get(i));
185             }
186             // Verify that the value of the specified field in the current record
187             // is equal to or greater than its value in the previous record,
188             // using a locale-specific collator.
189             //
190             // (Note: when used with certain text, this test case could potentially
191             // reflect inconsistencies, if any, between Java's collator and the
192             // collator used for ordering by the database.  To help avoid this,
193             // it might be useful to keep test strings fairly generic.)
194             if (i > 0 && values.get(i) != null && values.get(i - 1) != null) {
195                 Assert.assertTrue(localeSpecificCollator.compare(values.get(i), values.get(i - 1)) >= 0);
196             }
197             i++;
198         }
199
200     }
201
202     /*
203      * Tests whether a list of records, sorted by a String field in
204      * descending order, is returned in the expected order.
205      */
206     @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class,
207     dependsOnMethods = {"createList"})
208     public void sortByStringFieldDescending(String testName) throws Exception {
209
210         if (logger.isDebugEnabled()) {
211             logger.debug(testBanner(testName, CLASS_NAME));
212         }
213
214         String sortFieldName =
215                 asDescendingSort(qualifySortFieldName(MovementJAXBSchema.MOVEMENT_NOTE));
216         if (logger.isDebugEnabled()) {
217             logger.debug("Sorting on field name=" + sortFieldName);
218         }
219         MovementsCommonList list = readSortedList(sortFieldName);
220         List<MovementsCommonList.MovementListItem> items =
221                 list.getMovementListItem();
222
223         ArrayList<String> values = new ArrayList<String>();
224         Collator localeSpecificCollator = Collator.getInstance(LOCALE);
225         int i = 0;
226         for (MovementsCommonList.MovementListItem item : items) {
227             // Because movementNote is not currently a summary field
228             // (returned in summary list items), we will need to verify
229             // sort order by retrieving full records, using the
230             // IDs provided in the summary list items. amd then retriving
231             // the value of that field from each of those records.
232             MovementsCommon movement = read(item.getCsid());
233             values.add(i, movement.getMovementNote());
234             if (logger.isDebugEnabled()) {
235                 logger.debug("list-item[" + i + "] movementNote=" + values.get(i));
236             }
237             // Verify that the value of the specified field in the current record
238             // is less than or equal to than its value in the previous record,
239             // using a locale-specific collator.
240             //
241             // (Note: when used with certain text, this test case could potentially
242             // reflect inconsistencies, if any, between Java's collator and the
243             // collator used for ordering by the database.  To help avoid this,
244             // it might be useful to keep test strings fairly generic.)
245             if (i > 0 && values.get(i) != null && values.get(i - 1) != null) {
246                 Assert.assertTrue(localeSpecificCollator.compare(values.get(i), values.get(i - 1)) <= 0);
247             }
248             i++;
249         }
250
251     }
252
253     /*
254      * Tests whether a list of records, sorted by a dateTime field in
255      * ascending order, is returned in the expected order.
256      */
257     @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class,
258     dependsOnMethods = {"createList"})
259     public void sortByDateTimeFieldAscending(String testName) throws Exception {
260
261         if (logger.isDebugEnabled()) {
262             logger.debug(testBanner(testName, CLASS_NAME));
263         }
264
265         String sortFieldName = qualifySortFieldName(MovementJAXBSchema.LOCATION_DATE);
266         if (logger.isDebugEnabled()) {
267             logger.debug("Sorting on field name=" + sortFieldName);
268         }
269         MovementsCommonList list = readSortedList(sortFieldName);
270         List<MovementsCommonList.MovementListItem> items =
271                 list.getMovementListItem();
272
273         ArrayList<String> values = new ArrayList<String>();
274         Comparator<String> comparator = String.CASE_INSENSITIVE_ORDER;
275         int i = 0;
276         for (MovementsCommonList.MovementListItem item : items) {
277             values.add(i, item.getLocationDate());
278             if (logger.isDebugEnabled()) {
279                 logger.debug("list-item[" + i + "] locationDate=" + values.get(i));
280             }
281             // Verify that the value of the specified field in the current record
282             // is equal to or greater than its value in the previous record.
283             if (i > 0 && values.get(i) != null && values.get(i - 1) != null) {
284                 Assert.assertTrue(comparator.compare(values.get(i), values.get(i - 1)) >= 0);
285             }
286             i++;
287         }
288     }
289
290     /*
291      * Tests whether a list of records, sorted by a dateTime field in
292      * descending order, is returned in the expected order.
293      */
294     @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class,
295     dependsOnMethods = {"createList"})
296     public void sortByDateTimeFieldDescending(String testName) throws Exception {
297
298         if (logger.isDebugEnabled()) {
299             logger.debug(testBanner(testName, CLASS_NAME));
300         }
301
302         String sortFieldName =
303                 asDescendingSort(qualifySortFieldName(MovementJAXBSchema.LOCATION_DATE));
304         if (logger.isDebugEnabled()) {
305             logger.debug("Sorting on field name=" + sortFieldName);
306         }
307         MovementsCommonList list = readSortedList(sortFieldName);
308         List<MovementsCommonList.MovementListItem> items =
309                 list.getMovementListItem();
310
311         ArrayList<String> values = new ArrayList<String>();
312         Comparator<String> comparator = String.CASE_INSENSITIVE_ORDER;
313         int i = 0;
314         for (MovementsCommonList.MovementListItem item : items) {
315             values.add(i, item.getLocationDate());
316             if (logger.isDebugEnabled()) {
317                 logger.debug("list-item[" + i + "] locationDate=" + values.get(i));
318             }
319             // Verify that the value of the specified field in the current record
320             // is less than or equal to its value in the previous record.
321             if (i > 0 && values.get(i) != null && values.get(i - 1) != null) {
322                 Assert.assertTrue(comparator.compare(values.get(i), values.get(i - 1)) <= 0);
323             }
324             i++;
325         }
326     }
327
328     /*
329      * Tests whether a list of records, sorted by two different fields in
330      * ascending order, is returned in the expected order.
331      */
332     @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class,
333     dependsOnMethods = {"createList"})
334     public void sortByTwoFieldsAscending(String testName) throws Exception {
335
336         if (logger.isDebugEnabled()) {
337             logger.debug(testBanner(testName, CLASS_NAME));
338         }
339
340         String firstSortFieldName = qualifySortFieldName(MovementJAXBSchema.MOVEMENT_NOTE);
341         String secondSortFieldName = qualifySortFieldName(MovementJAXBSchema.LOCATION_DATE);
342         if (logger.isDebugEnabled()) {
343             logger.debug("Sorting on field names=" + firstSortFieldName + " and " + secondSortFieldName);
344         }
345         String sortExpression = firstSortFieldName + SORT_FIELD_SEPARATOR + secondSortFieldName;
346         MovementsCommonList list = readSortedList(sortExpression);
347         List<MovementsCommonList.MovementListItem> items =
348                 list.getMovementListItem();
349
350         ArrayList<String> firstFieldValues = new ArrayList<String>();
351         ArrayList<String> secondFieldValues = new ArrayList<String>();
352         Collator localeSpecificCollator = Collator.getInstance(LOCALE);
353         Comparator<String> comparator = String.CASE_INSENSITIVE_ORDER;
354         int i = 0;
355         for (MovementsCommonList.MovementListItem item : items) {
356             // Because movementNote is not currently a summary field
357             // (returned in summary list items), we will need to verify
358             // sort order by retrieving full records, using the
359             // IDs provided in the summary list items. amd then retriving
360             // the value of that field from each of those records.
361             MovementsCommon movement = read(item.getCsid());
362             firstFieldValues.add(i, movement.getMovementNote());
363             secondFieldValues.add(i, movement.getLocationDate());
364             if (logger.isDebugEnabled()) {
365                 logger.debug("list-item[" + i + "] movementNote=" + firstFieldValues.get(i));
366                 logger.debug("list-item[" + i + "] locationDate=" + secondFieldValues.get(i));
367             }
368             // Verify that the value of the specified field in the current record
369             // is less than or greater than its value in the previous record.
370             if (i > 0 && firstFieldValues.get(i) != null && firstFieldValues.get(i - 1) != null) {
371                 Assert.assertTrue(localeSpecificCollator.compare(firstFieldValues.get(i), firstFieldValues.get(i - 1)) >= 0);
372                 // If the value of the first sort field in the current record is identical to
373                 // its value in the previous record, verify that the value of the second sort
374                 // field is equal to or greater than its value in the previous record,
375                 // using a locale-specific collator.
376                 if (localeSpecificCollator.compare(firstFieldValues.get(i), firstFieldValues.get(i - 1)) == 0) {
377                     if (i > 0 && secondFieldValues.get(i) != null && secondFieldValues.get(i - 1) != null) {
378                         Assert.assertTrue(comparator.compare(secondFieldValues.get(i), secondFieldValues.get(i - 1)) >= 0);
379                     }
380                 }
381             }
382             i++;
383         }
384     }
385
386     /*
387      * Tests whether a list of records, sorted by one different fields in
388      * descending order and a second field in ascending order, is returned in the expected order.
389      */
390     @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class,
391     dependsOnMethods = {"createList"})
392     public void sortByOneFieldAscendingOneFieldsDescending(String testName) throws Exception {
393
394        if (logger.isDebugEnabled()) {
395             logger.debug(testBanner(testName, CLASS_NAME));
396         }
397
398         String firstSortFieldName =
399                 asDescendingSort(qualifySortFieldName(MovementJAXBSchema.LOCATION_DATE));
400         String secondSortFieldName = qualifySortFieldName(MovementJAXBSchema.MOVEMENT_NOTE);
401         if (logger.isDebugEnabled()) {
402             logger.debug("Sorting on field names=" + firstSortFieldName + " and " + secondSortFieldName);
403         }
404         String sortExpression = firstSortFieldName + SORT_FIELD_SEPARATOR + secondSortFieldName;
405         MovementsCommonList list = readSortedList(sortExpression);
406         List<MovementsCommonList.MovementListItem> items =
407                 list.getMovementListItem();
408
409         ArrayList<String> firstFieldValues = new ArrayList<String>();
410         ArrayList<String> secondFieldValues = new ArrayList<String>();
411         Collator localeSpecificCollator = Collator.getInstance(LOCALE);
412         Comparator<String> comparator = String.CASE_INSENSITIVE_ORDER;
413         int i = 0;
414         for (MovementsCommonList.MovementListItem item : items) {
415             // Because movementNote is not currently a summary field
416             // (returned in summary list items), we will need to verify
417             // sort order by retrieving full records, using the
418             // IDs provided in the summary list items. amd then retriving
419             // the value of that field from each of those records.
420             MovementsCommon movement = read(item.getCsid());
421             firstFieldValues.add(i, movement.getLocationDate());
422             secondFieldValues.add(i, movement.getMovementNote());
423             if (logger.isDebugEnabled()) {
424                 logger.debug("list-item[" + i + "] locationDate=" + firstFieldValues.get(i));
425                 logger.debug("list-item[" + i + "] movementNote=" + secondFieldValues.get(i));
426             }
427             // Verify that the value of the specified field in the current record
428             // is less than or equal to than its value in the previous record.
429             if (i > 0 && firstFieldValues.get(i) != null && firstFieldValues.get(i - 1) != null) {
430                 Assert.assertTrue(comparator.compare(firstFieldValues.get(i), firstFieldValues.get(i - 1)) <= 0);
431                 // If the value of the first sort field in the current record is identical to
432                 // its value in the previous record, verify that the value of the second sort
433                 // field is equal to or greater than its value in the previous record,
434                 // using a locale-specific collator.
435                 if (comparator.compare(firstFieldValues.get(i), firstFieldValues.get(i - 1)) == 0) {
436                     if (i > 0 && secondFieldValues.get(i) != null && secondFieldValues.get(i - 1) != null) {
437                         Assert.assertTrue(localeSpecificCollator.compare(secondFieldValues.get(i), secondFieldValues.get(i - 1)) >= 0);
438                     }
439                 }
440             }
441             i++;
442         }
443     }
444
445
446     /*
447      * Tests whether a request to sort by an empty field name is handled
448      * as expected: the query parameter is simply ignored, and a list
449      * of records is returned, unsorted, with a success result.
450      */
451     @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class)
452     public void sortWithEmptySortFieldName(String testName) throws Exception {
453
454         if (logger.isDebugEnabled()) {
455             logger.debug(testBanner(testName, CLASS_NAME));
456         }
457         testSetup(STATUS_OK, ServiceRequestType.READ);
458
459         // Submit the request to the service and store the response.
460         MovementClient client = new MovementClient();
461         final String EMPTY_SORT_FIELD_NAME = "";
462         ClientResponse<MovementsCommonList> res =
463                 client.readListSortedBy(EMPTY_SORT_FIELD_NAME);
464         int statusCode = res.getStatus();
465
466         // Check the status code of the response: does it match
467         // the expected response(s)?
468         if (logger.isDebugEnabled()) {
469             logger.debug(testName + ": status = " + statusCode);
470         }
471         Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
472                 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
473         Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
474
475     }
476
477     // Failure outcomes
478
479     /*
480      * Tests whether a request to sort by an unqualified field name is
481      * handled as expected.  The field name provided in this test is valid,
482      * but has not been qualified by being prefixed by a schema name and delimiter.
483      */
484     @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class)
485     public void sortWithUnqualifiedFieldName(String testName) throws Exception {
486
487         if (logger.isDebugEnabled()) {
488             logger.debug(testBanner(testName, CLASS_NAME));
489         }
490         // FIXME: Ultimately, this should return a BAD_REQUEST status.
491         testSetup(STATUS_INTERNAL_SERVER_ERROR, ServiceRequestType.READ);
492
493         // Submit the request to the service and store the response.
494         MovementClient client = new MovementClient();
495         ClientResponse<MovementsCommonList> res =
496                 client.readListSortedBy(MovementJAXBSchema.LOCATION_DATE);
497         int statusCode = res.getStatus();
498
499         // Check the status code of the response: does it match
500         // the expected response(s)?
501         if (logger.isDebugEnabled()) {
502             logger.debug(testName + ": status = " + statusCode);
503         }
504         Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
505                 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
506         Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
507
508     }
509
510     /*
511      * Tests whether a request to sort by an invalid identifier for the
512      * sort order (ascending or descending) is handled as expected.
513      */
514     @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTestImpl.class)
515     public void sortWithInvalidSortOrderIdentifier(String testName) throws Exception {
516
517         if (logger.isDebugEnabled()) {
518             logger.debug(testBanner(testName, CLASS_NAME));
519         }
520         // FIXME: Ultimately, this should return a BAD_REQUEST status.
521         testSetup(STATUS_INTERNAL_SERVER_ERROR, ServiceRequestType.READ);
522
523         // Submit the request to the service and store the response.
524         MovementClient client = new MovementClient();
525         final String INVALID_SORT_ORDER_IDENTIFIER = "NO_DIRECTION";
526         ClientResponse<MovementsCommonList> res =
527                 client.readListSortedBy(MovementJAXBSchema.LOCATION_DATE
528                 + " " + INVALID_SORT_ORDER_IDENTIFIER);
529         int statusCode = res.getStatus();
530
531         // Check the status code of the response: does it match
532         // the expected response(s)?
533         if (logger.isDebugEnabled()) {
534             logger.debug(testName + ": status = " + statusCode);
535         }
536         Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
537                 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
538         Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
539
540     }
541
542     /*
543      * Tests whether a request to sort by a malformed field name is
544      * handled as expected.
545      */
546 /*
547     @Test(dataProvider="testName", dataProviderClass=AbstractServiceTestImpl.class)
548     public void sortWithMalformedFieldName(String testName) throws Exception {
549
550     // FIXME: Implement this stub method.
551
552     // FIXME: Consider splitting this test into various tests, with
553     // different malformed field name formats that might confuse parsers
554     // and/or validation code.
555
556     // FIXME: Consider fixing DocumentFilter.setSortOrder() to return
557     // an error response to this test case, then revise this test case
558     // to expect that response.
559
560     }
561 */
562
563     // ---------------------------------------------------------------
564     // Cleanup of resources created during testing
565     // ---------------------------------------------------------------
566     /**
567      * Deletes all resources created by tests, after all tests have been run.
568      *
569      * This cleanup method will always be run, even if one or more tests fail.
570      * For this reason, it attempts to remove all resources created
571      * at any point during testing, even if some of those resources
572      * may be expected to be deleted by certain tests.
573      */
574     @AfterClass(alwaysRun = true)
575     public void cleanUp() {
576         String noTest = System.getProperty("noTestCleanup");
577         if (Boolean.TRUE.toString().equalsIgnoreCase(noTest)) {
578             if (logger.isDebugEnabled()) {
579                 logger.debug("Skipping Cleanup phase ...");
580             }
581             return;
582         }
583         if (logger.isDebugEnabled()) {
584             logger.debug("Cleaning up temporary resources created for testing ...");
585         }
586         // Delete all Movement resource(s) created during this test.
587         MovementClient movementClient = new MovementClient();
588         for (String resourceId : movementIdsCreated) {
589             // Note: Any non-success responses are ignored and not reported.
590             movementClient.delete(resourceId);
591         }
592     }
593
594     // ---------------------------------------------------------------
595     // Utility methods used by tests above
596     // ---------------------------------------------------------------
597
598     @Override
599     protected String getServiceName() {
600         return SERVICE_NAME;
601     }
602
603     @Override
604     public String getServicePathComponent() {
605         return SERVICE_PATH_COMPONENT;
606     }
607
608     private String getCommonSchemaName() {
609         // FIXME: While this convention - appending a suffix to the name of
610         // the service's first unique URL path component - works, it would
611         // be preferable to get the common schema name from configuration.
612         //
613         // Such configuration is provided for example, on the services side, in
614         // org.collectionspace.services.common.context.AbstractServiceContextImpl
615         return getServicePathComponent() + "_" + "common";
616     }
617
618     public String qualifySortFieldName(String fieldName) {
619         return getCommonSchemaName() + DELIMITER_SCHEMA_AND_FIELD + fieldName;
620     }
621
622     public String asDescendingSort(String qualifiedFieldName) {
623         return qualifiedFieldName + " " + KEYWORD_DESCENDING_SEARCH;
624     }
625
626     /*
627      * A data provider that provides a set of unsorted values, which are
628      * to be used in populating (seeding) values in test records.
629      *
630      * Data elements provided for each test record consist of:
631      * * An integer, reflecting expected sort order.
632      * * US English text, to populate the value of a free text (String) field.
633      * * An ISO 8601 timestamp, to populate the value of a calendar date (dateTime) field.
634      */
635     @DataProvider(name = "unsortedValues")
636     public Object[][] unsortedValues() {
637         // FIXME: ADR Add a test record-specific string so we have the option of
638         // constraining tests to only test records, in list or search results.
639         final String TEST_RECORD_SPECIFIC_STRING = CLASS_NAME + " " + TEST_SPECIFIC_KEYWORD;
640         return new Object[][]{
641                     {1, "Aardvark and plumeria. " + TEST_RECORD_SPECIFIC_STRING, "2009-01-29T00:00:05Z"},
642                     {10, "Zounds! " + TEST_RECORD_SPECIFIC_STRING, "2010-08-31T00:00:00Z"},
643                     {3, "Aardvark and plumeria. " + TEST_RECORD_SPECIFIC_STRING, "2010-08-30T00:00:00Z"},
644                     {7, "Bat fling off wall. " + TEST_RECORD_SPECIFIC_STRING, "2010-08-30T00:00:00Z"},
645                     {4, "Aardvarks and plumeria. " + TEST_RECORD_SPECIFIC_STRING, "2009-01-29T08:00:00Z"},
646                     {5, "Aardvarks and plumeria. " + TEST_RECORD_SPECIFIC_STRING, "2009-05-29T00:00:00Z"},
647                     {2, "Aardvark and plumeria. " + TEST_RECORD_SPECIFIC_STRING, "2009-05-29T00:00:00Z"},
648                     {9, "Zounds! " + TEST_RECORD_SPECIFIC_STRING, "2009-05-29T00:00:00Z"}, // Identical to next record
649                     {8, "Zounds! " + TEST_RECORD_SPECIFIC_STRING, "2009-05-29T00:00:00Z"},
650                     {6, "Bat flies off ball. " + TEST_RECORD_SPECIFIC_STRING, "2009-05-29T00:00:00Z"}
651                 };
652     }
653
654     /*
655      * Create multiple test records, initially in unsorted order,
656      * using values for various fields obtained from the data provider.
657      */
658     @Test(dataProvider = "unsortedValues")
659     public void createList(int expectedSortOrder, String movementNote,
660             String locationDate) throws Exception {
661
662         String testName = "createList";
663         if (logger.isDebugEnabled()) {
664             logger.debug(testBanner(testName, CLASS_NAME));
665         }
666         testSetup(STATUS_CREATED, ServiceRequestType.CREATE);
667
668         // Iterates through the sets of values returned by the data provider,
669         // and creates a corresponding test record for each set of values.
670         create(movementNote, locationDate);
671     }
672
673     private void create(String movementNote, String locationDate) throws Exception {
674
675         String testName = "create";
676         testSetup(STATUS_CREATED, ServiceRequestType.CREATE);
677
678         // Submit the request to the service and store the response.
679         PoxPayloadOut multipart = createMovementInstance(createIdentifier(),
680                 movementNote, locationDate);
681         MovementClient client = new MovementClient();
682         ClientResponse<Response> res = client.create(multipart);
683         int statusCode = res.getStatus();
684
685         // Check the status code of the response: does it match
686         // the expected response(s)?
687         //
688         // Specifically:
689         // Does it fall within the set of valid status codes?
690         // Does it exactly match the expected status code?
691         if (logger.isDebugEnabled()) {
692             logger.debug(testName + ": status = " + statusCode);
693         }
694         Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
695                 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
696         Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
697
698         // Store the IDs from every resource created by tests,
699         // so they can be deleted after tests have been run.
700         movementIdsCreated.add(extractId(res));
701     }
702
703     private MovementsCommon read(String csid) throws Exception {
704
705         String testName = "read";
706         testSetup(STATUS_OK, ServiceRequestType.READ);
707
708         // Submit the request to the service and store the response.
709         MovementClient client = new MovementClient();
710         ClientResponse<String> res = client.read(csid);
711         int statusCode = res.getStatus();
712
713         // Check the status code of the response: does it match
714         // the expected response(s)?
715         if (logger.isDebugEnabled()) {
716             logger.debug(testName + ": status = " + statusCode);
717         }
718         Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
719                 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
720         Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
721
722         // Extract and return the common part of the record.
723         PoxPayloadIn input = new PoxPayloadIn(res.getEntity());
724         PayloadInputPart payloadInputPart = input.getPart(client.getCommonPartName());
725         MovementsCommon movementCommon = null;
726         if (payloadInputPart != null) {
727                 movementCommon = (MovementsCommon) payloadInputPart.getBody();
728         }
729
730         return movementCommon;
731     }
732
733     private PoxPayloadOut createMovementInstance(
734             String movementReferenceNumber,
735             String movementNote,
736             String locationDate) {
737         MovementsCommon movementCommon = new MovementsCommon();
738         movementCommon.setMovementReferenceNumber(movementReferenceNumber);
739         movementCommon.setMovementNote(movementNote);
740         movementCommon.setLocationDate(locationDate);
741
742         PoxPayloadOut multipart = new PoxPayloadOut(this.getServicePathComponent());
743         PayloadOutputPart commonPart =
744             multipart.addPart(movementCommon, MediaType.APPLICATION_XML_TYPE);
745         commonPart.setLabel(new MovementClient().getCommonPartName());
746         if (logger.isDebugEnabled()) {
747             logger.debug("to be created, movement common");
748             logger.debug(objectAsXmlString(movementCommon, MovementsCommon.class));
749         }
750
751         return multipart;
752     }
753
754     private MovementsCommonList readSortedList(String sortFieldName) throws Exception {
755
756         String testName = "readSortedList";
757         testSetup(STATUS_OK, ServiceRequestType.READ);
758
759         // Submit the request to the service and store the response.
760         MovementClient client = new MovementClient();
761
762         ClientResponse<MovementsCommonList> res =
763                 client.readListSortedBy(sortFieldName);
764         MovementsCommonList list = res.getEntity();
765         int statusCode = res.getStatus();
766
767         // Check the status code of the response: does it match
768         // the expected response(s)?
769         if (logger.isDebugEnabled()) {
770             logger.debug(testName + ": status = " + statusCode);
771         }
772         Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
773                 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
774         Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
775
776         return list;
777
778     }
779
780     private MovementsCommonList keywordSearchSortedBy(String keywords,
781             String sortFieldName) throws Exception {
782
783         String testName = "keywordSearchSortedBy";
784         testSetup(STATUS_OK, ServiceRequestType.READ);
785
786         // Submit the request to the service and store the response.
787         MovementClient client = new MovementClient();
788
789         ClientResponse<MovementsCommonList> res =
790                 client.keywordSearchSortedBy(keywords, sortFieldName);
791         MovementsCommonList list = res.getEntity();
792         int statusCode = res.getStatus();
793
794         // Check the status code of the response: does it match
795         // the expected response(s)?
796         if (logger.isDebugEnabled()) {
797             logger.debug(testName + ": status = " + statusCode);
798         }
799         Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode),
800                 invalidStatusCodeMessage(REQUEST_TYPE, statusCode));
801         Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
802
803         return list;
804
805     }
806
807 }