@@ -4,6 +4,7 @@ import akka.actor.{ActorRef, ActorSystem, PoisonPill, Props, Terminated}
44import akka .event .DiagnosticLoggingAdapter
55import akka .testkit .{TestDuration , TestProbe }
66import akka .util .Timeout
7+ import cats .effect .IO
78import cats .effect .unsafe .IORuntime
89import com .ing .baker .il .failurestrategy .{BlockInteraction , FireEventAfterFailure , InteractionFailureStrategy , RetryWithIncrementalBackoff }
910import com .ing .baker .il .petrinet .{InteractionTransition , Place }
@@ -19,6 +20,7 @@ import com.ing.baker.runtime.akka.actor.process_instance.ProcessInstanceSpec._
1920import com .ing .baker .runtime .akka .actor .process_instance .dsl .TestUtils .{PlaceMethods , place }
2021import com .ing .baker .runtime .akka .actor .process_instance .dsl ._
2122import com .ing .baker .runtime .akka .actor .process_instance .internal .ExceptionStrategy .RetryWithDelay
23+ import com .ing .baker .runtime .akka .actor .process_instance .internal .Job
2224import com .ing .baker .runtime .akka .actor .process_instance .{ProcessInstanceProtocol => protocol }
2325import com .ing .baker .runtime .akka .internal .FatalInteractionException
2426import com .ing .baker .runtime .akka .namedCachedThreadPool
@@ -30,6 +32,7 @@ import org.mockito.ArgumentMatchers.any
3032import org .mockito .Mockito ._
3133import org .mockito .invocation .InvocationOnMock
3234import org .mockito .stubbing .Answer
35+ import org .scalatest .BeforeAndAfterEach
3336import org .scalatest .concurrent .ScalaFutures
3437import org .scalatest .matchers .should .Matchers
3538import org .scalatest .time .{Milliseconds , Span }
@@ -56,9 +59,12 @@ trait TestSequenceNet extends SequenceNet {
5659
5760object ProcessInstanceSpec {
5861
62+ var countDownLatch = new java.util.concurrent.CountDownLatch (0 )
63+
5964 def Added (n : Int ): EventInstance = EventInstance (n.toString, Map .empty)
6065
6166 val testExecutionContext = namedCachedThreadPool(s " Baker.CachedThreadPool " )
67+ val interactionExecutorContext = namedCachedThreadPool(s " Baker.InteractionExecutor " )
6268
6369 val instanceSettings = Settings (
6470 executionContext = testExecutionContext,
@@ -79,7 +85,14 @@ object ProcessInstanceSpec {
7985 CompiledRecipe (" name" , UUID .randomUUID().toString, topology, Marking .empty, Seq .empty, Option .empty, Option .empty),
8086 settings,
8187 runtime,
82- delayedTransitionActor)
88+ delayedTransitionActor) {
89+ override def executeJobViaExecutor (job : Job [RecipeInstanceState ], originalSender : ActorRef ): Unit = {
90+ IO .delay {
91+ countDownLatch.await(10 , TimeUnit .SECONDS )
92+ super .executeJobViaExecutor(job, originalSender)
93+ }.evalOn(interactionExecutorContext).unsafeRunAndForget()(IORuntime .global)
94+ }
95+ }
8396 )
8497 }
8598
@@ -96,7 +109,7 @@ object ProcessInstanceSpec {
96109 }
97110}
98111
99- class ProcessInstanceSpec extends AkkaTestBase (" ProcessInstanceSpec" ) with ScalaFutures with MockitoSugar with Matchers {
112+ class ProcessInstanceSpec extends AkkaTestBase (" ProcessInstanceSpec" ) with ScalaFutures with MockitoSugar with Matchers with BeforeAndAfterEach {
100113
101114 def dilatedMillis (millis : Long )(implicit system : ActorSystem ): Long = FiniteDuration (millis, TimeUnit .MILLISECONDS ).dilated.toMillis
102115
@@ -107,6 +120,11 @@ class ProcessInstanceSpec extends AkkaTestBase("ProcessInstanceSpec") with Scala
107120 Thread .sleep(dilatedMillis(100 ))
108121 }
109122
123+ override def beforeEach (): Unit = {
124+ countDownLatch = new java.util.concurrent.CountDownLatch (0 )
125+ super .beforeEach()
126+ }
127+
110128 " A persistent petri net actor" should {
111129
112130 " Respond with an Initialized response after processing an Initialize command" in new TestSequenceNet {
@@ -190,7 +208,6 @@ class ProcessInstanceSpec extends AkkaTestBase("ProcessInstanceSpec") with Scala
190208 }
191209
192210 " Be able to retry a failed (blocked) transition when requested" in new TestSequenceNet {
193-
194211 val counter = new AtomicInteger (0 )
195212
196213 override val sequence = Seq (
@@ -207,25 +224,99 @@ class ProcessInstanceSpec extends AkkaTestBase("ProcessInstanceSpec") with Scala
207224 expectMsgClass(classOf [Initialized ])
208225
209226 actor ! FireTransition (transitionId = 1 , input = null )
210-
211227 expectMsgClass(classOf [TransitionFailed ])
212228
229+ // Validate the instance is in the correct state
213230 actor ! GetState
214-
215231 val state : InstanceState = expectMsgClass(classOf [InstanceState ])
216-
217232 state.jobs.size shouldBe 1
218-
219233 val (jobId, jobState) = state.jobs.head
220-
221234 jobState.exceptionState should matchPattern {
222235 case Some (ExceptionState (_, _, BlockTransition )) =>
223236 }
224237
238+ // Set the Countdown latch so that the job is only finished when we want.
239+ countDownLatch = new java.util.concurrent.CountDownLatch (1 )
240+
241+ // Send the OverrideExceptionStrategy message
225242 actor ! OverrideExceptionStrategy (jobId, protocol.ExceptionStrategy .RetryWithDelay (0 ))
226243
227- // expect that the failure is resolved
244+ // Validate job is executing
245+ actor ! GetState
246+ val newState : InstanceState = expectMsgClass(classOf [InstanceState ])
247+ newState.jobs.size shouldBe 1
248+ val (newJobId, newJobState) = newState.jobs.head
249+ newJobId shouldBe jobId
250+ newJobState.exceptionState should matchPattern {
251+ case Some (ExceptionState (_, _, com.ing.baker.runtime.akka.actor.process_instance.ProcessInstanceProtocol .ExceptionStrategy .RetryWithDelay (0 ))) =>
252+ }
253+
254+ // Allow the job to finish
255+ countDownLatch.countDown()
256+
257+ // Validate the transition is fired successfully after retry
258+ expectMsgPF() { case TransitionFired (_, 1 , _, _, _, _, _) => }
259+
260+ // Validate no job is left open
261+ actor ! GetState
262+ val finalState : InstanceState = expectMsgClass(classOf [InstanceState ])
263+ finalState.jobs.size shouldBe 0
264+ }
265+
266+ " Be able to retry a failed (blocked) transition when requested with delay" in new TestSequenceNet {
267+ val counter = new AtomicInteger (0 )
268+
269+ override val sequence = Seq (
270+ transition() { _ =>
271+ if (counter.getAndIncrement() == 0 )
272+ throw new RuntimeException (" t1 failed!" )
273+ else
274+ Added (1 )
275+ })
276+
277+ val actor = createProcessInstance(petriNet, runtime)
278+
279+ actor ! Initialize (initialMarking, RecipeInstanceState (UUID .randomUUID().toString, UUID .randomUUID().toString, Map .empty, Map .empty, Seq .empty))
280+ expectMsgClass(classOf [Initialized ])
281+
282+ actor ! FireTransition (transitionId = 1 , input = null )
283+ expectMsgClass(classOf [TransitionFailed ])
284+
285+ // Validate the instance is in the correct state
286+ actor ! GetState
287+ val state : InstanceState = expectMsgClass(classOf [InstanceState ])
288+ state.jobs.size shouldBe 1
289+ val (jobId, jobState) = state.jobs.head
290+ jobState.exceptionState should matchPattern {
291+ case Some (ExceptionState (_, _, BlockTransition )) =>
292+ }
293+
294+ // Set the Countdown latch so that the job is only finished when we want.
295+ countDownLatch = new java.util.concurrent.CountDownLatch (1 )
296+
297+ // Send the OverrideExceptionStrategy message
298+ actor ! OverrideExceptionStrategy (jobId, protocol.ExceptionStrategy .RetryWithDelay (1000 ))
299+
300+ // Validate job is executing
301+ actor ! GetState
302+ val newState : InstanceState = expectMsgClass(classOf [InstanceState ])
303+ newState.jobs.size shouldBe 1
304+ val (newJobId, newJobState) = newState.jobs.head
305+ newJobId shouldBe jobId
306+ newJobState.exceptionState should matchPattern {
307+ case Some (ExceptionState (_, _, com.ing.baker.runtime.akka.actor.process_instance.ProcessInstanceProtocol .ExceptionStrategy .RetryWithDelay (1000 ))) =>
308+ }
309+
310+ // Allow the job to finish
311+ countDownLatch.countDown()
312+
313+ // Validate the transition is fired successfully after retry
228314 expectMsgPF() { case TransitionFired (_, 1 , _, _, _, _, _) => }
315+
316+ // Validate no job is left open
317+ actor ! GetState
318+ val finalState : InstanceState = expectMsgClass(classOf [InstanceState ])
319+ finalState.jobs.size shouldBe 0
229320 }
230321
231322 " Be able to resolve a failed (blocked) transition when requested" in new TestSequenceNet {
0 commit comments