com.github.podd.resources.test.UploadArtifactResourceImplTest.java Source code

Java tutorial

Introduction

Here is the source code for com.github.podd.resources.test.UploadArtifactResourceImplTest.java

Source

/**
 * PODD is an OWL ontology database used for scientific project management
 *
 * Copyright (C) 2009-2013 The University Of Queensland
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU Affero General Public License as published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License along with this program.
 * If not, see <http://www.gnu.org/licenses/>.
 */
package com.github.podd.resources.test;

import java.io.InputStream;
import java.io.StringReader;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import org.junit.Test;
import org.openrdf.model.Model;
import org.openrdf.model.Resource;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.model.vocabulary.RDFS;
import org.openrdf.rio.RDFFormat;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Status;
import org.restlet.ext.html.FormData;
import org.restlet.ext.html.FormDataSet;
import org.restlet.representation.Representation;
import org.restlet.resource.ClientResource;
import org.restlet.resource.ResourceException;

import com.github.ansell.restletutils.RestletUtilMediaType;
import com.github.podd.api.test.TestConstants;
import com.github.podd.exception.OntologyNotInProfileException;
import com.github.podd.utils.InferredOWLOntologyID;
import com.github.podd.utils.OntologyUtils;
import com.github.podd.utils.PODD;
import com.github.podd.utils.PoddWebConstants;

/**
 * @author kutila
 *
 */
public class UploadArtifactResourceImplTest extends AbstractResourceImplTest {
    /**
     * Test Upload attempt with an artifact that is inconsistent. Results in an HTTP 500 Internal
     * Server Error with detailed error causes in the RDF body.
     */
    @Test
    public void testErrorUploadWithInconsistentArtifactRdf() throws Exception {
        final MediaType mediaType = MediaType.APPLICATION_RDF_XML;
        final RDFFormat responseFormat = RDFFormat.forMIMEType(mediaType.getName(), RDFFormat.RDFXML);

        final ClientResource uploadArtifactClientResource = new ClientResource(
                this.getUrl(PoddWebConstants.PATH_ARTIFACT_UPLOAD));

        try {
            final Representation input = this.buildRepresentationFromResource(
                    TestConstants.TEST_ARTIFACT_BAD_2_LEAD_INSTITUTES, MediaType.APPLICATION_RDF_XML);

            this.doTestAuthenticatedRequest(uploadArtifactClientResource, Method.POST, input, mediaType,
                    Status.SERVER_ERROR_INTERNAL, AbstractResourceImplTest.WITH_ADMIN);
        } catch (final ResourceException e) {
            // verify: error details
            Assert.assertEquals("Not the expected HTTP status code", Status.SERVER_ERROR_INTERNAL, e.getStatus());

            final Model model = this.assertRdf(uploadArtifactClientResource.getResponseEntity(), responseFormat,
                    16);

            final Set<Resource> errors = model.filter(null, RDF.TYPE, PODD.ERR_TYPE_TOP_ERROR).subjects();
            Assert.assertEquals("Not the expected number of Errors", 1, errors.size());
            final Resource topError = errors.iterator().next();

            // Resource level error details
            Assert.assertEquals("Not the expected HTTP Status Code", "500",
                    model.filter(topError, PODD.HTTP_STATUS_CODE_VALUE, null).objectString());
            Assert.assertEquals("Not the expected Reason Phrase", "Internal Server Error",
                    model.filter(topError, PODD.HTTP_REASON_PHRASE, null).objectString());
            Assert.assertEquals("Not the expected RDFS:comment", "Error loading artifact to PODD",
                    model.filter(topError, RDFS.COMMENT, null).objectString());

            Assert.assertEquals("Expected 1 child error node", 1,
                    model.filter(topError, PODD.ERR_CONTAINS, null).size());
            final Resource errorNode = model.filter(topError, PODD.ERR_CONTAINS, null).objectResource();

            // Error cause details
            Assert.assertEquals("Not the expected Exception class",
                    "com.github.podd.exception.InconsistentOntologyException",
                    model.filter(errorNode, PODD.ERR_EXCEPTION_CLASS, null).objectString());

            Assert.assertEquals("Not the expected error source", "urn:temp:inconsistentArtifact:1",
                    model.filter(errorNode, PODD.ERR_SOURCE, null).objectString());

            Assert.assertTrue("Not the expected inconsistency explanation",
                    model.filter(errorNode, RDFS.COMMENT, null).objectString().contains(
                            "Individual urn:temp:object:1960 has more than 1 values for property http://purl.org/podd/ns/poddBase#hasLeadInstitution"));
        } finally {
            this.releaseClient(uploadArtifactClientResource);
        }
    }

    /**
     * Test Upload attempt with an artifact that is inconsistent. Results in an HTTP 500 Internal
     * Server Error with detailed error causes in the RDF body.
     */
    @Test
    public void testErrorUploadWithNotInOwlDlProfileArtifactRdf() throws Exception {
        final Representation input = this.buildRepresentationFromResource(
                TestConstants.TEST_ARTIFACT_BAD_NOT_OWL_DL, MediaType.APPLICATION_RDF_XML);

        final MediaType mediaType = MediaType.APPLICATION_RDF_XML;
        final RDFFormat responseFormat = RDFFormat.forMIMEType(mediaType.getName(), RDFFormat.RDFXML);

        final ClientResource uploadArtifactClientResource = new ClientResource(
                this.getUrl(PoddWebConstants.PATH_ARTIFACT_UPLOAD));

        try {
            this.doTestAuthenticatedRequest(uploadArtifactClientResource, Method.POST, input, mediaType,
                    Status.SERVER_ERROR_INTERNAL, AbstractResourceImplTest.WITH_ADMIN);
        } catch (final ResourceException e) {
            // verify: error details
            Assert.assertEquals("Not the expected HTTP status code", Status.SERVER_ERROR_INTERNAL, e.getStatus());

            final Model model = this.assertRdf(uploadArtifactClientResource.getResponseEntity(), responseFormat,
                    18);

            final Set<Resource> errors = model.filter(null, RDF.TYPE, PODD.ERR_TYPE_TOP_ERROR).subjects();
            Assert.assertEquals("Not the expected number of Errors", 1, errors.size());
            final Resource topError = errors.iterator().next();

            // Resource level error details
            Assert.assertEquals("Not the expected HTTP Status Code", "500",
                    model.filter(topError, PODD.HTTP_STATUS_CODE_VALUE, null).objectString());
            Assert.assertEquals("Not the expected Reason Phrase", "Internal Server Error",
                    model.filter(topError, PODD.HTTP_REASON_PHRASE, null).objectString());
            Assert.assertEquals("Not the expected RDFS:comment", "Error loading artifact to PODD",
                    model.filter(topError, RDFS.COMMENT, null).objectString());

            // Error cause details
            Assert.assertEquals("Not the expected Exception class", OntologyNotInProfileException.class.getName(),
                    model.filter(null, PODD.ERR_EXCEPTION_CLASS, null).objectString());

            Assert.assertEquals(
                    "Expected error sources not found", 2, model
                            .filter(null, PODD.ERR_SOURCE,
                                    PODD.VF.createLiteral(
                                            "ClassAssertion(owl:Individual <mailto:helen.daily@csiro.au>)"))
                            .size());
        } finally {
            this.releaseClient(uploadArtifactClientResource);
        }
    }

    /**
     * Test unauthenticated access to "upload artifact" leads to an UNAUTHORIZED error.
     */
    @Test
    public void testErrorUploadWithoutAuthentication() throws Exception {
        final ClientResource uploadArtifactClientResource = new ClientResource(
                this.getUrl(PoddWebConstants.PATH_ARTIFACT_UPLOAD));

        try {
            final Representation input = this.buildRepresentationFromResource(
                    "/test/artifacts/basicProject-1-internal-object.rdf", MediaType.APPLICATION_RDF_XML);

            final FormDataSet form = new FormDataSet();
            form.setMultipart(true);
            form.getEntries().add(new FormData("file", input));

            uploadArtifactClientResource.post(form, MediaType.TEXT_HTML);
            Assert.fail("Should have thrown a ResourceException with Status Code 401");
        } catch (final ResourceException e) {
            Assert.assertEquals("Not the expected HTTP status code", Status.CLIENT_ERROR_UNAUTHORIZED,
                    e.getStatus());
        } finally {
            this.releaseClient(uploadArtifactClientResource);
        }
    }

    /**
     * Test upload attempt without actual file leads to a BAD_REQUEST error
     */
    @Test
    public void testErrorUploadWithoutFile() throws Exception {
        final ClientResource uploadArtifactClientResource = new ClientResource(
                this.getUrl(PoddWebConstants.PATH_ARTIFACT_UPLOAD));

        try {
            final FormDataSet form = new FormDataSet();
            form.setMultipart(true);

            this.doTestAuthenticatedRequest(uploadArtifactClientResource, Method.POST, form, MediaType.TEXT_HTML,
                    Status.CLIENT_ERROR_BAD_REQUEST, AbstractResourceImplTest.WITH_ADMIN);
            Assert.fail("Should have thrown a ResourceException with Status Code 400");
        } catch (final ResourceException e) {
            Assert.assertEquals("Not the expected HTTP status code", Status.CLIENT_ERROR_BAD_REQUEST,
                    e.getStatus());
        } finally {
            this.releaseClient(uploadArtifactClientResource);
        }
    }

    /**
     * Test authenticated access to the upload Artifact page in HTML
     */
    @Test
    public void testGetUploadArtifactPageBasicHtml() throws Exception {
        // prepare: add an artifact
        final ClientResource getArtifactClientResource = new ClientResource(
                this.getUrl(PoddWebConstants.PATH_ARTIFACT_UPLOAD));

        try {
            final Representation results = this.doTestAuthenticatedRequest(getArtifactClientResource, Method.GET,
                    null, MediaType.TEXT_HTML, Status.SUCCESS_OK, AbstractResourceImplTest.WITH_ADMIN);

            final String body = this.getText(results);
            Assert.assertTrue(body.contains("Upload new artifact"));
            Assert.assertTrue(body.contains("type=\"file\""));

            this.assertFreemarker(body);
        } finally {
            this.releaseClient(getArtifactClientResource);
        }
    }

    // @Ignore("When this test is active, it seems to slow down other tests so far that they don't complete normally")
    @Test
    public final void testLoadArtifactConcurrency() throws Exception {
        // load test artifact
        final InputStream inputStream4Artifact = this.getClass()
                .getResourceAsStream(TestConstants.TEST_ARTIFACT_IMPORT_PSCIENCEv1);

        Assert.assertNotNull("Could not find test resource: " + TestConstants.TEST_ARTIFACT_IMPORT_PSCIENCEv1,
                inputStream4Artifact);

        final String nextTestArtifact = IOUtils.toString(inputStream4Artifact);

        final AtomicInteger threadSuccessCount = new AtomicInteger(0);
        final AtomicInteger perThreadSuccessCount = new AtomicInteger(0);
        final AtomicInteger threadStartCount = new AtomicInteger(0);
        final AtomicInteger perThreadStartCount = new AtomicInteger(0);
        final CountDownLatch openLatch = new CountDownLatch(1);
        // Changing this from 8 to 9 on my machine may be triggering a restlet
        // bug
        final int threadCount = 9;
        final int perThreadCount = 2;
        final CountDownLatch closeLatch = new CountDownLatch(threadCount);
        for (int i = 0; i < threadCount; i++) {
            final int number = i;
            final Runnable runner = new Runnable() {
                @Override
                public void run() {
                    try {
                        openLatch.await(55000, TimeUnit.MILLISECONDS);
                        threadStartCount.incrementAndGet();
                        for (int j = 0; j < perThreadCount; j++) {
                            perThreadStartCount.incrementAndGet();
                            ClientResource uploadArtifactClientResource = null;

                            try {
                                uploadArtifactClientResource = new ClientResource(
                                        UploadArtifactResourceImplTest.this
                                                .getUrl(PoddWebConstants.PATH_ARTIFACT_UPLOAD));

                                AbstractResourceImplTest.setupThreading(uploadArtifactClientResource.getContext());

                                final Representation input = UploadArtifactResourceImplTest.this
                                        .buildRepresentationFromResource(
                                                TestConstants.TEST_ARTIFACT_IMPORT_PSCIENCEv1,
                                                MediaType.APPLICATION_RDF_XML);

                                final Representation results = UploadArtifactResourceImplTest.this
                                        .doTestAuthenticatedRequest(uploadArtifactClientResource, Method.POST,
                                                input, MediaType.APPLICATION_RDF_XML, Status.SUCCESS_OK,
                                                AbstractResourceImplTest.WITH_ADMIN);

                                // verify: results (expecting the added
                                // artifact's ontology IRI)
                                final String body = UploadArtifactResourceImplTest.this.getText(results);

                                final Collection<InferredOWLOntologyID> ontologyIDs = OntologyUtils
                                        .stringToOntologyID(body, RDFFormat.RDFXML);

                                Assert.assertNotNull("No ontology IDs in response", ontologyIDs);
                                Assert.assertEquals("More than 1 ontology ID in response", 1, ontologyIDs.size());
                                Assert.assertTrue("Ontology ID not of expected format",
                                        ontologyIDs.iterator().next().toString().contains("artifact:1:version:1"));
                                perThreadSuccessCount.incrementAndGet();
                            } finally {
                                UploadArtifactResourceImplTest.this.releaseClient(uploadArtifactClientResource);
                            }
                        }
                        threadSuccessCount.incrementAndGet();
                    } catch (final Throwable e) {
                        e.printStackTrace();
                        Assert.fail("Failed in test: " + number);
                    } finally {
                        closeLatch.countDown();
                    }
                }
            };
            new Thread(runner, "TestThread" + number).start();
        }
        // all threads are waiting on the latch.
        openLatch.countDown(); // release the latch
        // all threads are now running concurrently.
        closeLatch.await(50000, TimeUnit.MILLISECONDS);
        // closeLatch.await();
        // Verify that there were no startup failures
        Assert.assertEquals("Some threads did not all start successfully", threadCount, threadStartCount.get());
        Assert.assertEquals("Some thread loops did not start successfully", perThreadCount * threadCount,
                perThreadStartCount.get());
        // Verify that there were no failures, as the count is only incremented
        // for successes, where
        // the closeLatch must always be called, even for failures
        Assert.assertEquals("Some thread loops did not complete successfully", perThreadCount * threadCount,
                perThreadSuccessCount.get());
        Assert.assertEquals("Some threads did not complete successfully", threadCount, threadSuccessCount.get());
    }

    /**
     * Test successful upload of a new artifact file while authenticated with the admin role.
     * Expects an HTML response.
     */
    @Test
    public void testUploadArtifactBasicHtml() throws Exception {
        final ClientResource uploadArtifactClientResource = new ClientResource(
                this.getUrl(PoddWebConstants.PATH_ARTIFACT_UPLOAD));
        try {
            final Representation input = this.buildRepresentationFromResource(
                    "/test/artifacts/basicProject-1-internal-object.rdf", MediaType.APPLICATION_RDF_XML);

            final FormDataSet form = new FormDataSet();
            form.setMultipart(true);
            form.getEntries().add(new FormData("file", input));

            final Representation results = this.doTestAuthenticatedRequest(uploadArtifactClientResource,
                    Method.POST, form, MediaType.TEXT_HTML, Status.SUCCESS_OK, AbstractResourceImplTest.WITH_ADMIN);

            // TODO: verify results once a proper success page is incorporated.
            final String body = this.getText(results);
            Assert.assertTrue(body.contains("Project successfully uploaded"));
            this.assertFreemarker(body);
        } finally {
            this.releaseClient(uploadArtifactClientResource);
        }
    }

    /**
     * Test successful upload of a new artifact file while authenticated with the admin role.
     * Expects a plain text response.
     */
    @Test
    public void testUploadArtifactBasicRdf() throws Exception {
        final ClientResource uploadArtifactClientResource = new ClientResource(
                this.getUrl(PoddWebConstants.PATH_ARTIFACT_UPLOAD));
        try {
            final Representation input = this.buildRepresentationFromResource(
                    "/test/artifacts/basicProject-1-internal-object.rdf", MediaType.APPLICATION_RDF_XML);

            final Representation results = this.doTestAuthenticatedRequest(uploadArtifactClientResource,
                    Method.POST, input, MediaType.TEXT_PLAIN, Status.SUCCESS_OK,
                    AbstractResourceImplTest.WITH_ADMIN);

            // verify: results (expecting the added artifact's ontology IRI)
            final String body = this.getText(results);
            Assert.assertTrue(body.contains("http://"));
            Assert.assertFalse(body.contains("html"));
            Assert.assertFalse(body.contains("\n"));
        } finally {
            this.releaseClient(uploadArtifactClientResource);
        }
    }

    /**
     * Test successful upload of a new artifact file while authenticated with the admin role.
     * Expects a plain text response.
     */
    @Test
    public void testUploadArtifactBasicRdfWithFormData() throws Exception {
        final ClientResource uploadArtifactClientResource = new ClientResource(
                this.getUrl(PoddWebConstants.PATH_ARTIFACT_UPLOAD));

        try {
            final Representation input = this.buildRepresentationFromResource(
                    "/test/artifacts/basicProject-1-internal-object.rdf", MediaType.APPLICATION_RDF_XML);

            final FormDataSet form = new FormDataSet();
            form.setMultipart(true);
            form.getEntries().add(new FormData("file", input));

            final Representation results = this.doTestAuthenticatedRequest(uploadArtifactClientResource,
                    Method.POST, form, MediaType.TEXT_PLAIN, Status.SUCCESS_OK,
                    AbstractResourceImplTest.WITH_ADMIN);

            // verify: results (expecting the added artifact's ontology IRI)
            final String body = this.getText(results);
            Assert.assertTrue(body.contains("http://"));
            Assert.assertFalse(body.contains("html"));
            Assert.assertFalse(body.contains("\n"));
        } finally {
            this.releaseClient(uploadArtifactClientResource);
        }
    }

    /**
     * Test successful upload of a new artifact file while authenticated with the admin role.
     * Expects a plain text response.
     */
    @Test
    public void testUploadArtifactBasicTurtle() throws Exception {
        final ClientResource uploadArtifactClientResource = new ClientResource(
                this.getUrl(PoddWebConstants.PATH_ARTIFACT_UPLOAD));

        try {
            final Representation input = this.buildRepresentationFromResource(
                    TestConstants.TEST_ARTIFACT_TTL_1_INTERNAL_OBJECT, MediaType.APPLICATION_RDF_TURTLE);

            final Representation results = this.doTestAuthenticatedRequest(uploadArtifactClientResource,
                    Method.POST, input, MediaType.APPLICATION_RDF_TURTLE, Status.SUCCESS_OK,
                    AbstractResourceImplTest.WITH_ADMIN);

            // verify: results (expecting the added artifact's ontology IRI)
            final String body = this.getText(results);

            final Collection<InferredOWLOntologyID> ontologyIDs = OntologyUtils.stringToOntologyID(body,
                    RDFFormat.TURTLE);

            Assert.assertNotNull("No ontology IDs in response", ontologyIDs);
            Assert.assertEquals("More than 1 ontology ID in response", 1, ontologyIDs.size());
            Assert.assertTrue("Ontology ID not of expected format",
                    ontologyIDs.iterator().next().toString().contains("artifact:1:version:1"));
        } finally {
            this.releaseClient(uploadArtifactClientResource);
        }
    }

    @Test
    public void testUploadNewProjectTemplate() throws Exception {
        // Rio.write(Rio.parse(this.getClass().getResourceAsStream("/test/artifacts/new-project-template.rj"),
        // "", RDFFormat.RDFJSON), System.out, RDFFormat.RDFJSON);

        final ClientResource uploadArtifactClientResource = new ClientResource(
                this.getUrl(PoddWebConstants.PATH_ARTIFACT_UPLOAD));
        try {
            final Representation input = this.buildRepresentationFromResource(
                    "/test/artifacts/new-project-template.rj", RestletUtilMediaType.APPLICATION_RDF_JSON);

            final Representation results = this.doTestAuthenticatedRequest(uploadArtifactClientResource,
                    Method.POST, input, RestletUtilMediaType.APPLICATION_RDF_JSON, Status.SUCCESS_OK,
                    AbstractResourceImplTest.WITH_ADMIN);

            // verify: results (expecting the added artifact's ontology IRI)
            final String body = this.getText(results);

            final Model model = this.assertRdf(new StringReader(body), RDFFormat.RDFJSON, 6);

            Assert.assertEquals(3, model.subjects().size());
            Assert.assertEquals(3, model.predicates().size());
            Assert.assertEquals(5, model.objects().size());
        } finally {
            this.releaseClient(uploadArtifactClientResource);
        }
    }

}