]> git.aero2k.de Git - tmp/jakarta-migration.git/commitdiff
CSPACE-360,CSPACE-384: For client test framework: Restored two inadvertently deleted...
authorAron Roberts <aron@socrates.berkeley.edu>
Fri, 28 Aug 2009 02:48:59 +0000 (02:48 +0000)
committerAron Roberts <aron@socrates.berkeley.edu>
Fri, 28 Aug 2009 02:48:59 +0000 (02:48 +0000)
services/client/src/main/java/org/collectionspace/services/client/AbstractServiceTest.java [new file with mode: 0644]
services/client/src/main/java/org/collectionspace/services/client/ServiceRequestType.java [new file with mode: 0644]
services/client/src/main/java/org/collectionspace/services/client/ServiceTest.java [moved from services/client/src/test/java/org/collectionspace/services/client/test/ServiceTest.java with 100% similarity]

diff --git a/services/client/src/main/java/org/collectionspace/services/client/AbstractServiceTest.java b/services/client/src/main/java/org/collectionspace/services/client/AbstractServiceTest.java
new file mode 100644 (file)
index 0000000..ed763da
--- /dev/null
@@ -0,0 +1,414 @@
+/**
+ * This document is a part of the source code and related artifacts
+ * for CollectionSpace, an open source collections management system
+ * for museums and related institutions:
+ *
+ * http://www.collectionspace.org
+ * http://wiki.collectionspace.org
+ *
+ * Copyright © 2009 Regents of the University of California
+ *
+ * Licensed under the Educational Community License (ECL), Version 2.0.
+ * You may not use this file except in compliance with this License.
+ *
+ * You may obtain a copy of the ECL 2.0 License at
+ *
+ * https://source.collectionspace.org/collection-space/LICENSE.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.collectionspace.services.client.test;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.commons.httpclient.methods.RequestEntity;
+import org.apache.commons.httpclient.methods.StringRequestEntity;
+
+import org.collectionspace.services.client.TestServiceClient;
+import org.collectionspace.services.client.test.ServiceRequestType;
+
+import org.jboss.resteasy.client.ClientResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * AbstractServiceTest, abstract base class for the client tests to be performed
+ * to test an entity or relation service.
+ *
+ * For Javadoc descriptions of this class's methods, see the ServiceTest interface.
+ */
+public abstract class AbstractServiceTest implements ServiceTest {
+
+    final Logger logger = LoggerFactory.getLogger(AbstractServiceTest.class);
+
+    // An HTTP client, used for performing several negative (failure) tests.
+    //
+    // @TODO To be replaced with RESTeasy's ClientRequest (a higher-level API
+    // that is based on HttpClient), per Issue CSPACE-386.
+    protected HttpClient httpClient = new HttpClient();
+
+    // A base-level client, used (only) to obtain the base service URL.
+    private static final TestServiceClient serviceClient = new TestServiceClient();
+
+    // A resource identifier believed to be non-existent in actual use,
+    // used when testing service calls that reference non-existent resources.
+    protected final String NON_EXISTENT_ID = createNonExistentIdentifier();
+
+    // The HTTP status code expected to be returned in the response,
+    // from a request made to a service (where relevant).
+    int EXPECTED_STATUS_CODE = 0;
+    
+    // The generic type of service request being tested (e.g. CREATE, UPDATE, DELETE).
+    //
+    // This makes it possible to check behavior specific to that type of request,
+    // such as the set of valid status codes that may be returned.
+    ServiceRequestType REQUEST_TYPE = ServiceRequestType.NON_EXISTENT;
+
+    // ---------------------------------------------------------------
+    // CRUD tests : CREATE tests
+    // ---------------------------------------------------------------
+
+    // Success outcomes
+
+    @Override
+    public abstract void create();
+    
+    protected void setupCreate() {
+        clearSetup();
+        // Expected status code: 201 Created
+        EXPECTED_STATUS_CODE = Response.Status.CREATED.getStatusCode();
+        // Type of service request being tested
+        REQUEST_TYPE = ServiceRequestType.CREATE;
+    }
+
+    @Override
+    public abstract void createMultiple();
+    
+    // No setup required for createMultiple()
+
+    // Failure outcomes
+
+    @Override
+    public void createNull() {
+    }
+    
+    // No setup required for createNull()
+
+    @Override
+    public abstract void createWithMalformedXml();
+
+    protected void setupCreateWithMalformedXml() {
+        clearSetup();
+        // Expected status code: 400 Bad Request
+        EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
+        REQUEST_TYPE = ServiceRequestType.CREATE;
+    }
+
+    @Override
+    public abstract void createWithWrongXmlSchema();
+
+    protected void setupCreateWithWrongXmlSchema() {
+        clearSetup();
+        // Expected status code: 400 Bad Request
+        EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
+        REQUEST_TYPE = ServiceRequestType.CREATE;
+    }
+
+
+    // ---------------------------------------------------------------
+    // CRUD tests : READ tests
+    // ---------------------------------------------------------------
+
+    // Success outcomes
+    
+    @Override
+    public abstract void read();
+
+    protected void setupRead() {
+        clearSetup();
+        // Expected status code: 200 OK
+        EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
+        REQUEST_TYPE = ServiceRequestType.READ;
+    }
+
+    // Failure outcomes
+
+    @Override
+    public abstract void readNonExistent();
+
+    protected void setupReadNonExistent() {
+        clearSetup();
+        // Expected status code: 404 Not Found
+        EXPECTED_STATUS_CODE = Response.Status.NOT_FOUND.getStatusCode();
+        REQUEST_TYPE = ServiceRequestType.READ;
+    }
+
+
+    // ---------------------------------------------------------------
+    // CRUD tests : READ (list, or multiple) tests
+    // ---------------------------------------------------------------
+
+    // Success outcomes
+
+    @Override
+    public abstract void readList();
+
+    protected void setupReadList() {
+        clearSetup();
+        // Expected status code: 200 OK
+        EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
+        REQUEST_TYPE = ServiceRequestType.READ_MULTIPLE;
+    }
+
+    // Failure outcomes
+
+    // None tested at present.
+
+
+    // ---------------------------------------------------------------
+    // CRUD tests : UPDATE tests
+    // ---------------------------------------------------------------
+
+    // Success outcomes
+    // ----------------
+
+    @Override
+    public abstract void update();
+
+    protected void setupUpdate() {
+        clearSetup();
+        // Expected status code: 200 OK
+        EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
+        REQUEST_TYPE = ServiceRequestType.UPDATE;
+    }
+
+    // Failure outcomes
+
+    @Override
+    public abstract void updateWithMalformedXml();
+
+    protected void setupUpdateWithMalformedXml() {
+        clearSetup();
+        // Expected status code: 400 Bad Request
+        EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
+        REQUEST_TYPE = ServiceRequestType.UPDATE;
+    }
+
+    @Override
+    public abstract void updateWithWrongXmlSchema();
+
+    protected void setupUpdateWithWrongXmlSchema() {
+        clearSetup();
+        // Expected status code: 400 Bad Request
+        EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode();
+        REQUEST_TYPE = ServiceRequestType.UPDATE;
+    }
+
+    @Override
+    public abstract void updateNonExistent();
+
+    protected void setupUpdateNonExistent() {
+        clearSetup();
+        // Expected status code: 404 Not Found
+        EXPECTED_STATUS_CODE = Response.Status.NOT_FOUND.getStatusCode();
+        REQUEST_TYPE = ServiceRequestType.UPDATE;
+    }
+
+
+    // ---------------------------------------------------------------
+    // CRUD tests : DELETE tests
+    // ---------------------------------------------------------------
+
+    // Success outcomes
+
+    @Override
+    public abstract void delete();
+    
+    protected void setupDelete() {
+        clearSetup();
+        // Expected status code: 200 OK
+        EXPECTED_STATUS_CODE = Response.Status.OK.getStatusCode();
+        REQUEST_TYPE = ServiceRequestType.DELETE;
+    }
+    
+    // Failure outcomes
+    
+    @Override
+    public abstract void deleteNonExistent();
+
+    protected void setupDeleteNonExistent() {
+        clearSetup();
+        // Expected status code: 404 Not Found
+        EXPECTED_STATUS_CODE = Response.Status.NOT_FOUND.getStatusCode();
+        REQUEST_TYPE = ServiceRequestType.DELETE;
+    }
+
+
+    // ---------------------------------------------------------------
+    // Abstract utility methods
+    //
+    // Must be implemented by classes that extend
+    // this abstract base class.
+    // ---------------------------------------------------------------
+
+    /**
+     * Returns the URL path component of the service.
+     *
+     * This component will follow directly after the
+     * base path, if any.
+     */
+    protected abstract String getServicePathComponent();
+
+
+    // ---------------------------------------------------------------
+    // Utility methods
+    // ---------------------------------------------------------------
+
+    /**
+     * Reinitializes setup values, to help expose any unintended reuse
+     * of those values between tests.
+     */
+    protected void clearSetup() {
+        EXPECTED_STATUS_CODE = 0;
+        REQUEST_TYPE = ServiceRequestType.NON_EXISTENT;
+    }
+
+    // @TODO Add Javadoc comments to all methods requiring them, below.
+
+    protected String invalidStatusCodeMessage(ServiceRequestType requestType, int statusCode) {
+        return 
+            "Status code '" + statusCode + "' in response is NOT within the expected set: " +
+            requestType.validStatusCodesAsString();
+    }
+    
+    protected String getServiceRootURL() {
+        return serviceClient.getBaseURL() + getServicePathComponent();
+    }
+
+    protected String getResourceURL(String resourceIdentifier) {
+        return getServiceRootURL() + "/" + resourceIdentifier;
+    }
+
+    protected int submitRequest(HttpMethod method) {
+     int statusCode = 0;
+        try {
+            statusCode = httpClient.executeMethod(method);
+        } catch(HttpException e) {
+            logger.error("Fatal protocol violation: ", e);
+        } catch(IOException e) {
+            logger.error("Fatal transport error: ", e);
+        } catch(Exception e) {
+            logger.error("Unknown exception: ", e);
+        } finally {
+            // Release the connection.
+            method.releaseConnection();
+        }
+        return statusCode;
+    }
+    
+    protected int submitRequest(EntityEnclosingMethod method, RequestEntity entity) {
+        int statusCode = 0;
+        try {
+            method.setRequestEntity(entity);
+            statusCode = httpClient.executeMethod(method);
+        } catch(HttpException e) {
+            logger.error("Fatal protocol violation: ", e);
+        } catch(IOException e) {
+            logger.error("Fatal transport error: ", e);
+        } catch(Exception e) {
+            logger.error("Unknown exception: ", e);
+        } finally {
+            // Release the connection.
+            method.releaseConnection();
+        }
+        return statusCode;
+    }
+    
+    protected StringRequestEntity getXmlEntity(String contents) {
+        if (contents == null) {
+            contents = "";
+        }
+        StringRequestEntity entity = null;
+        final String XML_DECLARATION =
+          "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
+        final String XML_CONTENT_TYPE=MediaType.APPLICATION_XML;
+        final String UTF8_CHARSET_NAME = "UTF-8";
+        try {
+            entity =
+                new StringRequestEntity(
+                  XML_DECLARATION + contents, XML_CONTENT_TYPE, UTF8_CHARSET_NAME);
+        } catch (UnsupportedEncodingException e) {
+            logger.error("Unsupported character encoding error: ", e);
+        }
+        return entity;
+    }
+
+    protected String extractId(ClientResponse<Response> res) {
+        MultivaluedMap mvm = res.getMetadata();
+        String uri = (String) ((ArrayList) mvm.get("Location")).get(0);
+        verbose("extractId:uri=" + uri);
+        String[] segments = uri.split("/");
+        String id = segments[segments.length - 1];
+        verbose("id=" + id);
+        return id;
+    }
+
+    protected void verbose(String msg) {
+        if (logger.isDebugEnabled()) {
+            logger.debug(msg);
+        }
+    }
+
+    protected void verbose(String msg, Object o, Class clazz) {
+        try{
+            verbose(msg);
+            JAXBContext jc = JAXBContext.newInstance(clazz);
+            Marshaller m = jc.createMarshaller();
+            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
+                    Boolean.TRUE);
+            m.marshal(o, System.out);
+        }catch(Exception e){
+            e.printStackTrace();
+        }
+    }
+
+    protected void verboseMap(MultivaluedMap map) {
+        for(Object entry : map.entrySet()){
+            MultivaluedMap.Entry mentry = (MultivaluedMap.Entry) entry;
+            verbose("    name=" + mentry.getKey() + " value=" + mentry.getValue());
+        }
+    }
+
+    protected String createIdentifier() {
+        long identifier = System.currentTimeMillis();
+        return Long.toString(identifier);
+    }
+
+    protected String createNonExistentIdentifier() {
+        return Long.toString(Long.MAX_VALUE);
+    }
+    
+}
+
+
diff --git a/services/client/src/main/java/org/collectionspace/services/client/ServiceRequestType.java b/services/client/src/main/java/org/collectionspace/services/client/ServiceRequestType.java
new file mode 100644 (file)
index 0000000..65ec773
--- /dev/null
@@ -0,0 +1,209 @@
+/**
+ * This document is a part of the source code and related artifacts
+ * for CollectionSpace, an open source collections management system
+ * for museums and related institutions:
+ *
+ * http://www.collectionspace.org
+ * http://wiki.collectionspace.org
+ *
+ * Copyright © 2009 Regents of the University of California
+ *
+ * Licensed under the Educational Community License (ECL), Version 2.0.
+ * You may not use this file except in compliance with this License.
+ *
+ * You may obtain a copy of the ECL 2.0 License at
+ * https://source.collectionspace.org/collection-space/LICENSE.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.collectionspace.services.client.test;
+
+import java.util.Arrays;
+import javax.ws.rs.core.Response;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * ServiceRequestType, identifies types of service requests
+ * and the valid HTTP status codes that can be returned from
+ * each type of request.
+ *
+ * Used by client tests of services.
+ * 
+ * $LastChangedRevision: 566 $
+ * $LastChangedDate$
+ */
+public enum ServiceRequestType {
+    
+    // Define each of the service request types and their valid HTTP status codes.
+    
+    CREATE {
+        @Override
+        public int[] validStatusCodes() { 
+              final int[] STATUS_CODES = {
+                    Response.Status.CREATED.getStatusCode(),
+                    Response.Status.BAD_REQUEST.getStatusCode(),
+                    Response.Status.FORBIDDEN.getStatusCode(),
+                    Response.Status.CONFLICT.getStatusCode(),
+                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()
+              };
+              Arrays.sort(STATUS_CODES);
+              return STATUS_CODES;
+        }
+        @Override
+        public boolean isValidStatusCode(int statusCode) {
+              if (Arrays.binarySearch(CREATE.validStatusCodes(), statusCode) >= 0) {
+                    return true;
+              } else {
+                    return false;
+              }
+        }
+        @Override
+        public String validStatusCodesAsString() {
+              return Arrays.toString(CREATE.validStatusCodes());
+        }
+    },  // Note that commas are required at the end of each enum block, except the last.
+    
+    
+    READ {
+        @Override
+        public int[] validStatusCodes() { 
+              final int[] STATUS_CODES = {
+                    Response.Status.OK.getStatusCode(),
+                    Response.Status.FORBIDDEN.getStatusCode(),
+                    Response.Status.NOT_FOUND.getStatusCode(),
+                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()
+              };
+              Arrays.sort(STATUS_CODES);
+              return STATUS_CODES;
+        }
+        @Override
+        public boolean isValidStatusCode(int statusCode) {
+              if (Arrays.binarySearch(READ.validStatusCodes(), statusCode) >= 0) {
+                    return true;
+              } else {
+                    return false;
+              }
+        }
+        @Override
+        public String validStatusCodesAsString() {
+              return Arrays.toString(READ.validStatusCodes());
+        }
+    },
+    
+    
+    READ_MULTIPLE {
+        @Override
+        public int[] validStatusCodes() { 
+              final int[] STATUS_CODES = {
+                    Response.Status.OK.getStatusCode(),
+                    Response.Status.BAD_REQUEST.getStatusCode(),
+                    Response.Status.FORBIDDEN.getStatusCode(),
+                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()
+              };
+              Arrays.sort(STATUS_CODES);
+              return STATUS_CODES;
+        }
+        @Override
+        public boolean isValidStatusCode(int statusCode) {
+              if (Arrays.binarySearch(READ_MULTIPLE.validStatusCodes(), statusCode) >= 0) {
+                    return true;
+              } else {
+                    return false;
+              }
+        }
+        @Override
+        public String validStatusCodesAsString() {
+              return Arrays.toString(READ_MULTIPLE.validStatusCodes());
+        }
+    },
+    
+    
+    UPDATE {
+        @Override
+        public int[] validStatusCodes() { 
+              final int[] STATUS_CODES = {
+                    Response.Status.OK.getStatusCode(),
+                    Response.Status.BAD_REQUEST.getStatusCode(),
+                    Response.Status.FORBIDDEN.getStatusCode(),
+                    Response.Status.NOT_FOUND.getStatusCode(),
+                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()
+              };
+              Arrays.sort(STATUS_CODES);
+              return STATUS_CODES;
+        }
+        @Override
+        public boolean isValidStatusCode(int statusCode) {
+              if (Arrays.binarySearch(UPDATE.validStatusCodes(), statusCode) >= 0) {
+                    return true;
+              } else {
+                    return false;
+              }
+        }
+        @Override
+        public String validStatusCodesAsString() {
+              return Arrays.toString(UPDATE.validStatusCodes());
+        }
+    },
+    
+    
+    DELETE {
+        @Override
+        public int[] validStatusCodes() { 
+              final int[] STATUS_CODES = {
+                    Response.Status.OK.getStatusCode(),
+                    Response.Status.FORBIDDEN.getStatusCode(),
+                    Response.Status.NOT_FOUND.getStatusCode(),
+                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()
+              };
+              Arrays.sort(STATUS_CODES);
+              return STATUS_CODES;
+        }
+        @Override
+        public boolean isValidStatusCode(int statusCode) {
+              if (Arrays.binarySearch(DELETE.validStatusCodes(), statusCode) >= 0) {
+                    return true;
+              } else {
+                    return false;
+              }
+        }
+        @Override
+        public String validStatusCodesAsString() {
+              return Arrays.toString(DELETE.validStatusCodes());
+        }
+    },
+    
+    
+    // Used by guard code.
+    NON_EXISTENT {
+        @Override
+        public int[] validStatusCodes() { 
+              final int[] STATUS_CODES = { 0 };
+              Arrays.sort(STATUS_CODES);
+              return STATUS_CODES;
+        }
+        @Override
+        public boolean isValidStatusCode(int statusCode) {
+              return false;
+        }
+        @Override
+        public String validStatusCodesAsString() {
+              return Arrays.toString(NON_EXISTENT.validStatusCodes());
+        }
+    };
+    
+    // Template methods to be implemented by each ServiceRequestType.
+    
+    public abstract int[] validStatusCodes();
+    
+    public abstract boolean isValidStatusCode(int statusCode);
+    
+    public abstract String validStatusCodesAsString();
+
+}