|  | 
| 29 | 29 | import static org.junit.jupiter.api.Assertions.assertTrue; | 
| 30 | 30 | import static org.mockito.ArgumentMatchers.any; | 
| 31 | 31 | import static org.mockito.ArgumentMatchers.argThat; | 
|  | 32 | +import static org.mockito.ArgumentMatchers.eq; | 
| 32 | 33 | import static org.mockito.BDDMockito.given; | 
| 33 | 34 | import static org.mockito.BDDMockito.then; | 
| 34 | 35 | import static org.mockito.Mockito.doAnswer; | 
|  | 
| 52 | 53 | import static org.neo4j.driver.testutil.TestUtil.verifyRunRx; | 
| 53 | 54 | 
 | 
| 54 | 55 | import java.util.Collections; | 
|  | 56 | +import java.util.List; | 
| 55 | 57 | import java.util.Set; | 
| 56 | 58 | import java.util.concurrent.CompletableFuture; | 
| 57 | 59 | import java.util.concurrent.CompletionStage; | 
| 58 | 60 | import java.util.concurrent.ExecutionException; | 
| 59 | 61 | import java.util.function.Consumer; | 
|  | 62 | +import java.util.function.Function; | 
| 60 | 63 | import java.util.function.Supplier; | 
| 61 | 64 | import java.util.stream.Stream; | 
|  | 65 | +import org.junit.jupiter.api.Named; | 
| 62 | 66 | import org.junit.jupiter.api.Test; | 
| 63 | 67 | import org.junit.jupiter.params.ParameterizedTest; | 
| 64 | 68 | import org.junit.jupiter.params.provider.Arguments; | 
|  | 
| 72 | 76 | import org.neo4j.driver.exceptions.ConnectionReadTimeoutException; | 
| 73 | 77 | import org.neo4j.driver.exceptions.Neo4jException; | 
| 74 | 78 | import org.neo4j.driver.exceptions.TransactionTerminatedException; | 
|  | 79 | +import org.neo4j.driver.internal.DatabaseBookmark; | 
| 75 | 80 | import org.neo4j.driver.internal.FailableCursor; | 
| 76 | 81 | import org.neo4j.driver.internal.InternalBookmark; | 
| 77 | 82 | import org.neo4j.driver.internal.messaging.BoltProtocol; | 
| 78 | 83 | import org.neo4j.driver.internal.messaging.v4.BoltProtocolV4; | 
|  | 84 | +import org.neo4j.driver.internal.messaging.v53.BoltProtocolV53; | 
| 79 | 85 | import org.neo4j.driver.internal.spi.Connection; | 
| 80 | 86 | import org.neo4j.driver.internal.spi.ResponseHandler; | 
| 81 | 87 | 
 | 
| @@ -476,6 +482,76 @@ void shouldHandleTerminationWhenAlreadyTerminated() throws ExecutionException, I | 
| 476 | 482 |         assertEquals(exception, actualException); | 
| 477 | 483 |     } | 
| 478 | 484 | 
 | 
|  | 485 | +    @ParameterizedTest | 
|  | 486 | +    @MethodSource("transactionClosingTestParams") | 
|  | 487 | +    void shouldThrowOnRunningNewQueriesWhenTransactionIsClosing(TransactionClosingTestParams testParams) { | 
|  | 488 | +        // Given | 
|  | 489 | +        var boltProtocol = mock(BoltProtocol.class); | 
|  | 490 | +        given(boltProtocol.version()).willReturn(BoltProtocolV53.VERSION); | 
|  | 491 | +        var closureStage = new CompletableFuture<DatabaseBookmark>(); | 
|  | 492 | +        var connection = connectionMock(boltProtocol); | 
|  | 493 | +        given(boltProtocol.beginTransaction(eq(connection), any(), any(), any(), any())) | 
|  | 494 | +                .willReturn(completedFuture(null)); | 
|  | 495 | +        given(boltProtocol.commitTransaction(connection)).willReturn(closureStage); | 
|  | 496 | +        given(boltProtocol.rollbackTransaction(connection)).willReturn(closureStage.thenApply(ignored -> null)); | 
|  | 497 | +        var tx = beginTx(connection); | 
|  | 498 | + | 
|  | 499 | +        // When | 
|  | 500 | +        testParams.closeAction().apply(tx); | 
|  | 501 | +        var exception = assertThrows( | 
|  | 502 | +                ClientException.class, () -> await(testParams.runAction().apply(tx))); | 
|  | 503 | + | 
|  | 504 | +        // Then | 
|  | 505 | +        assertEquals(testParams.expectedMessage(), exception.getMessage()); | 
|  | 506 | +    } | 
|  | 507 | + | 
|  | 508 | +    static List<Arguments> transactionClosingTestParams() { | 
|  | 509 | +        Function<UnmanagedTransaction, CompletionStage<?>> asyncRun = tx -> tx.runAsync(new Query("query")); | 
|  | 510 | +        Function<UnmanagedTransaction, CompletionStage<?>> reactiveRun = tx -> tx.runRx(new Query("query")); | 
|  | 511 | +        return List.of( | 
|  | 512 | +                Arguments.of(Named.of( | 
|  | 513 | +                        "commit and run async", | 
|  | 514 | +                        new TransactionClosingTestParams( | 
|  | 515 | +                                UnmanagedTransaction::commitAsync, | 
|  | 516 | +                                asyncRun, | 
|  | 517 | +                                "Cannot run more queries in this transaction, it is being committed"))), | 
|  | 518 | +                Arguments.of(Named.of( | 
|  | 519 | +                        "commit and run reactive", | 
|  | 520 | +                        new TransactionClosingTestParams( | 
|  | 521 | +                                UnmanagedTransaction::commitAsync, | 
|  | 522 | +                                reactiveRun, | 
|  | 523 | +                                "Cannot run more queries in this transaction, it is being committed"))), | 
|  | 524 | +                Arguments.of(Named.of( | 
|  | 525 | +                        "rollback and run async", | 
|  | 526 | +                        new TransactionClosingTestParams( | 
|  | 527 | +                                UnmanagedTransaction::rollbackAsync, | 
|  | 528 | +                                asyncRun, | 
|  | 529 | +                                "Cannot run more queries in this transaction, it is being rolled back"))), | 
|  | 530 | +                Arguments.of(Named.of( | 
|  | 531 | +                        "rollback and run reactive", | 
|  | 532 | +                        new TransactionClosingTestParams( | 
|  | 533 | +                                UnmanagedTransaction::rollbackAsync, | 
|  | 534 | +                                reactiveRun, | 
|  | 535 | +                                "Cannot run more queries in this transaction, it is being rolled back"))), | 
|  | 536 | +                Arguments.of(Named.of( | 
|  | 537 | +                        "close and run async", | 
|  | 538 | +                        new TransactionClosingTestParams( | 
|  | 539 | +                                UnmanagedTransaction::closeAsync, | 
|  | 540 | +                                asyncRun, | 
|  | 541 | +                                "Cannot run more queries in this transaction, it is being rolled back"))), | 
|  | 542 | +                Arguments.of(Named.of( | 
|  | 543 | +                        "close and run reactive", | 
|  | 544 | +                        new TransactionClosingTestParams( | 
|  | 545 | +                                UnmanagedTransaction::closeAsync, | 
|  | 546 | +                                reactiveRun, | 
|  | 547 | +                                "Cannot run more queries in this transaction, it is being rolled back")))); | 
|  | 548 | +    } | 
|  | 549 | + | 
|  | 550 | +    private record TransactionClosingTestParams( | 
|  | 551 | +            Function<UnmanagedTransaction, CompletionStage<?>> closeAction, | 
|  | 552 | +            Function<UnmanagedTransaction, CompletionStage<?>> runAction, | 
|  | 553 | +            String expectedMessage) {} | 
|  | 554 | + | 
| 479 | 555 |     private static UnmanagedTransaction beginTx(Connection connection) { | 
| 480 | 556 |         return beginTx(connection, Collections.emptySet()); | 
| 481 | 557 |     } | 
|  | 
0 commit comments