package com.github.agourlay.cornichon.steps.wrapped import java.util.concurrent.TimeUnit import cats.data.{ NonEmptyList, StateT } import com.github.agourlay.cornichon.core._ import com.github.agourlay.cornichon.core.Done._ import monix.eval.Task import scala.concurrent.duration.FiniteDuration case class RepeatDuringStep(nested: List[Step], duration: FiniteDuration) extends WrapperStep { val title = s"Repeat block during '$duration'" override val stateUpdate: StepState = StateT { runState => val initialDepth = runState.depth def repeatStepsDuring(runState: RunState, duration: FiniteDuration, retriesNumber: Long): Task[(Long, RunState, Either[FailedStep, Done])] = ScenarioRunner.runStepsShortCircuiting(nested, runState.resetLogStack) // reset logs at each loop to have the possibility to not aggregate in failure case .timed .flatMap { case (executionTime, run) => val (repeatedOnceMore, res) = run val remainingTime = duration - executionTime res.fold( failedStep => { // In case of failure only the logs of the last run are shown to avoid giant traces. Task.now((retriesNumber, repeatedOnceMore, Left(failedStep))) }, _ => { val successState = runState.mergeNested(repeatedOnceMore) if (remainingTime.gt(FiniteDuration(0, TimeUnit.MILLISECONDS))) repeatStepsDuring(successState, remainingTime, retriesNumber + 1) else // In case of success all logs are returned but they are not printed by default. Task.now((retriesNumber, successState, rightDone)) } ) } repeatStepsDuring(runState.nestedContext, duration, 0) .timed .map { case (executionTime, run) => val (retries, repeatedRunState, report) = run val (logStack, res) = report.fold( failedStep => { val wrappedLogStack = FailureLogInstruction(s"Repeat block during '$duration' failed after being retried '$retries' times", initialDepth, Some(executionTime)) +: repeatedRunState.logStack :+ failedTitleLog(initialDepth) val artificialFailedStep = FailedStep.fromSingle(failedStep.step, RepeatDuringBlockContainFailedSteps(duration, failedStep.errors)) (wrappedLogStack, Left(artificialFailedStep)) }, _ => { val wrappedLogStack = SuccessLogInstruction(s"Repeat block during '$duration' succeeded after '$retries' retries", initialDepth, Some(executionTime)) +: repeatedRunState.logStack :+ successTitleLog(initialDepth) (wrappedLogStack, rightDone) } ) (runState.mergeNested(repeatedRunState, logStack), res) } } } case class RepeatDuringBlockContainFailedSteps(duration: FiniteDuration, errors: NonEmptyList[CornichonError]) extends CornichonError { lazy val baseErrorMessage = s"RepeatDuring block failed before '$duration'" override val causedBy = errors.toList }