using System.Text.Json; using Microsoft.Extensions.Logging.Abstractions; using VolcanicCore.Api.Engine; using VolcanicCore.Core; using VolcanicCore.Nodes; // ImplicitUsings pulls System.Threading → collision with our ExecutionContext. using ExecutionContext = VolcanicCore.Api.Engine.ExecutionContext; namespace VolcanicCore.Tests; public class WebhookNodeTests { private sealed class NullReg : IConnectionInstanceRegistry { public Task GetAsync(string id, CancellationToken ct) => throw new InvalidOperationException(); } private sealed class FakeSink : ITriggerSink { public IReadOnlyList? LastItems { get; private set; } public Guid? LastAnswer { get; private set; } public Task EmitAsync(IReadOnlyList items, JsonElement? td, Guid? ans, CancellationToken ct) { LastItems = items; LastAnswer = ans; return Task.CompletedTask; } } [Fact] public async Task StartAsync_registers_path_and_dispose_removes_it() { var registry = new WebhookRegistry(); var node = new WebhookNode(registry); var sink = new FakeSink(); var ctx = new ExecutionContext("", "wf-a", "n1", JsonDocument.Parse("""{"path":"submit"}""").RootElement, null, NullLogger.Instance, null, new NullReg()); var handle = await node.StartAsync(ctx, sink, CancellationToken.None); var invoke = registry.Lookup("wf-a", "submit"); Assert.NotNull(invoke); var body = JsonDocument.Parse("""{"greeting":"hi"}""").RootElement; var channel = Guid.NewGuid(); await invoke!(body, channel, CancellationToken.None); Assert.Equal(channel, sink.LastAnswer); Assert.Equal("hi", sink.LastItems![0].Json.GetProperty("greeting").GetString()); await handle.DisposeAsync(); Assert.Null(registry.Lookup("wf-a", "submit")); } }