0

I have a following flow using Akka Stream scaladsl:

  @testOnly
  def restartableFlow: Flow[MyEvent, AmqpSendResult, NotUsed] = RestartFlow
    .withBackoff(restartSettings) { () =>
      Flow[MyEvent]
        .mapAsyncUnordered(3)(acceptMessages) // uses an injected instance of MqClient returns a Future[AmqpSendResult]
        .map {
          case AmqpSendFailure(command, exception) =>
            exception match {
              case NonFatal(_) =>
                sendToAmqp(command) // requeing of failed messages - out of scope of this question
                throw exception

            }
          case AmqpSendSuccess => AmqpSendSuccess
        }
    }

For testing I am relying on Specs2, ScalaMock and akka.stream.testkit. What I am trying to achieve with the test that is currently failing is this:

  1. two messages are being consumed (sent to) by the restartableFlow
  2. On the first task mqClient is mocked to throw an error and so the inner (supervised flow) should be restarted
  3. On the second task mqClient is mocked to successfully consume message
  4. I am using a TestSink and its subscriber probe - I would expect an error first and than a Next

However instead I get a timeout:

[error]   ! deliver subsequent messages after an AMQP outage
[error]    java.lang.AssertionError: assertion failed: timeout (3 seconds) during expectMsgClass waiting for class akka.stream.testkit.TestSubscriber$OnError (TestKit.scala:571)
[error] akka.testkit.TestKitBase.expectMsgClass_internal(TestKit.scala:571)
[error] akka.testkit.TestKitBase.expectMsgType(TestKit.scala:543)
[error] akka.testkit.TestKitBase.expectMsgType$(TestKit.scala:542)
[error] akka.testkit.TestKit.expectMsgType(TestKit.scala:973)
[error] akka.stream.testkit.TestSubscriber$ManualProbe.expectError(StreamTestKit.scala:486)

Finally here is the test:

    "deliver subsequent messages after an AMQP outage" in
      new ActorsTestSupport() with TestSetup {
        // given a working restartableFlow from AmqpOutputService
        val restartableFlow: Flow[MyEvent, AmqpSendResult, NotUsed] = getInstance(ConfigFactory.parseString("""
              amqp {
                  restart-settings {
                    initial-timeout: 1ms
                    max-timeout: 10s
                    backoff-factor: 2
                  }
                }
              """)).restartableFlow

        // and a MqClient that throws an error once
        (mqClient
          .sendCommandResponse[MyEvent](_: MyEvent))
          .expects(sampleMessage)
          .throwing(new RuntimeException())

        // and that then it continues on successfully passing messages
        (mqClient
          .sendCommandResponse[MyEvent](_: MyEvent))
          .expects(sampleMessage)
          .returning()
          .anyNumberOfTimes()

        // when a message is sent to the flow
        // the supervised stream should be restarted
        val ((probe, _), sub) = TestSource[MyEvent]()
          .via(restartableFlow)
          .watchTermination() {
            Keep.both
          }
          .toMat(testSink)(Keep.both) // testSink is just a TestSink[AmqpSendResult]()
          .run()

        sub.request(2)
        probe.sendNext(sampleMessage)
        sub.expectError()

        probe.sendNext(sampleMessage)
        sub.request(1)
        sub.expectNext()
      }
  }

I don't think that the problem is with ActorTestSupport or TestSetup - I have two other (more straightforward) tests that work using the same setup.

vasigorc
  • 882
  • 11
  • 22

0 Answers0