org.hawkular.inventory.impl.tinkerpop.test.BasicTest.java Source code

Java tutorial

Introduction

Here is the source code for org.hawkular.inventory.impl.tinkerpop.test.BasicTest.java

Source

/*
 * Copyright 2015 Red Hat, Inc. and/or its affiliates
 * and other contributors as indicated by the @author tags.
 *
 * Licensed 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.hawkular.inventory.impl.tinkerpop.test;

import com.tinkerpop.blueprints.Graph;
import com.tinkerpop.blueprints.GraphQuery;
import com.tinkerpop.blueprints.TransactionalGraph;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.impls.tg.TinkerGraph;
import com.tinkerpop.blueprints.util.wrappers.wrapped.WrappedGraph;
import com.tinkerpop.gremlin.java.GremlinPipeline;
import org.hawkular.inventory.api.Configuration;
import org.hawkular.inventory.api.EntityNotFoundException;
import org.hawkular.inventory.api.Feeds;
import org.hawkular.inventory.api.Metrics;
import org.hawkular.inventory.api.RelationNotFoundException;
import org.hawkular.inventory.api.Relationships;
import org.hawkular.inventory.api.ResolvableToMany;
import org.hawkular.inventory.api.ResolvableToSingle;
import org.hawkular.inventory.api.feeds.AcceptWithFallbackFeedIdStrategy;
import org.hawkular.inventory.api.feeds.RandomUUIDFeedIdStrategy;
import org.hawkular.inventory.api.filters.Defined;
import org.hawkular.inventory.api.filters.Filter;
import org.hawkular.inventory.api.filters.Related;
import org.hawkular.inventory.api.filters.RelationWith;
import org.hawkular.inventory.api.filters.With;
import org.hawkular.inventory.api.model.Entity;
import org.hawkular.inventory.api.model.Environment;
import org.hawkular.inventory.api.model.Feed;
import org.hawkular.inventory.api.model.Metric;
import org.hawkular.inventory.api.model.MetricType;
import org.hawkular.inventory.api.model.MetricUnit;
import org.hawkular.inventory.api.model.Relationship;
import org.hawkular.inventory.api.model.Resource;
import org.hawkular.inventory.api.model.ResourceType;
import org.hawkular.inventory.api.model.Tenant;
import org.hawkular.inventory.api.paging.Order;
import org.hawkular.inventory.api.paging.Page;
import org.hawkular.inventory.api.paging.Pager;
import org.hawkular.inventory.impl.tinkerpop.InventoryService;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import static org.hawkular.inventory.api.Relationships.WellKnown.contains;
import static org.hawkular.inventory.api.Relationships.WellKnown.owns;

/**
 * Test some basic functionality
 *
 * @author Lukas Krejci
 * @author Jirka Kremser
 */
public class BasicTest {

    TransactionalGraph graph;
    InventoryService inventory;

    @Before
    public void setup() throws Exception {
        Properties ps = new Properties();
        try (FileInputStream f = new FileInputStream(System.getProperty("graph.config"))) {
            ps.load(f);
        }

        Configuration config = Configuration.builder()
                .withFeedIdStrategy(new AcceptWithFallbackFeedIdStrategy(new RandomUUIDFeedIdStrategy()))
                .withConfiguration(ps).build();

        inventory = new InventoryService();
        inventory.initialize(config);

        graph = inventory.getGraph();

        try {
            inventory.tenants().delete("com.acme.tenant");
        } catch (Exception ignored) {
        }

        try {
            inventory.tenants().delete("com.example.tenant");
        } catch (Exception ignored) {
        }

        try {
            inventory.tenants().delete("perf0");
        } catch (Exception ignored) {
        }

        setupData();
    }

    private void setupData() throws Exception {
        //noinspection AssertWithSideEffects
        assert inventory.tenants()
                .create(Tenant.Blueprint.builder().withId("com.acme.tenant").withProperty("kachny", "moc").build())
                .entity().getId().equals("com.acme.tenant");
        assert inventory.tenants().get("com.acme.tenant").environments()
                .create(new Environment.Blueprint("production")).entity().getId().equals("production");
        assert inventory.tenants().get("com.acme.tenant").resourceTypes()
                .create(new ResourceType.Blueprint("URL", "1.0")).entity().getId().equals("URL");
        assert inventory.tenants().get("com.acme.tenant").metricTypes()
                .create(new MetricType.Blueprint("ResponseTime", MetricUnit.MILLI_SECOND)).entity().getId()
                .equals("ResponseTime");

        inventory.tenants().get("com.acme.tenant").resourceTypes().get("URL").metricTypes()
                .associate("ResponseTime");

        assert inventory.tenants().get("com.acme.tenant").environments().get("production").feedlessMetrics()
                .create(new Metric.Blueprint("ResponseTime", "host1_ping_response")).entity().getId()
                .equals("host1_ping_response");
        assert inventory.tenants().get("com.acme.tenant").environments().get("production").feedlessResources()
                .create(new Resource.Blueprint("host1", "URL")).entity().getId().equals("host1");
        inventory.tenants().get("com.acme.tenant").environments().get("production").feedlessResources().get("host1")
                .metrics().associate("host1_ping_response");

        assert inventory.tenants().get("com.acme.tenant").environments().get("production").feeds()
                .create(new Feed.Blueprint("feed1", null)).entity().getId().equals("feed1");

        assert inventory.tenants().get("com.acme.tenant").environments().get("production").feeds().get("feed1")
                .resources().create(new Resource.Blueprint("feedResource1", "URL")).entity().getId()
                .equals("feedResource1");

        assert inventory.tenants().get("com.acme.tenant").environments().get("production").feeds().get("feed1")
                .resources().create(new Resource.Blueprint("feedResource2", "URL")).entity().getId()
                .equals("feedResource2");

        assert inventory.tenants().get("com.acme.tenant").environments().get("production").feeds().get("feed1")
                .resources().create(new Resource.Blueprint("feedResource3", "URL")).entity().getId()
                .equals("feedResource3");

        assert inventory.tenants().get("com.acme.tenant").environments().get("production").feeds().get("feed1")
                .metrics().create(new Metric.Blueprint("ResponseTime", "feedMetric1")).entity().getId()
                .equals("feedMetric1");

        assert inventory.tenants().create(new Tenant.Blueprint("com.example.tenant")).entity().getId()
                .equals("com.example.tenant");
        assert inventory.tenants().get("com.example.tenant").environments()
                .create(new Environment.Blueprint("test")).entity().getId().equals("test");
        assert inventory.tenants().get("com.example.tenant").resourceTypes()
                .create(new ResourceType.Blueprint("Kachna", "1.0")).entity().getId().equals("Kachna");
        assert inventory.tenants().get("com.example.tenant").resourceTypes()
                .create(new ResourceType.Blueprint("Playroom", "1.0")).entity().getId().equals("Playroom");
        assert inventory.tenants().get("com.example.tenant").metricTypes()
                .create(new MetricType.Blueprint("Size", MetricUnit.BYTE)).entity().getId().equals("Size");
        inventory.tenants().get("com.example.tenant").resourceTypes().get("Playroom").metricTypes()
                .associate("Size");

        assert inventory.tenants().get("com.example.tenant").environments().get("test").feedlessMetrics()
                .create(new Metric.Blueprint("Size", "playroom1_size")).entity().getId().equals("playroom1_size");
        assert inventory.tenants().get("com.example.tenant").environments().get("test").feedlessMetrics()
                .create(new Metric.Blueprint("Size", "playroom2_size")).entity().getId().equals("playroom2_size");
        assert inventory.tenants().get("com.example.tenant").environments().get("test").feedlessResources()
                .create(new Resource.Blueprint("playroom1", "Playroom")).entity().getId().equals("playroom1");
        assert inventory.tenants().get("com.example.tenant").environments().get("test").feedlessResources()
                .create(new Resource.Blueprint("playroom2", "Playroom")).entity().getId().equals("playroom2");

        inventory.tenants().get("com.example.tenant").environments().get("test").feedlessResources()
                .get("playroom1").metrics().associate("playroom1_size");
        inventory.tenants().get("com.example.tenant").environments().get("test").feedlessResources()
                .get("playroom2").metrics().associate("playroom2_size");

        // some ad-hoc relationships
        Environment test = inventory.tenants().get("com.example.tenant").environments().get("test").entity();
        inventory.tenants().get("com.example.tenant").environments().get("test").feedlessResources()
                .get("playroom2").metrics().get("playroom2_size").relationships(Relationships.Direction.outgoing)
                .linkWith("yourMom", test, null);
        inventory.tenants().get("com.example.tenant").environments().get("test").feedlessResources()
                .get("playroom2").metrics().get("playroom2_size").relationships(Relationships.Direction.incoming)
                .linkWith("IamYourFather", test, null);
    }

    private void teardownData() throws Exception {
        Tenant t = new Tenant("com.example.tenant");
        Environment e = new Environment(t.getId(), "test");
        MetricType sizeType = new MetricType(t.getId(), "Size");
        ResourceType playRoomType = new ResourceType(t.getId(), "Playroom", "1.0");
        ResourceType kachnaType = new ResourceType(t.getId(), "Kachna", "1.0");
        Resource playroom1 = new Resource(t.getId(), e.getId(), null, "playroom1", playRoomType);
        Resource playroom2 = new Resource(t.getId(), e.getId(), null, "playroom2", playRoomType);
        Metric playroom1Size = new Metric(t.getId(), e.getId(), null, "playroom1_size", sizeType);
        Metric playroom2Size = new Metric(t.getId(), e.getId(), null, "playroom2_size", sizeType);

        inventory.inspect(e).feedlessMetrics().delete(playroom2Size.getId());
        assertDoesNotExist(playroom2Size);
        assertExists(t, e, sizeType, playRoomType, kachnaType, playroom1, playroom2, playroom1Size);

        inventory.inspect(t).resourceTypes().delete(kachnaType.getId());
        assertDoesNotExist(kachnaType);
        assertExists(t, e, sizeType, playRoomType, playroom1, playroom2, playroom1Size);

        try {
            inventory.inspect(t).metricTypes().delete(sizeType.getId());
            Assert.fail("Deleting a metric type which references some metrics should not be possible.");
        } catch (IllegalArgumentException ignored) {
            //good
        }

        inventory.inspect(e).feedlessMetrics().delete(playroom1Size.getId());
        assertDoesNotExist(playroom1Size);
        assertExists(t, e, sizeType, playRoomType, playroom1, playroom2);

        inventory.inspect(t).metricTypes().delete(sizeType.getId());
        assertDoesNotExist(sizeType);
        assertExists(t, e, playRoomType, playroom1, playroom2);

        try {
            inventory.inspect(t).resourceTypes().delete(playRoomType.getId());
            Assert.fail("Deleting a resource type which references some resources should not be possible.");
        } catch (IllegalArgumentException ignored) {
            //good
        }

        inventory.inspect(e).feedlessResources().delete(playroom1.getId());
        assertDoesNotExist(playroom1);
        assertExists(t, e, sizeType, playRoomType, playroom2);

        inventory.tenants().delete(t.getId());
        assertDoesNotExist(t);
        assertDoesNotExist(e);
        assertDoesNotExist(playRoomType);
        assertDoesNotExist(playroom2);
    }

    private void assertDoesNotExist(Entity e) {
        try {
            inventory.inspect(e, ResolvableToSingle.class).entity();
            Assert.fail(e + " should have been deleted");
        } catch (EntityNotFoundException ignored) {
            //good
        }
    }

    private void assertExists(Entity e) {
        try {
            inventory.inspect(e, ResolvableToSingle.class);
        } catch (EntityNotFoundException ignored) {
            Assert.fail(e + " should have been present in the inventory.");
        }
    }

    private void assertExists(Entity... es) {
        Stream.of(es).forEach(this::assertExists);
    }

    @After
    public void teardown() throws Exception {
        try {
            teardownData();
        } finally {
            inventory.close();
            deleteGraph();
        }
    }

    private static void deleteGraph() throws Exception {
        Path path = Paths.get("./", "__tinker.graph");

        if (!path.toFile().exists()) {
            return;
        }

        Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                Files.delete(file);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                Files.delete(dir);
                return FileVisitResult.CONTINUE;
            }
        });
    }

    @Test
    public void testTenants() throws Exception {
        Function<String, Void> test = (id) -> {
            GraphQuery q = graph.query().has("__type", "tenant").has("__eid", id);

            Iterator<Vertex> evs = q.vertices().iterator();
            assert evs.hasNext();
            Vertex ev = evs.next();
            assert !evs.hasNext();

            Tenant t = inventory.tenants().get(id).entity();

            assert ev.getProperty("__eid").equals(id);
            assert t.getId().equals(id);
            return null;
        };

        test.apply("com.acme.tenant");
        test.apply("com.example.tenant");

        GraphQuery query = graph.query().has("__type", "tenant");
        assert StreamSupport.stream(query.vertices().spliterator(), false).count() == 2;
    }

    @Test
    public void testEntitiesByRelationships() throws Exception {
        Function<Integer, Function<String, Function<String, Function<Integer, Function<String, Function<ResolvableToMany<?>, Consumer<ResolvableToMany<?>>>>>>>> testHelper = (numberOfParents -> parentType -> edgeLabel -> numberOfKids -> childType -> multipleParents -> multipleChildren -> {
            GremlinPipeline<Graph, Vertex> q1 = new GremlinPipeline<Graph, Vertex>(graph).V()
                    .has("__type", parentType).cast(Vertex.class);
            Iterator<Vertex> parentIterator = q1.iterator();

            GremlinPipeline<Graph, Vertex> q2 = new GremlinPipeline<Graph, Vertex>(graph).V()
                    .has("__type", parentType).out(edgeLabel).has("__type", childType).cast(Vertex.class);
            Iterator<Vertex> childIterator = q2.iterator();

            Iterator<?> multipleParentIterator = multipleParents.entities().iterator();
            Iterator<?> multipleChildrenIterator = multipleChildren.entities().iterator();

            for (int i = 0; i < numberOfParents; i++) {
                assert parentIterator.hasNext() : "There must be exactly " + numberOfParents + " " + parentType
                        + "s " + "that have outgoing edge labeled with " + edgeLabel + ". Gremlin "
                        + "query returned only " + i;
                assert multipleParentIterator.hasNext() : "There must be exactly " + numberOfParents + " "
                        + parentType + "s that have outgoing edge labeled with " + edgeLabel + ". Tested API "
                        + "returned only " + i;
                parentIterator.next();
                multipleParentIterator.next();
            }
            assert !parentIterator.hasNext() : "There must be " + numberOfParents + " " + parentType
                    + "s. Gremlin query returned more than " + numberOfParents;
            assert !multipleParentIterator.hasNext() : "There must be " + numberOfParents + " " + parentType
                    + "s. Tested API returned more than " + numberOfParents;

            for (int i = 0; i < numberOfKids; i++) {
                assert childIterator.hasNext() : "There must be exactly " + numberOfKids + " " + childType
                        + "s that are directly under " + parentType + " connected with " + edgeLabel
                        + ". Gremlin query returned only " + i;
                assert multipleChildrenIterator.hasNext();
                childIterator.next();
                multipleChildrenIterator.next();
            }
            assert !childIterator.hasNext() : "There must be exactly " + numberOfKids + " " + childType + "s";
            assert !multipleChildrenIterator.hasNext();
        });

        ResolvableToMany parents = inventory.tenants().getAll(Related.by("contains"));
        ResolvableToMany kids = inventory.tenants().getAll().environments().getAll(Related.asTargetBy("contains"));
        testHelper.apply(2).apply("tenant").apply("contains").apply(2).apply("environment").apply(parents)
                .accept(kids);

        kids = inventory.tenants().getAll().resourceTypes().getAll(Related.asTargetBy("contains"));
        testHelper.apply(2).apply("tenant").apply("contains").apply(3).apply("resourceType").apply(parents)
                .accept(kids);

        kids = inventory.tenants().getAll().metricTypes().getAll(Related.asTargetBy("contains"));
        testHelper.apply(2).apply("tenant").apply("contains").apply(2).apply("metricType").apply(parents)
                .accept(kids);

        parents = inventory.tenants().getAll().environments().getAll(Related.by("contains"));
        kids = inventory.tenants().getAll().environments().getAll().feedlessMetrics()
                .getAll(Related.asTargetBy("contains"));
        testHelper.apply(2).apply("environment").apply("contains").apply(3).apply("metric").apply(parents)
                .accept(kids);

        kids = inventory.tenants().getAll().environments().getAll().feedlessResources()
                .getAll(Related.asTargetBy("contains"));
        testHelper.apply(2).apply("environment").apply("contains").apply(3).apply("resource").apply(parents)
                .accept(kids);

        parents = inventory.tenants().getAll().environments().getAll(Related.by("contains"));
        kids = inventory.tenants().getAll().environments().getAll().allMetrics()
                .getAll(Related.asTargetBy("defines"));
        testHelper.apply(2).apply("metricType").apply("defines").apply(4).apply("metric").apply(parents)
                .accept(kids);
    }

    @Test
    public void testRelationshipServiceNamed1() throws Exception {
        Set<Relationship> contains = inventory.tenants().getAll().relationships().named("contains").entities();
        assert contains.stream().anyMatch(rel -> "com.acme.tenant".equals(rel.getSource().getId()) && "URL"
                .equals(rel.getTarget().getId())) : "Tenant 'com.acme.tenant' must contain ResourceType 'URL'.";
        assert contains.stream()
                .anyMatch(rel -> "com.acme.tenant".equals(rel.getSource().getId()) && "production".equals(rel
                        .getTarget().getId())) : "Tenant 'com.acme.tenant' must contain Environment 'production'.";
        assert contains.stream().anyMatch(rel -> "com.example.tenant".equals(rel.getSource().getId()) && "Size"
                .equals(rel.getTarget().getId())) : "Tenant 'com.example.tenant' must contain MetricType 'Size'.";

        contains.forEach((r) -> {
            assert r.getId() != null;
        });
    }

    @Test
    public void testRelationshipServiceNamed2() throws Exception {
        Set<Relationship> contains = inventory.tenants().get("com.example.tenant").environments().get("test")
                .relationships().named("contains").entities();
        assert contains.stream().anyMatch(rel -> "playroom1"
                .equals(rel.getTarget().getId())) : "Environment 'test' must contain 'playroom1'.";
        assert contains.stream().anyMatch(rel -> "playroom2"
                .equals(rel.getTarget().getId())) : "Environment 'test' must contain 'playroom2'.";
        assert contains.stream().anyMatch(rel -> "playroom2_size"
                .equals(rel.getTarget().getId())) : "Environment 'test' must contain 'playroom2_size'.";
        assert contains.stream().anyMatch(rel -> "playroom1_size"
                .equals(rel.getTarget().getId())) : "Environment 'test' must contain 'playroom1_size'.";
        assert contains.stream().allMatch(rel -> !"production".equals(
                rel.getSource().getId())) : "Environment 'production' cant be the source of these relationships.";

        contains.forEach((r) -> {
            assert r.getId() != null;
        });
    }

    @Test
    public void testRelationshipServiceLinkedWith() throws Exception {
        Set<Relationship> rels = inventory.tenants().get("com.example.tenant").environments().get("test")
                .feedlessResources().get("playroom2").metrics().get("playroom2_size")
                .relationships(Relationships.Direction.outgoing).named("yourMom").entities();
        assert rels != null && rels.size() == 1 : "There should be 1 relationship conforming the filters";
        assert "test"
                .equals(rels.iterator().next().getTarget().getId()) : "Target of relationship 'yourMom' should "
                        + "be the 'test' environment";

        rels = inventory.tenants().get("com.example.tenant").environments().get("test").feedlessResources()
                .get("playroom2").metrics().get("playroom2_size").relationships(Relationships.Direction.both)
                .named("IamYourFather").entities();
        assert rels != null && rels.size() == 1 : "There should be 1 relationship conforming the filters";
        assert "test".equals(rels.iterator().next().getSource().getId()) : "Source of relationship 'IamYourFather' "
                + "should be the 'test' environment";
    }

    @Test
    public void testRelationshipServiceLinkedWithAndDelete() throws Exception {
        Tenant tenant = inventory.tenants().get("com.example.tenant").entity();
        Relationship link = inventory.tenants().get("com.acme.tenant").environments().get("production")
                .feedlessResources().get("host1").relationships(Relationships.Direction.incoming)
                .linkWith("crossTenantLink", tenant, null).entity();

        assert inventory.tenants().get("com.example.tenant").relationships(Relationships.Direction.outgoing)
                .named("crossTenantLink").entities().size() == 1 : "Relation 'crossTenantLink' was not found.";
        // delete the relationship
        inventory.tenants().get("com.example.tenant").relationships(/*defaults to outgoing*/).delete(link.getId());
        assert inventory.tenants().get("com.example.tenant").relationships().named("crossTenantLink").entities()
                .size() == 0 : "Relation 'crossTenantLink' was found.";

        // try deleting again
        try {
            inventory.tenants().get("com.example.tenant").relationships(/*defaults to outgoing*/)
                    .delete(link.getId());
            assert true : "It shouldn't be possible to delete the same relationship twice";
        } catch (RelationNotFoundException e) {
            // good
        }
    }

    @Test
    public void testRelationshipServiceUpdateRelationship1() throws Exception {
        final String someKey = "k3y";
        final String someValue = "v4lu3";
        Relationship rel1 = inventory.tenants().get("com.example.tenant").environments().get("test")
                .feedlessResources().get("playroom2").metrics().get("playroom2_size")
                .relationships(Relationships.Direction.outgoing).named("yourMom").entities().iterator().next();
        assert null == rel1.getProperties().get(someKey) : "There should not be any property with key 'k3y'";

        Relationship rel2 = inventory.tenants().get("com.example.tenant").environments().get("test")
                .feedlessResources().get("playroom2").metrics().get("playroom2_size")
                .relationships(Relationships.Direction.outgoing).named("yourMom").entities().iterator().next();
        assert rel1.getId().equals(rel2.getId())
                && null == rel2.getProperties().get(someKey) : "There should not be"
                        + " any property with key 'k3y'";

        // persist the change
        inventory.tenants().get("com.example.tenant").environments().get("test").feedlessResources()
                .get("playroom2").metrics().get("playroom2_size").relationships(Relationships.Direction.outgoing)
                .update(rel1.getId(), Relationship.Update.builder().withProperty(someKey, someValue).build());

        Relationship rel3 = inventory.tenants().get("com.example.tenant").environments().get("test")
                .feedlessResources().get("playroom2").metrics().get("playroom2_size")
                .relationships(Relationships.Direction.outgoing).named("yourMom").entities().iterator().next();
        assert rel1.getId().equals(rel3.getId()) && someValue.equals(rel3.getProperties()
                .get(someKey)) : "There should be the property with key 'k3y' and value 'v4lu3'";
    }

    @Test
    public void testRelationshipServiceGetAllFilters() throws Exception {
        Set<Relationship> rels = inventory.tenants().get("com.example.tenant").environments().get("test")
                .relationships(Relationships.Direction.outgoing).getAll(RelationWith.name("contains")).entities();
        assert rels != null && rels.size() == 4 : "There should be 4 relationships conforming the filters";
        assert rels.stream().anyMatch(rel -> "playroom2_size".equals(rel.getTarget().getId()));
        assert rels.stream().anyMatch(rel -> "playroom1".equals(rel.getTarget().getId()));

        rels = inventory.tenants().get("com.example.tenant").environments().get("test")
                .relationships(Relationships.Direction.outgoing)
                .getAll(RelationWith.name("contains"), RelationWith.targetOfType(Metric.class)).entities();
        assert rels != null && rels.size() == 2 : "There should be 2 relationships conforming the filters";
        assert rels.stream()
                .allMatch(rel -> Metric.class.equals(rel.getTarget().getClass())) : "The type of all the "
                        + "targets should be the 'Metric'";

        rels = inventory.tenants().get("com.example.tenant").environments().get("test")
                .relationships(Relationships.Direction.incoming).getAll(RelationWith.name("contains")).entities();

        assert rels != null && rels.size() == 1 : "There should be just 1 relationship conforming the filters";
        assert "com.example.tenant".equals(rels.iterator().next().getSource().getId()) : "Tenant 'com.example"
                + ".tenant' was not found";

        rels = inventory.tenants().getAll().relationships().named(contains).environments().getAll().relationships()
                .getAll(RelationWith.properties("label", "contains"),
                        RelationWith.targetsOfTypes(Resource.class, Metric.class))
                .entities();
        assert rels != null && rels.size() == 6 : "There should be 6 relationships conforming the filters";
        assert rels.stream().allMatch(rel -> "test".equals(rel.getSource().getId())
                || "production".equals(rel.getSource().getId())) : "Source should be either 'test' or 'production'";
        assert rels.stream()
                .allMatch(rel -> Resource.class.equals(rel.getTarget().getClass()) || Metric.class
                        .equals(rel.getTarget().getClass())) : "Target should be either a metric or a "
                                + "resource";
    }

    @Test
    public void testRelationshipServiceGetAllFiltersWithSubsequentCalls() throws Exception {
        Metric metric = inventory.tenants().getAll().relationships().named(contains).environments().getAll()
                .relationships()
                .getAll(RelationWith.properties("label", "contains"),
                        RelationWith.targetsOfTypes(Resource.class, Metric.class))
                .metrics().get("playroom1_size").entity();
        assert "playroom1_size"
                .equals(metric.getId()) : "Metric playroom1_size was not found using various relation " + "filters";

        try {
            inventory.tenants().getAll().relationships().named(contains).environments().getAll().relationships()
                    .getAll(RelationWith.properties("label", "contains"),
                            RelationWith.targetsOfTypes(Resource.class))
                    .metrics().get("playroom1_size").entity();
            assert false : "this code should not be reachable. There should be no metric reachable under "
                    + "'RelationWith.targetsOfTypes(Resource.class))' filter";
        } catch (EntityNotFoundException e) {
            // good
        }
    }

    @Test
    public void testRelationshipServiceCallChaining() throws Exception {
        MetricType metricType = inventory.tenants().get("com.example.tenant").resourceTypes().get("Playroom")
                .relationships().named("owns").metricTypes().get("Size").entity();// not empty
        assert "Size".equals(metricType.getId()) : "ResourceType[Playroom] -owns-> MetricType[Size] was not found";

        try {
            inventory.tenants().get("com.example.tenant").resourceTypes().get("Playroom").relationships()
                    .named("contains").metricTypes().get("Size").entity();
            assert false : "There is no such an entity satisfying the query, this code shouldn't be reachable";
        } catch (EntityNotFoundException e) {
            // good
        }

        Set<Resource> resources = inventory.tenants().get("com.example.tenant").resourceTypes().get("Playroom")
                .relationships().named("defines").resources().getAll().entities();
        assert resources.stream().allMatch(res -> "playroom1".equals(res.getId()) || "playroom2".equals(
                res.getId())) : "ResourceType[Playroom] -defines-> resources called playroom1 and playroom2";

        resources = inventory.tenants().get("com.example.tenant").resourceTypes().get("Playroom").relationships()
                .named("owns").resources().getAll().entities(); // empty
        assert resources
                .isEmpty() : "No resources should be found under the relationship called owns from resource type";
    }

    @Test
    public void testEnvironments() throws Exception {
        BiFunction<String, String, Void> test = (tenantId, id) -> {
            GremlinPipeline<Graph, Vertex> q = new GremlinPipeline<Graph, Vertex>(graph).V().has("__type", "tenant")
                    .has("__eid", tenantId).out("contains").has("__type", "environment").has("__eid", id)
                    .cast(Vertex.class);

            Iterator<Vertex> envs = q.iterator();
            assert envs.hasNext();
            envs.next();
            assert !envs.hasNext();

            //query, we should get the same results
            Environment env = inventory.tenants().get(tenantId).environments().get(id).entity();
            assert env.getId().equals(id);

            return null;
        };

        test.apply("com.acme.tenant", "production");
        test.apply("com.example.tenant", "test");

        GraphQuery query = graph.query().has("__type", "environment");
        assert StreamSupport.stream(query.vertices().spliterator(), false).count() == 2;
    }

    @Test
    public void testResourceTypes() throws Exception {
        BiFunction<String, String, Void> test = (tenantId, id) -> {
            GremlinPipeline<Graph, Vertex> q = new GremlinPipeline<Graph, Vertex>(graph).V().has("__type", "tenant")
                    .has("__eid", tenantId).out("contains").has("__type", "resourceType").has("__eid", id)
                    .has("__version", "1.0").cast(Vertex.class);

            assert q.hasNext();

            ResourceType rt = inventory.tenants().get(tenantId).resourceTypes().get(id).entity();
            assert rt.getId().equals(id);

            return null;
        };

        test.apply("com.acme.tenant", "URL");
        test.apply("com.example.tenant", "Kachna");
        test.apply("com.example.tenant", "Playroom");

        GraphQuery query = graph.query().has("__type", "resourceType");
        assert StreamSupport.stream(query.vertices().spliterator(), false).count() == 3;
    }

    @Test
    public void testMetricDefinitions() throws Exception {
        BiFunction<String, String, Void> test = (tenantId, id) -> {

            GremlinPipeline<Graph, Vertex> q = new GremlinPipeline<Graph, Vertex>(graph).V().has("__type", "tenant")
                    .has("__eid", tenantId).out("contains").has("__type", "metricType").has("__eid", id)
                    .cast(Vertex.class);

            assert q.hasNext();

            MetricType md = inventory.tenants().get(tenantId).metricTypes().get(id).entity();
            assert md.getId().equals(id);

            return null;
        };

        test.apply("com.acme.tenant", "ResponseTime");
        test.apply("com.example.tenant", "Size");

        GraphQuery query = graph.query().has("__type", "metricType");
        assert StreamSupport.stream(query.vertices().spliterator(), false).count() == 2;
    }

    @Test
    public void testMetricDefsLinkedToResourceTypes() throws Exception {
        TriFunction<String, String, String, Void> test = (tenantId, resourceTypeId, id) -> {
            GremlinPipeline<Graph, Vertex> q = new GremlinPipeline<Graph, Vertex>(graph).V().has("__type", "tenant")
                    .has("__eid", tenantId).out("contains").has("__type", "resourceType")
                    .has("__eid", resourceTypeId).out("owns").has("__type", "metricType").has("__eid", id)
                    .cast(Vertex.class);

            assert q.hasNext();

            MetricType md = inventory.tenants().get(tenantId).resourceTypes().get(resourceTypeId).metricTypes()
                    .get(id).entity();
            assert md.getId().equals(id);

            return null;
        };

        test.apply("com.acme.tenant", "URL", "ResponseTime");
        test.apply("com.example.tenant", "Playroom", "Size");
    }

    @Test
    public void testMetrics() throws Exception {
        TetraFunction<String, String, String, String, Void> test = (tenantId, environmentId, metricDefId, id) -> {
            GremlinPipeline<Graph, Vertex> q = new GremlinPipeline<Graph, Vertex>(graph).V().has("__type", "tenant")
                    .has("__eid", tenantId).out("contains").has("__type", "environment").has("__eid", environmentId)
                    .out("contains").has("__type", "metric").has("__eid", id).as("metric").in("defines")
                    .has("__type", "metricType").has("__eid", metricDefId).back("metric").cast(Vertex.class);

            assert q.hasNext();

            Metric m = inventory.tenants().get(tenantId).environments().get(environmentId).feedlessMetrics()
                    .getAll(Defined.by(new MetricType(tenantId, metricDefId)), With.id(id)).entities().iterator()
                    .next();
            assert m.getId().equals(id);

            return null;
        };

        test.apply("com.acme.tenant", "production", "ResponseTime", "host1_ping_response");
        test.apply("com.example.tenant", "test", "Size", "playroom1_size");
        test.apply("com.example.tenant", "test", "Size", "playroom2_size");

        GraphQuery query = graph.query().has("__type", "metric");
        Assert.assertEquals(4, StreamSupport.stream(query.vertices().spliterator(), false).count());
    }

    @Test
    public void testResources() throws Exception {
        TetraFunction<String, String, String, String, Void> test = (tenantId, environmentId, resourceTypeId,
                id) -> {
            GremlinPipeline<Graph, Vertex> q = new GremlinPipeline<Graph, Vertex>(graph).V().has("__type", "tenant")
                    .has("__eid", tenantId).out("contains").has("__type", "environment").has("__eid", environmentId)
                    .out("contains").has("__type", "resource").has("__eid", id).as("resource").in("defines")
                    .has("__type", "resourceType").has("__eid", resourceTypeId).back("resource").cast(Vertex.class);

            assert q.hasNext();

            Resource r = inventory.tenants().get(tenantId).environments().get(environmentId).feedlessResources()
                    .getAll(Defined.by(new ResourceType(tenantId, resourceTypeId, "1.0")), With.id(id)).entities()
                    .iterator().next();
            assert r.getId().equals(id);

            return null;
        };

        test.apply("com.acme.tenant", "production", "URL", "host1");
        test.apply("com.example.tenant", "test", "Playroom", "playroom1");
        test.apply("com.example.tenant", "test", "Playroom", "playroom2");

        GraphQuery query = graph.query().has("__type", "resource");
        Assert.assertEquals(6, StreamSupport.stream(query.vertices().spliterator(), false).count());
    }

    @Test
    public void testAssociateMetricWithResource() throws Exception {
        TetraFunction<String, String, String, String, Void> test = (tenantId, environmentId, resourceId,
                metricId) -> {
            GremlinPipeline<Graph, Vertex> q = new GremlinPipeline<Graph, Vertex>(graph).V().has("__type", "tenant")
                    .has("__eid", tenantId).out("contains").has("__type", "environment").has("__eid", environmentId)
                    .out("contains").has("__type", "resource").has("__eid", resourceId).out("owns")
                    .has("__type", "metric").has("__eid", metricId).cast(Vertex.class);

            assert q.hasNext();

            Metric m = inventory.tenants().get(tenantId).environments().get(environmentId).feedlessResources()
                    .get(resourceId).metrics().get(metricId).entity();
            assert metricId.equals(m.getId());

            return null;
        };

        test.apply("com.acme.tenant", "production", "host1", "host1_ping_response");
        test.apply("com.example.tenant", "test", "playroom1", "playroom1_size");
        test.apply("com.example.tenant", "test", "playroom2", "playroom2_size");
    }

    @Test
    public void queryMultipleTenants() throws Exception {
        Set<Tenant> tenants = inventory.tenants().getAll().entities();

        assert tenants.size() == 2;
    }

    @Test
    public void queryMultipleEnvironments() throws Exception {
        Set<Environment> environments = inventory.tenants().getAll().environments().getAll().entities();

        assert environments.size() == 2;
    }

    @Test
    public void queryMultipleResourceTypes() throws Exception {
        Set<ResourceType> types = inventory.tenants().getAll().resourceTypes().getAll().entities();
        assert types.size() == 3;
    }

    @Test
    public void queryMultipleMetricDefs() throws Exception {
        Set<MetricType> types = inventory.tenants().getAll().metricTypes().getAll().entities();
        assert types.size() == 2;
    }

    @Test
    public void queryMultipleResources() throws Exception {
        Set<Resource> rs = inventory.tenants().getAll().environments().getAll().feedlessResources().getAll()
                .entities();
        assert rs.size() == 3;
    }

    @Test
    public void queryMultipleMetrics() throws Exception {
        Set<Metric> ms = inventory.tenants().getAll().environments().getAll().feedlessMetrics().getAll().entities();
        assert ms.size() == 3;
    }

    @Test
    public void testNoTwoFeedsWithSameID() throws Exception {
        Feeds.ReadWrite feeds = inventory.tenants().get("com.acme.tenant").environments().get("production").feeds();

        Feed f1 = feeds.create(new Feed.Blueprint("feed", null)).entity();
        Feed f2 = feeds.create(new Feed.Blueprint("feed", null)).entity();

        assert f1.getId().equals("feed");
        assert !f1.getId().equals(f2.getId());
    }

    @Test
    public void testNoTwoEquivalentEntitiesOnTheSamePath() throws Exception {
        try {
            inventory.tenants().create(new Tenant.Blueprint("com.acme.tenant"));
            Assert.fail("Creating tenant with existing ID should fail");
        } catch (Exception e) {
            //good
        }

        try {
            inventory.tenants().get("com.acme.tenant").environments()
                    .create(new Environment.Blueprint("production"));
            Assert.fail("Creating environment with existing ID should fail");
        } catch (Exception e) {
            //good
        }

        try {
            inventory.tenants().get("com.acme.tenant").environments().get("production").feedlessResources()
                    .create(new Resource.Blueprint("host1", "URL"));
            Assert.fail("Creating resource with existing ID should fail");
        } catch (Exception e) {
            //good
        }
    }

    @Test
    public void testContainsLoopsImpossible() throws Exception {
        try {
            inventory.tenants().get("com.example.tenant").relationships(Relationships.Direction.outgoing)
                    .linkWith("contains", new Tenant("com.example.tenant"), null);

            Assert.fail("Self-loops in contains should be disallowed");
        } catch (IllegalArgumentException e) {
            //expected
        }

        try {
            inventory.tenants().get("com.example.tenant").relationships(Relationships.Direction.incoming)
                    .linkWith("contains", new Tenant("com.example.tenant"), null);

            Assert.fail("Self-loops in contains should be disallowed");
        } catch (IllegalArgumentException e) {
            //expected
        }

        try {
            inventory.tenants().get("com.example.tenant").environments().get("test")
                    .relationships(Relationships.Direction.outgoing)
                    .linkWith("contains", new Tenant("com.example.tenant"), null);

            Assert.fail("Loops in contains should be disallowed");
        } catch (IllegalArgumentException e) {
            //expected
        }

        try {
            inventory.tenants().get("com.example.tenant").relationships(Relationships.Direction.incoming)
                    .linkWith("contains", new Environment("com.example.tenant", "test"), null);

            Assert.fail("Loops in contains should be disallowed");
        } catch (IllegalArgumentException e) {
            //expected
        }
    }

    @Test
    public void testContainsDiamondsImpossible() throws Exception {
        try {
            inventory.tenants().get("com.example.tenant").relationships(Relationships.Direction.outgoing)
                    .linkWith("contains", new ResourceType("com.acme.tenant", "URL", "1.0"), null);

            Assert.fail("Entity cannot be contained in 2 or more others");
        } catch (IllegalArgumentException e) {
            //expected
        }

        try {
            inventory.tenants().get("com.acme.tenant").resourceTypes().get("URL")
                    .relationships(Relationships.Direction.incoming)
                    .linkWith("contains", new Tenant("com.example.tenant"), null);

            Assert.fail("Entity cannot be contained in 2 or more others");
        } catch (IllegalArgumentException e) {
            //expected
        }
    }

    @Test
    public void testPropertiesCreated() throws Exception {
        Tenant t = inventory.tenants().get("com.acme.tenant").entity();

        Assert.assertEquals(1, t.getProperties().size());
        Assert.assertEquals("moc", t.getProperties().get("kachny"));
    }

    @Test
    public void testPropertiesUpdatedOnEntities() throws Exception {

        inventory.tenants().update("com.acme.tenant",
                Tenant.Update.builder().withProperty("ducks", "many").withProperty("hammer", "nails").build());

        Tenant t = inventory.tenants().get("com.acme.tenant").entity();

        Assert.assertEquals(2, t.getProperties().size());
        Assert.assertEquals("many", t.getProperties().get("ducks"));
        Assert.assertEquals("nails", t.getProperties().get("hammer"));

        //reset the change we made back...
        inventory.tenants().update("com.acme.tenant",
                Tenant.Update.builder().withProperty("kachny", "moc").build());
        testPropertiesCreated();
    }

    @Test
    public void testPropertiesUpdatedOnRelationships() throws Exception {

        Relationship r = inventory.tenants().get("com.acme.tenant").relationships()
                .getAll(RelationWith.name("contains")).entities().iterator().next();

        inventory.tenants().get("com.acme.tenant").relationships().update(r.getId(), Relationship.Update.builder()
                .withProperty("ducks", "many").withProperty("hammer", "nails").build());

        r = inventory.tenants().get("com.acme.tenant").relationships().getAll(RelationWith.name("contains"))
                .entities().iterator().next();

        Assert.assertEquals(2, r.getProperties().size());
        Assert.assertEquals("many", r.getProperties().get("ducks"));
        Assert.assertEquals("nails", r.getProperties().get("hammer"));

        //reset the change we made back...
        inventory.tenants().get("com.acme.tenant").relationships().update(r.getId(), new Relationship.Update(null));

        r = inventory.tenants().get("com.acme.tenant").relationships().getAll(RelationWith.name("contains"))
                .entities().iterator().next();

        Assert.assertEquals(0, r.getProperties().size());
    }

    @Test
    public void testPaging() throws Exception {
        Page<Metric> allResults = inventory.tenants().getAll().environments().getAll().feedlessMetrics().getAll()
                .entities(Pager.unlimited(Order.by("id", Order.Direction.DESCENDING)));

        assert allResults.size() == 3;

        Pager firstPage = new Pager(0, 1, Order.by("id", Order.Direction.DESCENDING));

        Metrics.Multiple metrics = inventory.tenants().getAll().environments().getAll().feedlessMetrics().getAll();

        Page<Metric> ms = metrics.entities(firstPage);
        assert ms.size() == 1;
        assert ms.getTotalSize() == 3;
        assert ms.get(0).equals(allResults.get(0));

        ms = metrics.entities(firstPage.nextPage());
        assert ms.size() == 1;
        assert ms.getTotalSize() == 3;
        assert ms.get(0).equals(allResults.get(1));

        ms = metrics.entities(firstPage.nextPage().nextPage());
        assert ms.size() == 1;
        assert ms.getTotalSize() == 3;
        assert ms.get(0).equals(allResults.get(2));

        ms = metrics.entities(firstPage.nextPage().nextPage().nextPage());
        assert ms.getTotalSize() == 3;
        assert ms.size() == 0;
    }

    @Test
    public void testGettingResourcesFromFeedsUsingEnvironments() throws Exception {
        Set<Resource> rs = inventory.tenants().get("com.acme.tenant").environments().get("production")
                .allResources().getAll().entities();

        Assert.assertTrue(rs.stream().anyMatch((r) -> "host1".equals(r.getId())));
        Assert.assertTrue(rs.stream().anyMatch((r) -> "feedResource1".equals(r.getId())));
        Assert.assertTrue(rs.stream().anyMatch((r) -> "feedResource2".equals(r.getId())));
        Assert.assertTrue(rs.stream().anyMatch((r) -> "feedResource3".equals(r.getId())));
    }

    @Test
    public void testGettingMetricsFromFeedsUsingEnvironments() throws Exception {
        Set<Metric> rs = inventory.tenants().get("com.acme.tenant").environments().get("production").allMetrics()
                .getAll().entities();

        Assert.assertTrue(rs.stream().anyMatch((r) -> "host1_ping_response".equals(r.getId())));
        Assert.assertTrue(rs.stream().anyMatch((r) -> "feedMetric1".equals(r.getId())));
    }

    @Test
    public void testAllPathsMentionedInExceptions() throws Exception {
        try {
            inventory.tenants().get("non-tenant").environments().get("non-env").allResources().getAll().metrics()
                    .get("m").entity();
            Assert.fail("Fetching non-existant entity should have failed");
        } catch (EntityNotFoundException e) {
            Filter[][] paths = e.getFilters();
            Assert.assertEquals(2, paths.length);
            Assert.assertArrayEquals(Filter
                    .by(With.type(Tenant.class), With.id("non-tenant"), Related.by(contains),
                            With.type(Environment.class), With.id("non-env"), Related.by(contains),
                            With.type(Resource.class), Related.by(owns), With.type(Metric.class), With.id("m"))
                    .get(), paths[0]);
            Assert.assertArrayEquals(Filter.by(With.type(Tenant.class), With.id("non-tenant"), Related.by(contains),
                    With.type(Environment.class), With.id("non-env"), Related.by(contains), With.type(Feed.class),
                    Related.by(contains), With.type(Resource.class), Related.by(owns), With.type(Metric.class),
                    With.id("m")).get(), paths[1]);
        }
    }

    @SuppressWarnings("UnusedDeclaration")
    public static class DummyTransactionalGraph extends WrappedGraph<TinkerGraph> implements TransactionalGraph {

        public DummyTransactionalGraph(org.apache.commons.configuration.Configuration configuration) {
            super(new TinkerGraph(configuration));
        }

        @Override
        @SuppressWarnings("deprecation")
        public void stopTransaction(Conclusion conclusion) {
        }

        @Override
        public void commit() {
        }

        @Override
        public void rollback() {
        }
    }

    private interface TriFunction<T, U, V, R> {
        R apply(T t, U u, V v);
    }

    private interface TetraFunction<T, U, V, W, R> {
        R apply(T t, U u, V v, W w);
    }
}