//https://github.com/Readify/Neo4jClient/issues/127 public async Task ReturnsCorrectError_WhenTransactionIsAutomaticallyRolledBack_ViaNeo4j_2_2_6_Plus(RestTestHarness.Neo4jVersion version) { /* In 2.2.6 ClientErrors (Constraint Violations etc) were changed to Automatically rollback. This created a 404 error when *we* tried to rollback on an error, as the transaction no longer existed. */ var initTransactionRequest = MockRequest.PostJson("/transaction", @"{ 'statements': [{'statement': 'MATCH n\r\nRETURN count(n)', 'resultDataContents':[], 'parameters': {}}]}"); var rollbackTransactionRequest = MockRequest.Delete("/transaction/1"); using (var testHarness = new RestTestHarness { { initTransactionRequest, MockResponse.Json(201, TransactionRestResponseHelper.GenerateCypherErrorResponse(1, "{\"code\":\"Neo.ClientError.Schema.ConstraintViolation\",\"message\":\"Node 19572 already exists with label User and property.\"}"), "http://foo/db/data/transaction/1") }, { rollbackTransactionRequest, MockResponse.Json(404, "{\"results\":[],\"errors\":[{\"code\":\"Neo.ClientError.Transaction.UnknownId\",\"message\":\"Unrecognized transaction id. Transaction may have timed out and been rolled back.\"}]}") } }) { var client = testHarness.CreateGraphClient(version); await client.ConnectAsync(); using (var transaction = client.BeginTransaction()) { await Assert.ThrowsAsync <NeoException>(async() => await client.Cypher.Match("n").Return(n => n.Count()).ExecuteWithoutResultsAsync()); } } }
//https://github.com/Readify/Neo4jClient/issues/127 public async Task ReturnsThe404_WhenVersionIsLessThan_2_2_6(RestTestHarness.Neo4jVersion version) { var initTransactionRequest = MockRequest.PostJson("/transaction", @"{ 'statements': [{'statement': 'MATCH n\r\nRETURN count(n)', 'resultDataContents':[], 'parameters': {}}]}"); var rollbackTransactionRequest = MockRequest.Delete("/transaction/1"); using (var testHarness = new RestTestHarness { { initTransactionRequest, MockResponse.Json(201, TransactionRestResponseHelper.GenerateCypherErrorResponse(1, "{\"code\":\"Neo.ClientError.Schema.ConstraintViolation\",\"message\":\"Node 19572 already exists with label User and property.\"}"), "http://foo/db/data/transaction/1") }, { rollbackTransactionRequest, MockResponse.Json(404, "{\"results\":[],\"errors\":[{\"code\":\"Neo.ClientError.Transaction.UnknownId\",\"message\":\"Unrecognized transaction id. Transaction may have timed out and been rolled back.\"}]}") } }) { var client = testHarness.CreateGraphClient(version); await client.ConnectAsync(); try { using (var transaction = client.BeginTransaction()) { await client.Cypher.Match("n").Return(n => n.Count()).ExecuteWithoutResultsAsync(); } } catch (Exception ex) { Assert.True(ex.Message.Contains("404")); } } }
public async Task UpdateTransactionEndpointAfterFirstRequest() { var initTransactionRequest = MockRequest.PostJson("/transaction", @"{ 'statements': [{'statement': 'MATCH n\r\nRETURN count(n)', 'resultDataContents':[], 'parameters': {}}]}"); var rollbackTransactionRequest = MockRequest.Delete("/transaction/1"); using (var testHarness = new RestTestHarness { { initTransactionRequest, MockResponse.Json(201, TransactionRestResponseHelper.GenerateInitTransactionResponse(1), "http://foo/db/data/transaction/1") }, { rollbackTransactionRequest, MockResponse.Json(200, @"{'results':[], 'errors':[] }") } }) { var client = await testHarness.CreateAndConnectTransactionalGraphClient(); using (var transaction = client.BeginTransaction()) { // dummy query to generate request await client.Cypher .Match("n") .Return(n => n.Count()) .ExecuteWithoutResultsAsync(); Assert.Equal( new Uri("http://foo/db/data/transaction/1"), ((INeo4jTransaction)((TransactionScopeProxy)transaction).TransactionContext).Endpoint); } } }
//https://github.com/Readify/Neo4jClient/issues/127 public async Task ReturnsThe404_WhenVersionIs_2_2_6_Plus_WhenActuallyTimingOut(RestTestHarness.Neo4jVersion version) { var initTransactionRequest = MockRequest.PostJson("/transaction", @"{ 'statements': [{'statement': 'MATCH n\r\nRETURN count(n)', 'resultDataContents':[], 'parameters': {}}]}"); var rollbackTransactionRequest = MockRequest.Delete("/transaction/1"); using (var testHarness = new RestTestHarness() { { initTransactionRequest, MockResponse.Json(201, TransactionRestResponseHelper.GenerateInitTransactionResponse(1), "http://foo/db/data/transaction/1") }, { rollbackTransactionRequest, MockResponse.Json(404, "{\"results\":[],\"errors\":[{\"code\":\"Neo.ClientError.Transaction.UnknownId\",\"message\":\"Unrecognized transaction id. Transaction may have timed out and been rolled back.\"}]}") } }) { var client = testHarness.CreateGraphClient(version); await client.ConnectAsync(); try { using (var transaction = client.BeginTransaction()) { await client.Cypher.Match("n").Return(n => n.Count()).ExecuteWithoutResultsAsync(); } throw new Exception("Should not reach this code, as there is an expected exception."); } catch (Exception ex) { Assert.True(ex.Message.Contains("404")); } } }
public async Task CommitFailsOnPendingAsyncRequests() { const string queryText = @"MATCH (n) RETURN count(n) as Total"; const string resultColumn = @"{'columns':['Total'], 'data':[{'row':[1]}]}"; var cypherQuery = new CypherQuery(queryText, new Dictionary <string, object>(), CypherResultMode.Projection, "neo4j"); var cypherApiQuery = new CypherStatementList { new CypherTransactionStatement(cypherQuery) }; using (var testHarness = new RestTestHarness(false) { { MockRequest.PostObjectAsJson("/transaction", cypherApiQuery), MockResponse.Json(201, TransactionRestResponseHelper.GenerateInitTransactionResponse(1, resultColumn), "http://foo/db/data/transaction/1") } }) { var client = await testHarness.CreateAndConnectTransactionalGraphClient(); var rawClient = (IRawGraphClient)client; using (var tran = client.BeginTransaction()) { await rawClient.ExecuteGetCypherResultsAsync <DummyTotal>(cypherQuery); var ex = await Assert.ThrowsAsync <InvalidOperationException>(async() => await tran.CommitAsync()); Assert.Equal("Cannot commit unless all tasks have been completed", ex.Message); } } }
public async Task RollbackWithoutRequestsShouldNotGenerateMessage() { var initTransactionRequest = MockRequest.PostJson("/transaction", @"{'statements': []}"); var rollbackTransactionRequest = MockRequest.Delete("/transaction/1"); using (var testHarness = new RestTestHarness { { initTransactionRequest, MockResponse.Json(201, TransactionRestResponseHelper.GenerateInitTransactionResponse(1), "http://foo/db/data/transaction/1") }, { rollbackTransactionRequest, MockResponse.Json(200, @"{'results':[], 'errors':[] }") } }.ShouldNotBeCalled(initTransactionRequest, rollbackTransactionRequest)) { var client = await testHarness.CreateAndConnectTransactionalGraphClient(); using (var transaction = client.BeginTransaction()) { // no requests await transaction.RollbackAsync(); } } }
public async Task ExecuteAsyncRequestInTransaction() { const string queryText = @"MATCH (n) RETURN count(n) as Total"; const string resultColumn = @"{'columns':['Total'], 'data':[{'row':[1]}]}"; var cypherQuery = new CypherQuery(queryText, new Dictionary <string, object>(), CypherResultMode.Projection, "neo4j"); var cypherApiQuery = new CypherStatementList { new CypherTransactionStatement(cypherQuery) }; var commitRequest = MockRequest.PostJson("/transaction/1/commit", @"{'statements': []}"); using (var testHarness = new RestTestHarness { { MockRequest.PostObjectAsJson("/transaction", cypherApiQuery), MockResponse.Json(201, TransactionRestResponseHelper.GenerateInitTransactionResponse(1, resultColumn), "http://foo/db/data/transaction/1") }, { commitRequest, MockResponse.Json(200, @"{'results':[], 'errors':[] }") } }) { var client = await testHarness.CreateAndConnectTransactionalGraphClient(); var rawClient = (IRawGraphClient)client; using (var tran = client.BeginTransaction()) { var totalObj = rawClient.ExecuteGetCypherResultsAsync <DummyTotal>(cypherQuery).Result.Single(); Assert.Equal(1, totalObj.Total); await tran.CommitAsync(); } } }
public async Task TransactionCommit() { var initTransactionRequest = MockRequest.PostJson("/transaction", @"{ 'statements': [{'statement': 'MATCH n\r\nRETURN count(n)', 'resultDataContents':[], 'parameters': {}}]}"); var commitRequest = MockRequest.PostJson("/transaction/1/commit", @"{'statements': []}"); using (var testHarness = new RestTestHarness { { initTransactionRequest, MockResponse.Json(201, TransactionRestResponseHelper.GenerateInitTransactionResponse(1), "http://foo/db/data/transaction/1") }, { commitRequest, MockResponse.Json(200, @"{'results':[], 'errors':[] }") } }) { var client = await testHarness.CreateAndConnectTransactionalGraphClient(); using (var transaction = client.BeginTransaction()) { // dummy query to generate request await client.Cypher .Match("n") .Return(n => n.Count()) .ExecuteWithoutResultsAsync(); await transaction.CommitAsync(); } } }
public async Task UsesTheSetDatabase() { const string database = "neo4jclient"; var initTransactionRequest = MockRequest.PostJson($"/db/{database}/tx", "{'statements': [{'statement': 'MATCH n\r\nRETURN count(n)', 'resultDataContents':[], 'parameters': {}}]}"); var rollbackTransactionRequest = MockRequest.Delete($"/db/{database}/tx/1"); var commitTransactionRequest = MockRequest.PostJson($"/db/{database}/tx/1/commit", EmptyStatements); var keepAliveTransactionRequest = MockRequest.PostJson($"/db/{database}/tx/1", EmptyStatements); using (var testHarness = new RestTestHarness(false, "http://foo:7474") { { initTransactionRequest, MockResponse.Json(201, TransactionRestResponseHelper.GenerateInitTransactionResponse(1), $"http://foo:7474/db/{database}/tx/1") }, { commitTransactionRequest, EmptyOkResponse }, { rollbackTransactionRequest, EmptyOkResponse }, { keepAliveTransactionRequest, EmptyOkResponse } }) { var client = await testHarness.CreateAndConnectTransactionalGraphClient(RestTestHarness.Neo4jVersion.Neo40); using (var transaction = client.BeginTransaction(TransactionScopeOption.Join, null, database)) { // dummy query to generate request await client.Cypher .Match("n") .Return(n => n.Count()) .ExecuteWithoutResultsAsync().ConfigureAwait(false); await transaction.KeepAliveAsync(); } } }
public async Task UsesTheDefaultDatabase_WhenNoneSet() { const string database = "neo4j"; var initTransactionRequest = MockRequest.PostJson($"/db/{database}/tx", "{'statements': [{'statement': 'MATCH n\r\nRETURN count(n)', 'resultDataContents':[], 'parameters': {}}]}"); var commitTransactionRequest = MockRequest.PostJson($"/db/{database}/tx/1/commit", EmptyStatements); using (var testHarness = new RestTestHarness(false, "http://foo:7474") { { initTransactionRequest, MockResponse.Json(201, TransactionRestResponseHelper.GenerateInitTransactionResponse(1), $"http://foo:7474/db/{database}/tx/1") }, { commitTransactionRequest, EmptyOkResponse } }) { var client = await testHarness.CreateAndConnectTransactionalGraphClient(RestTestHarness.Neo4jVersion.Neo40); using (var transaction = client.BeginTransaction()) { // dummy query to generate request await client.Cypher .Match("n") .Return(n => n.Count()) .ExecuteWithoutResultsAsync().ConfigureAwait(false); await transaction.CommitAsync().ConfigureAwait(false); var ex = await Assert.ThrowsAsync <ClosedTransactionException>(async() => await transaction.CommitAsync()); ex.Should().NotBeNull(); } } }
public async Task QueryInTransaction_ThrowsExceptionWhen_ExecutingCypherQueryAgainstADifferentDatabaseName() { const string database = "neo4jclient"; var initTransactionRequest = MockRequest.PostJson($"/db/{database}/tx", "{'statements': [{'statement': 'MATCH n\r\nRETURN count(n)', 'resultDataContents':[], 'parameters': {}}]}"); var commitTransactionRequest = MockRequest.PostJson($"/db/{database}/tx/1/commit", EmptyStatements); using (var testHarness = new RestTestHarness(false, "http://foo:7474") { { initTransactionRequest, MockResponse.Json(201, TransactionRestResponseHelper.GenerateInitTransactionResponse(1), $"http://foo:7474/db/{database}/tx/1") }, { commitTransactionRequest, EmptyOkResponse } }) { var client = await testHarness.CreateAndConnectTransactionalGraphClient(RestTestHarness.Neo4jVersion.Neo40); using (var transaction = client.BeginTransaction(TransactionScopeOption.Join, null, database)) { // dummy query to generate request var ex = Assert.Throws <InvalidOperationException>( () => client.Cypher .WithDatabase("neo4j") .Match("n") .Return(n => n.Count())); ex.Should().NotBeNull(); } } }
public async Task DeserializeResultsFromTransaction() { var initTransactionRequest = MockRequest.PostJson("/transaction", @"{ 'statements': [{'statement': 'MATCH n\r\nRETURN count(n)', 'resultDataContents':[], 'parameters': {}}]}"); var deserializationRequest = MockRequest.PostJson("/transaction/1", @"{ 'statements': [{'statement': 'MATCH n\r\nRETURN count(n)', 'resultDataContents':[], 'parameters': {}}]}"); var rollbackTransactionRequest = MockRequest.Delete("/transaction/1"); using (var testHarness = new RestTestHarness { { initTransactionRequest, MockResponse.Json(201, TransactionRestResponseHelper.GenerateInitTransactionResponse(1), "http://foo/db/data/transaction/1") }, { deserializationRequest, MockResponse.Json(200, @"{'results':[{'columns': ['count(n)'], 'data': [{'row': [0]}]}]}") }, { rollbackTransactionRequest, MockResponse.Json(200, @"{'results':[], 'errors':[] }") } }) { var client = await testHarness.CreateAndConnectTransactionalGraphClient(); using (var transaction = client.BeginTransaction()) { // dummy query to generate request await client.Cypher .Match("n") .Return(n => n.Count()) .ExecuteWithoutResultsAsync(); // this query will hit the deserializer var count = await client.Cypher .Match("n") .Return(n => n.Count()) .ResultsAsync; Assert.Equal(count.First(), 0); } } }
public async Task AsyncRequestsInTransactionShouldBeExecutedInOrder() { const string queryTextBase = @"MATCH (n) RETURN {0} as Total"; const string resultColumnBase = @"{{'columns':['Total'], 'data':[{{'row':[{0}]}}]}}"; const int asyncRequests = 15; var queries = new CypherQuery[asyncRequests]; var apiQueries = new CypherStatementList[asyncRequests]; var responses = new MockResponse[asyncRequests]; var testHarness = new RestHarnessWithCounter(); for (int i = 0; i < asyncRequests; i++) { queries[i] = new CypherQuery(string.Format(queryTextBase, i), new Dictionary <string, object>(), CypherResultMode.Projection, "neo4j"); apiQueries[i] = new CypherStatementList { new CypherTransactionStatement(queries[i]) }; responses[i] = MockResponse.Json(200, @"{'results':[" + string.Format(resultColumnBase, i) + @"], 'errors':[] }"); if (i > 0) { testHarness.Add(MockRequest.PostObjectAsJson("/transaction/1", apiQueries[i]), responses[i]); } } testHarness.Add( MockRequest.PostObjectAsJson("/transaction", apiQueries[0]), MockResponse.Json(201, TransactionRestResponseHelper.GenerateInitTransactionResponse(1, string.Format(resultColumnBase, 0)), "http://foo/db/data/transaction/1") ); testHarness.Add( MockRequest.PostJson("/transaction/1/commit", @"{'statements': []}"), MockResponse.Json(200, @"{'results':[], 'errors':[] }") ); try { var client = await testHarness.CreateAndConnectTransactionalGraphClient(); var rawClient = (IRawGraphClient)client; var tasks = new Task[asyncRequests]; using (var tran = client.BeginTransaction()) { for (int i = 0; i < asyncRequests; i++) { int tmpResult = i; tasks[i] = rawClient.ExecuteGetCypherResultsAsync <DummyTotal>(queries[i]).ContinueWith(task => { Assert.Equal(tmpResult, task.Result.Single().Total); }); } await Task.WhenAll(tasks); await tran.CommitAsync(); } } finally { testHarness.Dispose(); } // check that we have a total order Assert.Equal(asyncRequests, testHarness.Queue.Count); int lastElement = -1; for (int i = 0; i < asyncRequests; i++) { int headItem; Assert.True(testHarness.Queue.TryDequeue(out headItem)); headItem.Should().BeGreaterThan(lastElement); lastElement = headItem; } }