org.collectionspace.chain.csp.persistence.services.TestService.java Source code

Java tutorial

Introduction

Here is the source code for org.collectionspace.chain.csp.persistence.services.TestService.java

Source

/* Copyright 2010 University of Cambridge
 * 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
 */
package org.collectionspace.chain.csp.persistence.services;

import static org.junit.Assert.*;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.json.JSONException;
import org.json.JSONObject;

import org.apache.commons.io.IOUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;
import org.collectionspace.chain.csp.config.ConfigRoot;
import org.collectionspace.chain.csp.inner.CoreConfig;
import org.collectionspace.chain.csp.persistence.services.connection.ConnectionException;
import org.collectionspace.chain.csp.persistence.services.connection.RequestMethod;
import org.collectionspace.chain.csp.persistence.services.connection.ReturnUnknown;
import org.collectionspace.chain.csp.persistence.services.connection.ReturnedDocument;
import org.collectionspace.chain.csp.persistence.services.connection.ReturnedMultipartDocument;
import org.collectionspace.chain.csp.persistence.services.connection.ReturnedURL;
import org.collectionspace.chain.csp.schema.Record;
import org.collectionspace.chain.csp.schema.Spec;
import org.collectionspace.chain.util.json.JSONUtils;
import org.collectionspace.csp.api.container.CSPManager;
import org.collectionspace.csp.container.impl.CSPManagerImpl;
import org.collectionspace.csp.helper.core.ConfigFinder;

public class TestService extends ServicesBaseClass {
    private static final Logger log = LoggerFactory.getLogger(TestService.class);

    @Before
    public void checkServicesRunning() throws ConnectionException {
        setup();
    }

    @Test
    public void testAssumptionMechanism() {
        log.debug("Services Running!");
    }

    protected JSONObject getJSON(String in) throws IOException, JSONException {
        String path = getClass().getPackage().getName().replaceAll("\\.", "/");
        InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(path + "/" + in);

        assertNotNull("missing file:" + in, stream);
        String data = IOUtils.toString(stream, "UTF-8");
        stream.close();
        return new JSONObject(data);
    }

    @Test
    public void testXMLJSONConversion() throws Exception {

        CSPManager cspm = new CSPManagerImpl();
        cspm.register(new CoreConfig());
        cspm.register(new Spec());
        cspm.register(new ServicesStorageGenerator());
        cspm.go();
        // argh - test break when config changes *sob*
        cspm.configure(getRootSource(), new ConfigFinder(null), false);
        ConfigRoot root = cspm.getConfigRoot();
        Spec spec = (Spec) root.getRoot(Spec.SPEC_ROOT);

        testXMLJSON(spec, "location", "location.xml", "location.json");
        testXMLJSON(spec, "concept", "concept.xml", "concept.json");
        testXMLJSON(spec, "place", "placeXMLJSON.xml", "placeJSON.json");
        testXMLJSON(spec, "citation", "citation.xml", "citation.json");
        testXMLJSON(spec, "work", "work.xml", "work.json");
        testXMLJSON(spec, "collection-object", "objectsXMLJSON.xml", "objectsJSON.json");

        testXMLJSON(spec, "acquisition", "acquisitionXMLJSON.xml", "acquisitionJSON.json");
        testXMLJSON(spec, "intake", "intake.xml", "intake.json");
        testXMLJSON(spec, "loanin", "loanin.xml", "loanin.json");
        testXMLJSON(spec, "loanout", "loanout.xml", "loanout.json");
        testXMLJSON(spec, "valuationcontrol", "valuationcontrol.xml", "valuationcontrol.json");
        testXMLJSON(spec, "movement", "movement.xml", "movement.json");
        testXMLJSON(spec, "objectexit", "objectexit.xml", "objectexit.json");
        testXMLJSON(spec, "group", "group.xml", "group.json");
        testXMLJSON(spec, "media", "media.xml", "mediaJSON.json");
        testXMLJSON(spec, "conditioncheck", "conditioncheck.xml", "conditioncheck.json");
        testXMLJSON(spec, "conservation", "conservation.xml", "conservation.json");
        testXMLJSON(spec, "exhibition", "exhibition.xml", "exhibition.json");

        testXMLJSON(spec, "role", "role.xml", "role.json");
        testXMLJSON(spec, "permrole", "rolepermissions.xml", "rolepermissions.json");
        testXMLJSON(spec, "userrole", "accountrole.xml", "accountrole.json");

        // testXMLJSON(spec,
        // "permission","permissionXMLJSON.xml","permissionsJSON.json");
        // testXMLJSON(spec,
        // "organization","orgauthref.xml","permissionsJSON.json");
    }

    /**
     * I wouldn't call this a robust multipart test - needs more work but works
     * fine for single part multipart xml
     * 
     * @throws Exception
     */
    @Test
    public void testJSONXMLConversion() throws Exception {

        CSPManager cspm = new CSPManagerImpl();
        cspm.register(new CoreConfig());
        cspm.register(new Spec());
        cspm.register(new ServicesStorageGenerator());
        cspm.go();
        // argh - test break when config changes *sob*
        cspm.configure(getRootSource(), new ConfigFinder(null), false);
        ConfigRoot root = cspm.getConfigRoot();
        Spec spec = (Spec) root.getRoot(Spec.SPEC_ROOT);

        testJSONXML(spec, "location", "location.xml", "location.json");
        testJSONXML(spec, "concept", "concept.xml", "concept.json");
        testJSONXML(spec, "citation", "citation.xml", "citation.json");
        testJSONXML(spec, "place", "placeXMLJSON.xml", "placeJSON.json");
        testJSONXML(spec, "work", "work.xml", "work.json");
        // CSPACE-6135: In CollectionObject, the computedCurrentLocation field is services-readonly,
        // so the JSON->XML->JSON conversion produces JSON that does not match the initial JSON
        // (computedCurrentLocation is omitted from the XML, so it does not appear in the JSON
        // converted back from the XML). In this case, we need to supply a third parameter to
        // specify the expected round-trip JSON. objectsReturnedJSON.json is identical to
        // objectsJSON.json, except computedCurrentLocation has been removed.
        testJSONXML(spec, "collection-object", "objectsXMLJSON.xml", "objectsJSON.json",
                "objectsReturnedJSON.json");

        testJSONXML(spec, "acquisition", "acquisitionXMLJSON.xml", "acquisitionJSON.json");

        testJSONXML(spec, "media", "media.xml", "mediaJSON.json");
        testJSONXML(spec, "loanin", "loanin.xml", "loanin.json");
        testJSONXML(spec, "loanout", "loanout.xml", "loanout.json");
        testJSONXML(spec, "intake", "intake.xml", "intake.json");
        testJSONXML(spec, "movement", "movement.xml", "movement.json");
        testJSONXML(spec, "valuationcontrol", "valuationcontrol.xml", "valuationcontrol.json");
        testJSONXML(spec, "objectexit", "objectexit.xml", "objectexit.json");
        testJSONXML(spec, "group", "group.xml", "group.json");
        testJSONXML(spec, "conditioncheck", "conditioncheck.xml", "conditioncheck.json");
        testJSONXML(spec, "conservation", "conservation.xml", "conservation.json");
        testJSONXML(spec, "exhibition", "exhibition.xml", "exhibition.json");

        testJSONXML(spec, "role", "role.xml", "role.json");

        // testJSONXML(spec,"permrole","rolepermissions.xml","rolepermissions.json");
        // testJSONXML(spec, "userrole","accountrole.xml","accountrole.json");
        // testJSONXML(spec,
        // "permission","permissionXMLJSON.xml","permissionsJSON.json");
    }

    /**
     * Tests conversion of a JSON file to XML. This implementation does not compare the
     * resultant generated XML to an expected XML file. Instead, it converts the resultant
     * XML back into JSON, and compares that round-trip JSON to an expected JSON file.
     * 
     * @param spec
     * @param objtype
     * @param xmlfile         Name of the file containing the expected XML (not currently used)
     * @param jsonfile         Name of the file containing JSON to be converted to XML
     * @param returnedjsonfile   Name of the file containing the expected round-trip JSON, converted back from XML 
     * @throws Exception
     */
    private void testJSONXML(Spec spec, String objtype, String xmlfile, String jsonfile, String returnedjsonfile)
            throws Exception {

        log.info("Converting JSON to XML for record type " + objtype);
        Record r = spec.getRecord(objtype);
        JSONObject j = getJSON(jsonfile);
        //log.info("Original JSON:\n" + j.toString());
        Map<String, Document> parts = new HashMap<String, Document>();
        Document doc = null;
        JSONObject testjson = new JSONObject();
        for (String section : r.getServicesRecordPathKeys()) {
            if (section.equals("common")) {
                String path = r.getServicesRecordPath(section);
                String[] record_path = path.split(":", 2);
                doc = XmlJsonConversion.convertToXml(r, j, section, "");
                parts.put(record_path[0], doc);
                //log.info("After JSON->XML conversion:\n" + doc.asXML());
                JSONObject repeatjson = org.collectionspace.chain.csp.persistence.services.XmlJsonConversion
                        .convertToJson(r, doc, "", "common", "", "");// this is where we
                // specify the
                // multipart
                // section
                // we are considering
                for (String name : JSONObject.getNames(repeatjson)) {
                    testjson.put(name, repeatjson.get(name));
                }
                //log.info("After XML->JSON re-conversion:\n" + testjson.toString());
            }
        }
        // convert json -> xml and back to json and see if it still looks the
        // same..
        JSONObject expectedjson = getJSON(returnedjsonfile);
        boolean result = JSONUtils.checkJSONEquivOrEmptyStringKey(expectedjson, testjson);
        if (!result) {
            log.info("Original JSON:\n" + j.toString());
            log.info("After JSON->XML conversion:\n" + doc.asXML());
            log.info("After XML->JSON re-conversion:\n" + testjson.toString());
        }
        assertTrue("JSON->XML->JSON round-trip for record type: " + objtype + " doesn't match original JSON",
                result);

    }

    /**
     * Tests conversion of a JSON file to XML, comparing the round-trip JSON to the original JSON.
     */
    private void testJSONXML(Spec spec, String objtype, String xmlfile, String jsonfile) throws Exception {
        testJSONXML(spec, objtype, xmlfile, jsonfile, jsonfile);
    }

    /**
     * This doesn't currently test multipart xml conversion
     * 
     * @param spec
     * @param objtype
     * @param xmlfile
     * @param jsonfile
     * @throws Exception
     */
    private void testXMLJSON(Spec spec, String objtype, String xmlfile, String jsonfile) throws Exception {

        log.info("Converting XML to JSON for record type " + objtype);
        Document testxml = getDocument(xmlfile);
        String test = testxml.asXML();
        log.trace("Original XML:\n" + test);
        JSONObject j = getJSON(jsonfile);
        //log.info("Original JSON:\n" + j.toString());
        Record r = spec.getRecord(objtype);
        JSONObject repeatjson = org.collectionspace.chain.csp.persistence.services.XmlJsonConversion
                .convertToJson(r, testxml, "", "common", "", ""); // this is where we specify the multipart section
        // we are considering
        boolean result = JSONUtils.checkJSONEquivOrEmptyStringKey(repeatjson, j);
        if (!result) {
            log.info("Original JSON:\n" + j.toString());
            log.info("After XML->JSON conversion:\n" + repeatjson.toString());
        }
        assertTrue("Generated JSON for record type: " + objtype + " doesn't match original JSON", result);

    }

    @Test
    public void testPersonContactPostViaCSIDs() throws Exception {
        String filename = "";
        String partname = "";
        ReturnedURL url = null;
        Map<String, Document> parts = new HashMap<String, Document>();
        StringBuilder serviceurl = new StringBuilder("");
        ReturnedMultipartDocument rdocs = null;
        ReturnedDocument rdoc = null;
        int status = 0;
        Document doc = null;
        String text = "";
        String xpath = "";

        // POST (Create) a person authority
        serviceurl.append("personauthorities/");
        partname = "personauthorities_common";
        filename = "personAuth.xml";
        log.info("Testing create at " + serviceurl + " with " + filename + " and partname=" + partname);
        parts.put(partname, getDocument(filename));
        url = conn.getMultipartURL(RequestMethod.POST, serviceurl.toString(), parts, creds, cache);
        assertEquals(201, url.getStatus());
        String authUrl = url.getURL();
        String authId = url.getURLTail();
        // Test creation with a GET
        if (partname != null) {
            rdocs = conn.getMultipartXMLDocument(RequestMethod.GET, authUrl, null, creds, cache);
            status = rdocs.getStatus();
            doc = rdocs.getDocument(partname);
        }
        assertEquals(200, status);
        assertNotNull(doc);

        log.info("CREATED PERSONAUTHORITY AT " + authUrl);

        // POST (Create) a person item within the person authority
        serviceurl.append(authId + "/items/");
        partname = "persons_common";
        String partname1 = "relations-common-list";
        filename = "personItem.xml";
        String filename1 = "relationshipItem.xml";
        log.info("Testing create at " + serviceurl + " with " + filename + " and partname=" + partname);
        if (partname != null) {
            parts = new HashMap<String, Document>();
            parts.put(partname, getDocument(filename));
            //      parts.put(partname1, getDocument(filename1));
            url = conn.getMultipartURL(RequestMethod.POST, serviceurl.toString(), parts, creds, cache);
        }
        assertEquals(201, url.getStatus());
        String itemUrl = url.getURL();
        String itemId = url.getURLTail();
        // Test creation with a GET
        if (partname != null) {
            rdocs = conn.getMultipartXMLDocument(RequestMethod.GET, itemUrl, null, creds, cache);
            status = rdocs.getStatus();
            doc = rdocs.getDocument(partname);
        }
        assertEquals(200, status);
        assertNotNull(doc);

        // Test that the parent authority lists this item as a child
        String parentUrl = authUrl + "/items/";
        log.info("LIST from " + parentUrl);
        rdoc = conn.getXMLDocument(RequestMethod.GET, parentUrl, null, creds, cache);
        status = rdoc.getStatus();
        doc = rdoc.getDocument();
        assertEquals(200, status);
        log.info(doc.asXML());
        xpath = "//totalItems";
        Node n = doc.selectSingleNode(xpath);
        assertNotNull(n);
        text = n.getText();
        assertNotNull(text);
        log.info("Value of XPath expression '" + xpath + "' = " + text);
        assert (!text.trim().equals("0"));
        xpath = "//list-item/csid";
        List<Node> nodes = doc.selectNodes(xpath);
        assertNotNull(nodes);
        assert (nodes.size() > 0);
        boolean foundItemInAuthority = false;
        for (Node node : nodes) {
            log.info("found '" + node.getText().trim() + "' comparing to " + itemId);
            if (node.getText().trim().equals(itemId)) {
                foundItemInAuthority = true;
            }
        }
        assert (foundItemInAuthority);
        log.info("CREATED PERSON AT " + itemUrl);

        // POST (Create) a contact sub-resource within the person item
        // and perform a full POST, GET, DELETE cycle on that contact
        serviceurl.append(itemId + "/contacts");
        partname = "contacts_common";
        filename = "personItemContact.xml";
        log.info("ADDING CONTACT USING THIS URL " + serviceurl);
        testPostGetDelete(serviceurl.toString(), partname, filename,
                "contacts_common/emailGroupList/emailGroup/email", "test@example.com");
        // DELETE (Delete) the person item within the person authority
        status = conn.getNone(RequestMethod.DELETE, itemUrl, null, creds, cache);
        assertEquals(200, status);
        // Now try to delete non-existent (make sure CSPACE-73 hasn't regressed)
        status = conn.getNone(RequestMethod.DELETE, itemUrl, null, creds, cache);
        assertEquals(404, status);
        // GET once more to make sure it isn't there
        if (partname != null) {
            rdocs = conn.getMultipartXMLDocument(RequestMethod.GET, itemUrl, null, creds, cache);
            status = rdocs.getStatus();
            doc = rdocs.getDocument(partname);
        }
        assertEquals(404, status); // ensures CSPACE-209 hasn't regressed
        assertNull(doc);
        log.info("DELETED PERSON");

        // DELETE (Delete) the person authority
        status = conn.getNone(RequestMethod.DELETE, authUrl, null, creds, cache);
        assertEquals(200, status);
        // Now try to delete non-existent (make sure CSPACE-73 hasn't regressed)
        status = conn.getNone(RequestMethod.DELETE, authUrl, null, creds, cache);
        assertEquals(404, status);
        // GET once more to make sure it isn't there
        if (partname != null) {
            rdocs = conn.getMultipartXMLDocument(RequestMethod.GET, authUrl, null, creds, cache);
            status = rdocs.getStatus();
            doc = rdocs.getDocument(partname);
        }
        assertEquals(404, status); // ensures CSPACE-209 hasn't regressed
        assertNull(doc);
        log.info("DELETED PERSON AUTHORITY");

    }

    @Test
    public void testNewPersonAuthority() throws Exception {
        String personAuthFile = "personAuth.xml";
        String personAuthItemFile = "personAuthItem.xml";
        String personAuthpartname = "personauthorities_common";
        String personAuthServiceUrl = "personauthorities/";
        Map<String, Document> parts = new HashMap<String, Document>();
        parts.put(personAuthpartname, getDocument(personAuthFile));

        log.info("Testing create at " + personAuthServiceUrl + " with " + personAuthFile + " and partname="
                + personAuthpartname);

        ReturnedURL url = conn.getMultipartURL(RequestMethod.POST, personAuthServiceUrl, parts, creds, cache);
        assertEquals(201, url.getStatus());

        String id = url.getURLTail();
        log.info(url.getURL());

        String personAuthUrl = url.getURL();
        // Test creation with a GET

        ReturnedMultipartDocument rdocs = conn.getMultipartXMLDocument(RequestMethod.GET, personAuthUrl, null,
                creds, cache);
        int status = rdocs.getStatus();
        assertEquals(200, status);

        Document doc = rdocs.getDocument(personAuthpartname);
        log.info(doc.asXML());

        // DELETE
        conn.getNone(RequestMethod.DELETE, "personauthorities/" + id, null, creds, cache);
    }

    //@Test
    public void testPersonContact() throws Exception {
        String serviceurl = "personauthorities/urn:cspace:name(person)/items";
        String filename = "personItem.xml";
        String partname = "persons_common";
        ReturnedURL url;
        log.info("Testing " + serviceurl + " with " + filename + " and partname=" + partname);

        // TODO add document parsing for PUT, and for POSTs that require
        // uniqueness (to maintain self-contained tests that don't destroy
        // existing data)

        // POST (Create)
        if (partname != null) {
            Map<String, Document> parts = new HashMap<String, Document>();
            parts.put(partname, getDocument(filename));
            url = conn.getMultipartURL(RequestMethod.POST, serviceurl, parts, creds, cache);
        } else {
            url = conn.getURL(RequestMethod.POST, serviceurl, getDocument(filename), creds, cache);
        }

        assertEquals(201, url.getStatus());

        // log.info("CREATE PERSON" + url.getURL());
        // assertTrue(url.getURL().startsWith("/"+serviceurl)); // ensures e.g.
        // CSPACE-305 hasn't regressed
        log.info("CREATE PERSON" + url.getURL());
        // create contact person

        String serviceurlContact = "personauthorities/urn:cspace:name(person)/items/" + url.getURLTail()
                + "/contacts";
        String filenameContact = "personItemContact.xml";
        String partnameContact = "contacts_common";
        log.info("ADD CONTACT USING THIS URL " + serviceurlContact);

        testPostGetDelete(serviceurlContact, partnameContact, filenameContact, "contacts_common/email",
                "email@example.com");

        // DELETE (Delete)
        int status = conn.getNone(RequestMethod.DELETE, url.getURL(), null, creds, cache);
        assertEquals(200, status);
        // Now try to delete non-existent (make sure CSPACE-73 hasn't regressed)
        status = conn.getNone(RequestMethod.DELETE, url.getURL(), null, creds, cache);
        assertEquals(404, status);

        log.info("DELETE PERSON");
        // GET once more to make sure it isn't there
        int getStatus;
        Document doc;
        if (partname != null) {
            ReturnedMultipartDocument rdocs = conn.getMultipartXMLDocument(RequestMethod.GET, url.getURL(), null,
                    creds, cache);
            getStatus = rdocs.getStatus();
            doc = rdocs.getDocument(partname);
        } else {
            ReturnedDocument rdoc = conn.getXMLDocument(RequestMethod.GET, url.getURL(), null, creds, cache);
            getStatus = rdoc.getStatus();
            doc = rdoc.getDocument();
        }
        assertEquals(404, getStatus); // ensures CSPACE-209 hasn't regressed
        assertNull(doc);
    }

    //@Test
    public void testOrgContact() throws Exception {
        String serviceurl = "orgauthorities/urn:cspace:name(organization)/items";
        String filename = "orgItem.xml";
        String partname = "organizations_common";
        ReturnedURL url;
        log.info("Testing " + serviceurl + " with " + filename + " and partname=" + partname);

        // TODO add document parsing for PUT, and for POSTs that require
        // uniqueness (to maintain self-contained tests that don't destroy
        // existing data)

        // POST (Create)
        if (partname != null) {
            Map<String, Document> parts = new HashMap<String, Document>();
            parts.put(partname, getDocument(filename));
            url = conn.getMultipartURL(RequestMethod.POST, serviceurl, parts, creds, cache);
        } else {
            url = conn.getURL(RequestMethod.POST, serviceurl, getDocument(filename), creds, cache);
        }

        assertEquals(201, url.getStatus());

        // doesn't work because name urn gets translated to id
        // assertTrue(url.getURL().startsWith("/"+serviceurl)); // ensures e.g.
        // CSPACE-305 hasn't regressed
        log.info("CREATE ORG" + url.getURL());
        // create contact person

        String serviceurlContact = "orgauthorities/urn:cspace:name(organization)/items/" + url.getURLTail()
                + "/contacts";
        String filenameContact = "personItemContact.xml";
        String partnameContact = "contacts_common";
        log.info("ADD CONTACT USING THIS URL " + serviceurlContact);

        testPostGetDelete(serviceurlContact, partnameContact, filenameContact, "contacts_common/email",
                "email@example.com");

        // DELETE (Delete)
        int status = conn.getNone(RequestMethod.DELETE, url.getURL(), null, creds, cache);
        assertEquals(200, status);
        // Now try to delete non-existent (make sure CSPACE-73 hasn't regressed)
        status = conn.getNone(RequestMethod.DELETE, url.getURL(), null, creds, cache);
        assertEquals(404, status);

        log.info("DELETE ORG");
        // GET once more to make sure it isn't there
        int getStatus;
        Document doc;
        if (partname != null) {
            ReturnedMultipartDocument rdocs = conn.getMultipartXMLDocument(RequestMethod.GET, url.getURL(), null,
                    creds, cache);
            getStatus = rdocs.getStatus();
            doc = rdocs.getDocument(partname);
        } else {
            ReturnedDocument rdoc = conn.getXMLDocument(RequestMethod.GET, url.getURL(), null, creds, cache);
            getStatus = rdoc.getStatus();
            doc = rdoc.getDocument();
        }
        assertEquals(404, getStatus); // ensures CSPACE-209 hasn't regressed
        assertNull(doc);
    }

    //@Test 
    // remove test as never know if all the bits for the report are there to test
    public void testReporting() throws Exception {

        ReturnedURL url;
        int getStatus;
        Document doc;
        String serviceurl = "acquisitions/";
        String partname = "acquisitions_common";
        String filename = "acquisitionXMLJSON.xml";
        String xpath = "acquisitions_common/accessionDate";
        String expected = "April 1, 2010";

        // POST (Create Acquisition Record)
        if (partname != null) {
            Map<String, Document> parts = new HashMap<String, Document>();
            parts.put(partname, getDocument(filename));
            url = conn.getMultipartURL(RequestMethod.POST, serviceurl, parts, creds, cache);
        } else {
            url = conn.getURL(RequestMethod.POST, serviceurl, getDocument(filename), creds, cache);
        }

        assertEquals("Failed to receive 201 status code on create", 201, url.getStatus());

        //find report

        ReturnedDocument doc3 = conn.getXMLDocument(RequestMethod.GET, "reports?doctype=Acquisition", null, creds,
                cache);
        assertEquals(200, doc3.getStatus());
        Set<String> csids = new HashSet<String>();
        String reportcsid = "";
        for (Node n : (List<Node>) doc3.getDocument().selectNodes("abstract-common-list/list-item/csid")) {
            reportcsid = n.getText();
        }
        assertFalse("No Acquisition report to test with", reportcsid.equals(""));
        if (!reportcsid.equals("")) {
            //only runs if there is a report to run
            String reportsurl = "reports/" + reportcsid;

            String csid = url.getURLTail();
            Document report = getDocument("reportrun.xml");
            report.selectSingleNode("invocationContext/singleCSID").setText(csid);

            //DO REPORT
            //run report
            ReturnUnknown doc2 = conn.getReportDocument(RequestMethod.POST, reportsurl, report, creds, cache);
            JSONObject out = new JSONObject();
            out.put("getByteBody", doc2.getBytes());
            out.put("contenttype", doc2.getContentType());

            assertEquals("Failed to receive 200 status code on create", 200, doc2.getStatus());
        }

        // DELETE (Delete Acquisition)
        int status = conn.getNone(RequestMethod.DELETE, url.getURL(), null, creds, cache);
        assertEquals("Failed to receive expected 200 status code on delete", 200, status);
        // Now try to delete non-existent (make sure CSPACE-73 hasn't regressed)
        status = conn.getNone(RequestMethod.DELETE, url.getURL(), null, creds, cache);
        assertEquals("Failed to receive expected 404 status code on repeated delete of same record", 404, status);

        log.info("DELETE");
        // GET once more to make sure it isn't there
        if (partname != null) {
            ReturnedMultipartDocument rdocs = conn.getMultipartXMLDocument(RequestMethod.GET, url.getURL(), null,
                    creds, cache);
            getStatus = rdocs.getStatus();
            doc = rdocs.getDocument(partname);
        } else {
            ReturnedDocument rdoc = conn.getXMLDocument(RequestMethod.GET, url.getURL(), null, creds, cache);
            getStatus = rdoc.getStatus();
            doc = rdoc.getDocument();
        }
        assertEquals("Failed to receive expected 404 status code on repeated delete of same record", 404,
                getStatus); // ensures CSPACE-209 hasn't regressed
        assertNull("Contents of deleted record were unexpectedly not null", doc);
    }

    @Test
    public void testAllPostGetDelete() throws Exception {
        // TODO Add vocab (the previous testVocabPost method was just an exact
        // copy of testRolesPost!)
        // TODO Add everything from CSPACE-1876 and more
        // testPostGetDelete("collectionobjects/", "collectionobjects_common",
        // "objectCreate.xml", "collectionobjects_common/objectNumber", "2");

        testPostGetDelete("media/", "media_common", "media.xml", "media_common/title", "EX2011.5");
        testCRUD("collectionobjects/", "collectionobjects_common", "objectCreate.xml", "objectsXMLJSON.xml",
                "collectionobjects_common/objectNumber", "2010.1.9");

        //testPostGetDelete("acquisitions/", "acquisitions_common",
        //      "acquisitionXMLJSON.xml", "acquisitions_common/accessionDate",
        //      "2010-04-01T04:00:00Z");
        testPostGetDelete("intakes/", "intakes_common", "intake.xml", "intakes_common/entryNumber", "IN2010.337");
        testPostGetDelete("loansin/", "loansin_common", "loanin.xml", "loansin_common/loanInNumber", "LI2010.1.21");
        testPostGetDelete("loansout/", "loansout_common", "loanout.xml", "loansout_common/loanOutNumber",
                "LO2010.117");
        testPostGetDelete("movements/", "movements_common", "movement.xml",
                "movements_common/movementReferenceNumber", "MV2010.99");
        testPostGetDelete("valuationcontrols/", "valuationcontrols_common", "valuationcontrol.xml",
                "valuationcontrols_common/valuationcontrolRefNumber", "VC2013.4.22");
        testPostGetDelete("objectexit/", "objectexit_common", "objectexit.xml", "objectexit_common/exitNumber",
                "EX2011.5");
        testPostGetDelete("groups/", "groups_common", "group.xml", "groups_common/title", "This is my group");
        testPostGetDelete("conditionchecks/", "conditionchecks_common", "conditioncheck.xml",
                "conditionchecks_common/conditionCheckRefNumber", "CC2013.001");
        testPostGetDelete("conservation/", "conservation_common", "conservation.xml",
                "conservation_common/conservationNumber", "CT2015.1");
        testPostGetDelete("exhibitions/", "exhibitions_common", "exhibition.xml",
                "exhibitions_common/exhibitionNumber", "EX123");

        // testPostGetDelete("relations/", "relations_common",
        // "relationship.xml", "relations_common/relationshipType", "affects");

        // TODO make roleName dynamically vary otherwise POST fails if already
        // exists (something like buildObject)
        // testPostGetDelete("authorization/roles/", null, "role.xml",
        // "role/description", "this role is for test users");
        // testPostGetDelete("authorization/permissions/", null,
        // "permissions.xml", "permission/resourceName", "testthing");

        // TODO might be worth adding test for CSPACE-1947 (POST with wrong
        // label "succeeds")
    }

    // TODO merge this method with testPostGetDelete - this is Chris's temporary
    // fork to help testing repeatable fields
    /**
     * Test Create, Update, Read and Delete for a record type
     * 
     * @param serviceurl
     * @param partname
     * @param Createfilename
     * @param Updatefilename
     * @param xpath
     * @param expected
     */
    private void testCRUD(String serviceurl, String partname, String Createfilename, String Updatefilename,
            String xpath, String expected) throws Exception {
        ReturnedURL url;

        log.info("Testing " + serviceurl + " with " + Createfilename + " and partname=" + partname);

        // TODO add document parsing for PUT, and for POSTs that require
        // uniqueness (to maintain self-contained tests that don't destroy
        // existing data)

        // POST (Create)
        if (partname != null) {
            Map<String, Document> parts = new HashMap<String, Document>();
            parts.put(partname, getDocument(Createfilename));
            url = conn.getMultipartURL(RequestMethod.POST, serviceurl, parts, creds, cache);
        } else {
            url = conn.getURL(RequestMethod.POST, serviceurl, getDocument(Createfilename), creds, cache);
        }

        assertEquals(201, url.getStatus());

        assertTrue(url.getURL().startsWith("/" + serviceurl)); // ensures e.g.
        // CSPACE-305
        // hasn't
        // regressed

        // GET (Read)
        int getStatus;
        Document doc;
        if (partname != null) {
            ReturnedMultipartDocument rdocs = conn.getMultipartXMLDocument(RequestMethod.GET, url.getURL(), null,
                    creds, cache);
            getStatus = rdocs.getStatus();
            doc = rdocs.getDocument(partname);
        } else {
            ReturnedDocument rdoc = conn.getXMLDocument(RequestMethod.GET, url.getURL(), null, creds, cache);
            getStatus = rdoc.getStatus();
            doc = rdoc.getDocument();
        }
        assertEquals(200, getStatus);
        assertNotNull(doc);
        Node n = doc.selectSingleNode(xpath);
        assertNotNull(n);
        String text = n.getText();
        assertEquals(expected, text);
        // Update
        if (partname != null) {
            Map<String, Document> parts = new HashMap<String, Document>();
            parts.put(partname, getDocument(Updatefilename));
            conn.getMultipartXMLDocument(RequestMethod.PUT, url.getURL(), parts, creds, cache);
            ReturnedMultipartDocument rdocs = conn.getMultipartXMLDocument(RequestMethod.GET, url.getURL(), null,
                    creds, cache);
            getStatus = rdocs.getStatus();
            doc = rdocs.getDocument(partname);
        } else {
            conn.getXMLDocument(RequestMethod.PUT, url.getURL(), getDocument(Updatefilename), creds, cache);
            ReturnedDocument rdoc = conn.getXMLDocument(RequestMethod.GET, url.getURL(), null, creds, cache);
            getStatus = rdoc.getStatus();
            doc = rdoc.getDocument();
        }

        assertEquals(200, getStatus);
        assertNotNull(doc);
        n = doc.selectSingleNode(xpath);
        assertNotNull(n);
        text = n.getText();
        assertEquals(expected, text);

        //log.info(doc.asXML());
        // Get

        // DELETE (Delete)
        int status = conn.getNone(RequestMethod.DELETE, url.getURL(), null, creds, cache);
        assertEquals(200, status);
        // Now try to delete non-existent (make sure CSPACE-73 hasn't regressed)
        status = conn.getNone(RequestMethod.DELETE, url.getURL(), null, creds, cache);
        assertEquals(404, status);

        // GET once more to make sure it isn't there
        if (partname != null) {
            ReturnedMultipartDocument rdocs = conn.getMultipartXMLDocument(RequestMethod.GET, url.getURL(), null,
                    creds, cache);
            getStatus = rdocs.getStatus();
            doc = rdocs.getDocument(partname);
        } else {
            ReturnedDocument rdoc = conn.getXMLDocument(RequestMethod.GET, url.getURL(), null, creds, cache);
            getStatus = rdoc.getStatus();
            doc = rdoc.getDocument();
        }
        assertEquals(404, getStatus); // ensures CSPACE-209 hasn't regressed
        assertNull(doc);
    }

    private void testPostGetDelete(String serviceurl, String partname, String filename, String xpath,
            String expected) throws Exception {
        ReturnedURL url;
        log.info("Testing " + serviceurl + " with " + filename + " and partname=" + partname);

        // TODO add document parsing for PUT, and for POSTs that require
        // uniqueness (to maintain self-contained tests that don't destroy
        // existing data)

        // POST (Create)
        if (partname != null) {
            Map<String, Document> parts = new HashMap<String, Document>();
            parts.put(partname, getDocument(filename));
            url = conn.getMultipartURL(RequestMethod.POST, serviceurl, parts, creds, cache);
        } else {
            url = conn.getURL(RequestMethod.POST, serviceurl, getDocument(filename), creds, cache);
        }

        assertEquals("Failed to receive 201 status code on create", 201, url.getStatus());

        // assertTrue(url.getURL().startsWith("/"+serviceurl)); // ensures e.g.
        // CSPACE-305 hasn't regressed

        log.info("CREATED RECORD " + url.getURL());
        // GET (Read)
        int getStatus;
        Document doc;
        if (partname != null) {
            ReturnedMultipartDocument rdocs = conn.getMultipartXMLDocument(RequestMethod.GET, url.getURL(), null,
                    creds, cache);
            getStatus = rdocs.getStatus();
            doc = rdocs.getDocument(partname);
        } else {
            ReturnedDocument rdoc = conn.getXMLDocument(RequestMethod.GET, url.getURL(), null, creds, cache);
            getStatus = rdoc.getStatus();
            doc = rdoc.getDocument();
        }
        assertEquals("Failed to receive expected 200 status code on read", 200, getStatus);
        log.trace("RETRIEVED RECORD " + doc.asXML());
        assertNotNull("Record received on read was unexpectedly null", doc);
        Node n = doc.selectSingleNode(xpath);
        assertNotNull("Expected XPath expression was not found in record", n);
        String text = n.getText();
        assertEquals("Expected value was not found in record", expected, text);

        // List
        log.info("LIST from " + serviceurl);
        ReturnedDocument rdoc1 = conn.getXMLDocument(RequestMethod.GET, "/" + serviceurl, null, creds, cache);
        getStatus = rdoc1.getStatus();
        doc = rdoc1.getDocument();

        assertEquals("Failed to receive expected 200 status code on list read", 200, getStatus);
        log.trace("LISTLISTLIST");
        log.trace(doc.asXML());
        log.trace("LISTLISTLIST");

        // DELETE (Delete)
        int status = conn.getNone(RequestMethod.DELETE, url.getURL(), null, creds, cache);
        assertEquals("Failed to receive expected 200 status code on delete", 200, status);
        // Now try to delete non-existent (make sure CSPACE-73 hasn't regressed)
        status = conn.getNone(RequestMethod.DELETE, url.getURL(), null, creds, cache);
        assertEquals("Failed to receive expected 404 status code on repeated delete of same record", 404, status);

        log.info("DELETE");
        // GET once more to make sure it isn't there
        if (partname != null) {
            ReturnedMultipartDocument rdocs = conn.getMultipartXMLDocument(RequestMethod.GET, url.getURL(), null,
                    creds, cache);
            getStatus = rdocs.getStatus();
            doc = rdocs.getDocument(partname);
        } else {
            ReturnedDocument rdoc = conn.getXMLDocument(RequestMethod.GET, url.getURL(), null, creds, cache);
            getStatus = rdoc.getStatus();
            doc = rdoc.getDocument();
        }
        assertEquals("Failed to receive expected 404 status code on repeated delete of same record", 404,
                getStatus); // ensures CSPACE-209 hasn't regressed
        assertNull("Contents of deleted record were unexpectedly not null", doc);

    }

    // @Test
    public void testPermissionsPost() throws Exception {
        // TODO check whether this is needed anymore - it should be covered by
        // PostGetDelete above?
        // TODO check whether should be commented back in - Chris was debugging
        // permissions
        // Map<String,Document> parts=new HashMap<String,Document>();
        // ReturnedURL
        // url=conn.getURL(RequestMethod.POST,"authorization/permissions/",getDocument("permissions.xml"),creds,cache);
        // assertEquals(201,url.getStatus());
        // int
        // status=conn.getNone(RequestMethod.DELETE,url.getURL(),null,creds,cache);
        // assertEquals(200,status); // XXX CSPACE-73, should be 404
    }

    /*
     * @Ignore public void testRolePermissionsPost() throws Exception { //
     * ReturnedURLurl=conn.getURL(RequestMethod.POST,
     * "authorization/roles/cbdb4f45-2fac-461b-93ef-6fec21a2ad97/permroles"
     * ,getDocument("rolepermissions.xml"),creds,cache); //
     * assertEquals(201,url.getStatus());
     * 
     * 
     * 
     * // TODO check whether should be commented back in - Chris was debugging
     * permissions // NOTE this test is more complex than PostGetDelete and
     * perhaps should remain as a separate test? //create a permission //
     * Map<String,Document> parts=new HashMap<String,Document>(); // ReturnedURL
     * url
     * =conn.getURL(RequestMethod.POST,"authorization/permissions/",getDocument
     * ("permissions.xml"),creds,cache); // assertEquals(201,url.getStatus());
     * 
     * //create permissionRole for the permission above // url =
     * conn.getURL(RequestMethod.POST,
     * "authorization/permissions/"+url.getURLTail()+"/permroles",
     * getDocument("rolepermissions.xml"), creds, cache); // assertEquals(201,
     * url.getStatus()); //delete the permissionRole // int
     * status=conn.getNone(RequestMethod.DELETE,url.getURL(),null,creds,cache);
     * // assertEquals(200,status); // XXX CSPACE-73, should be 404
     * 
     * //delete the permission //
     * status=conn.getNone(RequestMethod.DELETE,url.getURL(),null,creds,cache);
     * // assertEquals(200,status); // XXX CSPACE-73, should be 404 }
     */

    @Test
    public void testAuthorityCreateUpdateDelete() throws Exception {

        Map<String, Document> parts = new HashMap<String, Document>();
        parts.put("personauthorities_common", getDocument("personAuth.xml"));
        Map<String, Document> parts1 = new HashMap<String, Document>();
        parts1.put("personauthorities_common", getDocument("personAuth.xml"));
        String id;
        // CREATE
        ReturnedURL url = conn.getMultipartURL(RequestMethod.POST, "personauthorities/", parts, creds, cache);
        assertEquals(201, url.getStatus());
        id = url.getURLTail();
        // UPDATE
        ReturnedMultipartDocument doc = conn.getMultipartXMLDocument(RequestMethod.PUT, "personauthorities/" + id,
                parts1, creds, cache);
        assertEquals(200, doc.getStatus()); // XXX shouldn't this be 201?
        // DELETE
        conn.getNone(RequestMethod.DELETE, "personauthorities/" + id, null, creds, cache);
        assertEquals(200, doc.getStatus());

    }

    @Test
    public void testObjectsPut() throws Exception {
        Map<String, Document> parts = new HashMap<String, Document>();
        parts.put("collectionobjects_common", getDocument("obj1.xml"));
        ReturnedURL url = conn.getMultipartURL(RequestMethod.POST, "collectionobjects/", parts, creds, cache);
        assertEquals(201, url.getStatus());
        ReturnedMultipartDocument doc = conn.getMultipartXMLDocument(RequestMethod.PUT, url.getURL(),
                buildObject("32", "obj2.xml", "collectionobjects_common"), creds, cache);
        assertEquals(201, url.getStatus()); // 201?
        doc = conn.getMultipartXMLDocument(RequestMethod.GET, url.getURL(), null, creds, cache);
        assertEquals(200, doc.getStatus());
        String num = doc.getDocument("collectionobjects_common")
                .selectSingleNode("collectionobjects_common/objectNumber").getText();
        assertEquals("32", num);
    }

    // TODO pre-emptive cache population

    private Map<String, Document> buildObject(String objid, String src, String part)
            throws DocumentException, IOException {
        InputStream data_stream = getResource(src);
        String data = IOUtils.toString(data_stream);
        data_stream.close();
        data = data.replaceAll("<<objnum>>", objid);
        SAXReader reader = new SAXReader();
        Document doc = reader.read(new StringReader(data));
        Map<String, Document> parts = new HashMap<String, Document>();
        parts.put(part, doc);
        return parts;
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testSearch() throws Exception {
        // Insert one non-aardvark
        Map<String, Document> parts = new HashMap<String, Document>();
        Document doc1 = getDocument("obj1.xml");
        parts.put("collectionobjects_common", doc1);
        ReturnedURL url = conn.getMultipartURL(RequestMethod.POST, "collectionobjects/", parts, creds, cache);
        assertEquals(201, url.getStatus());
        String uid1 = url.getURL();
        String non = url.getURLTail();
        // Insert one aardvark
        parts = new HashMap<String, Document>();
        Document doc2 = getDocument("obj-search.xml");
        parts.put("collectionobjects_common", doc2);
        url = conn.getMultipartURL(RequestMethod.POST, "collectionobjects/", parts, creds, cache);
        String uid2 = url.getURL();
        assertEquals(201, url.getStatus());
        String good = url.getURLTail();
        // search for aardvark
        ReturnedDocument doc = conn.getXMLDocument(RequestMethod.GET, "collectionobjects?kw=aardvark", null, creds,
                cache);
        assertEquals(200, doc.getStatus());
        Set<String> csids = new HashSet<String>();
        for (Node n : (List<Node>) doc.getDocument().selectNodes("abstract-common-list/list-item/csid")) {
            csids.add(n.getText());
        }

        // delete non-aadvark and aadvark
        int status = conn.getNone(RequestMethod.DELETE, uid1, null, creds, cache);
        assertEquals(200, status); // XXX CSPACE-73, should be 404
        status = conn.getNone(RequestMethod.DELETE, uid2, null, creds, cache);
        assertEquals(200, status); // XXX CSPACE-73, should be 404

        // test
        assertFalse(csids.size() == 0);
        assertTrue(csids.contains(good));
        assertFalse(csids.contains(non));

    }
}