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