001/*
002 * Licensed to DuraSpace under one or more contributor license agreements.
003 * See the NOTICE file distributed with this work for additional information
004 * regarding copyright ownership.
005 *
006 * DuraSpace licenses this file to you under the Apache License,
007 * Version 2.0 (the "License"); you may not use this file except in
008 * compliance with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.fcrepo.http.commons.responses;
019
020import static java.util.stream.Stream.of;
021import static com.google.common.util.concurrent.Futures.addCallback;
022import static javax.ws.rs.core.MediaType.valueOf;
023import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDdateTime;
024import static org.apache.jena.graph.NodeFactory.createLiteral;
025import static org.apache.jena.graph.NodeFactory.createURI;
026import static org.apache.jena.graph.Triple.create;
027import static org.apache.jena.rdf.model.ModelFactory.createDefaultModel;
028import static org.apache.jena.rdf.model.ResourceFactory.createProperty;
029import static org.apache.jena.rdf.model.ResourceFactory.createResource;
030import static org.apache.jena.rdf.model.ResourceFactory.createTypedLiteral;
031import static org.fcrepo.http.commons.domain.RDFMediaType.TURTLE_TYPE;
032import static org.junit.Assert.assertTrue;
033import static org.mockito.Mockito.mock;
034import static org.slf4j.LoggerFactory.getLogger;
035
036import java.io.ByteArrayInputStream;
037import java.io.ByteArrayOutputStream;
038import java.io.IOException;
039import java.io.InputStream;
040import java.io.OutputStream;
041import java.util.HashMap;
042import java.util.Map;
043import java.util.stream.Stream;
044
045import javax.jcr.Session;
046import javax.ws.rs.WebApplicationException;
047import javax.ws.rs.core.MediaType;
048
049import com.google.common.util.concurrent.FutureCallback;
050import com.google.common.util.concurrent.MoreExecutors;
051
052import org.apache.jena.rdf.model.RDFNode;
053import org.apache.jena.riot.RiotException;
054import org.fcrepo.kernel.api.rdf.DefaultRdfStream;
055import org.fcrepo.kernel.api.RdfStream;
056import org.junit.Before;
057import org.junit.Test;
058import org.mockito.Mock;
059import org.mockito.stubbing.Answer;
060import org.slf4j.Logger;
061
062import org.apache.jena.graph.Node;
063import org.apache.jena.graph.Triple;
064import org.apache.jena.rdf.model.Model;
065
066import org.junit.runner.RunWith;
067import org.mockito.junit.MockitoJUnitRunner;
068
069/**
070 * <p>RdfStreamStreamingOutputTest class.</p>
071 *
072 * @author ajs6f
073 */
074@RunWith(MockitoJUnitRunner.class)
075public class RdfStreamStreamingOutputTest {
076
077    private RdfStreamStreamingOutput testRdfStreamStreamingOutput;
078
079    private static final Triple triple = create(createURI("info:testSubject"),
080            createURI("info:testPredicate"), createURI("info:testObject"));
081
082    @Mock
083    private Node mockNode;
084
085    private final RdfStream testRdfStream = new DefaultRdfStream(triple.getSubject(), of(triple));
086
087    private final Map<String, String> testNamespaces = new HashMap<>();
088
089    @Mock
090    private RdfStream mockRdfStream;
091
092    @Mock
093    private Session mockSession;
094
095    private final MediaType testMediaType = valueOf("application/rdf+xml");
096
097    private static final Logger LOGGER =
098            getLogger(RdfStreamStreamingOutputTest.class);
099
100    @Before
101    public void setUp() {
102        testRdfStreamStreamingOutput =
103            new RdfStreamStreamingOutput(testRdfStream, testNamespaces, testMediaType);
104    }
105
106    @Test
107    public void testWrite() throws IOException {
108        assertOutputContainsTriple(triple);
109    }
110
111    public void assertOutputContainsTriple(final Triple expected) throws IOException {
112        try (final RdfStream input = new DefaultRdfStream(expected.getSubject(), of(expected));
113                final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
114            new RdfStreamStreamingOutput(input, testNamespaces, testMediaType).write(output);
115            try ( final InputStream resultStream = new ByteArrayInputStream(output.toByteArray())) {
116                final Model result = createDefaultModel().read(resultStream, null);
117                assertTrue("Didn't find our test triple!", result.contains(result.asStatement(expected)));
118            }
119        }
120    }
121
122    @Test
123    public void testWriteWithNamespace() throws IOException {
124        final Map<String, String> namespaces = new HashMap<>();
125        namespaces.put("a", "info:");
126        try (final RdfStream input = new DefaultRdfStream(triple.getSubject(), of(triple));
127                final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
128            new RdfStreamStreamingOutput(input, namespaces, TURTLE_TYPE).write(output);
129            final String s = output.toString("UTF-8");
130            assertTrue(s.replaceAll("\\s+", " ").contains("@prefix a: <info:>"));
131        }
132    }
133
134    @Test
135    public void testWriteWithTypedObject() throws IOException {
136        assertOutputContainsTriple(create(createURI("info:testSubject"),
137                createURI("info:testPredicate"),
138                createTypedLiteral(0).asNode()));
139    }
140
141    @Test
142    public void testWriteWithBlankSubject() throws IOException {
143        try (final RdfStream input = new DefaultRdfStream(createResource().asNode(), of(create(createResource()
144                .asNode(), createURI("info:testPredicate"), createTypedLiteral(0).asNode())));
145                final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
146            new RdfStreamStreamingOutput(input, testNamespaces, testMediaType).write(output);
147            try (final InputStream resultStream = new ByteArrayInputStream(output.toByteArray())) {
148                final Model result = createDefaultModel().read(resultStream, null);
149                assertTrue(result.contains(null, createProperty("info:testPredicate"), createTypedLiteral(0)));
150            }
151        }
152    }
153
154
155    @Test
156    public void testWriteWithBlankObject() throws IOException {
157        final Stream<Triple> triples =
158                of(create(createResource().asNode(), createURI("info:testPredicate"), createResource().asNode()));
159        try (final RdfStream input = new DefaultRdfStream(createResource().asNode(), triples);
160                final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
161            new RdfStreamStreamingOutput(input, testNamespaces, testMediaType).write(output);
162            try (final InputStream resultStream = new ByteArrayInputStream(output.toByteArray())) {
163                final Model result = createDefaultModel().read(resultStream, null);
164                assertTrue(result.contains(null, createProperty("info:testPredicate"), (RDFNode) null));
165            }
166        }
167    }
168
169    @Test
170    public void testWriteWithDatetimeObject() throws IOException {
171        assertOutputContainsTriple(create(createURI("info:testSubject"),
172                createURI("info:testPredicate"), createLiteral("2014-01-01T01:02:03Z", XSDdateTime)));
173    }
174
175    @Test
176    public void testWriteWithLanguageLiteral() throws IOException {
177        assertOutputContainsTriple(create(createURI("info:testSubject"),
178                createURI("info:testPredicate"),
179                createLiteral("french string", "fr")));
180    }
181
182    @Test(expected = WebApplicationException.class)
183    public void testWriteWithException() throws IOException {
184
185        final FutureCallback<Void> callback = new FutureCallback<Void>() {
186
187            @Override
188            public void onSuccess(final Void v) {
189                throw new AssertionError("Should never happen!");
190            }
191
192            @Override
193            public void onFailure(final Throwable e) {
194                LOGGER.debug("Got exception:", e.getMessage());
195                assertTrue("Got wrong kind of exception!", e instanceof RiotException);
196            }
197        };
198        addCallback(testRdfStreamStreamingOutput, callback, MoreExecutors.directExecutor());
199        try (final OutputStream mockOutputStream = mock(OutputStream.class, (Answer<Object>) invocation -> {
200            throw new RiotException("Expected.");
201        })) {
202            testRdfStreamStreamingOutput.write(mockOutputStream);
203        }
204    }
205}