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