2

I have recently upgraded quarkus to 2.9.0.CR1 and am now having issues in some of my tests

I am using reactive panache in this, through the repository pattern.

Note: i am able to access the db fine if i launch the app and try to manually test.

My test:

@QuarkusTest
public class MyTest {
   @InjectMock
   MYRepository repository;

   @Test 
   public void test_a() {
       ...
       myService.callMethod().await().indefinitely();
       ...
   }

}

My Service being tested:

@ApplicationScoped
public class MyService {

    @Inject
    MyRepository repository;

    @Inject 
    SessionFactory sessionFactory;

   public Uni<MyDto> callMethod() {
          return Uni.createFrom().item(...) 
                    ... 
                   .flatMap(a -> sessionFactory.openSession()
                                              .chain(session -> session
                                                      .withTransaction(s -> ..../*some db fetch's and persists */)
                                                      .eventually(session::close)));

   }
}

I get the following exceptions however:

Multiple exceptions caught:
        [Exception 0] io.smallrye.mutiny.CompositeException: Multiple exceptions caught:
        [Exception 0] java.lang.IllegalStateException: HR000069: Detected use of the reactive Session from a different Thread than the one which was used to open the reactive Session - this suggests an invalid integration; original thread [166]: 'vert.x-eventloop-thread-3' current Thread [129]: 'executor-thread-0'
        [Exception 1] java.lang.IllegalStateException: HR000068: This method should exclusively be invoked from a Vert.x EventLoop thread; currently running on thread 'executor-thread-0'
        [Exception 1] java.lang.IllegalStateException: HR000069: Detected use of the reactive Session from a different Thread than the one which was used to open the reactive Session - this suggests an invalid integration; original thread [166]: 'vert.x-eventloop-thread-3' current Thread [129]: 'executor-thread-0'
        at io.smallrye.mutiny.groups.UniOnItemOrFailure.lambda$call$1(UniOnItemOrFailure.java:75)
        at io.smallrye.context.impl.wrappers.SlowContextualBiFunction.apply(SlowContextualBiFunction.java:21)
        at io.smallrye.mutiny.operators.uni.UniOnItemOrFailureFlatMap$UniOnItemOrFailureFlatMapProcessor.performInnerSubscription(UniOnItemOrFailureFlatMap.java:86)
        at io.smallrye.mutiny.operators.uni.UniOnItemOrFailureFlatMap$UniOnItemOrFailureFlatMapProcessor.onFailure(UniOnItemOrFailureFlatMap.java:65)
        at io.smallrye.mutiny.operators.uni.UniOperatorProcessor.onFailure(UniOperatorProcessor.java:54)
        at io.smallrye.mutiny.helpers.EmptyUniSubscription.propagateFailureEvent(EmptyUniSubscription.java:40)
        at io.smallrye.mutiny.operators.uni.builders.UniCreateFromCompletionStage.subscribe(UniCreateFromCompletionStage.java:26)
        at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
        at io.smallrye.mutiny.operators.uni.UniRunSubscribeOn.lambda$subscribe$0(UniRunSubscribeOn.java:27)
        at org.hibernate.reactive.context.impl.VertxContext.execute(VertxContext.java:78)
        at io.smallrye.mutiny.operators.uni.UniRunSubscribeOn.subscribe(UniRunSubscribeOn.java:25)
        at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
        at io.smallrye.mutiny.operators.uni.UniOnItemOrFailureFlatMap.subscribe(UniOnItemOrFailureFlatMap.java:27)
        at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
        at io.smallrye.mutiny.operators.uni.UniOnItemOrFailureFlatMap$UniOnItemOrFailureFlatMapProcessor.performInnerSubscription(UniOnItemOrFailureFlatMap.java:99)
        at io.smallrye.mutiny.operators.uni.UniOnItemOrFailureFlatMap$UniOnItemOrFailureFlatMapProcessor.onFailure(UniOnItemOrFailureFlatMap.java:65)
        at io.smallrye.mutiny.operators.uni.UniOnItemOrFailureConsume$UniOnItemOrFailureConsumeProcessor.onFailure(UniOnItemOrFailureConsume.java:46)
        at io.smallrye.mutiny.operators.uni.UniOperatorProcessor.onFailure(UniOperatorProcessor.java:54)
        at io.smallrye.mutiny.operators.uni.UniOperatorProcessor.onFailure(UniOperatorProcessor.java:54)
        at io.smallrye.mutiny.operators.uni.UniOperatorProcessor.onFailure(UniOperatorProcessor.java:54)
        at io.smallrye.mutiny.operators.uni.UniOnCancellationCall$UniOnCancellationCallProcessor.onFailure(UniOnCancellationCall.java:59)
        at io.smallrye.mutiny.operators.uni.UniOnFailureFlatMap$UniOnFailureFlatMapProcessor.performInnerSubscription(UniOnFailureFlatMap.java:94)
        at io.smallrye.mutiny.operators.uni.UniOnFailureFlatMap$UniOnFailureFlatMapProcessor.dispatch(UniOnFailureFlatMap.java:83)
        at io.smallrye.mutiny.operators.uni.UniOnFailureFlatMap$UniOnFailureFlatMapProcessor.onFailure(UniOnFailureFlatMap.java:60)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.performInnerSubscription(UniOnItemTransformToUni.java:73)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.onItem(UniOnItemTransformToUni.java:57)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.onItem(UniOnItemTransformToUni.java:60)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransform$UniOnItemTransformProcessor.onItem(UniOnItemTransform.java:43)
        at io.smallrye.mutiny.operators.uni.builders.UniCreateFromCompletionStage$CompletionStageUniSubscription.forwardResult(UniCreateFromCompletionStage.java:63)
        at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863)
        at java.base/java.util.concurrent.CompletableFuture.uniWhenCompleteStage(CompletableFuture.java:887)
        at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:2325)
        at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:144)
        at io.smallrye.mutiny.operators.uni.builders.UniCreateFromCompletionStage$CompletionStageUniSubscription.forward(UniCreateFromCompletionStage.java:51)
        at io.smallrye.mutiny.operators.uni.builders.UniCreateFromCompletionStage.subscribe(UniCreateFromCompletionStage.java:35)
        at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransform.subscribe(UniOnItemTransform.java:22)
        at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.performInnerSubscription(UniOnItemTransformToUni.java:81)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.onItem(UniOnItemTransformToUni.java:57)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.onItem(UniOnItemTransformToUni.java:60)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.onItem(UniOnItemTransformToUni.java:60)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransform$UniOnItemTransformProcessor.onItem(UniOnItemTransform.java:43)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.onItem(UniOnItemTransformToUni.java:60)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.onItem(UniOnItemTransformToUni.java:60)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.onItem(UniOnItemTransformToUni.java:60)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransform$UniOnItemTransformProcessor.onItem(UniOnItemTransform.java:43)
        at io.smallrye.mutiny.operators.uni.UniOnItemConsume$UniOnItemComsumeProcessor.onItem(UniOnItemConsume.java:43)
        at io.smallrye.mutiny.operators.uni.UniOnItemConsume$UniOnItemComsumeProcessor.onItem(UniOnItemConsume.java:43)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.onItem(UniOnItemTransformToUni.java:60)
        at io.smallrye.mutiny.operators.uni.builders.UniCreateFromFuture.lambda$dispatchDeferredResult$1(UniCreateFromFuture.java:83)
        at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:548)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:833)
        Suppressed: java.lang.IllegalStateException: HR000069: Detected use of the reactive Session from a different Thread than the one which was used to open the reactive Session - this suggests an invalid integration; original thread [166]: 'vert.x-eventloop-thread-3' current Thread [129]: 'executor-thread-0'
                at org.hibernate.reactive.common.InternalStateAssertions.assertCurrentThreadMatches(InternalStateAssertions.java:46)
                at org.hibernate.reactive.session.impl.ReactiveSessionImpl.threadCheck(ReactiveSessionImpl.java:154)
                at org.hibernate.reactive.session.impl.ReactiveSessionImpl.checkOpen(ReactiveSessionImpl.java:1557)
                at org.hibernate.internal.AbstractSharedSessionContract.checkOpenOrWaitingForAutoClose(AbstractSharedSessionContract.java:413)
                at org.hibernate.internal.SessionImpl.closeWithoutOpenChecks(SessionImpl.java:411)
                at org.hibernate.internal.SessionImpl.close(SessionImpl.java:398)
                at org.hibernate.reactive.session.impl.ReactiveSessionImpl.reactiveClose(ReactiveSessionImpl.java:1489)
                at io.smallrye.context.impl.wrappers.SlowContextualSupplier.get(SlowContextualSupplier.java:21)
                at io.smallrye.mutiny.operators.uni.builders.UniCreateFromCompletionStage.subscribe(UniCreateFromCompletionStage.java:24)
                ... 51 more
Caused by: io.smallrye.mutiny.CompositeException: Multiple exceptions caught:
        [Exception 0] java.lang.IllegalStateException: HR000069: Detected use of the reactive Session from a different Thread than the one which was used to open the reactive Session - this suggests an invalid integration; original thread [166]: 'vert.x-eventloop-thread-3' current Thread [129]: 'executor-thread-0'
        [Exception 1] java.lang.IllegalStateException: HR000068: This method should exclusively be invoked from a Vert.x EventLoop thread; currently running on thread 'executor-thread-0'
        ... 37 more
        Suppressed: java.lang.IllegalStateException: HR000068: This method should exclusively be invoked from a Vert.x EventLoop thread; currently running on thread 'executor-thread-0'
                at org.hibernate.reactive.common.InternalStateAssertions.assertUseOnEventLoop(InternalStateAssertions.java:40)
                at org.hibernate.reactive.session.impl.ReactiveSessionImpl.getReactiveConnection(ReactiveSessionImpl.java:1478)
                at org.hibernate.reactive.mutiny.impl.MutinySessionImpl$Transaction.rollback(MutinySessionImpl.java:467)
                at io.smallrye.context.impl.wrappers.SlowContextualSupplier.get(SlowContextualSupplier.java:21)
                at io.smallrye.mutiny.groups.UniOnFailure.lambda$call$5(UniOnFailure.java:133)
                at io.smallrye.context.impl.wrappers.SlowContextualFunction.apply(SlowContextualFunction.java:21)
                at io.smallrye.mutiny.groups.UniOnFailure.lambda$call$4(UniOnFailure.java:102)
                at io.smallrye.context.impl.wrappers.SlowContextualFunction.apply(SlowContextualFunction.java:21)
                at io.smallrye.mutiny.operators.uni.UniOnFailureFlatMap$UniOnFailureFlatMapProcessor.performInnerSubscription(UniOnFailureFlatMap.java:92)
                ... 36 more
Caused by: java.lang.IllegalStateException: HR000069: Detected use of the reactive Session from a different Thread than the one which was used to open the reactive Session - this suggests an invalid integration; original thread [166]: 'vert.x-eventloop-thread-3' current Thread [129]: 'executor-thread-0'
        at org.hibernate.reactive.common.InternalStateAssertions.assertCurrentThreadMatches(InternalStateAssertions.java:46)
        at org.hibernate.reactive.session.impl.ReactiveSessionImpl.threadCheck(ReactiveSessionImpl.java:154)
        at org.hibernate.reactive.session.impl.ReactiveSessionImpl.getReactiveActionQueue(ReactiveSessionImpl.java:164)
        at org.hibernate.reactive.mutiny.impl.MutinySessionImpl$Transaction.beforeCompletion(MutinySessionImpl.java:475)
        at io.smallrye.context.impl.wrappers.SlowContextualSupplier.get(SlowContextualSupplier.java:21)
        at io.smallrye.mutiny.groups.UniOnItem.lambda$call$3(UniOnItem.java:99)
        at io.smallrye.context.impl.wrappers.SlowContextualFunction.apply(SlowContextualFunction.java:21)
        at io.smallrye.mutiny.groups.UniOnItem.lambda$call$2(UniOnItem.java:79)
        at io.smallrye.context.impl.wrappers.SlowContextualFunction.apply(SlowContextualFunction.java:21)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.performInnerSubscription(UniOnItemTransformToUni.java:68)
        ... 33 more

Edit: Beefier example after some more fixes trying to get this to work:

Test:


  @Test
  @TestReactiveTransaction
  public void test_SanintisationAccess_NotExclusiveTenant(UniAsserter uniAsserter) {
    final CustomerDto creationResult = given()
        .when()
        .header("Content-Type", MediaTypes.JSON_TYPE)
        .header(Headers.TENANT, TENANT)
        .header(Headers.EXCLUSIVE_TENANT, exclusiveTenant)
        .and()
        .body("{}")
        .post(BASE_URL)
        .then()
        .extract().response().getBody().as(CustomerDto.class);

    // db validation
 uniAsserter.assertThat(() -> dbUtils.getAllTableEntries(CustomerActive.TABLE), activeCustomers -> {
      assertEquals(1, activeCustomers.size());
    });

    uniAsserter.assertThat(() -> dbUtils.getAllTableEntries(CustomerAudit.TABLE), auditRecords -> {
      assertEquals(1, auditRecords.size());
    });


    final Supplier<Uni<Row>> auditCustomer = () -> dbUtils.getAllTableEntries(CustomerAudit.TABLE).map(auditRecords -> auditRecords.get(0));

    final Response deactivationResponse = given()
        .when()
        .delete(URL_ID, new HashMap<>(){{
          put(PATH_ID, creationResult.getId());
        }})
        .then()
        .extract().response();

    // response validation
    assertEquals(204, deactivationResponse.getStatusCode(), () -> "Got: " + deactivationResponse.prettyPrint());

    final Response response = given()
        .when()
        .delete(URL_ID, new HashMap<>(){{
          put(PATH_ID, creationResult.getId());
        }})
        .then()
        .extract().response();


    assertEquals(403, response.getStatusCode(), () -> "Got: " + response.prettyPrint());
  }

Controller content:

  @POST
  @Path("/customer")
  @ReactiveTransactional
  public Uni<CustomerDto> createCustomer(CustomerPostDto customerPostDto) {
    assertNotEmpty(customerPostDto, "customer creation details");
    return myService.myMethod(customerPostDto);
  }
mangusbrother
  • 3,988
  • 11
  • 51
  • 103

1 Answers1

1

You can't invoke Hibernate Reactive in a unit test like that.

You have to ensure that the call to the database is done on a Vert.x thread and as part of a request context. This is not trivial to do with stock JUnit, but Quarkus does provide some utilities to help you do that.

Essentially you would write something like:

   @Test 
   public void test_a(UniAsserter uniAsserter) {
       uniAsserter.assertThat(() -> myService.callMethod(), () -> {
           // perform some assertion
       });
   }

See this example for more details.

geoand
  • 60,071
  • 24
  • 172
  • 190
  • This seems to also be happening when I try to do the api call with resteasy. Should I somehow have the api call be triggered on a different thread as well? – mangusbrother May 16 '22 at 09:27
  • I meant restassured – mangusbrother May 16 '22 at 11:49
  • I have added some more details aftr the proposed changes. I'm struggling a bit to find documentaiton regarding testing reactive transactions. Can you take a look in case you spot something missing please? – mangusbrother May 16 '22 at 13:14
  • Have you tried wrapping the restassured calls in `uniAsserter.execute`? – geoand May 17 '22 at 05:15
  • wrapped the full test in it and still failed. (I need the result from the rest assured called so iw tould need to be in the same runnable since it doesn't return the result) – mangusbrother May 17 '22 at 07:22
  • By trying this approach, I get `uniAsserter` == null. There is anything I could do in the wrong way? – A.Casanova Sep 13 '22 at 09:58
  • I got it. my asserter was set to null because it is mandatory to add one of the `@TestReactiveTransaction` or `@RunOnVertxContext` annotations (just after the `@Test`one) since otherwise the `UniAsserter asserter` will be always null. – A.Casanova Sep 13 '22 at 10:18