89 lines
3.1 KiB
C#
89 lines
3.1 KiB
C#
using Microsoft.Data.Sqlite;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using w4c_workflows.Data;
|
|
using w4c_workflows.Services.Credentials;
|
|
using w4c_workflows.Services.Nodes;
|
|
using w4c_workflows.Services.Nodes.Executors;
|
|
using w4c_workflows.Services.Nodes.Interpolation;
|
|
using Xunit;
|
|
|
|
namespace w4c_workflows.Tests;
|
|
|
|
/// <summary>
|
|
/// End-to-end check that a node workflow run emits one audit entry per executed
|
|
/// node through the real <see cref="NodeWorkflowRunner"/> wiring.
|
|
/// </summary>
|
|
public class NodeWorkflowAuditTests
|
|
{
|
|
private const string Tenant = "audit-tenant";
|
|
|
|
private const string Yaml = """
|
|
name: audit-demo
|
|
tasks:
|
|
- id: prepare
|
|
node: { type: core.set }
|
|
parameters:
|
|
mode: manual
|
|
fields:
|
|
- name: status
|
|
value: ready
|
|
next: decide
|
|
- id: decide
|
|
node: { type: core.if }
|
|
parameters:
|
|
left: "={{ $json.status }}"
|
|
operator: equals
|
|
right: ready
|
|
edges:
|
|
- { output: 0, to: ok }
|
|
- { output: 1, to: nope }
|
|
- id: ok
|
|
node: { type: core.noop }
|
|
- id: nope
|
|
node: { type: core.noop }
|
|
""";
|
|
|
|
[Fact]
|
|
public async Task Audit_records_one_entry_per_executed_node()
|
|
{
|
|
var connection = new SqliteConnection("DataSource=:memory:");
|
|
await connection.OpenAsync();
|
|
await using var _connection = connection;
|
|
await using var db = new WorkflowsDbContext(new DbContextOptionsBuilder<WorkflowsDbContext>()
|
|
.UseSqlite(connection)
|
|
.Options);
|
|
await WorkflowsSchema.ApplyAsync(db, liteMode: true);
|
|
|
|
var catalog = NodeTestData.CoreCatalog();
|
|
var workflow = WorkflowDataHelpers.CompileAndSave(db, Tenant, Yaml, "workflows/audit.yaml");
|
|
var run = WorkflowDataHelpers.AddPendingRun(db, workflow, "[{}]");
|
|
|
|
var sink = new RecordingActionAuditSink();
|
|
var executors = new List<INodeExecutor>
|
|
{
|
|
new NoOpNodeExecutor(),
|
|
new SetNodeExecutor(),
|
|
new IfNodeExecutor(),
|
|
};
|
|
var graphRunner = new NodeGraphRunner(
|
|
new NodeExecutorRegistry(executors), new NodeParameterInterpolator());
|
|
var vault = new CredentialVault(new ReversibleTestCipher(), new CredentialTypeCatalog());
|
|
var runner = new NodeWorkflowRunner(catalog, graphRunner, vault, null, sink);
|
|
|
|
var outcome = await runner.RunAsync(db, run, workflow, workingDirectory: null, default);
|
|
|
|
Assert.True(outcome.Succeeded, outcome.Error);
|
|
Assert.Equal(4, sink.Entries.Count);
|
|
Assert.All(sink.Entries, entry =>
|
|
{
|
|
Assert.Equal("node.executed", entry.Action);
|
|
Assert.Equal("succeeded", entry.Outcome);
|
|
Assert.Equal(Tenant, entry.TenantId);
|
|
Assert.Equal(run.Id.ToString(), entry.RunId);
|
|
});
|
|
Assert.Equal(
|
|
new[] { "decide", "nope", "ok", "prepare" },
|
|
sink.Entries.Select(e => e.NodeId!).OrderBy(x => x, StringComparer.Ordinal).ToArray());
|
|
}
|
|
}
|