/** * 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.IOException; import java.io.InputStream; import java.io.StringReader; import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.List; import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.openrdf.model.Model; import org.openrdf.model.Resource; import org.openrdf.model.impl.ValueFactoryImpl; import org.openrdf.rio.RDFFormat; import org.openrdf.rio.RDFParseException; import org.openrdf.rio.Rio; import org.openrdf.rio.UnsupportedRDFormatException; import org.restlet.data.MediaType; import org.restlet.data.Method; import org.restlet.data.Status; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; import org.restlet.resource.ClientResource; import org.restlet.resource.ResourceException; import com.github.podd.api.test.TestConstants; import com.github.podd.impl.data.test.SSHService; 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 * */ // @Ignore("Tests fail sometimes with the SSH Service timing out due to lack of entropy. Should be run before releases.") public class DataReferenceAttachResourceImplTest extends AbstractResourceImplTest { /** SSH File Repository server for tests */ protected SSHService sshd; private Path sshDir = null; /** * Given the path to a resource containing an incomplete File Reference object, this method * constructs a complete File Reference and returns it as an RDF/XML string. * * @param fragmentSource * Location of resource containing incomplete File Reference * @return String containing RDF statements */ private String buildFileReferenceString(final String fragmentSource, final RDFFormat format, final Path testDirectory) throws Exception { // read the fragment's RDF statements into a Model final InputStream inputStream = this.getClass().getResourceAsStream(fragmentSource); final Model model = Rio.parse(inputStream, "", format); // path to be set as part of the file reference final Path completePath = testDirectory.resolve(TestConstants.TEST_REMOTE_FILE_NAME); Files.copy( this.getClass().getResourceAsStream( TestConstants.TEST_REMOTE_FILE_PATH + "/" + TestConstants.TEST_REMOTE_FILE_NAME), completePath, StandardCopyOption.REPLACE_EXISTING); final Resource aliasUri = model.filter(null, PODD.PODD_BASE_HAS_ALIAS, null).subjects().iterator().next(); model.add(aliasUri, PODD.PODD_BASE_HAS_FILE_PATH, ValueFactoryImpl.getInstance().createLiteral(testDirectory.toAbsolutePath().toString())); model.add(aliasUri, PODD.PODD_BASE_HAS_FILENAME, ValueFactoryImpl.getInstance().createLiteral(TestConstants.TEST_REMOTE_FILE_NAME)); // get a String representation of the statements in the Model final StringWriter out = new StringWriter(); Rio.write(model, out, format); return out.toString(); } /** * Override this to change the test aliases for a given test. * * @return A {@link Model} containing the statements relevant to test aliases. * @throws IOException * @throws UnsupportedRDFormatException * @throws RDFParseException */ @Override protected Model getTestAliases() throws RDFParseException, UnsupportedRDFormatException, IOException { String configuration = IOUtils.toString(this.getClass().getResourceAsStream("/test/test-alias.ttl"), StandardCharsets.UTF_8); configuration = configuration.replace("9856", Integer.toString(this.sshd.TEST_SSH_SERVICE_PORT)); return Rio.parse(new StringReader(configuration), "", RDFFormat.TURTLE); } @Before @Override public void setUp() throws Exception { this.sshDir = this.tempDirectory.newFolder("podd-filerepository-manager-impl-test").toPath(); this.sshd = new SSHService(); this.sshd.startTestSSHServer(this.sshDir); super.setUp(); } protected void startRepositorySource() throws Exception { } protected void stopRepositorySource() throws Exception { if(this.sshd != null) { this.sshd.stopTestSSHServer(this.sshDir); } } /** * Test successful attach of a file reference in RDF/XML */ @Test public void testAttachFileReferenceRdfWithoutVerification() throws Exception { // prepare: add an artifact (with PURLs so where to attach FileRef is // known in advance) final InferredOWLOntologyID artifactID = this.loadTestArtifact(TestConstants.TEST_ARTIFACT_20130206, MediaType.APPLICATION_RDF_TURTLE); final ClientResource fileRefAttachClientResource = new ClientResource(this.getUrl(PoddWebConstants.PATH_ATTACH_DATA_REF)); try { fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_ARTIFACT_IDENTIFIER, artifactID .getOntologyIRI().toString()); fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_ARTIFACT_VERSION_IDENTIFIER, artifactID .getVersionIRI().toString()); // Query parameter Verification policy - NOT SUPPLIED - defaults to // false final Representation input = this.buildRepresentationFromResource(TestConstants.TEST_ARTIFACT_FRAGMENT_NEW_FILE_REF_OBJECT, MediaType.APPLICATION_RDF_XML); final Representation results = this.doTestAuthenticatedRequest(fileRefAttachClientResource, Method.POST, input, MediaType.APPLICATION_RDF_XML, Status.SUCCESS_OK, AbstractResourceImplTest.WITH_ADMIN); final Model ontologyIDModel = this.assertRdf(results, RDFFormat.RDFXML, 3); Assert.assertEquals(2, ontologyIDModel.subjects().size()); Assert.assertEquals(2, ontologyIDModel.predicates().size()); Assert.assertEquals(2, ontologyIDModel.objects().size()); Assert.assertEquals(1, ontologyIDModel.contexts().size()); final List<InferredOWLOntologyID> ontologyIDs = OntologyUtils.modelToOntologyIDs(ontologyIDModel); Assert.assertEquals(1, ontologyIDs.size()); final Model artifactModel = this.getArtifactAsModel(artifactID.getOntologyIRI().toString()); // DebugUtils.printContents(artifactModel); Assert.assertEquals(99, artifactModel.size()); Assert.assertEquals(20, artifactModel.subjects().size()); Assert.assertEquals(33, artifactModel.predicates().size()); Assert.assertEquals(75, artifactModel.objects().size()); Assert.assertEquals(1, artifactModel.contexts().size()); // final String body = this.getText(results); // verify: Inferred Ontology ID is received in RDF format // Assert.assertTrue("Response not in RDF format", // body.contains("<rdf:RDF")); // Assert.assertTrue("Artifact version has not been updated properly", // body.contains("artifact:1:version:2")); // Assert.assertTrue("Version IRI not in response", // body.contains("versionIRI")); // Assert.assertTrue("Inferred version not in response", // body.contains("inferredVersion")); // // // verify: new file reference has been added to the artifact // final String artifactBody = // Assert.assertTrue("New file ref not added to artifact", // artifactBody.contains("Rice tree scan 003454-98")); // Assert.assertTrue("New file ref not added to artifact", // artifactBody.contains("object-rice-scan-34343-a")); } finally { this.releaseClient(fileRefAttachClientResource); } } /** * Test successful attach of a file reference in RDF/XML with verification. This test starts up * an SSH server as a File Repository source and can be slow to complete. */ @Test public void testAttachFileReferenceRdfWithVerification() throws Exception { this.startRepositorySource(); try { // prepare: add an artifact (with PURLs so where to attach FileRef // is known in advance) final InferredOWLOntologyID artifactID = this.loadTestArtifact(TestConstants.TEST_ARTIFACT_20130206, MediaType.APPLICATION_RDF_TURTLE); // prepare: build test fragment with correct value set for // poddBase:hasPath final String fileReferenceAsString = this.buildFileReferenceString(TestConstants.TEST_ARTIFACT_FRAGMENT_NEW_FILE_REF_VERIFIABLE, RDFFormat.RDFXML, this.sshDir); Assert.assertFalse("Input DataReference could not be genereated", fileReferenceAsString.isEmpty()); final ClientResource fileRefAttachClientResource = new ClientResource(this.getUrl(PoddWebConstants.PATH_ATTACH_DATA_REF)); try { fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_ARTIFACT_IDENTIFIER, artifactID .getOntologyIRI().toString()); fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_ARTIFACT_VERSION_IDENTIFIER, artifactID.getVersionIRI().toString()); fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_VERIFICATION_POLICY, Boolean.toString(true)); final Representation input = new StringRepresentation(fileReferenceAsString, MediaType.APPLICATION_RDF_XML); final Representation results = this.doTestAuthenticatedRequest(fileRefAttachClientResource, Method.POST, input, MediaType.APPLICATION_RDF_XML, Status.SUCCESS_OK, AbstractResourceImplTest.WITH_ADMIN); final String body = this.getText(results); // verify: Inferred Ontology ID is received in RDF format Assert.assertTrue("Response not in RDF format", body.contains("<rdf:RDF")); Assert.assertTrue("Artifact version has not been updated properly", body.contains("artifact:1:version:2")); Assert.assertTrue("Version IRI not in response", body.contains("versionIRI")); Assert.assertFalse("Inferred version in response", body.contains("inferredVersion")); // verify: new file reference has been added to the artifact final String artifactBody = this.getArtifactAsString(artifactID.getOntologyIRI().toString(), MediaType.APPLICATION_RDF_XML); Assert.assertTrue("New file ref not added to artifact", artifactBody.contains("Rice tree scan 003454-98")); Assert.assertTrue("New file ref not added to artifact", artifactBody.contains("object-rice-scan-34343-a")); } finally { this.releaseClient(fileRefAttachClientResource); } } finally { this.stopRepositorySource(); } } /** * Test successful attach of a file reference in Turtle */ @Test public void testAttachFileReferenceTurtleWithoutVerification() throws Exception { // prepare: add an artifact (with PURLs so where to attach FileRef is // known in advance) final InferredOWLOntologyID artifactID = this.loadTestArtifact(TestConstants.TEST_ARTIFACT_20130206, MediaType.APPLICATION_RDF_TURTLE); final ClientResource fileRefAttachClientResource = new ClientResource(this.getUrl(PoddWebConstants.PATH_ATTACH_DATA_REF)); try { fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_ARTIFACT_IDENTIFIER, artifactID .getOntologyIRI().toString()); fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_ARTIFACT_VERSION_IDENTIFIER, artifactID .getVersionIRI().toString()); // Query parameter Verification policy - NOT SUPPLIED - defaults to // false final Representation input = this.buildRepresentationFromResource(TestConstants.TEST_ARTIFACT_FRAGMENT_NEW_FILE_REF_OBJECT_TTL, MediaType.APPLICATION_RDF_TURTLE); final Representation results = this.doTestAuthenticatedRequest(fileRefAttachClientResource, Method.POST, input, MediaType.APPLICATION_RDF_TURTLE, Status.SUCCESS_OK, AbstractResourceImplTest.WITH_ADMIN); final String body = this.getText(results); // verify: An updated Inferred Ontology ID is received Assert.assertTrue("Artifact version has not been updated properly", body.contains("artifact:1:version:2")); Assert.assertTrue("Version IRI not in response", body.contains("versionIRI")); Assert.assertFalse("Inferred version in response", body.contains("inferredVersion")); // verify: new file reference has been added to the artifact final String artifactBody = this.getArtifactAsString(artifactID.getOntologyIRI().toString(), MediaType.APPLICATION_RDF_XML); Assert.assertTrue("New file ref not added to artifact", artifactBody.contains("Rice tree scan 003454-98")); Assert.assertTrue("New file ref not added to artifact", artifactBody.contains("object-rice-scan-34343-a")); } finally { this.releaseClient(fileRefAttachClientResource); } } /** * Test successful attach of a file reference in Turtle with verification. This test starts up * an SSH server as a File Repository source and can be slow to complete. */ @Test public void testAttachFileReferenceTurtleWithVerification() throws Exception { this.startRepositorySource(); try { // prepare: add an artifact (with PURLs so where to attach FileRef // is known in advance) final InferredOWLOntologyID artifactID = this.loadTestArtifact(TestConstants.TEST_ARTIFACT_20130206, MediaType.APPLICATION_RDF_TURTLE); // prepare: build test fragment with correct value set for // poddBase:hasPath final String fileReferenceAsString = this.buildFileReferenceString(TestConstants.TEST_ARTIFACT_FRAGMENT_NEW_FILE_REF_VERIFIABLE_TTL, RDFFormat.TURTLE, this.sshDir); Assert.assertFalse("Input DataReference could not be genereated", fileReferenceAsString.isEmpty()); final ClientResource fileRefAttachClientResource = new ClientResource(this.getUrl(PoddWebConstants.PATH_ATTACH_DATA_REF)); try { fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_ARTIFACT_IDENTIFIER, artifactID .getOntologyIRI().toString()); fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_ARTIFACT_VERSION_IDENTIFIER, artifactID.getVersionIRI().toString()); fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_VERIFICATION_POLICY, Boolean.toString(true)); final Representation input = new StringRepresentation(fileReferenceAsString, MediaType.APPLICATION_RDF_TURTLE); final Representation results = this.doTestAuthenticatedRequest(fileRefAttachClientResource, Method.POST, input, MediaType.APPLICATION_RDF_TURTLE, Status.SUCCESS_OK, AbstractResourceImplTest.WITH_ADMIN); final String body = this.getText(results); // verify: Inferred Ontology ID is received in RDF format Assert.assertTrue("Artifact version has not been updated properly", body.contains("artifact:1:version:2")); Assert.assertTrue("Version IRI not in response", body.contains("versionIRI")); Assert.assertFalse("Inferred version in response", body.contains("inferredVersion")); // verify: new file reference has been added to the artifact final String artifactBody = this.getArtifactAsString(artifactID.getOntologyIRI().toString(), MediaType.APPLICATION_RDF_XML); Assert.assertTrue("New file ref not added to artifact", artifactBody.contains("Rice tree scan 003454-98")); Assert.assertTrue("New file ref not added to artifact", artifactBody.contains("object-rice-scan-34343-a")); } finally { this.releaseClient(fileRefAttachClientResource); } } finally { this.stopRepositorySource(); } } /** * Test attach a file reference which fails verification in RDF/XML. * * NOTE: This test causes Restlet to print a stack trace when the "502 Bad Gateway" Exception is * thrown. */ @Test public void testErrorAttachFileReferenceRdfFileVerificationFailure() throws Exception { // prepare: add an artifact (with PURLs so where to attach FileRef is // known in advance) final InferredOWLOntologyID artifactID = this.loadTestArtifact(TestConstants.TEST_ARTIFACT_20130206, MediaType.APPLICATION_RDF_TURTLE); final Representation input = this.buildRepresentationFromResource(TestConstants.TEST_ARTIFACT_FRAGMENT_NEW_FILE_REF_OBJECT, MediaType.APPLICATION_RDF_XML); final ClientResource fileRefAttachClientResource = new ClientResource(this.getUrl(PoddWebConstants.PATH_ATTACH_DATA_REF)); try { fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_ARTIFACT_IDENTIFIER, artifactID .getOntologyIRI().toString()); fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_ARTIFACT_VERSION_IDENTIFIER, artifactID .getVersionIRI().toString()); fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_VERIFICATION_POLICY, Boolean.toString(true)); this.doTestAuthenticatedRequest(fileRefAttachClientResource, Method.POST, input, MediaType.APPLICATION_RDF_XML, Status.CLIENT_ERROR_BAD_REQUEST, AbstractResourceImplTest.WITH_ADMIN); Assert.fail("Should have thrown a ResourceException"); } catch(final ResourceException e) { Assert.assertEquals(Status.SERVER_ERROR_BAD_GATEWAY, e.getStatus()); final Representation responseEntity = fileRefAttachClientResource.getResponseEntity(); Assert.assertTrue(this.getText(responseEntity).contains("File Reference validation resulted in failures")); } finally { this.releaseClient(fileRefAttachClientResource); } } @Test public void testErrorAttachFileReferenceRdfWithoutArtifactID() throws Exception { // prepare: dummy artifact details final String artifactUri = "urn:purl:dummy:artifact:uri:artifact:1"; this.buildRepresentationFromResource(TestConstants.TEST_ARTIFACT_FRAGMENT_NEW_FILE_REF_OBJECT, MediaType.APPLICATION_RDF_XML); final ClientResource fileRefAttachClientResource = new ClientResource(this.getUrl(PoddWebConstants.PATH_ATTACH_DATA_REF)); try { // Query parameter Artifact ID - NOT SUPPLIED fileRefAttachClientResource .addQueryParameter(PoddWebConstants.KEY_ARTIFACT_VERSION_IDENTIFIER, artifactUri); fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_VERIFICATION_POLICY, Boolean.toString(true)); // there is no need to authenticate, have a test artifact or send // RDF content as the // artifact ID is checked for first fileRefAttachClientResource.post(null, MediaType.TEXT_PLAIN); Assert.fail("Should have thrown a ResourceException"); } catch(final ResourceException e) { Assert.assertEquals(Status.CLIENT_ERROR_BAD_REQUEST, e.getStatus()); } finally { this.releaseClient(fileRefAttachClientResource); } } /** * Test attach a file reference without authentication */ @Test public void testErrorAttachFileReferenceRdfWithoutAuthentication() throws Exception { // prepare: dummy artifact details final String artifactUri = "urn:purl:dummy:artifact:uri:artifact:1"; final Representation input = this.buildRepresentationFromResource(TestConstants.TEST_ARTIFACT_FRAGMENT_NEW_FILE_REF_OBJECT, MediaType.APPLICATION_RDF_XML); final ClientResource fileRefAttachClientResource = new ClientResource(this.getUrl(PoddWebConstants.PATH_ATTACH_DATA_REF)); // invoke without authentication try { fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_ARTIFACT_IDENTIFIER, artifactUri); fileRefAttachClientResource .addQueryParameter(PoddWebConstants.KEY_ARTIFACT_VERSION_IDENTIFIER, artifactUri); fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_VERIFICATION_POLICY, Boolean.toString(true)); fileRefAttachClientResource.post(input, MediaType.APPLICATION_RDF_XML); Assert.fail("Should have thrown a ResourceException"); } catch(final ResourceException e) { Assert.assertEquals(Status.CLIENT_ERROR_UNAUTHORIZED, e.getStatus()); } finally { this.releaseClient(fileRefAttachClientResource); } } @Test public void testErrorAttachFileReferenceRdfWithoutVersionIRI() throws Exception { // prepare: dummy artifact details final String artifactUri = "urn:purl:dummy:artifact:uri:artifact:1"; final Representation input = this.buildRepresentationFromResource(TestConstants.TEST_ARTIFACT_FRAGMENT_NEW_FILE_REF_OBJECT, MediaType.APPLICATION_RDF_XML); final ClientResource fileRefAttachClientResource = new ClientResource(this.getUrl(PoddWebConstants.PATH_ATTACH_DATA_REF)); // authentication is required as version IRI is checked AFTER // authentication try { fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_ARTIFACT_IDENTIFIER, artifactUri); // Query parameter Version IRI - NOT SUPPLIED fileRefAttachClientResource.addQueryParameter(PoddWebConstants.KEY_VERIFICATION_POLICY, Boolean.toString(true)); this.doTestAuthenticatedRequest(fileRefAttachClientResource, Method.POST, input, MediaType.APPLICATION_RDF_XML, Status.CLIENT_ERROR_BAD_REQUEST, AbstractResourceImplTest.WITH_ADMIN); Assert.fail("Should have thrown a ResourceException"); } catch(final ResourceException e) { Assert.assertEquals(Status.CLIENT_ERROR_BAD_REQUEST, e.getStatus()); } finally { this.releaseClient(fileRefAttachClientResource); } } }