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