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:
6 * http://www.collectionspace.org
7 * http://wiki.collectionspace.org
9 * Copyright © 2009 Regents of the University of California
11 * Licensed under the Educational Community License (ECL), Version 2.0.
12 * You may not use this file except in compliance with this License.
14 * You may obtain a copy of the ECL 2.0 License at
15 * https://source.collectionspace.org/collection-space/LICENSE.txt
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.
24 package org.collectionspace.services.client.test;
26 import java.util.ArrayList;
27 import java.util.List;
28 import javax.ws.rs.core.MultivaluedMap;
29 import javax.ws.rs.core.Response;
30 import javax.xml.bind.JAXBContext;
31 import javax.xml.bind.Marshaller;
32 import org.jboss.resteasy.client.ClientResponse;
33 import org.testng.Assert;
34 import org.testng.annotations.Test;
36 import org.collectionspace.services.client.CollectionObjectClient;
37 import org.collectionspace.services.collectionobject.CollectionObject;
38 import org.collectionspace.services.collectionobject.CollectionObjectList;
40 import java.io.IOException;
41 import java.io.UnsupportedEncodingException;
42 import java.util.Arrays;
44 import javax.ws.rs.core.MediaType;
45 import javax.ws.rs.core.Response.Status;
46 // import org.jboss.resteasy.client.ClientRequest;
47 import org.collectionspace.services.client.TestServiceClient;
48 import org.apache.commons.httpclient.Header;
49 import org.apache.commons.httpclient.HttpClient;
50 import org.apache.commons.httpclient.HttpException;
51 import org.apache.commons.httpclient.HttpMethod;
52 import org.apache.commons.httpclient.HttpStatus;
53 import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
54 import org.apache.commons.httpclient.methods.GetMethod;
55 import org.apache.commons.httpclient.methods.PostMethod;
56 import org.apache.commons.httpclient.methods.PutMethod;
57 import org.apache.commons.httpclient.methods.RequestEntity;
58 import org.apache.commons.httpclient.methods.StringRequestEntity;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
64 * CollectionObjectServiceTest, carries out tests against a
65 * deployed and running CollectionObject Service.
67 * $LastChangedRevision$
70 public class CollectionObjectServiceTest {
72 // Instance variables specific to this test.
73 final Logger logger = LoggerFactory.getLogger(CollectionObjectServiceTest.class);
74 private CollectionObjectClient client = new CollectionObjectClient();
76 // Instance variables common to all entity service test classes.
77 private String knownObjectId = null;
78 private final String NON_EXISTENT_ID = createNonExistentIdentifier();
79 private HttpClient httpClient = new HttpClient();
80 private TestServiceClient serviceClient = new TestServiceClient();
83 // ---------------------------------------------------------------
84 // Service Discovery tests
85 // ---------------------------------------------------------------
90 // ---------------------------------------------------------------
91 // CRUD tests : CREATE tests
92 // ---------------------------------------------------------------
98 * Tests creation of a new resource of the specified type.
100 * The 'Location' header will contain the URL for the newly created object.
101 * This is required by the extractId() utility method, below.
103 * The newly-created resource is also used by other test(s)
104 * (e.g. update, delete) which follow, below.
107 public void create() {
109 // Expected status code: 201 Created
110 final int EXPECTED_STATUS_CODE = Response.Status.CREATED.getStatusCode();
112 // Submit the request to the service and store the response.
113 String identifier = this.createIdentifier();
114 CollectionObject collectionObject = createCollectionObject(identifier);
115 ClientResponse<Response> res = client.createCollectionObject(collectionObject);
116 int statusCode = res.getStatus();
118 // Experiment to determine if we can programmatically obtain the HTTP method
119 // used in the request, from the response headers, so we can provide that to
120 // statusCodeWithinExpectedSet(), below.
122 // It doesn't appear that we can; tried this also with 'curl -i {url}' without success.
124 // Need to look into whether this is currently returned from our Java Client Library
125 // 'service access' classes, or whether we can add this functionality there.
127 MultivaluedMap metadata = res.getMetadata();
128 Set<String> set = metadata.keySet();
129 for (String key : set) {
130 // Note: values in the metadata (HTTP response headers) are of type List
131 verbose(key + " : " + metadata.get(key).toString());
135 // Check the status code of the response: does it match the expected response(s)?
136 verbose("create: status = " + statusCode);
137 final String REQUEST_TYPE="CREATE"; // @TODO Consider replacing with enum.
138 Assert.assertTrue(statusCodeWithinExpectedSet(REQUEST_TYPE, statusCode),
139 "Status code '" + statusCode + "' in response is NOT within the expected set.");
140 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
142 // Store the ID returned from this create operation for additional tests below.
143 knownObjectId = extractId(res);
147 * Creates two or more new objects of the specified type.
149 * Repeatedly calls the create test, above, and relies on its
152 * The newly-created objects are also used by other test(s)
153 * (e.g. read multiple/list) which follow, below.
155 @Test(dependsOnMethods = {"create"})
156 public void createMultiple() {
157 for(int i = 0; i < 3; i++){
166 * Tests creation of a resource of the specified type by sending a null to the client proxy.
169 @Test(dependsOnMethods = {"create"}, expectedExceptions = IllegalArgumentException.class)
170 public void createNull() {
172 // Expected result: IllegalArgumentException
173 ClientResponse<Response> res = client.createCollectionObject(null);
177 * Tests creation of a resource of the specified type by sending malformed XML data
178 * in the entity body of the request.
181 @Test(dependsOnMethods = {"create", "testSubmitRequest"})
182 public void createWithMalformedXml() {
184 // Expected status code: 400 Bad Request
185 final int EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
187 // @TODO This test is currently commented out, because it returns a
188 // 500 Internal Server Error status code, rather than the expected status code.
190 // Submit the request to the service and store the response.
191 String url = getServiceRootURL();
192 PostMethod method = new PostMethod(url);
193 final String MALFORMED_XML_DATA =
194 "<malformed_xml>wrong schema contents</malformed_xml"; // Note: intentionally missing bracket.
195 StringRequestEntity entity = getXmlEntity(MALFORMED_XML_DATA);
196 int statusCode = submitRequest(method, entity);
198 // Check the status code of the response: does it match the expected response(s)?
199 verbose("createWithMalformedXml url=" + url + " status=" + statusCode);
200 final String REQUEST_TYPE="CREATE"; // @TODO Consider replacing with enum.
201 Assert.assertTrue(statusCodeWithinExpectedSet(REQUEST_TYPE, statusCode),
202 "Status code '" + statusCode + "' in response is NOT within the expected set.");
203 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
208 * Tests creation of a resource of the specified type by sending data
209 * in the wrong schema (e.g. in a format that doesn't match the object's schema)
210 * in the entity body of the request.
213 @Test(dependsOnMethods = {"create", "testSubmitRequest", "createWithMalformedXml"})
214 public void createWithWrongSchema() {
216 // Expected status code: 400 Bad Request
217 final int EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
219 // @TODO This test is currently commented out, because it returns a
220 // 500 Internal Server Error status code, rather than the expected status code.
222 // Submit the request to the service and store the response.
223 String url = getServiceRootURL();
224 PostMethod method = new PostMethod(url);
225 final String WRONG_SCHEMA_DATA = "<wrong_schema>wrong schema contents</wrong_schema>";
226 StringRequestEntity entity = getXmlEntity(WRONG_SCHEMA_DATA);
227 int statusCode = submitRequest(method, entity);
229 // Check the status code of the response: does it match the expected response(s)?
230 verbose("createWithWrongSchema url=" + url + " status=" + statusCode);
231 final String REQUEST_TYPE="CREATE"; // @TODO Consider replacing with enum.
232 Assert.assertTrue(statusCodeWithinExpectedSet(REQUEST_TYPE, statusCode),
233 "Status code '" + statusCode + "' in response is NOT within the expected set.");
234 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
239 * Tests creation of a resource of the specified type,
240 * by a user who is not authorized to perform this action.
243 @Test(dependsOnMethods = {"create"})
244 public void createWithoutAuthorization() {
246 // Expected status code: 403 Forbidden
247 final int EXPECTED_STATUS_CODE = Response.Status.FORBIDDEN.getStatusCode();
249 // @TODO Currently only a stub. This test can be implemented
250 // when the service is revised to require authorization.
255 * Tests creation of a duplicate object of the specified type,
256 * whose unique resource identifier duplicates that of an existing object.
259 @Test(dependsOnMethods = {"create"})
260 public void createDuplicate() {
262 // Expected status code: 409 Conflict
263 final int EXPECTED_STATUS_CODE = Response.Status.CONFLICT.getStatusCode();
265 // @TODO This test is currently commented out because our current
266 // services do not appear to permit creation of duplicate records.
267 // Please see below for more details.
269 // Note: there doesn't appear to be a way to create a duplicate
270 // resource (object) by POSTing:
272 // 1. We can't POST to a specific resource by ID; that returns a
273 // response with a 405 Method Not Allowed status code.
275 // 2. If we POST to the container in which new resources are created,
276 // it doesn't appear that we can specify the CSID that the newly-created
277 // resource (object) will receive.
279 // If the two points above are accurate, this test is thus unneeded, until
280 // and unless, in our service(s), we begin detecting duplicates via a
281 // technique that isn't dependent on CSIDs; for instance, by checking for
282 // duplicate data in other information units (fields) whose values must be unique.
284 // One possible example of the above: checking for duplicate Accession numbers
285 // in the "Object entry" information unit in CollectionObject resources.
289 // ---------------------------------------------------------------
290 // CRUD tests : READ tests
291 // ---------------------------------------------------------------
297 * Tests reading (i.e. retrieval) of a resource of the specified type.
299 @Test(dependsOnMethods = {"create"})
302 // Expected status code: 200 OK
303 final int EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
305 // Submit the request to the service and store the response.
306 ClientResponse<CollectionObject> res =
307 client.getCollectionObject(knownObjectId);
308 int statusCode = res.getStatus();
310 // Check the status code of the response: does it match the expected response(s)?
311 verbose("read: status = " + statusCode);
312 final String REQUEST_TYPE="READ"; // @TODO Consider replacing with enum.
313 Assert.assertTrue(statusCodeWithinExpectedSet(REQUEST_TYPE, statusCode),
314 "Status code '" + statusCode + "' in response is NOT within the expected set.");
315 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
322 * Tests reading (i.e. retrieval) of a resource of the specified type by a user who
323 * is not authorized to perform this action.
326 @Test(dependsOnMethods = {"read"})
327 public void readWithoutAuthorization() {
329 // Expected status code: 403 Forbidden
330 final int EXPECTED_STATUS_CODE = Response.Status.FORBIDDEN.getStatusCode();
332 // @TODO Currently only a stub. This test can be implemented
333 // when the service is revised to require authorization.
338 * Tests reading (i.e. retrieval) of a non-existent object of the specified type,
339 * whose resource identifier does not exist at the specified URL.
341 @Test(dependsOnMethods = {"read"})
342 public void readNonExistent() {
344 // Expected status code: 404 Not Found
345 final int EXPECTED_STATUS_CODE = Response.Status.NOT_FOUND.getStatusCode();
347 // Submit the request to the service and store the response.
348 ClientResponse<CollectionObject> res =
349 client.getCollectionObject(NON_EXISTENT_ID);
350 int statusCode = res.getStatus();
352 // Check the status code of the response: does it match the expected response(s)?
353 verbose("readNonExistent: status = " + res.getStatus());
354 final String REQUEST_TYPE="READ"; // @TODO Consider replacing with enum.
355 Assert.assertTrue(statusCodeWithinExpectedSet(REQUEST_TYPE, statusCode),
356 "Status code '" + statusCode + "' in response is NOT within the expected set.");
357 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
361 // ---------------------------------------------------------------
362 // CRUD tests : READ (list, or multiple) tests
363 // ---------------------------------------------------------------
369 * Tests reading (i.e. retrieval) of a list of multiple objects of the specified type.
371 * Also expected: The entity body in the response contains
372 * a representation of a list of objects of the specified type.
374 @Test(dependsOnMethods = {"createMultiple"})
375 public void readList() {
377 // Expected status code: 200 OK
378 final int EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
380 // Submit the request to the service and store the response.
381 ClientResponse<CollectionObjectList> res = client.getCollectionObjectList();
382 CollectionObjectList coList = res.getEntity();
383 int statusCode = res.getStatus();
385 // Check the status code of the response: does it match the expected response(s)?
386 verbose("readList: status = " + res.getStatus());
387 final String REQUEST_TYPE="READ_MULTIPLE"; // @TODO Consider replacing with enum.
388 Assert.assertTrue(statusCodeWithinExpectedSet(REQUEST_TYPE, statusCode),
389 "Status code '" + statusCode + "' in response is NOT within the expected set.");
390 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
392 // Optionally output additional data about list members for debugging.
393 boolean iterateThroughList = false;
394 if (iterateThroughList && logger.isDebugEnabled()) {
395 List<CollectionObjectList.CollectionObjectListItem> coItemList =
396 coList.getCollectionObjectListItem();
398 for(CollectionObjectList.CollectionObjectListItem pli : coItemList){
399 verbose("readList: list-item[" + i + "] csid=" + pli.getCsid());
400 verbose("readList: list-item[" + i + "] objectNumber=" + pli.getObjectNumber());
401 verbose("readList: list-item[" + i + "] URI=" + pli.getUri());
409 * Tests reading (i.e. retrieval) of a list of multiple objects of the specified type
410 * when the contents of the list are expected to be empty.
412 * Also expected: The entity body in the response contains
413 * a representation of an empty list of objects of the specified type.
416 @Test(dependsOnMethods = {"readList"})
417 public void readEmptyList() {
419 // Expected status code: 200 OK
420 // (NOTE: *not* 204 No Content)
421 final int EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
423 // @TODO Currently only a stub. Consider how to implement this.
431 * Tests reading (i.e. retrieval) of a list of objects of the specified type
432 * when sending unrecognized query parameters with the request.
435 @Test(dependsOnMethods = {"readList"})
436 public void readListWithBadParams() {
438 // Expected status code: 400 Bad Request
439 final int EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
441 // @TODO This test is currently commented out, because it returns a
442 // 200 OK status code, rather than the expected status code.
444 // @TODO Another variant of this test should use a URL for the service
445 // root that ends in a trailing slash.
447 // Submit the request to the service and store the response.
448 String url = getServiceRootURL() + "?param=nonexistent";
449 GetMethod method = new GetMethod(url);
450 int statusCode = submitRequest(method);
452 // Check the status code of the response: does it match the expected response(s)?
453 verbose("readListWithBadParams: url=" + url + " status=" + statusCode);
454 final String REQUEST_TYPE="READ_MULTIPLE"; // @TODO Consider replacing with enum.
455 Assert.assertTrue(statusCodeWithinExpectedSet(REQUEST_TYPE, statusCode),
456 "Status code '" + statusCode + "' in response is NOT within the expected set.");
457 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
462 * Tests reading (i.e. retrieval) of a list of objects of the specified type by a user who
463 * is not authorized to perform this action.
467 @Test(dependsOnMethods = {"readList"})
468 public void readListWithoutAuthorization() {
470 // Expected status code: 403 Forbidden
471 final int EXPECTED_STATUS_CODE = Response.Status.FORBIDDEN.getStatusCode();
473 // @TODO Currently only a stub. This test can be implemented
474 // when the service is revised to require authorization.
479 // ---------------------------------------------------------------
480 // CRUD tests : UPDATE tests
481 // ---------------------------------------------------------------
487 * Tests updating the content of a resource of the specified type.
489 * Also expected: The entity body in the response contains
490 * a representation of the updated object of the specified type.
492 @Test(dependsOnMethods = {"create"})
493 public void update() {
495 // Expected status code: 200 OK
496 final int EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
498 // Retrieve an existing resource that we can update.
499 ClientResponse<CollectionObject> res =
500 client.getCollectionObject(knownObjectId);
501 verbose("read: status = " + res.getStatus());
502 Assert.assertEquals(res.getStatus(), EXPECTED_STATUS_CODE);
503 CollectionObject collectionObject = res.getEntity();
504 verbose("Got object to update with ID: " + knownObjectId,
505 collectionObject, CollectionObject.class);
507 // Update the content of this resource.
508 //collectionObject.setCsid("updated-" + knownObjectId);
509 collectionObject.setObjectNumber("updated-" + collectionObject.getObjectNumber());
510 collectionObject.setObjectName("updated-" + collectionObject.getObjectName());
512 // Submit the request to the service and store the response.
513 res = client.updateCollectionObject(knownObjectId, collectionObject);
514 int statusCode = res.getStatus();
515 CollectionObject updatedCollectionObject = res.getEntity();
517 // Check the status code of the response: does it match the expected response(s)?
518 verbose("update: status = " + res.getStatus());
519 final String REQUEST_TYPE="UPDATE"; // @TODO Consider replacing with enum.
520 Assert.assertTrue(statusCodeWithinExpectedSet(REQUEST_TYPE, statusCode),
521 "Status code '" + statusCode + "' in response is NOT within the expected set.");
522 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
524 // Check the contents of the response: does it match what was submitted?
525 verbose("update: ", updatedCollectionObject, CollectionObject.class);
526 Assert.assertEquals(updatedCollectionObject.getObjectName(),
527 collectionObject.getObjectName());
531 * Tests updating the content of a resource of the specified type
532 * by sending malformed XML data in the entity body of the request.
535 @Test(dependsOnMethods = {"create", "testSubmitRequest"})
536 public void updateWithMalformedXml() {
538 // Expected status code: 400 Bad Request
539 final int EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
541 // @TODO This test is currently commented out, because it returns a
542 // 500 Internal Server Error status code, rather than the expected status code.
544 // Submit the request to the service and store the response.
545 String url = getResourceURL(knownObjectId);
546 PutMethod method = new PutMethod(url);
547 final String MALFORMED_XML_DATA =
548 "<malformed_xml>wrong schema contents</malformed_xml"; // Note: intentionally missing bracket.
549 StringRequestEntity entity = getXmlEntity(MALFORMED_XML_DATA);
550 int statusCode = submitRequest(method, entity);
552 // Check the status code of the response: does it match the expected response(s)?
553 verbose("updateWithMalformedXml: url=" + url + " status=" + statusCode);
554 final String REQUEST_TYPE="UPDATE"; // @TODO Consider replacing with enum.
555 Assert.assertTrue(statusCodeWithinExpectedSet(REQUEST_TYPE, statusCode),
556 "Status code '" + statusCode + "' in response is NOT within the expected set.");
557 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
562 * Tests updating the content of a resource of the specified type
563 * by sending data in the wrong schema (e.g. in a format that
564 * doesn't match the object's schema) in the entity body of the request.
567 @Test(dependsOnMethods = {"create", "testSubmitRequest", "createWithMalformedXml"})
568 public void updateWithWrongSchema() {
570 // Expected status code: 400 Bad Request
571 final int EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
573 // @TODO This test is currently commented out, because it returns a
574 // 500 Internal Server Error status code, rather than the expected status code.
576 // Submit the request to the service and store the response.
577 String url = getResourceURL(knownObjectId);
578 PutMethod method = new PutMethod(url);
579 final String WRONG_SCHEMA_DATA = "<wrong_schema>wrong schema contents</wrong_schema>";
580 StringRequestEntity entity = getXmlEntity(WRONG_SCHEMA_DATA);
581 int statusCode = submitRequest(method, entity);
583 // Check the status code of the response: does it match the expected response(s)?
584 verbose("updateWithWrongSchema: url=" + url + " status=" + statusCode);
585 final String REQUEST_TYPE="UPDATE"; // @TODO Consider replacing with enum.
586 Assert.assertTrue(statusCodeWithinExpectedSet(REQUEST_TYPE, statusCode),
587 "Status code '" + statusCode + "' in response is NOT within the expected set.");
588 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
593 * Tests updating the content of a resource of the specified type,
594 * by a user who is not authorized to perform this action.
597 @Test(dependsOnMethods = {"update"})
598 public void updateWithoutAuthorization() {
600 // Expected status code: 403 Forbidden
601 final int EXPECTED_STATUS_CODE = Response.Status.FORBIDDEN.getStatusCode();
603 // @TODO Currently only a stub. This test can be implemented
604 // when the service is revised to require authorization.
609 * Tests updating the content of a non-existent object of the specified type,
610 * whose resource identifier does not exist.
612 @Test(dependsOnMethods = {"update"})
613 public void updateNonExistent() {
615 // Expected status code: 404 Not Found
616 final int EXPECTED_STATUS_CODE = Response.Status.NOT_FOUND.getStatusCode();
618 // Submit the request to the service and store the response.
619 // Note: The ID used in this 'create' call may be arbitrary.
620 // The only relevant ID may be the one used in updateCollectionObject(), below.
621 CollectionObject collectionObject = createCollectionObject(NON_EXISTENT_ID);
622 ClientResponse<CollectionObject> res =
623 client.updateCollectionObject(NON_EXISTENT_ID, collectionObject);
624 int statusCode = res.getStatus();
626 // Check the status code of the response: does it match the expected response(s)?
627 verbose("updateNonExistent: status = " + res.getStatus());
628 final String REQUEST_TYPE="UPDATE"; // @TODO Consider replacing with enum.
629 Assert.assertTrue(statusCodeWithinExpectedSet(REQUEST_TYPE, statusCode),
630 "Status code '" + statusCode + "' in response is NOT within the expected set.");
631 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
635 // ---------------------------------------------------------------
636 // CRUD tests : DELETE tests
637 // ---------------------------------------------------------------
643 * Tests deleting an object of the specified type.
645 * Expected status code: 200 OK
647 @Test(dependsOnMethods =
648 {"create", "read", "testSubmitRequest", "update"})
649 public void delete() {
651 // Expected status code: 200 OK
652 final int EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
654 // Submit the request to the service and store the response.
655 ClientResponse<Response> res = client.deleteCollectionObject(knownObjectId);
656 int statusCode = res.getStatus();
658 // Check the status code of the response: does it match the expected response(s)?
659 verbose("delete: status = " + res.getStatus());
660 final String REQUEST_TYPE="DELETE"; // @TODO Consider replacing with enum.
661 Assert.assertTrue(statusCodeWithinExpectedSet(REQUEST_TYPE, statusCode),
662 "Status code '" + statusCode + "' in response is NOT within the expected set.");
663 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
670 * Tests deleting an object of the specified type,
671 * by a user who is not authorized to perform this action.
674 @Test(dependsOnMethods = {"delete"})
675 public void deleteWithoutAuthorization() {
677 // Expected status code: 403 Forbidden
678 final int EXPECTED_STATUS_CODE = Response.Status.FORBIDDEN.getStatusCode();
680 // @TODO Currently only a stub. This test can be implemented
681 // when the service is revised to require authorization.
686 * Tests deleting a non-existent object of the specified type,
687 * whose resource identifier does not exist at the specified URL.
689 @Test(dependsOnMethods = {"delete"})
690 public void deleteNonExistent() {
692 // Expected status code: 404 Not Found
693 final int EXPECTED_STATUS_CODE = Response.Status.NOT_FOUND.getStatusCode();
695 // Submit the request to the service and store the response.
696 ClientResponse<Response> res =
697 client.deleteCollectionObject(NON_EXISTENT_ID);
698 int statusCode = res.getStatus();
700 // Check the status code of the response: does it match the expected response(s)?
701 verbose("deleteNonExistent: status = " + res.getStatus());
702 final String REQUEST_TYPE="DELETE"; // @TODO Consider replacing with enum.
703 Assert.assertTrue(statusCodeWithinExpectedSet(REQUEST_TYPE, statusCode),
704 "Status code '" + statusCode + "' in response is NOT within the expected set.");
705 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE);
709 // ---------------------------------------------------------------
710 // Utility tests : tests of code used in tests above
711 // ---------------------------------------------------------------
714 * Tests the HttpClient-based code used to submit data, in various methods below.
716 @Test(dependsOnMethods = {"create", "read"})
717 public void testSubmitRequest() {
719 // Expected status code: 200 OK
720 final int EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
722 // Submit the request to the service and store the response.
723 String url = getResourceURL(knownObjectId);
724 GetMethod method = new GetMethod(url);
725 int statusCode = submitRequest(method);
727 // Check the status code of the response: does it match the expected response(s)?
728 verbose("testSubmitRequest: url=" + url + " status=" + statusCode);
729 final String REQUEST_TYPE="CREATE"; // @TODO Consider replacing with enum.
730 Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE,
731 "expected " + EXPECTED_STATUS_CODE);
736 // ---------------------------------------------------------------
737 // Utility methods used by tests above
738 // ---------------------------------------------------------------
740 // @TODO Add Javadoc comments to all of these methods.
742 // -----------------------------
743 // Methods specific to this test
744 // -----------------------------
746 private CollectionObject createCollectionObject(String identifier) {
747 CollectionObject collectionObject = createCollectionObject("objectNumber-" + identifier,
748 "objectName-" + identifier);
749 return collectionObject;
752 private CollectionObject createCollectionObject(String objectNumber, String objectName) {
753 CollectionObject collectionObject = new CollectionObject();
754 collectionObject.setObjectNumber(objectNumber);
755 collectionObject.setObjectName(objectName);
756 return collectionObject;
759 private String getServicePathComponent() {
760 // @TODO Determine if it is possible to obtain this value programmatically.
761 // We set this in an annotation in the CollectionObjectProxy interface, for instance.
762 final String SERVICE_PATH_COMPONENT = "collectionobjects";
763 return SERVICE_PATH_COMPONENT;
766 // -------------------------------------------------------------
767 // Methods common to all entity service test classes.
769 // These can be moved out of individual service test classes
770 // into a common class, perhaps at the top-level 'client' module.
771 // -------------------------------------------------------------
774 Intent of the utility method below, per Sanjay:
775 Utility that asserts various HTTP status codes received for a given type of a request.
776 This should be used from any test we write for a service. For example, this utility
777 could take the following two params.
778 import javax.ws.rs.core.Response;
779 final static public boolean checkStatus(String method, Response.Status status);
780 Perhaps you could find about method used from org.jboss.resteasy.client.ClientResponse metadata.
782 See comments in the 'create' test, above, regarding finding the method used in the request.
785 private boolean statusCodeWithinExpectedSet(String requestType, int statusCode) {
787 // @TODO Consider implementing this via enums for request types,
788 // rather than Strings. This might also allow us to define their
789 // values in the enum definition class.
791 if (requestType.equalsIgnoreCase("CREATE")) {
792 int[] validStatusCodes = {
793 Response.Status.CREATED.getStatusCode(),
794 Response.Status.BAD_REQUEST.getStatusCode(),
795 Response.Status.FORBIDDEN.getStatusCode(),
796 Response.Status.CONFLICT.getStatusCode(),
797 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() };
798 if (Arrays.binarySearch(validStatusCodes, statusCode) >= 0) {
799 verbose("status code is IN expected set");
802 verbose("status code is NOT in expected set");
805 } else if (requestType.equalsIgnoreCase("READ")) {
806 int[] validStatusCodes = {
807 Response.Status.OK.getStatusCode(),
808 Response.Status.FORBIDDEN.getStatusCode(),
809 Response.Status.NOT_FOUND.getStatusCode(),
810 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() };
811 if (Arrays.binarySearch(validStatusCodes, statusCode) >= 0) {
812 verbose("status code is IN expected set");
815 verbose("status code is NOT in expected set");
818 } else if (requestType.equalsIgnoreCase("READ_MULTIPLE")) {
819 int[] validStatusCodes = {
820 Response.Status.OK.getStatusCode(),
821 Response.Status.BAD_REQUEST.getStatusCode(),
822 Response.Status.FORBIDDEN.getStatusCode(),
823 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() };
824 if (Arrays.binarySearch(validStatusCodes, statusCode) >= 0) {
825 verbose("status code is IN expected set");
828 verbose("status code is NOT in expected set");
831 } else if (requestType.equalsIgnoreCase("UPDATE")) {
832 int[] validStatusCodes = {
833 Response.Status.OK.getStatusCode(),
834 Response.Status.BAD_REQUEST.getStatusCode(),
835 Response.Status.FORBIDDEN.getStatusCode(),
836 Response.Status.NOT_FOUND.getStatusCode(),
837 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() };
838 if (Arrays.binarySearch(validStatusCodes, statusCode) >= 0) {
839 verbose("status code is IN expected set");
842 verbose("status code is NOT in expected set");
845 } else if (requestType.equalsIgnoreCase("DELETE")) {
846 int[] validStatusCodes = {
847 Response.Status.OK.getStatusCode(),
848 Response.Status.FORBIDDEN.getStatusCode(),
849 Response.Status.NOT_FOUND.getStatusCode(),
850 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() };
851 if (Arrays.binarySearch(validStatusCodes, statusCode) >= 0) {
852 verbose("status code is IN expected set");
855 verbose("status code is NOT in expected set");
859 logger.error("Request type '" + requestType + " must match a recognized type.");
864 private String getServiceRootURL() {
865 return serviceClient.getBaseURL() + getServicePathComponent();
868 private String getResourceURL(String resourceIdentifier) {
869 return getServiceRootURL() + "/" + resourceIdentifier;
872 private int submitRequest(HttpMethod method) {
875 statusCode = httpClient.executeMethod(method);
876 } catch(HttpException e) {
877 logger.error("Fatal protocol violation: ", e);
878 } catch(IOException e) {
879 logger.error("Fatal transport error: ", e);
880 } catch(Exception e) {
881 logger.error("Unknown exception: ", e);
883 // Release the connection.
884 method.releaseConnection();
889 private int submitRequest(EntityEnclosingMethod method, RequestEntity entity) {
892 method.setRequestEntity(entity);
893 statusCode = httpClient.executeMethod(method);
894 } catch(HttpException e) {
895 logger.error("Fatal protocol violation: ", e);
896 } catch(IOException e) {
897 logger.error("Fatal transport error: ", e);
898 } catch(Exception e) {
899 logger.error("Unknown exception: ", e);
901 // Release the connection.
902 method.releaseConnection();
907 private StringRequestEntity getXmlEntity(String contents) {
908 if (contents == null) {
911 StringRequestEntity entity = null;
912 final String XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
913 final String XML_CONTENT_TYPE=MediaType.APPLICATION_XML;
914 final String UTF8_CHARSET_NAME = "UTF-8";
917 new StringRequestEntity(XML_DECLARATION + contents, XML_CONTENT_TYPE, UTF8_CHARSET_NAME);
918 } catch (UnsupportedEncodingException e) {
919 logger.error("Unsupported character encoding error: ", e);
924 private String extractId(ClientResponse<Response> res) {
925 MultivaluedMap mvm = res.getMetadata();
926 String uri = (String) ((ArrayList) mvm.get("Location")).get(0);
927 verbose("extractId:uri=" + uri);
928 String[] segments = uri.split("/");
929 String id = segments[segments.length - 1];
934 private void verbose(String msg) {
935 if (logger.isDebugEnabled()) {
940 private void verbose(String msg, Object o, Class clazz) {
943 JAXBContext jc = JAXBContext.newInstance(clazz);
944 Marshaller m = jc.createMarshaller();
945 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
947 m.marshal(o, System.out);
953 private void verboseMap(MultivaluedMap map) {
954 for(Object entry : map.entrySet()){
955 MultivaluedMap.Entry mentry = (MultivaluedMap.Entry) entry;
956 verbose(" name=" + mentry.getKey() + " value=" + mentry.getValue());
960 private String createIdentifier() {
961 long identifier = System.currentTimeMillis();
962 return Long.toString(identifier);
965 private String createNonExistentIdentifier() {
966 return Long.toString(Long.MAX_VALUE);