w4c-workflows-api/w4c-workflows-api.Tests/WorkflowDataHelpers.cs

165 lines
5.1 KiB
C#
Raw Permalink Normal View History

using w4c_workflows.Data;
using w4c_workflows.Models;
using w4c_workflows.Services;
using Xunit;
namespace w4c_workflows.Tests;
/// <summary>Shared compile-and-persist helpers for lifecycle/dispatcher tests.</summary>
internal static class WorkflowDataHelpers
{
public const string LinearYaml = """
name: linear
mode: function
language: shell
entry: { file: root.sh }
tasks:
- id: a
parent: root
next: b
language: shell
entry: { file: a.sh }
- id: b
parent: root
language: shell
entry: { file: b.sh }
""";
public const string DurableYaml = """
name: durable
mode: durable
language: shell
entry: { file: root.sh }
tasks:
- id: a
parent: root
language: shell
entry: { file: a.sh }
""";
/// <summary>Three-task success chain where every chained task declares its own
/// compensation (<c>onError</c>) target, for saga reverse-compensation tests.</summary>
public const string SagaYaml = """
name: saga
mode: function
language: shell
entry: { file: root.sh }
tasks:
- id: a
parent: root
next: b
onError: compensate_a
language: shell
entry: { file: a.sh }
- id: b
parent: root
next: c
onError: compensate_b
language: shell
entry: { file: b.sh }
- id: c
parent: root
onError: compensate_c
language: shell
entry: { file: c.sh }
- id: compensate_a
parent: root
language: shell
entry: { file: compensate_a.sh }
- id: compensate_b
parent: root
language: shell
entry: { file: compensate_b.sh }
- id: compensate_c
parent: root
language: shell
entry: { file: compensate_c.sh }
""";
2026-09-11 22:02:46 +00:00
/// <summary>Two-step node-mode workflow (both steps run in-process, no subprocess).</summary>
public const string NodeYaml = """
name: node-linear
tasks:
- id: seed
node: { type: core.set }
parameters:
mode: json
jsonOutput: { value: 1 }
next: shape
- id: shape
node: { type: core.set }
parameters:
mode: manual
keepOnlySet: true
fields:
- name: copied
value: '={{ $json.value }}'
""";
/// <summary>Node-mode workflow whose step has no installed executor (fails the run).</summary>
public const string NodeNoExecutorYaml = """
name: node-no-executor
tasks:
- id: fetch
node: { type: core.httpRequest }
parameters:
url: https://example.com
""";
public static Workflow CompileAndSave(WorkflowsDbContext db, string tenantId, string yaml, string path = "workflows/linear.yaml")
{
var compiler = new WorkflowCompiler(new WorkflowValidator(new LanguageRegistry()));
var result = compiler.Compile(yaml, path, tenantId);
Assert.True(result.Success, string.Join("\n", result.Errors));
var workflow = result.Workflow!.Workflow;
workflow.Tasks = result.Workflow.Tasks.ToList();
2026-09-11 22:02:46 +00:00
workflow.TaskEdges = result.Workflow.Edges.ToList();
db.Workflows.Add(workflow);
db.SaveChanges();
return workflow;
}
public static WorkflowTask TaskByKey(Workflow workflow, string key)
=> workflow.Tasks.Single(t => t.Key == key);
public static WorkflowRun AddPendingRun(WorkflowsDbContext db, Workflow workflow, string? input = null, Guid? startTaskId = null)
{
var run = new WorkflowRun
{
Id = Guid.NewGuid(),
WorkflowId = workflow.Id,
TenantId = workflow.TenantId,
Status = RunStatus.Pending,
TriggerJson = "{\"type\":\"event\"}",
InputJson = input,
CorrelationId = "test-correlation",
StartTaskId = startTaskId,
};
db.WorkflowRuns.Add(run);
db.SaveChanges();
return run;
}
/// <summary>Adds a run in a terminal/in-flight status with an explicit start time.</summary>
public static WorkflowRun AddRun(WorkflowsDbContext db, Workflow workflow, string status, DateTime? startedAt = null, DateTime? finishedAt = null, string? error = null)
{
var run = new WorkflowRun
{
Id = Guid.NewGuid(),
WorkflowId = workflow.Id,
TenantId = workflow.TenantId,
Status = status,
TriggerJson = "{\"type\":\"event\"}",
CorrelationId = $"corr-{Guid.NewGuid():N}",
StartedAt = startedAt,
FinishedAt = finishedAt,
Error = error,
};
db.WorkflowRuns.Add(run);
db.SaveChanges();
return run;
}
}