103 lines
3.4 KiB
C#
103 lines
3.4 KiB
C#
using System.Text.Json.Nodes;
|
|
using Microsoft.Extensions.Configuration;
|
|
using w4c_workflows.Models.Nodes;
|
|
using w4c_workflows.Services;
|
|
using w4c_workflows.Services.Execution;
|
|
using w4c_workflows.Services.Nodes;
|
|
using w4c_workflows.Services.Nodes.Executors;
|
|
using Xunit;
|
|
|
|
namespace w4c_workflows.Tests;
|
|
|
|
/// <summary>
|
|
/// The <c>core.code</c> node bridges the node kernel to the language runtimes:
|
|
/// input items go in as JSON, the script's JSON output comes back as items.
|
|
/// </summary>
|
|
public class CodeNodeExecutorTests
|
|
{
|
|
private static CodeNodeExecutor Executor()
|
|
{
|
|
var config = new ConfigurationBuilder()
|
|
.AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
["Workflows:TaskTimeoutSeconds"] = "60",
|
|
})
|
|
.Build();
|
|
var runtimes = new RuntimeRegistry(
|
|
new LanguageRegistry(),
|
|
new IScriptExecutor[] { new SubprocessScriptExecutor("shell", "sh", config) });
|
|
return new CodeNodeExecutor(runtimes);
|
|
}
|
|
|
|
private static NodeExecutionContext Context(
|
|
string? language,
|
|
string? entryFile,
|
|
IReadOnlyList<FlowItem> input,
|
|
string? workingDirectory)
|
|
=> new()
|
|
{
|
|
Blueprint = NodeTestData.CoreCatalog().Get("core.code")!,
|
|
Parameters = new JsonObject
|
|
{
|
|
["language"] = language,
|
|
["entryFile"] = entryFile,
|
|
},
|
|
Inputs = new IReadOnlyList<FlowItem>[] { input },
|
|
WorkingDirectory = workingDirectory,
|
|
TenantId = "tenant-1",
|
|
RunId = "run-1",
|
|
TaskId = "task-1",
|
|
NodeName = "code",
|
|
};
|
|
|
|
private static FlowItem Item(string json) => FlowItem.FromJson(JsonNode.Parse(json)!.AsObject());
|
|
|
|
[Fact]
|
|
public async Task Runs_a_shell_script_and_maps_its_json_output_to_items()
|
|
{
|
|
using var dir = new TempDir();
|
|
dir.Write("task.sh", "#!/bin/sh\nread input\necho '{\"ok\":true,\"seen\":\"input\"}'\n");
|
|
|
|
var outcome = await Executor().RunAsync(
|
|
Context("shell", "task.sh", new[] { Item("""{"x":1}""") }, dir.Path), default);
|
|
|
|
Assert.True(outcome.Succeeded, outcome.Failure?.Message);
|
|
var item = Assert.Single(outcome.Outputs[0]);
|
|
Assert.True(item.Json["ok"]!.GetValue<bool>());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Missing_language_fails_with_an_invalid_parameter_code()
|
|
{
|
|
var outcome = await Executor().RunAsync(
|
|
Context(null, "task.sh", Array.Empty<FlowItem>(), null), default);
|
|
|
|
Assert.False(outcome.Succeeded);
|
|
Assert.Equal("invalid_parameter", outcome.Failure!.Code);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Unknown_language_fails_with_a_clear_code()
|
|
{
|
|
var outcome = await Executor().RunAsync(
|
|
Context("cobol", "task.cob", Array.Empty<FlowItem>(), null), default);
|
|
|
|
Assert.False(outcome.Succeeded);
|
|
Assert.Equal("unknown_language", outcome.Failure!.Code);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task A_non_zero_exit_fails_the_node()
|
|
{
|
|
using var dir = new TempDir();
|
|
dir.Write("task.sh", "#!/bin/sh\necho boom 1>&2\nexit 2\n");
|
|
|
|
var outcome = await Executor().RunAsync(
|
|
Context("shell", "task.sh", Array.Empty<FlowItem>(), dir.Path), default);
|
|
|
|
Assert.False(outcome.Succeeded);
|
|
Assert.Equal("script_failed", outcome.Failure!.Code);
|
|
Assert.Contains("boom", outcome.Failure.Message);
|
|
}
|
|
}
|