void HandleTaskResult(Task <object> t, WorkableTask task, WorkableLogger tasklogger) { tasklogger.LogPerformance(); if (t.IsCanceled) { tasklogger.Warning("Workflow execution was aborted"); task.Status = TaskStatus.Canceled; } else if (t.IsFaulted) { tasklogger.Error("Workflow failed to execute", t.Exception?.InnerException ?? t.Exception); task.Status = TaskStatus.Failure; } else { if (t.Result is SuspendState state) { tasklogger.Info($"Workflow was suspended at '{state}'"); task.SuspensionState = state; task.Status = TaskStatus.Suspended; // don't finish task when it gets suspended // else it would get serialized and suspension state is lost // TODO: this could get refactored to allow for state serialization return; } task.Result = t.Result; tasklogger.Info($"Workflow executed successfully with result '{task.Result}'"); task.Status = TaskStatus.Success; } task.Finished = DateTime.Now; taskservice.FinishTask(task.Id).GetAwaiter().GetResult(); }
/// <inheritdoc /> public async Task <WorkableTask> Execute(WorkflowInstance workflow, IDictionary <string, object> arguments, bool profiling, TimeSpan?wait = null) { WorkableTask task = taskservice.CreateTask(WorkableType.Workflow, workflow.Id, workflow.Revision, workflow.Name, arguments); WorkableLogger tasklogger = new WorkableLogger(logger, task); try { task.Task = Task.Run(() => { StateVariableProvider variables = new StateVariableProvider(ProcessImports(tasklogger, workflow.StartNode.Parameters?.Imports)); variables.Add(arguments); WorkflowInstanceState workflowstate = new WorkflowInstanceState(new WorkflowIdentifier(workflow.Id, workflow.Revision, workflow.Name), tasklogger, variables, GetWorkflowInstance, this, workflow.Language, profiling); return(Execute(workflowstate, task.Token.Token, workflow.StartNode)); }).ContinueWith(t => HandleTaskResult(t, task, tasklogger)); } catch (Exception e) { tasklogger.Error("Failed to execute workflow", e); task.Finished = DateTime.Now; task.Status = TaskStatus.Failure; await taskservice.FinishTask(task.Id); } if (wait.HasValue && !task.Task.IsCompleted) { await Task.WhenAny(task.Task, Task.Delay(wait.Value)); } return(task); }
async Task <WorkableTask> Execute(IScript scriptinstance, WorkableTask scripttask, IDictionary <string, object> variables, TimeSpan?wait) { WorkableLogger scriptlogger = new WorkableLogger(logger, scripttask); scripttask.Task = Execute(scriptinstance, scriptlogger, variables, scripttask.Token.Token).ContinueWith(t => { if (t.IsCanceled) { scriptlogger.Warning("Script execution was aborted"); scripttask.Status = TaskStatus.Canceled; } else if (t.IsFaulted) { scriptlogger.Error("Script failed to execute", t.Exception?.InnerException ?? t.Exception); scripttask.Status = TaskStatus.Failure; } else { scripttask.Result = t.Result; scriptlogger.Info($"Script executed successfully with result '{scripttask.Result}'"); scripttask.Status = TaskStatus.Success; } scripttask.Finished = DateTime.Now; scriptinstances.FinishTask(scripttask.Id).GetAwaiter().GetResult(); }); if (wait.HasValue && !scripttask.Task.IsCompleted) { await Task.WhenAny(scripttask.Task, Task.Delay(wait.Value)); } return(scripttask); }
WorkableTask GenerateListVersion(WorkableTask task) { return(new WorkableTask { Id = task.Id, WorkableId = task.WorkableId, WorkableRevision = task.WorkableRevision, WorkableName = task.WorkableName, Started = task.Started, Finished = task.Finished, Runtime = (task.Finished ?? DateTime.Now) - task.Started, Status = task.Status, Result = task.Result }); }
/// <inheritdoc /> public Task StoreTask(WorkableTask task) { task.Finished ??= DateTime.Now; return(insert.ExecuteAsync(task.Id, task.Type, task.WorkableId, task.WorkableRevision, task.WorkableName, task.Parameters.Serialize(), task.Started, task.Finished, task.Status, task.Result.Serialize(), task.Log.Serialize())); }
/// <inheritdoc /> public async Task <WorkableTask> Execute(NamedCode code, IDictionary <string, object> variables = null, TimeSpan?wait = null) { IDictionary <string, object> runtimevariables = await variables.TranslateVariables(scriptcompiler, ScriptLanguage.NCScript); WorkableTask scripttask = scriptinstances.CreateTask(WorkableType.Script, 0, 0, code.Name, runtimevariables); try { return(await Execute(await scriptcompiler.CompileCodeAsync(code.Code, code.Language), scripttask, runtimevariables, wait)); } catch (Exception e) { scripttask.Log.Add(e.ToString()); scripttask.Status = TaskStatus.Failure; await scriptinstances.FinishTask(scripttask.Id); } return(scripttask); }
public async Task ListRunningTask() { Mock <IScriptImportService> importservice = new Mock <IScriptImportService>(); importservice.Setup(s => s.Clone(It.IsAny <WorkableLogger>())).Returns(() => importservice.Object); IEntityManager database = TestSetup.CreateMemoryDatabase(); CacheService cache = new CacheService(new NullLogger <CacheService>()); IScriptCompiler compiler = new ScriptCompiler(new NullLogger <ScriptCompiler>(), new ScriptParser(), cache, null, new Mock <IScriptService>().Object, new Mock <IArchiveService>().Object, importservice.Object, null, null); WorkflowCompiler workflowcompiler = new WorkflowCompiler(new NullLogger <WorkflowCompiler>(), cache, null, compiler); ITaskService taskservice = new DatabaseTaskService(database); WorkflowExecutionService executionservice = new WorkflowExecutionService(new NullLogger <WorkflowExecutionService>(), taskservice, null, null, workflowcompiler); WorkableTask task = await executionservice.Execute(await workflowcompiler.BuildWorkflow(new WorkflowStructure { Name = "JS Test", Nodes = new[] { new NodeData { Type = NodeType.Start }, new NodeData { Type = NodeType.Node, } }, Transitions = new[] { new IndexTransition { OriginIndex = 0, TargetIndex = 1 }, new IndexTransition { OriginIndex = 1, TargetIndex = 0, } } }), new Dictionary <string, object>(), false); Assert.AreEqual(TaskStatus.Running, task.Status); Page <WorkableTask> tasks = await taskservice.ListTasks(); Assert.AreEqual(1, tasks.Total); Assert.AreEqual(1, tasks.Result.Length); task.Token.Cancel(); await Task.WhenAny(task.Task, Task.Delay(1000)); Assert.AreEqual(TaskStatus.Canceled, task.Status); }
public async Task <WorkableTask> Execute([FromBody] ExecuteWorkflowParameters parameters) { logger.LogInformation("Executing {workflow} with parameters '{parameters}'", parameters.Id?.ToString() ?? parameters.Name, string.Join(";", parameters.Parameters?.Select(p => $"{p.Key}={p.Value}") ?? new string[0])); try { if (parameters.Id.HasValue) { if (!string.IsNullOrEmpty(parameters.Name)) { throw new ArgumentException("Either id or name has to be set, not both"); } return(await executionservice.Execute(await compiler.BuildWorkflow(parameters.Id.Value, parameters.Revision), parameters.Parameters, parameters.Profile ?? false, parameters.Wait)); } if (!string.IsNullOrEmpty(parameters.Name)) { return(await executionservice.Execute(await compiler.BuildWorkflow(parameters.Name, parameters.Revision), parameters.Parameters, parameters.Profile ?? false, parameters.Wait)); } if (parameters.Workflow != null) { return(await executionservice.Execute(await compiler.BuildWorkflow(parameters.Workflow), parameters.Parameters, parameters.Profile ?? false, parameters.Wait)); } } catch (Exception e) { WorkableTask failtask = new WorkableTask { Id = Guid.NewGuid(), Status = TaskStatus.Failure, Started = DateTime.Now, Finished = DateTime.Now, Log = new List <string>(new[] { e.ToString() }), Parameters = parameters.Parameters, Type = WorkableType.Workflow, }; await taskservice.StoreTask(failtask); return(failtask); } throw new ArgumentException("Workflow id or name is required"); }
/// <inheritdoc /> public WorkableTask CreateTask(WorkableType type, long workableid, int workablerevision, string workablename, IDictionary <string, object> variables) { WorkableTask task = new WorkableTask { Id = Guid.NewGuid(), Type = type, WorkableId = workableid, WorkableRevision = workablerevision, WorkableName = workablename, Log = new List <string>(), Started = DateTime.Now, Parameters = variables, Status = TaskStatus.Running, Token = new CancellationTokenSource() }; runningtasks[task.Id] = task; return(task); }
/// <inheritdoc /> public async Task <WorkableTask> Execute(long id, int?revision = null, IDictionary <string, object> variables = null, TimeSpan?wait = null) { CompiledScript script = await scriptcompiler.CompileScriptAsync(id, revision); IDictionary <string, object> runtimevariables = await variables.TranslateVariables(scriptcompiler, ScriptLanguage.NCScript); WorkableTask scripttask = scriptinstances.CreateTask(WorkableType.Script, script.Id, script.Revision, script.Name, runtimevariables); try { return(await Execute(script.Instance, scripttask, runtimevariables, wait)); } catch (Exception e) { scripttask.Log.Add(e.ToString()); scripttask.Status = TaskStatus.Failure; await scriptinstances.FinishTask(scripttask.Id); } return(scripttask); }
public async Task ExecuteJavascriptExpression() { Mock <IScriptImportService> importservice = new Mock <IScriptImportService>(); importservice.Setup(s => s.Clone(It.IsAny <WorkableLogger>())).Returns(() => importservice.Object); IEntityManager database = TestSetup.CreateMemoryDatabase(); CacheService cache = new CacheService(new NullLogger <CacheService>()); IScriptCompiler compiler = new ScriptCompiler(new NullLogger <ScriptCompiler>(), new ScriptParser(), cache, null, new Mock <IScriptService>().Object, new Mock <IArchiveService>().Object, importservice.Object, null, null); WorkflowCompiler workflowcompiler = new WorkflowCompiler(new NullLogger <WorkflowCompiler>(), cache, null, compiler); WorkflowExecutionService executionservice = new WorkflowExecutionService(new NullLogger <WorkflowExecutionService>(), new DatabaseTaskService(database), null, null, workflowcompiler); WorkableTask task = await executionservice.Execute(await workflowcompiler.BuildWorkflow(new WorkflowStructure { Name = "JS Test", Nodes = new[] { new NodeData { Type = NodeType.Start }, new NodeData { Type = NodeType.Expression, Parameters = new Dictionary <string, object> { ["Code"] = "return a+b", ["Language"] = ScriptLanguage.JavaScript } } }, Transitions = new[] { new IndexTransition { OriginIndex = 0, TargetIndex = 1 }, } }), new Dictionary <string, object> { ["a"] = 3, ["b"] = 4 }, false); await task.Task; Assert.AreEqual(TaskStatus.Success, task.Status); Assert.AreEqual(7, task.Result); }
public async Task ExecuteWithDictionary() { Mock <ITaskService> taskservice = new Mock <ITaskService>(); taskservice.Setup(s => s.CreateTask(WorkableType.Script, 0, 0, "Test", It.IsAny <IDictionary <string, object> >())).Returns(new WorkableTask() { Token = new CancellationTokenSource(), Log = new List <string>(), Status = TaskStatus.Running }); ScriptExecutionService service = new ScriptExecutionService(new NullLogger <ScriptExecutionService>(), taskservice.Object, new TestCompiler()); WorkableTask task = await service.Execute(new NamedCode { Name = "Test", Code = "return(param.property)" }, new Dictionary <string, object> { ["param"] = "{\"property\":3}" }, TimeSpan.FromSeconds(10)); Assert.AreEqual(TaskStatus.Success, task.Status); Assert.AreEqual(3, task.Result); }
/// <inheritdoc /> public async Task <WorkableTask> Continue(Guid taskid, IDictionary <string, object> variables = null, TimeSpan?wait = null) { WorkableTask task = await taskservice.GetTask(taskid); if (task.Status != TaskStatus.Suspended) { throw new ArgumentException($"Task '{taskid}' is not suspended."); } if (task.SuspensionState == null) { throw new InvalidOperationException($"Task '{taskid}' has no suspension state linked to it."); } WorkableLogger tasklogger = new WorkableLogger(logger, task); tasklogger.Info("Resuming execution of workflow", string.Join("\n", variables?.Select(p => $"{p.Key}={p.Value}") ?? new string[0])); try { task.Status = TaskStatus.Running; task.Task = Task.Run(async() => { WorkflowInstanceState workflowstate = new WorkflowInstanceState(task.SuspensionState.Workflow, tasklogger, task.SuspensionState.Variables, GetWorkflowInstance, this, task.SuspensionState.Language, task.SuspensionState.Profiling); return(await ContinueState(task.SuspensionState, workflowstate, tasklogger, variables, task.Token.Token)); }).ContinueWith(t => HandleTaskResult(t, task, tasklogger)); } catch (Exception e) { tasklogger.Error("Failed to execute workflow", e); task.Finished = DateTime.Now; task.Status = TaskStatus.Failure; await taskservice.FinishTask(task.Id); } if (wait.HasValue && !task.Task.IsCompleted) { await Task.WhenAny(task.Task, Task.Delay(wait.Value)); } return(task); }
public async Task ProfileLoopPerformance() { Mock <IScriptImportService> importservice = new Mock <IScriptImportService>(); importservice.Setup(s => s.Clone(It.IsAny <WorkableLogger>())).Returns(() => importservice.Object); IEntityManager database = TestSetup.CreateMemoryDatabase(); CacheService cache = new CacheService(new NullLogger <CacheService>()); IScriptCompiler compiler = new ScriptCompiler(new NullLogger <ScriptCompiler>(), new ScriptParser(), cache, null, new Mock <IScriptService>().Object, new Mock <IArchiveService>().Object, importservice.Object, null, null); WorkflowCompiler workflowcompiler = new WorkflowCompiler(new NullLogger <WorkflowCompiler>(), cache, null, compiler); WorkflowExecutionService executionservice = new WorkflowExecutionService(new NullLogger <WorkflowExecutionService>(), new DatabaseTaskService(database), null, null, workflowcompiler); WorkableTask task = await executionservice.Execute(await workflowcompiler.BuildWorkflow(new WorkflowStructure { Name = "JS Test", Nodes = new[] { new NodeData { Type = NodeType.Start }, new NodeData { Type = NodeType.Value, Parameters = new Dictionary <string, object> { ["Value"] = "0" }, Variable = "result" }, new NodeData { Type = NodeType.Iterator, Parameters = new Dictionary <string, object> { ["Collection"] = "[1,2,3,4,5]", ["Item"] = "number" }, }, new NodeData { Type = NodeType.BinaryOperation, Parameters = new Dictionary <string, object> { ["Lhs"] = "$result", ["Rhs"] = "$number", ["Op"] = BinaryOperation.Add }, Variable = "result" }, new NodeData { Type = NodeType.Value, Parameters = new Dictionary <string, object> { ["Value"] = "$result" } } }, Transitions = new[] { new IndexTransition { OriginIndex = 0, TargetIndex = 1 }, new IndexTransition { OriginIndex = 1, TargetIndex = 2, }, new IndexTransition { OriginIndex = 2, TargetIndex = 3, Type = TransitionType.Loop, Log = "$\"Adding number {$number}\"" }, new IndexTransition { OriginIndex = 3, TargetIndex = 2 }, new IndexTransition { OriginIndex = 2, TargetIndex = 4 }, } }), new Dictionary <string, object>(), true); await task.Task; Assert.AreEqual(TaskStatus.Success, task.Status); Assert.That(task.Log.Any(l => l.Contains("calls"))); Assert.AreEqual(15, task.Result); }
public async Task ExecuteSubWorkflowWithParametersInLoop() { Mock <IScriptImportService> importservice = new Mock <IScriptImportService>(); importservice.Setup(s => s.Clone(It.IsAny <WorkableLogger>())).Returns(() => importservice.Object); Mock <IArchiveService> archiveservice = new Mock <IArchiveService>(); IEntityManager database = TestSetup.CreateMemoryDatabase(); CacheService cache = new CacheService(new NullLogger <CacheService>()); IScriptCompiler compiler = new ScriptCompiler(new NullLogger <ScriptCompiler>(), new ScriptParser(), cache, null, new Mock <IScriptService>().Object, archiveservice.Object, importservice.Object, null, null); IWorkflowService workflowservice = new DatabaseWorkflowService(database, archiveservice.Object); WorkflowCompiler workflowcompiler = new WorkflowCompiler(new NullLogger <WorkflowCompiler>(), cache, workflowservice, compiler); WorkflowExecutionService executionservice = new WorkflowExecutionService(new NullLogger <WorkflowExecutionService>(), new DatabaseTaskService(database), null, workflowservice, workflowcompiler); await workflowservice.CreateWorkflow(new WorkflowStructure { Name = "Submarine", Nodes = new [] { new NodeData { Type = NodeType.Start }, new NodeData { Type = NodeType.Value, Parameters = new Dictionary <string, object> { ["Value"] = "$value" } } }, Transitions = new[] { new IndexTransition { OriginIndex = 0, TargetIndex = 1 }, } }); WorkableTask task = await executionservice.Execute(await workflowcompiler.BuildWorkflow(new WorkflowStructure { Name = "Subcall Test", Nodes = new[] { new NodeData { Type = NodeType.Start }, new NodeData { Type = NodeType.Value, Parameters = new Dictionary <string, object> { ["Value"] = "0", }, Variable = "result" }, new NodeData { Type = NodeType.Iterator, Parameters = new Dictionary <string, object> { ["Item"] = "value", ["Collection"] = "[1,2,3,4,5]" } }, new NodeData { Type = NodeType.Workflow, Parameters = new Dictionary <string, object> { ["Name"] = "Submarine", ["Arguments"] = new Dictionary <string, object> { ["value"] = "$value" } }, Variable = "subresult" }, new NodeData { Type = NodeType.BinaryOperation, Parameters = new Dictionary <string, object> { ["Lhs"] = "$result", ["Operation"] = BinaryOperation.Add, ["Rhs"] = "$subresult" }, Variable = "result" }, new NodeData { Type = NodeType.Value, Parameters = new Dictionary <string, object> { ["Value"] = "$result", } }, }, Transitions = new[] { new IndexTransition { OriginIndex = 0, TargetIndex = 1 }, new IndexTransition { OriginIndex = 1, TargetIndex = 2 }, new IndexTransition { OriginIndex = 2, TargetIndex = 3, Type = TransitionType.Loop }, new IndexTransition { OriginIndex = 2, TargetIndex = 5 }, new IndexTransition { OriginIndex = 3, TargetIndex = 4 }, new IndexTransition { OriginIndex = 4, TargetIndex = 2 }, } }), new Dictionary <string, object>(), false); await task.Task; Assert.AreEqual(TaskStatus.Success, task.Status); Assert.AreEqual(15, task.Result); }
public async Task SuspendAndContinueWithParameters() { IEntityManager database = TestSetup.CreateMemoryDatabase(); CacheService cache = new CacheService(new NullLogger <CacheService>()); IScriptCompiler compiler = new ScriptCompiler(new NullLogger <ScriptCompiler>(), new ScriptParser(), cache, null, new Mock <IScriptService>().Object, new Mock <IArchiveService>().Object, null, null, null); WorkflowCompiler workflowcompiler = new WorkflowCompiler(new NullLogger <WorkflowCompiler>(), cache, null, compiler); WorkflowExecutionService executionservice = new WorkflowExecutionService(new NullLogger <WorkflowExecutionService>(), new DatabaseTaskService(database), null, null, workflowcompiler); WorkableTask task = await executionservice.Execute(await workflowcompiler.BuildWorkflow(new WorkflowStructure { Name = "Test", Nodes = new[] { new NodeData { Type = NodeType.Start }, new NodeData { Type = NodeType.Suspend, Parameters = new Dictionary <string, object> { ["Variable"] = "x" } }, new NodeData { Type = NodeType.Value, Parameters = new Dictionary <string, object> { ["Value"] = 10 } }, new NodeData { Type = NodeType.Value, Parameters = new Dictionary <string, object> { ["Value"] = 5 } } }, Transitions = new[] { new IndexTransition { OriginIndex = 0, TargetIndex = 1, }, new IndexTransition { OriginIndex = 1, TargetIndex = 2, Condition = "$x>=3" }, new IndexTransition { OriginIndex = 1, TargetIndex = 3, } } }), new Dictionary <string, object>(), false); await task.Task; Assert.AreEqual(TaskStatus.Suspended, task.Status); task = await executionservice.Continue(task.Id, new Dictionary <string, object> { ["x"] = 5 }); await task.Task; Assert.AreEqual(TaskStatus.Success, task.Status); Assert.AreEqual(10, task.Result); }
public async Task ProfileSubWorkflow() { Mock <IScriptImportService> importservice = new Mock <IScriptImportService>(); importservice.Setup(s => s.Clone(It.IsAny <WorkableLogger>())).Returns(() => importservice.Object); Mock <IArchiveService> archiveservice = new Mock <IArchiveService>(); IEntityManager database = TestSetup.CreateMemoryDatabase(); CacheService cache = new CacheService(new NullLogger <CacheService>()); IScriptCompiler compiler = new ScriptCompiler(new NullLogger <ScriptCompiler>(), new ScriptParser(), cache, null, new Mock <IScriptService>().Object, archiveservice.Object, importservice.Object, null, null); IWorkflowService workflowservice = new DatabaseWorkflowService(database, archiveservice.Object); WorkflowCompiler workflowcompiler = new WorkflowCompiler(new NullLogger <WorkflowCompiler>(), cache, workflowservice, compiler); WorkflowExecutionService executionservice = new WorkflowExecutionService(new NullLogger <WorkflowExecutionService>(), new DatabaseTaskService(database), null, workflowservice, workflowcompiler); await workflowservice.CreateWorkflow(new WorkflowStructure { Name = "Submarine", Nodes = new [] { new NodeData { Type = NodeType.Start }, new NodeData { Type = NodeType.Value, Name = "Subvalue", Parameters = new Dictionary <string, object> { ["Value"] = "$job.id" } } }, Transitions = new[] { new IndexTransition { OriginIndex = 0, TargetIndex = 1 }, } }); WorkableTask task = await executionservice.Execute(await workflowcompiler.BuildWorkflow(new WorkflowStructure { Name = "Subcall Test", Nodes = new[] { new NodeData { Type = NodeType.Start }, new NodeData { Type = NodeType.Workflow, Name = "Call Submarine", Parameters = new Dictionary <string, object> { ["Name"] = "Submarine", ["Arguments"] = new Dictionary <string, object> { ["job"] = new Dictionary <string, object> { ["id"] = 1.0 } } } } }, Transitions = new[] { new IndexTransition { OriginIndex = 0, TargetIndex = 1 }, } }), new Dictionary <string, object>(), true); await task.Task; Assert.AreEqual(TaskStatus.Success, task.Status); Assert.That(task.Log.Any(l => l.Contains("calls"))); Assert.AreEqual(1.0, task.Result); }
/// <summary> /// creates a new <see cref="WorkableLogger"/> /// </summary> /// <param name="logger">logger to send logs to</param> /// <param name="instance">script instance to add logs to</param> public WorkableLogger(ILogger logger, WorkableTask instance) { this.logger = logger; this.instance = instance; }
public async Task SumConditionalLoop() { IEntityManager database = TestSetup.CreateMemoryDatabase(); CacheService cache = new CacheService(new NullLogger <CacheService>()); IScriptCompiler compiler = new ScriptCompiler(new NullLogger <ScriptCompiler>(), new ScriptParser(), cache, null, new Mock <IScriptService>().Object, new Mock <IArchiveService>().Object, null, new Mock <IPythonService>().Object, null); WorkflowCompiler workflowcompiler = new WorkflowCompiler(new NullLogger <WorkflowCompiler>(), cache, null, compiler); WorkflowExecutionService executionservice = new WorkflowExecutionService(new NullLogger <WorkflowExecutionService>(), new DatabaseTaskService(database), null, null, workflowcompiler); WorkableTask task = await executionservice.Execute(await workflowcompiler.BuildWorkflow(new WorkflowStructure { Name = "Test", Nodes = new[] { new NodeData { Type = NodeType.Start }, new NodeData { Type = NodeType.Value, Parameters = new Dictionary <string, object> { ["Value"] = 0 }, Variable = "result" }, new NodeData { Type = NodeType.Iterator, Parameters = new Dictionary <string, object> { ["Collection"] = "[1,5,2,2,8,7,4]" } }, new NodeData { Type = NodeType.BinaryOperation, Parameters = new Dictionary <string, object> { ["lhs"] = "$result", ["rhs"] = "$item", ["operation"] = "Add" }, Variable = "result" }, new NodeData { Type = NodeType.Value, Parameters = new Dictionary <string, object> { ["Value"] = "$result" } } }, Transitions = new[] { new IndexTransition { OriginIndex = 0, TargetIndex = 1, }, new IndexTransition { OriginIndex = 1, TargetIndex = 2, }, new IndexTransition { OriginIndex = 2, TargetIndex = 3, Type = TransitionType.Loop, Condition = "$item&1==0" }, new IndexTransition { OriginIndex = 2, TargetIndex = 4, }, new IndexTransition { OriginIndex = 3, TargetIndex = 2 } } }), new Dictionary <string, object>(), false); await task.Task; Assert.AreEqual(Dto.TaskStatus.Success, task.Status); Assert.AreEqual(16, task.Result); }