| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.tinkerpop.gremlin.process.traversal.dsl.graph; |
| |
| import org.apache.commons.configuration2.MapConfiguration; |
| import org.apache.tinkerpop.gremlin.process.remote.RemoteConnection; |
| import org.apache.tinkerpop.gremlin.process.traversal.Traversal; |
| import org.apache.tinkerpop.gremlin.process.traversal.Order; |
| import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy; |
| import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy; |
| import org.apache.tinkerpop.gremlin.structure.T; |
| import org.apache.tinkerpop.gremlin.structure.Graph; |
| import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph; |
| import org.apache.tinkerpop.gremlin.util.tools.CollectionFactory; |
| import org.junit.Test; |
| import org.mockito.invocation.InvocationOnMock; |
| import org.mockito.stubbing.Answer; |
| |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| |
| import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal; |
| import static org.hamcrest.MatcherAssert.assertThat; |
| import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertTrue; |
| import static org.mockito.Answers.RETURNS_DEEP_STUBS; |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| /** |
| * @author Stephen Mallette (http://stephen.genoprime.com) |
| */ |
| public class GraphTraversalSourceTest { |
| |
| private static final GraphTraversalSource g = traversal().withEmbedded(EmptyGraph.instance()); |
| |
| @Test |
| public void shouldCloseRemoteConnectionOnWithRemote() throws Exception { |
| final RemoteConnection mock = mock(RemoteConnection.class); |
| final GraphTraversalSource g = traversal().withRemote(mock); |
| g.close(); |
| |
| verify(mock, times(1)).close(); |
| } |
| |
| @Test |
| public void shouldSupportMapBasedStrategies() throws Exception { |
| GraphTraversalSource g = EmptyGraph.instance().traversal(); |
| assertFalse(g.getStrategies().getStrategy(SubgraphStrategy.class).isPresent()); |
| g = g.withStrategies(SubgraphStrategy.create(new MapConfiguration(new HashMap<String, Object>() {{ |
| put("vertices", __.hasLabel("person")); |
| put("vertexProperties", __.limit(0)); |
| put("edges", __.hasLabel("knows")); |
| }}))); |
| assertTrue(g.getStrategies().getStrategy(SubgraphStrategy.class).isPresent()); |
| g = g.withoutStrategies(SubgraphStrategy.class); |
| assertFalse(g.getStrategies().getStrategy(SubgraphStrategy.class).isPresent()); |
| // |
| assertFalse(g.getStrategies().getStrategy(ReadOnlyStrategy.class).isPresent()); |
| g = g.withStrategies(ReadOnlyStrategy.instance()); |
| assertTrue(g.getStrategies().getStrategy(ReadOnlyStrategy.class).isPresent()); |
| g = g.withoutStrategies(ReadOnlyStrategy.class); |
| assertFalse(g.getStrategies().getStrategy(ReadOnlyStrategy.class).isPresent()); |
| } |
| |
| @Test(expected = IllegalArgumentException.class) |
| public void shouldFailAddVWithNullVertexLabel() { |
| g.addV((String) null); |
| } |
| |
| @Test(expected = IllegalArgumentException.class) |
| public void shouldFailAddVWithNullVertexLabelTraversal() { |
| g.addV((Traversal<?, String>) null); |
| } |
| |
| @Test(expected = IllegalArgumentException.class) |
| public void shouldFailMergeEForBadInput() { |
| g.mergeE(CollectionFactory.asMap(T.value, "nope")); |
| } |
| |
| @Test(expected = IllegalArgumentException.class) |
| public void shouldFailMergeVForBadInput() { |
| g.mergeV(CollectionFactory.asMap(T.value, "nope")); |
| } |
| |
| /** |
| * This test demonstrates how one method for using Mockito to mock {@code g} to help with unit testing |
| * applications where the logic of the query itself does not need to be validated, but rather surrounding |
| * logic of the application. It is really important to note that all internal processing of the query is |
| * ignored under this model and no TinkerPop code is executed so even invalid queries that are incorrect |
| * will return the static data encoded to the mock. |
| */ |
| @Test |
| public void shouldDemonstrateMocking() { |
| // intercept the terminating step of toList() and return whatever results needed for the nature of |
| // the test. you could just have easily mocked next() or other terminators. they key to this model lies |
| // in all other methods simply returning the mock itself so that the chaining aspect of hooking steps |
| // together continues to return the mock GraphTraversal. be sure to mock whatever terminator that you |
| // intend to use or it will fall into that default path and there will likely be an error or other |
| // unexpected behavior |
| final GraphTraversal<?,?> mockTraversal = mock(GraphTraversal.class, invocation -> { |
| if (invocation.getMethod().getName().equals("toList")) |
| return Arrays.asList("peter", "lop", "josh"); |
| else |
| return invocation.getMock(); |
| }); |
| |
| // the mock of GraphTraversalSource is basically "g". Your test would want to inject this "g" to your |
| // application logic to use rather than a real one that is probably produced by traversal().withRemote(...). |
| // the mock returns either itself or the mock of GraphTraversal depending on the return type of the method |
| // called. all other calls go to the real method. that could result in errors but if you're just testing the |
| // Gremlin language (and that's the use case here) this configuration should be sufficient. |
| // |
| // as an aside, it may be tempting to try to mock Graph so that it can be given directly to |
| // traversal().withEmbedded(...) but that turns out to be hard based on how the construction happens in there |
| // with reflection that seems to bypass the mock attempts. this approach seems simpler but just requires your |
| // application logic to take the mock "g" somehow. |
| final GraphTraversalSource mockG = mock(GraphTraversalSource.class, invocation -> { |
| final Class<?> returnType = invocation.getMethod().getReturnType(); |
| if (returnType.isAssignableFrom(GraphTraversalSource.class)) { |
| return invocation.getMock(); |
| } else if (returnType.isAssignableFrom(GraphTraversal.class)) { |
| return mockTraversal; |
| } else { |
| return invocation.callRealMethod(); |
| } |
| }); |
| |
| final GraphTraversalSource g = mockG; // traversal().withRemote(...) |
| final List<Object> l = g.V().values("name").order().by(Order.shuffle).limit(3).toList(); |
| assertThat(l, containsInAnyOrder("peter", "lop", "josh")); |
| } |
| } |