/**
* Copyright (C) 2010-2017 Structr GmbH
*
* This file is part of Structr <http://structr.org>.
*
* Structr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Structr 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Structr. If not, see <http://www.gnu.org/licenses/>.
*/
package org.structr.common;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.structr.api.NotFoundException;
import org.structr.api.NotInTransactionException;
import org.structr.api.graph.RelationshipType;
import org.structr.common.error.FrameworkException;
import org.structr.core.GraphObject;
import org.structr.core.Result;
import org.structr.core.entity.AbstractNode;
import org.structr.core.entity.AbstractRelationship;
import org.structr.core.entity.DynamicResourceAccess;
import org.structr.core.entity.GenericNode;
import org.structr.core.entity.GenericRelationship;
import org.structr.core.entity.Group;
import org.structr.core.entity.Localization;
import org.structr.core.entity.Location;
import org.structr.core.entity.MailTemplate;
import org.structr.core.entity.OneThreeOneToOne;
import org.structr.core.entity.OneTwoOneToOne;
import org.structr.core.entity.Person;
import org.structr.core.entity.Relation;
import org.structr.core.entity.ResourceAccess;
import org.structr.core.entity.SchemaNode;
import org.structr.core.entity.SchemaRelationshipNode;
import org.structr.core.entity.SixOneOneToOne;
import org.structr.core.entity.TestFour;
import org.structr.core.entity.TestNine;
import org.structr.core.entity.TestOne;
import org.structr.core.entity.TestSeven;
import org.structr.core.entity.TestSix;
import org.structr.core.entity.TestTen;
import org.structr.core.entity.TestThree;
import org.structr.core.entity.TestTwo;
import org.structr.core.entity.TestUser;
import org.structr.core.entity.relationship.NodeHasLocation;
import org.structr.core.graph.NodeAttribute;
import org.structr.core.graph.NodeInterface;
import org.structr.core.graph.Tx;
import org.structr.core.property.PropertyKey;
import org.structr.core.property.PropertyMap;
import org.structr.core.property.StringProperty;
/**
*
*/
public class BasicTest extends StructrTest {
private static final Logger logger = LoggerFactory.getLogger(BasicTest.class);
@Test
public void test00SimpleCreateOperation() {
try (final Tx tx = app.tx()) {
final PropertyMap properties = new PropertyMap();
properties.put(TestSix.name, "name");
// test null value for a 1:1 related property
properties.put(TestSix.oneToOneTestThree, null);
app.create(TestSix.class, properties);
tx.success();
} catch (FrameworkException fex) {
fail("Unexpected exception");
}
try (final Tx tx = app.tx()) {
final TestSix test = app.nodeQuery(TestSix.class).getFirst();
assertNotNull("Invalid simple object creation result", test);
assertEquals("Invalid simple object creation result", "name", test.getProperty(AbstractNode.name));
assertEquals("Invalid simple object creation result", null, test.getProperty(TestSix.oneToOneTestThree));
tx.success();
} catch (FrameworkException fex) {
fail("Unexpected exception");
}
}
/**
* Test successful deletion of a node.
*
* The node shouldn't be found afterwards.
* Creation and deletion are executed in two different transactions.
*
*/
@Test
public void test01DeleteNode() {
try {
final PropertyMap props = new PropertyMap();
final String type = "GenericNode";
final String name = "GenericNode-name";
NodeInterface node = null;
String uuid = null;
props.put(AbstractNode.type, type);
props.put(AbstractNode.name, name);
try (final Tx tx = app.tx()) {
node = app.create(GenericNode.class, props);
tx.success();
}
assertTrue(node != null);
try (final Tx tx = app.tx()) {
uuid = node.getUuid();
}
try (final Tx tx = app.tx()) {
app.delete(node);
tx.success();
}
try (final Tx tx = app.tx()) {
Result result = app.nodeQuery().uuid(uuid).getResult();
assertEquals("Node should have been deleted", 0, result.size());
} catch (FrameworkException fe) {}
} catch (FrameworkException ex) {
logger.warn("", ex);
logger.error(ex.toString());
fail("Unexpected exception");
}
}
@Test
public void test01DeleteRelationship() {
try {
final TestOne testOne = createTestNode(TestOne.class);
final TestSix testSix = createTestNode(TestSix.class);
SixOneOneToOne rel = null;
assertNotNull(testOne);
assertNotNull(testSix);
try (final Tx tx = app.tx()) {
rel = app.create(testSix, testOne, SixOneOneToOne.class);
tx.success();
}
assertNotNull(rel);
try {
// try to delete relationship
rel.getRelationship().delete();
fail("Should have raised an org.neo4j.graphdb.NotInTransactionException");
} catch (NotInTransactionException e) {}
// Relationship still there
assertNotNull(rel);
try (final Tx tx = app.tx()) {
app.delete(rel);
tx.success();
}
try (final Tx tx = app.tx()) {
String uuid = rel.getUuid();
fail("Deleted entity should have thrown an exception on access.");
} catch (NotFoundException iex) {
}
} catch (FrameworkException ex) {
logger.warn("", ex);
logger.error(ex.toString());
fail("Unexpected exception");
}
}
/**
* DELETE_NONE should not trigger any delete cascade
*/
@Test
public void test03CascadeDeleteNone() {
/* this test is flawed in that it expects the cascading
* not to take place but expects to run without an
* exception, but deleting one node of the two will leave
* the other (TestTwo) in an invalid state according
* to its isValid() method!
try {
// Create a relationship with DELETE_NONE
AbstractRelationship rel = cascadeRel(TestOne.class, TestTwo.class, Relation.DELETE_NONE);
AbstractNode startNode = rel.getStartNode();
AbstractNode endNode = rel.getEndNode();
final String startNodeId = startNode.getUuid();
final String endNodeId = endNode.getUuid();
boolean exception = false;
deleteCascade(startNode);
assertNodeNotFound(startNodeId);
assertNodeExists(endNodeId);
// Create another relationship with DELETE_NONE
rel = cascadeRel(TestOne.class, TestTwo.class, Relation.DELETE_NONE);
try {
deleteCascade(rel.getEndNode());
} catch (FrameworkException fex) {
assertEquals(422, fex.getStatus());
exception = true;
}
assertTrue("Exception should be raised", exception);
} catch (FrameworkException ex) {
logger.warn("", ex);
logger.error(ex.toString());
fail("Unexpected exception");
}
*/
}
/**
* DELETE_INCOMING should not trigger delete cascade from start to end node,
* but from end to start node
*/
@Test
public void test04CascadeDeleteIncoming() {
/* this test is flawed in that it expects the cascading
* not to take place but expectes to run without an
* exception, but deleting one node of the two will leave
* the other (TestTwo) in an invalid state according
* to its isValid() method!
try {
// Create a relationship with DELETE_INCOMING
AbstractRelationship rel = cascadeRel(TestOne.class, TestTwo.class, Relation.DELETE_INCOMING);
final String startNodeId = rel.getStartNode().getUuid();
final String endNodeId = rel.getEndNode().getUuid();
boolean exception = false;
deleteCascade(rel.getStartNode());
// Start node should not be found after deletion
assertNodeNotFound(startNodeId);
// End node should be found after deletion of start node
assertNodeExists(endNodeId);
// Create another relationship with DELETE_INCOMING
rel = cascadeRel(TestOne.class, TestTwo.class, Relation.DELETE_INCOMING);
try {
deleteCascade(rel.getEndNode());
} catch (FrameworkException fex) {
assertEquals(422, fex.getStatus());
exception = true;
}
assertTrue("Exception should be raised", exception);
} catch (FrameworkException ex) {
logger.warn("", ex);
logger.error(ex.toString());
fail("Unexpected exception");
}
*/
}
/**
* DELETE_OUTGOING should trigger delete cascade from start to end node,
* but not from end to start node.
*/
@Test
public void test05CascadeDeleteOutgoing() {
try {
// Create a relationship with DELETE_OUTGOING
AbstractRelationship rel = cascadeRel(TestOne.class, TestTwo.class, Relation.SOURCE_TO_TARGET);
NodeInterface sourceNode;
NodeInterface targetNode;
String startNodeId;
String endNodeId;
try (final Tx tx = app.tx()) {
startNodeId = rel.getSourceNode().getUuid();
endNodeId = rel.getTargetNode().getUuid();
sourceNode = rel.getSourceNode();
}
deleteCascade(sourceNode);
try (final Tx tx = app.tx()) {
// Start node should not be found after deletion
assertNodeNotFound(startNodeId);
// End node should not be found after deletion
assertNodeNotFound(endNodeId);
}
// Create another relationship with DELETE_OUTGOING
rel = cascadeRel(TestOne.class, TestTwo.class, Relation.SOURCE_TO_TARGET);
try (final Tx tx = app.tx()) {
startNodeId = rel.getSourceNode().getUuid();
endNodeId = rel.getTargetNode().getUuid();
targetNode = rel.getTargetNode();
}
deleteCascade(targetNode);
try (final Tx tx = app.tx()) {
// End node should not be found after deletion
assertNodeNotFound(endNodeId);
// Start node should still exist deletion of end node
assertNodeExists(startNodeId);
}
} catch (FrameworkException ex) {
logger.warn("", ex);
logger.error(ex.toString());
fail("Unexpected exception");
}
}
/**
* DELETE_INCOMING + DELETE_OUTGOING should trigger delete cascade from start to end node
* and from end node to start node
*/
@Test
public void test06CascadeDeleteBidirectional() {
try {
// Create a relationship with DELETE_INCOMING
AbstractRelationship rel = cascadeRel(TestOne.class, TestTwo.class, Relation.TARGET_TO_SOURCE | Relation.SOURCE_TO_TARGET);
NodeInterface sourceNode;
NodeInterface targetNode;
String startNodeId;
String endNodeId;
try (final Tx tx = app.tx()) {
startNodeId = rel.getSourceNode().getUuid();
endNodeId = rel.getTargetNode().getUuid();
sourceNode = rel.getSourceNode();
}
deleteCascade(sourceNode);
try (final Tx tx = app.tx()) {
// Start node should not be found after deletion
assertNodeNotFound(startNodeId);
// End node should not be found after deletion of start node
assertNodeNotFound(endNodeId);
}
// Create a relationship with DELETE_INCOMING
rel = cascadeRel(TestOne.class, TestTwo.class, Relation.TARGET_TO_SOURCE | Relation.SOURCE_TO_TARGET);
try (final Tx tx = app.tx()) {
startNodeId = rel.getSourceNode().getUuid();
endNodeId = rel.getTargetNode().getUuid();
targetNode = rel.getTargetNode();
}
deleteCascade(targetNode);
try (final Tx tx = app.tx()) {
// End node should not be found after deletion
assertNodeNotFound(endNodeId);
// Start node should not be found after deletion of end node
assertNodeNotFound(startNodeId);
}
} catch (FrameworkException ex) {
logger.warn("", ex);
logger.error(ex.toString());
fail("Unexpected exception");
}
}
/**
* DELETE_IF_CONSTRAINT_WOULD_BE_VIOLATED should
* trigger delete cascade from start to end node only
* if the remote node would not be valid afterwards
*/
@Test
public void test07CascadeDeleteConditional() {
try {
AbstractRelationship rel = cascadeRel(TestOne.class, TestTwo.class, Relation.CONSTRAINT_BASED);
NodeInterface sourceNode;
String startNodeId;
String endNodeId;
try (final Tx tx = app.tx()) {
startNodeId = rel.getSourceNode().getUuid();
endNodeId = rel.getTargetNode().getUuid();
sourceNode = rel.getSourceNode();
}
deleteCascade(sourceNode);
try (final Tx tx = app.tx()) {
// Start node should be deleted
assertNodeNotFound(startNodeId);
// End node should be deleted
assertNodeNotFound(endNodeId);
}
rel = cascadeRel(TestOne.class, TestThree.class, Relation.CONSTRAINT_BASED);
try (final Tx tx = app.tx()) {
startNodeId = rel.getSourceNode().getUuid();
endNodeId = rel.getTargetNode().getUuid();
sourceNode = rel.getSourceNode();
}
deleteCascade(sourceNode);
try (final Tx tx = app.tx()) {
// Start node should be deleted
assertNodeNotFound(startNodeId);
// End node should still be there
assertNodeExists(endNodeId);
}
rel = cascadeRel(TestOne.class, TestFour.class, Relation.CONSTRAINT_BASED);
try (final Tx tx = app.tx()) {
startNodeId = rel.getSourceNode().getUuid();
endNodeId = rel.getTargetNode().getUuid();
sourceNode = rel.getSourceNode();
}
deleteCascade(sourceNode);
try (final Tx tx = app.tx()) {
// Start node should be deleted
assertNodeNotFound(startNodeId);
// End node should still be there
assertNodeExists(endNodeId);
}
} catch (FrameworkException ex) {
logger.warn("", ex);
logger.error(ex.toString());
fail("Unexpected exception");
}
}
@Test
public void test08OverlappingDeleteCascades() {
/*
* This test creates a ternary tree of depth 2 (39 nodes)
* linked with relationship type "TEN", with two additional
* links throughout the tree. It creates a situation where
* two delete cascades overlap when a node is deleted and
* tests the correct handling of such a situation.
*
* 1-------+ 1->2, 1->3, 1->4, 1->D
* / | \ |
* / | \ |
* 2 3 4 /
* /|\ /|\ /|\/
* 567 89A BCD
*/
try {
final List<TestTen> rootNodes = new LinkedList<>();
final List<TestTen> allChildren = new LinkedList<>();
final List<TestTen> allGrandChildren = new LinkedList<>();
try (final Tx tx = app.tx()) {
// create some nodes..
rootNodes.addAll(createTestNodes(TestTen.class, 3));
for (final TestTen node : rootNodes) {
final List<TestTen> children = createTestNodes(TestTen.class, 3);
node.setProperty(TestTen.tenTenChildren, children);
for (final TestTen child : children) {
final List<TestTen> grandChildren = createTestNodes(TestTen.class, 3);
child.setProperty(TestTen.tenTenChildren, grandChildren);
allGrandChildren.addAll(grandChildren);
}
allChildren.addAll(children);
}
// create some additional links off a different type but with cascading delete
rootNodes.get(0).setProperty(TestTen.testChild, allGrandChildren.get(0));
allChildren.get(0).setProperty(TestTen.testChild, allGrandChildren.get(1));
tx.success();
}
// check preconditions: exactly 39 nodes should exist
try (final Tx tx = app.tx()) {
assertEquals("Wrong number of nodes", 39, app.nodeQuery(TestTen.class).getAsList().size());
tx.success();
}
// delete one root node
try (final Tx tx = app.tx()) {
app.delete(rootNodes.get(0));
tx.success();
}
// check conditions after deletion, 26 nodes shoud exist
try (final Tx tx = app.tx()) {
assertEquals("Wrong number of nodes", 26, app.nodeQuery(TestTen.class).getAsList().size());
tx.success();
}
} catch (FrameworkException ex) {
logger.warn("", ex);
logger.error(ex.toString());
fail("Unexpected exception");
}
}
@Test
public void test01CreateNode() {
try {
try {
// Create node out of transaction => should give a NotInTransactionException
app.create(TestOne.class);
fail("Should have raised a NotInTransactionException");
} catch (NotInTransactionException e) {
}
try {
// Try to create node without parameters => should fail
app.create(TestOne.class);
fail("Should have raised a NotInTransactionException");
} catch (NotInTransactionException e) {}
AbstractNode node = null;
try (final Tx tx = app.tx()) {
node = app.create(TestOne.class);
tx.success();
}
assertTrue(node != null);
assertTrue(node instanceof TestOne);
} catch (FrameworkException ex) {
logger.error("", ex);
fail("Unexpected exception");
}
}
@Test
public void test02CreateNodeWithExistingUuid() {
try {
final PropertyMap props = new PropertyMap();
TestOne node = null;
final String uuid = StringUtils.replace(UUID.randomUUID().toString(), "-", "");
props.put(GraphObject.id, uuid);
try (final Tx tx = app.tx()) {
node = app.create(TestOne.class, props);
tx.success();
}
try (final Tx tx = app.tx()) {
assertTrue(node != null);
assertTrue(node instanceof TestOne);
assertEquals(node.getUuid(), uuid);
}
} catch (FrameworkException ex) {
logger.error(ex.toString());
fail("Unexpected exception");
}
}
@Test
public void test02CreateTwoNodesWithSameUuidInSameTx() {
try {
final PropertyMap props = new PropertyMap();
TestOne node = null;
final String uuid = StringUtils.replace(UUID.randomUUID().toString(), "-", "");
props.put(GraphObject.id, uuid);
try (final Tx tx = app.tx()) {
node = app.create(TestOne.class, props);
assertTrue(node != null);
assertTrue(node instanceof TestOne);
assertEquals(node.getUuid(), uuid);
node = app.create(TestOne.class, props);
tx.success();
fail("Validation failed!");
}
} catch (FrameworkException ex) {
}
}
@Test
public void test02CreateTwoNodesWithSameUuidInTwoTx() {
try {
final PropertyMap props = new PropertyMap();
TestOne node = null;
final String uuid = StringUtils.replace(UUID.randomUUID().toString(), "-", "");
props.put(GraphObject.id, uuid);
try (final Tx tx = app.tx()) {
node = app.create(TestOne.class, props);
assertTrue(node != null);
assertTrue(node instanceof TestOne);
assertEquals(node.getUuid(), uuid);
tx.success();
} catch (FrameworkException ex) {
logger.error(ex.toString());
fail("Unexpected exception");
}
try (final Tx tx = app.tx()) {
node = app.create(TestOne.class, props);
tx.success();
fail("Validation failed!");
}
} catch (FrameworkException ex) {
}
}
@Test
public void test03CreateRelationship() {
try {
final List<GenericNode> nodes = createTestNodes(GenericNode.class, 2);
final NodeInterface startNode = nodes.get(0);
final NodeInterface endNode = nodes.get(1);
NodeHasLocation rel = null;
assertTrue(startNode != null);
assertTrue(endNode != null);
try (final Tx tx = app.tx()) {
rel = app.create(startNode, endNode, NodeHasLocation.class);
tx.success();
}
try (final Tx tx = app.tx()) {
assertEquals(startNode.getUuid(), rel.getSourceNodeId());
assertEquals(endNode.getUuid(), rel.getTargetNodeId());
assertEquals(RelType.IS_AT.name(), rel.getType());
assertEquals(NodeHasLocation.class, rel.getClass());
}
} catch (FrameworkException ex) {
logger.error(ex.toString());
fail("Unexpected exception");
}
}
/**
* Create a node for each configured entity class and check the type
*/
@Test
public void test04CheckNodeEntities() {
AccessControlTest.clearResourceAccess();
final PropertyMap props = new PropertyMap();
try (final Tx tx = app.tx()) {
List<Class> entityList = Collections.EMPTY_LIST;
try {
entityList = getClasses("org.structr.core.entity");
} catch (IOException | ClassNotFoundException ex) {
logger.error("", ex);
}
assertTrue(entityList.contains(AbstractNode.class));
assertTrue(entityList.contains(GenericNode.class));
assertTrue(entityList.contains(Location.class));
assertTrue(entityList.contains(Person.class));
assertTrue(entityList.contains(ResourceAccess.class));
// Don't test these, it would fail due to violated constraints
entityList.remove(TestTwo.class);
entityList.remove(TestNine.class);
entityList.remove(MailTemplate.class);
entityList.remove(SchemaNode.class);
entityList.remove(SchemaRelationshipNode.class);
for (Class type : entityList) {
// for (Entry<String, Class> entity : entities.entrySet()) {
// Class entityClass = entity.getValue();
if (AbstractNode.class.isAssignableFrom(type)) {
props.clear();
// For Group, fill mandatory fields
if (type.equals(Group.class)) {
props.put(Group.name, "Group-0");
}
// For TestSeven, fill mandatory fields
if (type.equals(TestSeven.class)) {
props.put(TestSeven.name, "TestSeven-0");
}
// For ResourceAccess, fill mandatory fields
if (type.equals(ResourceAccess.class)) {
props.put(ResourceAccess.signature, "/X");
props.put(ResourceAccess.flags, 6L);
}
// For DynamicResourceAccess, fill mandatory fields
if (type.equals(DynamicResourceAccess.class)) {
props.put(DynamicResourceAccess.signature, "/Y");
props.put(DynamicResourceAccess.flags, 6L);
}
// For Localization, fill mandatory fields
if (type.equals(Localization.class)) {
props.put(Localization.name, "localizationKey");
props.put(Localization.locale, "de_DE");
}
// For Location, set coordinates
if (type.equals(Location.class)) {
props.put(Location.latitude, 12.34);
props.put(Location.longitude, 56.78);
}
logger.info("Creating node of type {}", type);
NodeInterface node = app.create(type, props);
assertTrue(type.getSimpleName().equals(node.getProperty(AbstractNode.type)));
// Remove mandatory fields for ResourceAccess from props map
if (type.equals(ResourceAccess.class)) {
props.remove(ResourceAccess.signature);
props.remove(ResourceAccess.flags);
}
}
}
tx.success();
} catch (FrameworkException ex) {
logger.error(ex.toString());
logger.warn("", ex);
fail("Unexpected exception");
}
}
/**
* Create a node for each configured entity class and check the type
*/
@Test
public void test05CheckRelationshipEntities() {
try (final Tx tx = app.tx()) {
List<Class> entityList = null;
try {
entityList = getClasses("org.structr.core.entity");
} catch (IOException | ClassNotFoundException ex) {
logger.error("Unable to get list of entity classes", ex);
}
assertTrue(entityList.contains(AbstractRelationship.class));
assertTrue(entityList.contains(GenericRelationship.class));
for (Class entityClass : entityList) {
// for (Entry<String, Class> entity : entities.entrySet()) {
// Class entityClass = entity.getValue();
if (AbstractRelationship.class.isAssignableFrom(entityClass)) {
String type = entityClass.getSimpleName();
logger.info("Creating relationship of type {}", type);
List<GenericNode> nodes = createTestNodes(GenericNode.class, 2);
final NodeInterface startNode = nodes.get(0);
final NodeInterface endNode = nodes.get(1);
final RelationshipType relType = RelType.IS_AT;
NodeHasLocation rel = app.create(startNode, endNode, NodeHasLocation.class);
assertTrue(rel != null);
assertTrue(rel.getType().equals(relType.name()));
}
}
tx.success();
} catch (Throwable ex) {
logger.warn("", ex);
logger.error(ex.toString());
fail("Unexpected exception");
}
}
/**
* FIXME: this test is disabled, to be discussed!
*
* Creation of duplicate relationships is blocked.
*
* A relationship is considered duplicate if all of the following criteria are met:
*
* - same start node
* - same end node
* - same relationship type
* - same set of property keys and values
*
public void test06DuplicateRelationships() {
try {
List<NodeInterface> nodes = createTestNodes(GenericNode.class, 2);
final NodeInterface startNode = nodes.get(0);
final NodeInterface endNode = nodes.get(1);
final PropertyMap props = new PropertyMap();
props.put(new StringProperty("foo"), "bar");
props.put(new IntProperty("bar"), 123);
transactionCommand.execute(new StructrTransaction() {
@Override
public Object execute() throws FrameworkException {
LocationRelationship rel1 = createRelationshipCommand.execute(startNode, endNode, LocationRelationship.class, props);
assertTrue(rel1 != null);
createRelationshipCommand.execute(startNode, endNode, LocationRelationship.class, props);
return null;
}
});
fail("Creating a duplicate relationship should throw an exception.");
} catch (FrameworkException ex) {
}
}
*/
@Test
public void test01ModifyNode() {
try {
final PropertyMap props = new PropertyMap();
final String type = "UnknownTestType";
final String name = "GenericNode-name";
NodeInterface node = null;
props.put(AbstractNode.type, type);
props.put(AbstractNode.name, name);
try (final Tx tx = app.tx()) {
node = app.create(GenericNode.class, props);
tx.success();
}
try (final Tx tx = app.tx()) {
// Check defaults
assertEquals(GenericNode.class.getSimpleName(), node.getProperty(AbstractNode.type));
assertTrue(node.getProperty(AbstractNode.name).equals(name));
assertTrue(!node.getProperty(AbstractNode.hidden));
assertTrue(!node.getProperty(AbstractNode.deleted));
assertTrue(!node.getProperty(AbstractNode.visibleToAuthenticatedUsers));
assertTrue(!node.getProperty(AbstractNode.visibleToPublicUsers));
}
final String name2 = "GenericNode-name-äöüß";
try (final Tx tx = app.tx()) {
// Modify values
node.setProperty(AbstractNode.name, name2);
node.setProperty(AbstractNode.hidden, true);
node.setProperty(AbstractNode.deleted, true);
node.setProperty(AbstractNode.visibleToAuthenticatedUsers, true);
node.setProperty(AbstractNode.visibleToPublicUsers, true);
tx.success();
}
try (final Tx tx = app.tx()) {
assertTrue(node.getProperty(AbstractNode.name).equals(name2));
assertTrue(node.getProperty(AbstractNode.hidden));
assertTrue(node.getProperty(AbstractNode.deleted));
assertTrue(node.getProperty(AbstractNode.visibleToAuthenticatedUsers));
assertTrue(node.getProperty(AbstractNode.visibleToPublicUsers));
}
} catch (FrameworkException ex) {
logger.error(ex.toString());
fail("Unexpected exception");
}
}
/**
* Test the results of setProperty and getProperty of a relationship
*/
@Test
public void test02ModifyRelationship() {
try {
final NodeHasLocation rel = (createTestRelationships(NodeHasLocation.class, 1)).get(0);
final PropertyKey key1 = new StringProperty("jghsdkhgshdhgsdjkfgh");
final String val1 = "54354354546806849870";
try (final Tx tx = app.tx()) {
rel.setProperty(key1, val1);
tx.success();
}
try (final Tx tx = app.tx()) {
assertTrue("Expected relationship to have a value for key '" + key1.dbName() + "'", rel.getRelationship().hasProperty(key1.dbName()));
assertEquals(val1, rel.getRelationship().getProperty(key1.dbName()));
Object vrfy1 = rel.getProperty(key1);
assertEquals(val1, vrfy1);
}
final String val2 = "öljkhöohü8osdfhoödhi";
try (final Tx tx = app.tx()) {
rel.setProperty(key1, val2);
tx.success();
}
try (final Tx tx = app.tx()) {
Object vrfy2 = rel.getProperty(key1);
assertEquals(val2, vrfy2);
}
} catch (FrameworkException ex) {
logger.error(ex.toString());
fail("Unexpected exception");
}
}
/**
* Test the results of setProperty and getProperty of a node
*/
@Test
public void test03ModifyConstantBooleanProperty() {
try {
final PropertyMap props = new PropertyMap();
final String type = "Group";
final String name = "TestGroup-1";
NodeInterface node = null;
props.put(AbstractNode.type, type);
props.put(AbstractNode.name, name);
try (final Tx tx = app.tx()) {
node = app.create(Group.class, props);
tx.success();
}
try (final Tx tx = app.tx()) {
// Check defaults
assertEquals(Group.class.getSimpleName(), node.getProperty(AbstractNode.type));
assertTrue(node.getProperty(AbstractNode.name).equals(name));
assertTrue(node.getProperty(Group.isGroup));
}
final String name2 = "TestGroup-2";
try (final Tx tx = app.tx()) {
// Modify values
node.setProperty(AbstractNode.name, name2);
node.setProperty(Group.isGroup, false);
fail("Should have failed with an exception: Group.isGroup is_read_only_property");
tx.success();
} catch (FrameworkException expected) {}
} catch (FrameworkException ex) {
logger.error(ex.toString());
fail("Unexpected exception");
}
}
@Test
public void test01CompareAscending() {
try {
TestOne a = createTestNode(TestOne.class);
TestOne b = createTestNode(TestOne.class);
try (final Tx tx = app.tx()) {
GraphObjectComparator comp = new GraphObjectComparator(TestOne.anInt, GraphObjectComparator.ASCENDING);
try {
comp.compare(null, null);
fail("Should have raised an NullPointerException");
} catch (NullPointerException e) {}
try {
comp.compare(a, null);
fail("Should have raised an NullPointerException");
} catch (NullPointerException e) {}
try {
comp.compare(null, b);
fail("Should have raised an NullPointerException");
} catch (NullPointerException e) {}
try {
comp.compare(null, b);
fail("Should have raised an NullPointerException");
} catch (NullPointerException e) {}
// a: null
// b: null
// a == b => 0
assertEquals(0, comp.compare(a, b));
// a: 0
// b: null
// a < b => -1
setPropertyTx(a, TestOne.anInt, 0);
assertEquals(-1, comp.compare(a, b));
// a: null
// b: 0
// a > b => 1
setPropertyTx(a, TestOne.anInt, null);
setPropertyTx(b, TestOne.anInt, 0);
assertEquals(1, comp.compare(a, b));
// a: 1
// b: 2
// a < b => -1
setPropertyTx(a, TestOne.anInt, 1);
setPropertyTx(b, TestOne.anInt, 2);
assertEquals(-1, comp.compare(a, b));
// a: 2
// b: 1
// a > b => 1
setPropertyTx(a, TestOne.anInt, 2);
setPropertyTx(b, TestOne.anInt, 1);
assertEquals(1, comp.compare(a, b));
}
} catch (FrameworkException ex) {
logger.error(ex.toString());
fail("Unexpected exception");
}
}
@Test
public void test01CompareDescending() {
try {
TestOne a = createTestNode(TestOne.class);
TestOne b = createTestNode(TestOne.class);
GraphObjectComparator comp = new GraphObjectComparator(TestOne.anInt, GraphObjectComparator.DESCENDING);
try {
comp.compare(null, null);
fail("Should have raised an NullPointerException");
} catch (NullPointerException e) {}
try {
comp.compare(a, null);
fail("Should have raised an NullPointerException");
} catch (NullPointerException e) {}
try {
comp.compare(null, b);
fail("Should have raised an NullPointerException");
} catch (NullPointerException e) {}
try {
comp.compare(null, b);
fail("Should have raised an NullPointerException");
} catch (NullPointerException e) {}
try (final Tx tx = app.tx()) {
// a: null
// b: null
// a == b => 0
assertEquals(0, comp.compare(a, b));
}
// a: 0
// b: null
// a > b => 1
setPropertyTx(a, TestOne.anInt, 0);
try (final Tx tx = app.tx()) {
assertEquals(1, comp.compare(a, b));
}
// a: null
// b: 0
// a < b => -1
setPropertyTx(a, TestOne.anInt, null);
setPropertyTx(b, TestOne.anInt, 0);
try (final Tx tx = app.tx()) {
assertEquals(-1, comp.compare(a, b));
}
// a: 1
// b: 2
// a > b => 1
setPropertyTx(a, TestOne.anInt, 1);
setPropertyTx(b, TestOne.anInt, 2);
try (final Tx tx = app.tx()) {
assertEquals(1, comp.compare(a, b));
}
// a: 2
// b: 1
// a < b => -1
setPropertyTx(a, TestOne.anInt, 2);
setPropertyTx(b, TestOne.anInt, 1);
try (final Tx tx = app.tx()) {
assertEquals(-1, comp.compare(a, b));
}
} catch (FrameworkException ex) {
logger.error(ex.toString());
fail("Unexpected exception");
}
}
@Test
public void testNodeCacheInvalidationInRelationshipWrapper() {
try (final Tx tx = app.tx()) {
final TestOne testOne = createTestNode(TestOne.class);
final TestTwo testTwo1 = createTestNode(TestTwo.class, new NodeAttribute<>(TestTwo.testOne, testOne));
final OneTwoOneToOne rel = testOne.getOutgoingRelationship(OneTwoOneToOne.class);
final TestTwo testTwo2 = rel.getTargetNode();
testTwo1.setProperty(AbstractNode.name, "test");
assertEquals("Cache invalidation failure!", "test", testTwo2.getProperty(AbstractNode.name));
tx.success();
} catch (FrameworkException fex) {
logger.warn("", fex);
fail("Unexpected exception.");
}
// fill cache with other nodes
try {
createTestNodes(TestSix.class, 1000);
} catch (FrameworkException fex) {
logger.warn("", fex);
fail("Unexpected exception.");
}
try (final Tx tx = app.tx()) {
final TestOne testOne = app.nodeQuery(TestOne.class).getFirst();
final TestTwo testTwo1 = app.nodeQuery(TestTwo.class).getFirst();
final OneTwoOneToOne rel = testOne.getOutgoingRelationship(OneTwoOneToOne.class);
final TestTwo testTwo2 = rel.getTargetNode();
testTwo1.setProperty(AbstractNode.name, "test2");
assertEquals("Cache invalidation failure!", "test2", testTwo2.getProperty(AbstractNode.name));
tx.success();
} catch (FrameworkException fex) {
logger.warn("", fex);
fail("Unexpected exception.");
}
}
@Test
public void testRelationshipEndNodeTypeRestriction() {
// this test makes sure that relationships with identical relationship
// types are filtered according to the types of their end nodes
try (final Tx tx = app.tx()) {
// create two OWNS relationships with different end node types
final TestOne testOne = app.create(TestOne.class, "testone");
final TestThree testThree = app.create(TestThree.class, "testthree");
final TestUser testUser = app.create(TestUser.class, "testuser");
testOne.setProperty(TestOne.testThree, testThree);
testThree.setProperty(TestThree.owner, testUser);
tx.success();
} catch (FrameworkException fex) {
logger.warn("", fex);
fail("Unexpected exception.");
}
try (final Tx tx = app.tx()) {
final List<OneThreeOneToOne> rels = app.relationshipQuery(OneThreeOneToOne.class).getAsList();
assertEquals("Relationship query returns wrong number of results", 1, rels.size());
for (final OneThreeOneToOne rel : rels) {
assertEquals("Relationship query returns wrong type", OneThreeOneToOne.class, rel.getClass());
}
tx.success();
} catch (FrameworkException fex) {
logger.warn("", fex);
fail("Unexpected exception.");
}
}
// ----- private methods -----
private AbstractRelationship cascadeRel(final Class type1, final Class type2, final int cascadeDeleteFlag) throws FrameworkException {
try (final Tx tx = app.tx()) {
AbstractNode start = createTestNode(type1);
AbstractNode end = createTestNode(type2);
AbstractRelationship rel = createTestRelationship(start, end, NodeHasLocation.class);
rel.setProperty(AbstractRelationship.cascadeDelete, cascadeDeleteFlag);
tx.success();
return rel;
}
}
private void deleteCascade(final NodeInterface node) throws FrameworkException {
try (final Tx tx = app.tx()) {
app.delete(node);
tx.success();
}
}
private void setPropertyTx(final GraphObject obj, final PropertyKey key, final Object value) {
try (final Tx tx = app.tx()) {
obj.setProperty(key, value);
tx.success();
} catch (FrameworkException ex) {
}
}
}