/* * Copyright (c) 2002-2017 "Neo Technology," * Network Engine for Objects in Lund AB [http://neotechnology.com] * * This file is part of Neo4j. * * 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.neo4j.driver.v1.integration; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.neo4j.driver.v1.Record; import org.neo4j.driver.v1.StatementResult; import org.neo4j.driver.v1.Transaction; import org.neo4j.driver.v1.Value; import org.neo4j.driver.v1.exceptions.ClientException; import org.neo4j.driver.v1.util.TestNeo4jSession; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class TransactionIT { @Rule public ExpectedException exception = ExpectedException.none(); @Rule public TestNeo4jSession session = new TestNeo4jSession(); @Test public void shouldRunAndCommit() throws Throwable { // When try ( Transaction tx = session.beginTransaction() ) { tx.run( "CREATE (n:FirstNode)" ); tx.run( "CREATE (n:SecondNode)" ); tx.success(); } // Then the outcome of both statements should be visible StatementResult result = session.run( "MATCH (n) RETURN count(n)" ); long nodes = result.single().get( "count(n)" ).asLong(); assertThat( nodes, equalTo( 2L ) ); } @Test public void shouldRunAndRollbackByDefault() throws Throwable { // When try ( Transaction tx = session.beginTransaction() ) { tx.run( "CREATE (n:FirstNode)" ); tx.run( "CREATE (n:SecondNode)" ); } // Then there should be no visible effect of the transaction StatementResult cursor = session.run( "MATCH (n) RETURN count(n)" ); long nodes = cursor.single().get( "count(n)" ).asLong(); assertThat( nodes, equalTo( 0L ) ); } @Test public void shouldRetrieveResults() throws Throwable { // Given session.run( "CREATE (n {name:'Steve Brook'})" ); // When try ( Transaction tx = session.beginTransaction() ) { StatementResult res = tx.run( "MATCH (n) RETURN n.name" ); // Then assertThat( res.single().get( "n.name" ).asString(), equalTo( "Steve Brook" ) ); } } @Test public void shouldNotAllowSessionLevelStatementsWhenThereIsATransaction() throws Throwable { // Given session.beginTransaction(); // Expect exception.expect( ClientException.class ); // When session.run( "anything" ); } @Test public void shouldBeClosedAfterRollback() throws Throwable { // When Transaction tx = session.beginTransaction(); tx.close(); // Then assertFalse( tx.isOpen() ); } @Test public void shouldBeClosedAfterCommit() throws Throwable { // When Transaction tx = session.beginTransaction(); tx.success(); tx.close(); // Then assertFalse( tx.isOpen() ); } @Test public void shouldBeOpenBeforeCommit() throws Throwable { // When Transaction tx = session.beginTransaction(); // Then assertTrue( tx.isOpen() ); } @Test public void shouldHandleNullParametersGracefully() { // When session.run( "match (n) return count(n)", (Value) null ); // Then // pass - no exception thrown } //See GH #146 @Test public void shouldHandleFailureAfterClosingTransaction() { // GIVEN a successful query in a transaction Transaction tx = session.beginTransaction(); StatementResult result = tx.run( "CREATE (n) RETURN n" ); result.consume(); tx.success(); tx.close(); // EXPECT exception.expect( ClientException.class ); //WHEN running a malformed query in the original session session.run( "CREAT (n) RETURN n" ).consume(); } @SuppressWarnings( "ConstantConditions" ) @Test public void shouldHandleNullRecordParameters() throws Throwable { // When try ( Transaction tx = session.beginTransaction() ) { Record params = null; tx.run( "CREATE (n:FirstNode)", params ); tx.success(); } // Then it wasn't the end of the world as we know it } @SuppressWarnings( "ConstantConditions" ) @Test public void shouldHandleNullValueParameters() throws Throwable { // When try ( Transaction tx = session.beginTransaction() ) { Value params = null; tx.run( "CREATE (n:FirstNode)", params ); tx.success(); } // Then it wasn't the end of the world as we know it } @SuppressWarnings( "ConstantConditions" ) @Test public void shouldHandleNullMapParameters() throws Throwable { // When try ( Transaction tx = session.beginTransaction() ) { Map<String,Object> params = null; tx.run( "CREATE (n:FirstNode)", params ); tx.success(); } // Then it wasn't the end of the world as we know it } @SuppressWarnings( "deprecation" ) @Test public void shouldBeAbleToRunMoreStatementsAfterResetOnNoErrorState() throws Throwable { // Given session.reset(); // When Transaction tx = session.beginTransaction(); tx.run( "CREATE (n:FirstNode)" ); tx.success(); tx.close(); // Then the outcome of both statements should be visible StatementResult result = session.run( "MATCH (n) RETURN count(n)" ); long nodes = result.single().get( "count(n)" ).asLong(); assertThat( nodes, equalTo( 1L ) ); } @SuppressWarnings( "deprecation" ) @Test public void shouldHandleResetBeforeRun() throws Throwable { // Expect exception.expect( ClientException.class ); exception.expectMessage( "Cannot run more statements in this transaction, because previous statements in the " + "transaction has failed and the transaction has been rolled back. Please start a new" + " transaction to run another statement." ); // When Transaction tx = session.beginTransaction(); session.reset(); tx.run( "CREATE (n:FirstNode)" ); } private Transaction globalTx = null; @SuppressWarnings( "deprecation" ) @Test public void shouldHandleResetFromMultipleThreads() throws Throwable { // When ExecutorService runner = Executors.newFixedThreadPool( 2 ); runner.execute( new Runnable() { @Override public void run() { globalTx = session.beginTransaction(); globalTx.run( "CREATE (n:FirstNode)" ); try { Thread.sleep( 1000 ); } catch ( InterruptedException e ) { new AssertionError( e ); } globalTx = session.beginTransaction(); globalTx.run( "CREATE (n:FirstNode)" ); globalTx.success(); globalTx.close(); } } ); runner.execute( new Runnable() { @Override public void run() { try { Thread.sleep( 500 ); } catch ( InterruptedException e ) { new AssertionError( e ); } session.reset(); } } ); runner.awaitTermination( 5, TimeUnit.SECONDS ); // Then the outcome of both statements should be visible StatementResult result = session.run( "MATCH (n) RETURN count(n)" ); long nodes = result.single().get( "count(n)" ).asLong(); assertThat( nodes, equalTo( 1L ) ); } @Test public void shouldRollBackTxIfErrorWithoutConsume() throws Throwable { // Given Transaction tx = session.beginTransaction(); tx.run( "invalid" ); // send run, pull_all tx.success(); try { tx.close(); fail("should fail tx in tx.close()"); } // When send run_commit, and pull_all // Then error and should also send ack_fail, roll_back and pull_all catch ( ClientException e ) { try ( Transaction anotherTx = session.beginTransaction() ) { StatementResult cursor = anotherTx.run( "RETURN 1" ); int val = cursor.single().get( "1" ).asInt(); assertThat( val, equalTo( 1 ) ); } } } @Test public void shouldRollBackTxIfErrorWithConsume() throws Throwable { // Given try ( Transaction tx = session.beginTransaction() ) { StatementResult result = tx.run( "invalid" ); tx.success(); // When result.consume(); // run, pull_all fail( "Should fail tx due to syntax error" ); } // ack_fail, roll_back, pull_all // Then catch ( ClientException e ) { try ( Transaction tx = session.beginTransaction() ) { StatementResult cursor = tx.run( "RETURN 1" ); int val = cursor.single().get( "1" ).asInt(); Assert.assertThat( val, equalTo( 1 ) ); } } } }