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