Add project files.
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
<Project>
|
||||
|
||||
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="GitHubActionsTestLogger" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="NSubstitute" />
|
||||
<PackageReference Include="coverlet.collector" PrivateAssets="all" />
|
||||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,10 @@
|
||||
<Project>
|
||||
|
||||
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Testcontainers.PostgreSql"/>
|
||||
<PackageReference Include="Testcontainers.RabbitMq"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,92 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\apps\Elsa.Server.Web\Elsa.Server.Web.csproj"/>
|
||||
<ProjectReference Include="..\..\..\src\clients\Elsa.Api.Client\Elsa.Api.Client.csproj"/>
|
||||
<ProjectReference Include="..\..\..\src\common\Elsa.Testing.Shared.Component\Elsa.Testing.Shared.Component.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.Caching.Distributed.MassTransit\Elsa.Caching.Distributed.MassTransit.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="xunit.runner.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\BasicWorkflows\hello-world.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\WorkflowCompletion\fork.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\LogPersistenceModes\input-output-logging-1.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\LogPersistenceModes\input-output-logging-2.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\LogPersistenceModes\input-output-logging-3.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\LogPersistenceModes\input-output-logging-3-child.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\WorkflowCompletion\hello-world.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\CachingAndWorkflowDefinitionActivity\Workflows\workflow-definition-child.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\CachingAndWorkflowDefinitionActivity\Workflows\workflow-definition-grand-child.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\CachingAndWorkflowDefinitionActivity\Workflows\workflow-definition-parent.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\WorkflowActivities\Workflows\CacheInvalidation_Child.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\WorkflowActivities\Workflows\CacheInvalidation_Parent.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\WorkflowActivities\Workflows\CacheInvalidation_Childv2.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\WorkflowJsonStructures\main.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\WorkflowJsonStructures\sub.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\WorkflowDefinitionRefresh\dynamic-endpoint-http-workflow.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\WorkflowDefinitionReload\http-workflow.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\WorkflowDefinitionReload\Workflows\http-workflow.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\HttpWorkflows\Workflows\http-hello-world.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\InputOutput\Workflows\consumer.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Scenarios\InputOutput\Workflows\provider.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=helpers/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=helpers_005Ccontracts/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=helpers_005Ceventargs/@EntryIndexedValue">True</s:Boolean>
|
||||
</wpf:ResourceDictionary>
|
||||
@@ -0,0 +1,25 @@
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Abstractions;
|
||||
|
||||
[Collection(nameof(AppCollection))]
|
||||
public abstract class AppComponentTest(App app) : IDisposable
|
||||
{
|
||||
protected WorkflowServer WorkflowServer { get; } = app.WorkflowServer;
|
||||
protected Cluster Cluster { get; } = app.Cluster;
|
||||
protected Infrastructure Infrastructure { get; } = app.Infrastructure;
|
||||
protected IServiceScope Scope { get; } = app.WorkflowServer.Services.CreateScope();
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
// Disposing the Scope here and in other places where it is created somehow seems to cause the test runner to hang when running other test projects.
|
||||
// Let's comment it out for the time being.
|
||||
//Scope.Dispose();
|
||||
OnDispose();
|
||||
}
|
||||
|
||||
protected virtual void OnDispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json.Serialization;
|
||||
using Elsa.Expressions.Models;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Testing.Shared.Services;
|
||||
using Elsa.Workflows.Memory;
|
||||
using Elsa.Workflows.Models;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Activities;
|
||||
|
||||
public class TriggerSignal : CodeActivity
|
||||
{
|
||||
/// <inheritdoc />
|
||||
[JsonConstructor]
|
||||
private TriggerSignal(string? source = null, int? line = null) : base(source, line)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public TriggerSignal(string eventName, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(new Literal<string>(eventName), source, line)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public TriggerSignal(Func<string> eventName, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null)
|
||||
: this(Expression.DelegateExpression(eventName), source, line)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public TriggerSignal(Func<ExpressionExecutionContext, string?> eventName, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null)
|
||||
: this(Expression.DelegateExpression(eventName), source, line)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public TriggerSignal(Variable<string> variable, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(source, line) => EventName = new Input<string>(variable);
|
||||
|
||||
/// <inheritdoc />
|
||||
public TriggerSignal(Literal<string> literal, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(source, line) => EventName = new Input<string>(literal);
|
||||
|
||||
/// <inheritdoc />
|
||||
public TriggerSignal(Expression expression, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(source, line) => EventName = new Input<string>(expression, new MemoryBlockReference());
|
||||
|
||||
/// <inheritdoc />
|
||||
public TriggerSignal(Input<string> eventName, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(source, line) => EventName = eventName;
|
||||
|
||||
public Input<string> EventName { get; set; }
|
||||
|
||||
protected override void Execute(ActivityExecutionContext context)
|
||||
{
|
||||
var testEventManager = context.GetRequiredService<SignalManager>();
|
||||
var eventName = EventName.Get(context);
|
||||
testEventManager.Trigger(eventName);
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
using Elsa.MassTransit.Messages;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Testing.Shared.Services;
|
||||
using Hangfire.Annotations;
|
||||
using MassTransit;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Consumers;
|
||||
|
||||
[UsedImplicitly]
|
||||
public class WorkflowDefinitionEventConsumer(WorkflowDefinitionEvents workflowDefinitionEvents) : IConsumer<WorkflowDefinitionDeleted>
|
||||
{
|
||||
public Task Consume(ConsumeContext<WorkflowDefinitionDeleted> context)
|
||||
{
|
||||
workflowDefinitionEvents.OnWorkflowDefinitionDeleted(new WorkflowDefinitionDeletedEventArgs(context.Message.Id));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
using Elsa.Caching;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Testing.Shared.Services;
|
||||
using Hangfire.Annotations;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Decorators;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a decorator for the `IChangeTokenSignaler` interface that triggers change token events.
|
||||
/// This class is used to raise change token signal events and delegate the actual work to the decorated service.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class EventPublishingChangeTokenSignaler(IChangeTokenSignaler decoratedService, TriggerChangeTokenSignalEvents triggerChangeTokenSignalEvents) : IChangeTokenSignaler
|
||||
{
|
||||
public IChangeToken GetToken(string key)
|
||||
{
|
||||
return decoratedService.GetToken(key);
|
||||
}
|
||||
|
||||
public ValueTask TriggerTokenAsync(string key, CancellationToken cancellationToken = default)
|
||||
{
|
||||
triggerChangeTokenSignalEvents.RaiseChangeTokenSignalTriggered(new TriggerChangeTokenSignalEventArgs(key));
|
||||
return decoratedService.TriggerTokenAsync(key, cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Hangfire.Annotations;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Fixtures;
|
||||
|
||||
[UsedImplicitly]
|
||||
public class App : IAsyncLifetime
|
||||
{
|
||||
public App()
|
||||
{
|
||||
Infrastructure = new();
|
||||
Cluster = new(Infrastructure);
|
||||
WorkflowServer = Cluster.Pod1;
|
||||
}
|
||||
|
||||
public Infrastructure Infrastructure { get; set; }
|
||||
public Cluster Cluster { get; set; }
|
||||
public WorkflowServer WorkflowServer { get; set; }
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
await Infrastructure.InitializeAsync();
|
||||
}
|
||||
|
||||
public async Task DisposeAsync()
|
||||
{
|
||||
await Cluster.DisposeAsync();
|
||||
await Infrastructure.DisposeAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Elsa.Workflows.ComponentTests.Fixtures;
|
||||
|
||||
[CollectionDefinition(nameof(AppCollection))]
|
||||
public class AppCollection : ICollectionFixture<App>
|
||||
{
|
||||
// This class has no code, and is never created. Its purpose is simply
|
||||
// to be the place to apply [CollectionDefinition] and all the
|
||||
// ICollectionFixture<> interfaces.
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Hangfire.Annotations;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Fixtures;
|
||||
|
||||
[UsedImplicitly]
|
||||
public class Cluster(Infrastructure infrastructure) : IAsyncDisposable
|
||||
{
|
||||
public WorkflowServer Pod3 { get; set; } = new(infrastructure, "http://localhost:5003");
|
||||
public WorkflowServer Pod2 { get; set; } = new(infrastructure, "http://localhost:5002");
|
||||
public WorkflowServer Pod1 { get; set; } = new(infrastructure, "http://localhost:5001");
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await Pod3.DisposeAsync();
|
||||
await Pod2.DisposeAsync();
|
||||
await Pod1.DisposeAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Testcontainers.PostgreSql;
|
||||
using Testcontainers.RabbitMq;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Fixtures;
|
||||
|
||||
public class Infrastructure : IAsyncLifetime
|
||||
{
|
||||
public readonly PostgreSqlContainer DbContainer = new PostgreSqlBuilder()
|
||||
.WithImage("postgres:latest")
|
||||
.WithDatabase("elsa")
|
||||
.WithUsername("postgres")
|
||||
.WithPassword("postgres")
|
||||
.Build();
|
||||
|
||||
public readonly RabbitMqContainer RabbitMqContainer = new RabbitMqBuilder()
|
||||
.WithImage("rabbitmq:4-management")
|
||||
.Build();
|
||||
|
||||
public Task InitializeAsync()
|
||||
{
|
||||
return Task.WhenAll(
|
||||
DbContainer.StartAsync(),
|
||||
RabbitMqContainer.StartAsync());
|
||||
}
|
||||
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
return Task.WhenAll(
|
||||
DbContainer.StopAsync(),
|
||||
RabbitMqContainer.StopAsync());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
using System.Reflection;
|
||||
using Elsa.Alterations.Extensions;
|
||||
using Elsa.Caching;
|
||||
using Elsa.EntityFrameworkCore.Extensions;
|
||||
using Elsa.EntityFrameworkCore.Modules.Alterations;
|
||||
using Elsa.EntityFrameworkCore.Modules.Identity;
|
||||
using Elsa.EntityFrameworkCore.Modules.Management;
|
||||
using Elsa.EntityFrameworkCore.Modules.Runtime;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Identity.Providers;
|
||||
using Elsa.MassTransit.Extensions;
|
||||
using Elsa.Testing.Shared.Handlers;
|
||||
using Elsa.Testing.Shared.Services;
|
||||
using Elsa.Workflows.ComponentTests.Consumers;
|
||||
using Elsa.Workflows.ComponentTests.Decorators;
|
||||
using Elsa.Workflows.ComponentTests.Materializers;
|
||||
using Elsa.Workflows.ComponentTests.WorkflowProviders;
|
||||
using Elsa.Workflows.Management;
|
||||
using Elsa.Workflows.Runtime.Distributed.Extensions;
|
||||
using FluentStorage;
|
||||
using Hangfire.Annotations;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Refit;
|
||||
using static Elsa.Api.Client.RefitSettingsHelper;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Fixtures;
|
||||
|
||||
[UsedImplicitly]
|
||||
public class WorkflowServer(Infrastructure infrastructure, string url) : WebApplicationFactory<Program>
|
||||
{
|
||||
public TClient CreateApiClient<TClient>()
|
||||
{
|
||||
var client = CreateClient();
|
||||
client.BaseAddress = new(client.BaseAddress!, "/elsa/api");
|
||||
client.Timeout = TimeSpan.FromMinutes(1);
|
||||
return RestService.For<TClient>(client, CreateRefitSettings(Services));
|
||||
}
|
||||
|
||||
public HttpClient CreateHttpWorkflowClient()
|
||||
{
|
||||
var client = CreateClient();
|
||||
client.BaseAddress = new(client.BaseAddress!, "/workflows/");
|
||||
client.Timeout = TimeSpan.FromMinutes(1);
|
||||
return client;
|
||||
}
|
||||
|
||||
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
||||
{
|
||||
var dbConnectionString = infrastructure.DbContainer.GetConnectionString();
|
||||
var rabbitMqConnectionString = infrastructure.RabbitMqContainer.GetConnectionString();
|
||||
|
||||
builder.UseUrls(url);
|
||||
|
||||
if (Program.ConfigureForTest == null)
|
||||
{
|
||||
Program.ConfigureForTest = elsa =>
|
||||
{
|
||||
elsa.AddWorkflowsFrom<WorkflowServer>();
|
||||
elsa.AddActivitiesFrom<WorkflowServer>();
|
||||
elsa.UseDefaultAuthentication(defaultAuthentication => defaultAuthentication.UseAdminApiKey());
|
||||
elsa.UseFluentStorageProvider(sp =>
|
||||
{
|
||||
var assemblyLocation = Assembly.GetExecutingAssembly().Location;
|
||||
var assemblyDirectory = Path.GetDirectoryName(assemblyLocation)!;
|
||||
var workflowsDirectorySegments = new[]
|
||||
{
|
||||
assemblyDirectory, "Scenarios"
|
||||
};
|
||||
var workflowsDirectory = Path.Join(workflowsDirectorySegments);
|
||||
return StorageFactory.Blobs.DirectoryFiles(workflowsDirectory);
|
||||
});
|
||||
elsa.UseMassTransit(massTransit =>
|
||||
{
|
||||
//massTransit.UseRabbitMq(rabbitMqConnectionString);
|
||||
massTransit.Services.AddSingleton<WorkflowDefinitionEvents>();
|
||||
massTransit.AddConsumer<WorkflowDefinitionEventConsumer>("elsa-test-workflow-definition-updates", true);
|
||||
});
|
||||
elsa.UseIdentity(identity => identity.UseEntityFrameworkCore(ef => ef.UsePostgreSql(dbConnectionString)));
|
||||
elsa.UseWorkflowManagement(management =>
|
||||
{
|
||||
management.UseEntityFrameworkCore(ef => ef.UsePostgreSql(dbConnectionString));
|
||||
management.UseMassTransitDispatcher();
|
||||
management.UseCache();
|
||||
});
|
||||
elsa.UseWorkflowRuntime(runtime =>
|
||||
{
|
||||
runtime.UseEntityFrameworkCore(ef => ef.UsePostgreSql(dbConnectionString));
|
||||
runtime.UseCache();
|
||||
runtime.UseMassTransitDispatcher();
|
||||
runtime.UseDistributedRuntime();
|
||||
});
|
||||
elsa.UseDistributedCache(distributedCaching =>
|
||||
{
|
||||
distributedCaching.UseMassTransit();
|
||||
});
|
||||
elsa.UseJavaScript(options =>
|
||||
{
|
||||
options.AllowClrAccess = true;
|
||||
options.ConfigureEngine(engine =>
|
||||
{
|
||||
engine.SetValue("getStaticValue", () => StaticValueHolder.Value);
|
||||
});
|
||||
});
|
||||
elsa.UseAlterations(alterations =>
|
||||
{
|
||||
alterations.UseEntityFrameworkCore(ef => ef.UsePostgreSql(dbConnectionString));
|
||||
});
|
||||
elsa.UseHttp(http =>
|
||||
{
|
||||
http.UseCache();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
builder.ConfigureTestServices(services =>
|
||||
{
|
||||
services
|
||||
.AddSingleton<SignalManager>()
|
||||
.AddScoped<WorkflowEvents>()
|
||||
.AddScoped<WorkflowDefinitionEvents>()
|
||||
.AddSingleton<TriggerChangeTokenSignalEvents>()
|
||||
.AddScoped<IWorkflowMaterializer, TestWorkflowMaterializer>()
|
||||
.AddNotificationHandlersFrom<WorkflowServer>()
|
||||
.AddWorkflowDefinitionProvider<TestWorkflowProvider>()
|
||||
.AddNotificationHandlersFrom<WorkflowEventHandlers>()
|
||||
.Decorate<IChangeTokenSignaler, EventPublishingChangeTokenSignaler>()
|
||||
;
|
||||
});
|
||||
}
|
||||
|
||||
protected override void ConfigureClient(HttpClient client)
|
||||
{
|
||||
client.DefaultRequestHeaders.Authorization = new("ApiKey", AdminApiKeyProvider.DefaultApiKey);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.ComponentTests.WorkflowProviders;
|
||||
using Elsa.Workflows.Management;
|
||||
using Elsa.Workflows.Management.Entities;
|
||||
using Elsa.Workflows.Runtime;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Materializers;
|
||||
|
||||
/// <summary>
|
||||
/// A workflow materializer that deserializes workflows created from <see cref="TestWorkflowProvider"/>.
|
||||
/// </summary>
|
||||
public class TestWorkflowMaterializer(IEnumerable<IWorkflowsProvider> workflowProviders) : IWorkflowMaterializer
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the materializer.
|
||||
/// </summary>
|
||||
public const string MaterializerName = "Test";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => MaterializerName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ValueTask<Workflow> MaterializeAsync(WorkflowDefinition definition, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var testProvider = (TestWorkflowProvider)workflowProviders.Single(x => x is TestWorkflowProvider);
|
||||
var materializedWorkflow = testProvider.MaterializedWorkflows.First(x => x.Workflow.Identity.Id == definition.Id);
|
||||
|
||||
return ValueTask.FromResult(materializedWorkflow.Workflow);
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
using Elsa.Workflows.Runtime;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.WorkflowProviders;
|
||||
|
||||
public class TestWorkflowProvider : IWorkflowsProvider
|
||||
{
|
||||
public ICollection<MaterializedWorkflow> MaterializedWorkflows { get; set; } = new List<MaterializedWorkflow>();
|
||||
public string Name => "Test";
|
||||
|
||||
public ValueTask<IEnumerable<MaterializedWorkflow>> GetWorkflowsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return new(MaterializedWorkflows);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.Activities.FlowJoins;
|
||||
|
||||
public class Tests(App app) : AppComponentTest(app)
|
||||
{
|
||||
// https://github.com/elsa-workflows/elsa-core/issues/5348
|
||||
[Fact]
|
||||
public async Task FlowchartWithSingleFlowJoin_ShouldExecuteSuccessfully()
|
||||
{
|
||||
var workflowRunner = Scope.ServiceProvider.GetRequiredService<IWorkflowRunner>();
|
||||
var result = await workflowRunner.RunAsync<SingleJoinWorkflow>();
|
||||
Assert.Equal(WorkflowSubStatus.Finished, result.WorkflowState.SubStatus);
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
using Elsa.Workflows.Activities.Flowchart.Activities;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.Activities.FlowJoins;
|
||||
|
||||
public class SingleJoinWorkflow : WorkflowBase
|
||||
{
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.Root = new Elsa.Workflows.Activities.Flowchart.Activities.Flowchart
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new FlowJoin()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using System.Net;
|
||||
using Elsa.Api.Client.Resources.WorkflowDefinitions.Contracts;
|
||||
using Elsa.Testing.Shared.Extensions;
|
||||
using Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.Execute;
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.BasicWorkflows;
|
||||
|
||||
public class HelloWorldTests(App app) : AppComponentTest(app)
|
||||
{
|
||||
[Fact]
|
||||
public async Task HelloWorldWorkflow_ShouldReturnOk()
|
||||
{
|
||||
var client = WorkflowServer.CreateApiClient<IExecuteWorkflowApi>();
|
||||
using var response = await client.ExecuteAsync("1590068018aa4f0a");
|
||||
var model = await response.ReadAsJsonAsync<Response>(WorkflowServer.Services);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal(WorkflowSubStatus.Finished, model.WorkflowState.SubStatus);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"id": "1590068018aa4f0a",
|
||||
"definitionId": "1590068018aa4f0a",
|
||||
"name": "Hello World",
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "969b0703a9379c3b",
|
||||
"nodeId": "Workflow1:969b0703a9379c3b",
|
||||
"activities": [
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Hello World!"
|
||||
}
|
||||
},
|
||||
"id": "b039045bb7443e57",
|
||||
"nodeId": "Workflow1:969b0703a9379c3b:b039045bb7443e57",
|
||||
"name": "WriteLine1",
|
||||
"type": "Elsa.WriteLine",
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -231.796875,
|
||||
"y": 269
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": []
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
using Elsa.Common.Models;
|
||||
using Elsa.Testing.Shared.Services;
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Elsa.Workflows.ComponentTests.Scenarios.BulkDispatchWorkflows.Workflows;
|
||||
using Elsa.Workflows.Models;
|
||||
using Elsa.Workflows.Runtime;
|
||||
using Elsa.Workflows.Runtime.Messages;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.BulkDispatchWorkflows;
|
||||
|
||||
public class BulkDispatchWorkflowsTests : AppComponentTest
|
||||
{
|
||||
private readonly SignalManager _signalManager;
|
||||
private readonly IWorkflowRuntime _workflowRuntime;
|
||||
|
||||
public BulkDispatchWorkflowsTests(App app) : base(app)
|
||||
{
|
||||
_workflowRuntime = Scope.ServiceProvider.GetRequiredService<IWorkflowRuntime>();
|
||||
_signalManager = Scope.ServiceProvider.GetRequiredService<SignalManager>();
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// Dispatches and waits for child workflows to complete.
|
||||
// /// </summary>
|
||||
// [Fact(Skip = "This test is flaky and needs to be fixed.")]
|
||||
// public async Task DispatchAndWaitWorkflow_ShouldWaitForChildWorkflowToComplete()
|
||||
// {
|
||||
// var workflowClient = await _workflowRuntime.CreateClientAsync();
|
||||
// await workflowClient.CreateInstanceAsync(new CreateWorkflowInstanceRequest
|
||||
// {
|
||||
// WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionId(GreetEmployeesWorkflow.DefinitionId, VersionOptions.Published)
|
||||
// });
|
||||
// await workflowClient.RunInstanceAsync(RunWorkflowInstanceRequest.Empty);
|
||||
// await _signalManager.WaitAsync<string>("Completed");
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Individual items are sent as input to child workflows.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task DispatchWorkflows_ChildWorkflowsShouldReceiveCurrentItem()
|
||||
{
|
||||
var workflowClient = await _workflowRuntime.CreateClientAsync();
|
||||
var request = new CreateAndRunWorkflowInstanceRequest
|
||||
{
|
||||
WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionId(MixFruitsWorkflow.DefinitionId, VersionOptions.Published)
|
||||
};
|
||||
await workflowClient.CreateAndRunInstanceAsync(request);
|
||||
|
||||
await _signalManager.WaitAsync("Apple");
|
||||
await _signalManager.WaitAsync("Banana");
|
||||
await _signalManager.WaitAsync("Cherry");
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Hangfire.Annotations;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.BulkDispatchWorkflows.Workflows;
|
||||
|
||||
[UsedImplicitly]
|
||||
public class EmployeeGreetingWorkflow : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = Guid.NewGuid().ToString();
|
||||
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.WithDefinitionId(DefinitionId);
|
||||
var employeeName = builder.WithInput<string>("Employee");
|
||||
builder.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new WriteLine(x => $"Hello {x.GetInput<string>(employeeName)}")
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.ComponentTests.Activities;
|
||||
using Hangfire.Annotations;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.BulkDispatchWorkflows.Workflows;
|
||||
|
||||
[UsedImplicitly]
|
||||
public class FruitWorkflow : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = Guid.NewGuid().ToString();
|
||||
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.WithDefinitionId(DefinitionId);
|
||||
var item = builder.WithInput<string>("Item");
|
||||
builder.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new WriteLine(x => $"Mixing {x.GetInput<string>(item)}"),
|
||||
new TriggerSignal(x => x.GetInput<string>(item))
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
using Elsa.Testing.Shared.Activities;
|
||||
using Elsa.Workflows.Activities;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.BulkDispatchWorkflows.Workflows;
|
||||
|
||||
public class GreetEmployeesWorkflow : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = Guid.NewGuid().ToString();
|
||||
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
var employeeNames = new[]
|
||||
{
|
||||
"Alice", "Bob", "Charlie"
|
||||
};
|
||||
|
||||
var inputEntries = employeeNames.Select(x => new Dictionary<string, object>
|
||||
{
|
||||
["Employee"] = x
|
||||
});
|
||||
|
||||
builder.WithDefinitionId(DefinitionId);
|
||||
builder.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new Runtime.Activities.BulkDispatchWorkflows
|
||||
{
|
||||
WorkflowDefinitionId = new(EmployeeGreetingWorkflow.DefinitionId),
|
||||
Items = new(inputEntries),
|
||||
WaitForCompletion = new(true)
|
||||
},
|
||||
new TriggerSignal("Completed")
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
using Elsa.Workflows.Activities;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.BulkDispatchWorkflows.Workflows;
|
||||
|
||||
public class MixFruitsWorkflow : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = Guid.NewGuid().ToString();
|
||||
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
var fruits = new[]
|
||||
{
|
||||
"Apple", "Banana", "Cherry"
|
||||
};
|
||||
|
||||
builder.WithDefinitionId(DefinitionId);
|
||||
builder.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new Runtime.Activities.BulkDispatchWorkflows
|
||||
{
|
||||
WorkflowDefinitionId = new(FruitWorkflow.DefinitionId),
|
||||
Items = new(fruits),
|
||||
WaitForCompletion = new(true)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Elsa.Workflows.Management;
|
||||
using Elsa.Workflows.Management.Filters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.CachingAndWorkflowDefinitionActivity;
|
||||
|
||||
// Tests the behavior of the WorkflowDefinitionActivity.
|
||||
// See https://github.com/elsa-workflows/elsa-core/issues/5314
|
||||
public class WorkflowDefinitionActivityTests : AppComponentTest
|
||||
{
|
||||
private readonly ITestOutputHelper _testOutputHelper;
|
||||
private const string GrandChildDefinitionId = "29595e7b37a4836d";
|
||||
|
||||
private readonly IWorkflowDefinitionCacheManager _workflowDefinitionCacheManager;
|
||||
private readonly IWorkflowInstanceStore _workflowInstanceStore;
|
||||
private readonly HttpClient _httpWorkflowClient;
|
||||
|
||||
public WorkflowDefinitionActivityTests(App app, ITestOutputHelper testOutputHelper) : base(app)
|
||||
{
|
||||
_testOutputHelper = testOutputHelper;
|
||||
_httpWorkflowClient = WorkflowServer.CreateHttpWorkflowClient();
|
||||
_workflowDefinitionCacheManager = Scope.ServiceProvider.GetRequiredService<IWorkflowDefinitionCacheManager>();
|
||||
_workflowInstanceStore = Scope.ServiceProvider.GetRequiredService<IWorkflowInstanceStore>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendHttpRequest_WhileEvictingCache_ShouldNotGenerateFaults()
|
||||
{
|
||||
var requestTasks = Enumerable.Range(0, 200).Select(SendRequestAsync).ToList();
|
||||
await Task.WhenAll(requestTasks);
|
||||
|
||||
var filter = new WorkflowInstanceFilter
|
||||
{
|
||||
WorkflowSubStatus = WorkflowSubStatus.Faulted
|
||||
};
|
||||
var faultedWorkflows = (await _workflowInstanceStore.FindManyAsync(filter)).ToList();
|
||||
var faultCount = faultedWorkflows.Count;
|
||||
|
||||
foreach (var faultedWorkflow in faultedWorkflows)
|
||||
foreach (var incident in faultedWorkflow.WorkflowState.Incidents)
|
||||
_testOutputHelper.WriteLine(incident.Message);
|
||||
|
||||
Assert.Equal(0, faultCount);
|
||||
}
|
||||
|
||||
private async Task SendRequestAsync(int index = 0)
|
||||
{
|
||||
var requestTask = _httpWorkflowClient.PostAsync("parent", new StringContent("{}"));
|
||||
var evictionTask = _workflowDefinitionCacheManager.EvictWorkflowDefinitionAsync(GrandChildDefinitionId);
|
||||
await Task.WhenAll(requestTask, evictionTask);
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"id": "26d186d68deb1250",
|
||||
"definitionId": "a3390d1f4c2594a8",
|
||||
"name": "Child",
|
||||
"createdAt": "2024-04-30T08:22:44.543407+00:00",
|
||||
"version": 1,
|
||||
"toolVersion": "3.2.0.0",
|
||||
"variables": [],
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"outcomes": [],
|
||||
"customProperties": {},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"options": {
|
||||
"usableAsActivity": true,
|
||||
"autoUpdateConsumingWorkflows": true
|
||||
},
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "e6c941b6f2292fcf",
|
||||
"nodeId": "Workflow2:e6c941b6f2292fcf",
|
||||
"metadata": {},
|
||||
"customProperties": {
|
||||
"source": "FlowchartJsonConverter.cs:45",
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"workflowDefinitionId": "29595e7b37a4836d",
|
||||
"workflowDefinitionVersionId": "f36dc264f14be3c6",
|
||||
"latestAvailablePublishedVersion": 1,
|
||||
"latestAvailablePublishedVersionId": "f36dc264f14be3c6",
|
||||
"id": "377b23f4235ec26d",
|
||||
"nodeId": "Workflow2:e6c941b6f2292fcf:377b23f4235ec26d",
|
||||
"name": "GrandChild1",
|
||||
"type": "GrandChild",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -193.95703125,
|
||||
"y": 23
|
||||
},
|
||||
"size": {
|
||||
"width": 115.1875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": []
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"id": "f36dc264f14be3c6",
|
||||
"definitionId": "29595e7b37a4836d",
|
||||
"name": "Grand Child",
|
||||
"createdAt": "2024-04-30T08:22:44.537237+00:00",
|
||||
"version": 1,
|
||||
"toolVersion": "3.2.0.0",
|
||||
"variables": [],
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"outcomes": [],
|
||||
"customProperties": {},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"options": {
|
||||
"usableAsActivity": true,
|
||||
"autoUpdateConsumingWorkflows": true
|
||||
},
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "29044d8e2ba192c0",
|
||||
"nodeId": "Workflow1:29044d8e2ba192c0",
|
||||
"metadata": {},
|
||||
"customProperties": {
|
||||
"source": "FlowchartJsonConverter.cs:45",
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Grand Child"
|
||||
}
|
||||
},
|
||||
"id": "fb00878500fde1d3",
|
||||
"nodeId": "Workflow1:29044d8e2ba192c0:fb00878500fde1d3",
|
||||
"name": "WriteLine1",
|
||||
"type": "Elsa.WriteLine",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -234.796875,
|
||||
"y": -191
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": []
|
||||
}
|
||||
}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
{
|
||||
"id": "9178b94f961b1cf",
|
||||
"definitionId": "189be5173f90b1f6",
|
||||
"name": "Parent",
|
||||
"createdAt": "2024-04-30T08:42:17.838484+00:00",
|
||||
"version": 6,
|
||||
"toolVersion": "3.2.0.0",
|
||||
"variables": [],
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"outcomes": [],
|
||||
"customProperties": {
|
||||
"Elsa:WorkflowContextProviderTypes": []
|
||||
},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"options": {
|
||||
"autoUpdateConsumingWorkflows": false
|
||||
},
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "96f60810318a6a89",
|
||||
"nodeId": "Workflow3:96f60810318a6a89",
|
||||
"metadata": {},
|
||||
"customProperties": {
|
||||
"source": "FlowchartJsonConverter.cs:45",
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"path": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "parent"
|
||||
}
|
||||
},
|
||||
"supportedMethods": {
|
||||
"typeName": "String[]",
|
||||
"expression": {
|
||||
"type": "Object",
|
||||
"value": "[\u0022POST\u0022]"
|
||||
}
|
||||
},
|
||||
"authorize": {
|
||||
"typeName": "Boolean",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
"policy": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal"
|
||||
}
|
||||
},
|
||||
"requestTimeout": null,
|
||||
"requestSizeLimit": null,
|
||||
"fileSizeLimit": null,
|
||||
"allowedFileExtensions": null,
|
||||
"blockedFileExtensions": null,
|
||||
"allowedMimeTypes": null,
|
||||
"exposeRequestTooLargeOutcome": false,
|
||||
"exposeFileTooLargeOutcome": false,
|
||||
"exposeInvalidFileExtensionOutcome": false,
|
||||
"exposeInvalidFileMimeTypeOutcome": false,
|
||||
"parsedContent": null,
|
||||
"files": null,
|
||||
"routeData": null,
|
||||
"queryStringData": null,
|
||||
"headers": null,
|
||||
"result": null,
|
||||
"id": "7e20498ffc94dbc6",
|
||||
"nodeId": "Workflow3:96f60810318a6a89:7e20498ffc94dbc6",
|
||||
"name": "HttpEndpoint1",
|
||||
"type": "Elsa.HttpEndpoint",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": true,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -520,
|
||||
"y": -120
|
||||
},
|
||||
"size": {
|
||||
"width": 176.390625,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"workflowDefinitionId": "a3390d1f4c2594a8",
|
||||
"workflowDefinitionVersionId": "26d186d68deb1250",
|
||||
"latestAvailablePublishedVersion": 1,
|
||||
"latestAvailablePublishedVersionId": "26d186d68deb1250",
|
||||
"id": "ed6a03040f651906",
|
||||
"nodeId": "Workflow3:96f60810318a6a89:ed6a03040f651906",
|
||||
"name": "Child1",
|
||||
"type": "Child",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -236.5810546875,
|
||||
"y": -120
|
||||
},
|
||||
"size": {
|
||||
"width": 67.7734375,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"source": {
|
||||
"activity": "7e20498ffc94dbc6",
|
||||
"port": "Done"
|
||||
},
|
||||
"target": {
|
||||
"activity": "ed6a03040f651906",
|
||||
"port": "In"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Elsa.Workflows.Management;
|
||||
using Elsa.Workflows.Management.Models;
|
||||
using Elsa.Workflows.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.ClusteredHosting;
|
||||
|
||||
public class ActivityRegistrySyncTests(App app) : AppComponentTest(app)
|
||||
{
|
||||
private readonly IServiceScope _pod1Scope = app.Cluster.Pod1.Services.CreateScope();
|
||||
private readonly IServiceScope _pod2Scope = app.Cluster.Pod2.Services.CreateScope();
|
||||
private readonly IServiceScope _pod3Scope = app.Cluster.Pod3.Services.CreateScope();
|
||||
|
||||
[Fact(Skip = "Not yet implemented")]
|
||||
public async Task ImportWorkflowActivity_ShouldUpdateOtherPods()
|
||||
{
|
||||
var pod1ActivityRegistry = _pod1Scope.ServiceProvider.GetRequiredService<IActivityRegistry>();
|
||||
var pod2ActivityRegistry = _pod2Scope.ServiceProvider.GetRequiredService<IActivityRegistry>();
|
||||
var pod3ActivityRegistry = _pod3Scope.ServiceProvider.GetRequiredService<IActivityRegistry>();
|
||||
|
||||
var sub1Descriptor = pod1ActivityRegistry.Find("Sub");
|
||||
Assert.Null(sub1Descriptor);
|
||||
|
||||
var importer = _pod1Scope.ServiceProvider.GetRequiredService<IWorkflowDefinitionImporter>();
|
||||
var request = new SaveWorkflowDefinitionRequest
|
||||
{
|
||||
Model = new WorkflowDefinitionModel
|
||||
{
|
||||
Name = "Sub",
|
||||
DefinitionId = "Sub",
|
||||
Options = new WorkflowOptions
|
||||
{
|
||||
UsableAsActivity = true,
|
||||
AutoUpdateConsumingWorkflows = true
|
||||
}
|
||||
},
|
||||
Publish = true
|
||||
};
|
||||
await importer.ImportAsync(request);
|
||||
|
||||
sub1Descriptor = pod1ActivityRegistry.Find("Sub");
|
||||
Assert.NotNull(sub1Descriptor);
|
||||
|
||||
sub1Descriptor = pod2ActivityRegistry.Find("Sub");
|
||||
Assert.NotNull(sub1Descriptor);
|
||||
|
||||
sub1Descriptor = pod3ActivityRegistry.Find("Sub");
|
||||
Assert.NotNull(sub1Descriptor);
|
||||
}
|
||||
|
||||
protected override void OnDispose()
|
||||
{
|
||||
_pod1Scope.Dispose();
|
||||
_pod2Scope.Dispose();
|
||||
_pod3Scope.Dispose();
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
using Elsa.Common.Models;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Testing.Shared.Services;
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Elsa.Workflows.ComponentTests.Scenarios.DispatchWorkflows.Workflows;
|
||||
using Elsa.Workflows.Models;
|
||||
using Elsa.Workflows.Runtime;
|
||||
using Elsa.Workflows.Runtime.Messages;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.DispatchWorkflows;
|
||||
|
||||
public class DispatchWorkflowsTests : AppComponentTest
|
||||
{
|
||||
private readonly SignalManager _signalManager;
|
||||
private readonly IWorkflowRuntime _workflowRuntime;
|
||||
|
||||
public DispatchWorkflowsTests(App app) : base(app)
|
||||
{
|
||||
_workflowRuntime = Scope.ServiceProvider.GetRequiredService<IWorkflowRuntime>();
|
||||
_signalManager = Scope.ServiceProvider.GetRequiredService<SignalManager>();
|
||||
}
|
||||
|
||||
[Fact (Skip = "This test is flaky and needs to be fixed.")]
|
||||
public async Task DispatchAndWaitWorkflow_ShouldWaitForChildWorkflowToComplete()
|
||||
{
|
||||
var workflowClient = await _workflowRuntime.CreateClientAsync();
|
||||
await workflowClient.CreateInstanceAsync(new CreateWorkflowInstanceRequest
|
||||
{
|
||||
WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionId(DispatchAndWaitWorkflow.DefinitionId, VersionOptions.Published)
|
||||
});
|
||||
await workflowClient.RunInstanceAsync(RunWorkflowInstanceRequest.Empty);
|
||||
await _signalManager.WaitAsync<string>("Completed");
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
using Elsa.Scheduling.Activities;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Hangfire.Annotations;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.DispatchWorkflows.Workflows;
|
||||
|
||||
[UsedImplicitly]
|
||||
public class ChildWorkflow : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = Guid.NewGuid().ToString();
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.WithDefinitionId(DefinitionId);
|
||||
builder.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new Delay(TimeSpan.FromMilliseconds(250)),
|
||||
new WriteLine("Hello from Child!")
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
using Elsa.Testing.Shared.Activities;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.Runtime.Activities;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.DispatchWorkflows.Workflows;
|
||||
|
||||
public class DispatchAndWaitWorkflow : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = Guid.NewGuid().ToString();
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.WithDefinitionId(DefinitionId);
|
||||
builder.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new DispatchWorkflow
|
||||
{
|
||||
WorkflowDefinitionId = new(ChildWorkflow.DefinitionId),
|
||||
WaitForCompletion = new (true)
|
||||
},
|
||||
new TriggerSignal("Completed")
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
using Elsa.Common.Models;
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Elsa.Workflows.ComponentTests.Scenarios.ExecuteWorkflows.Workflows;
|
||||
using Elsa.Workflows.Models;
|
||||
using Elsa.Workflows.Runtime;
|
||||
using Elsa.Workflows.Runtime.Messages;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.ExecuteWorkflows;
|
||||
|
||||
public class ExecuteWorkflowsTests : AppComponentTest
|
||||
{
|
||||
private readonly IWorkflowRuntime _workflowRuntime;
|
||||
|
||||
public ExecuteWorkflowsTests(App app) : base(app)
|
||||
{
|
||||
_workflowRuntime = Scope.ServiceProvider.GetRequiredService<IWorkflowRuntime>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExecuteWorkflow_ShouldExecuteWorkflow()
|
||||
{
|
||||
var workflowClient = await _workflowRuntime.CreateClientAsync();
|
||||
await workflowClient.CreateInstanceAsync(new CreateWorkflowInstanceRequest
|
||||
{
|
||||
WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionId(MainWorkflow.DefinitionId, VersionOptions.Published)
|
||||
});
|
||||
await workflowClient.RunInstanceAsync(RunWorkflowInstanceRequest.Empty);
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
using System.Text.Json;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.Runtime;
|
||||
using Elsa.Workflows.Runtime.Activities;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.ExecuteWorkflows.Workflows;
|
||||
|
||||
public class MainWorkflow : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = Guid.NewGuid().ToString();
|
||||
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.WithDefinitionId(DefinitionId);
|
||||
var workflowResult = builder.WithVariable<ExecuteWorkflowResult>();
|
||||
builder.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new ExecuteWorkflow
|
||||
{
|
||||
WorkflowDefinitionId = new(SubroutineWorkflow.DefinitionId),
|
||||
Input = new(new Dictionary<string, object>
|
||||
{
|
||||
["Value"] = 21
|
||||
}),
|
||||
Result = new(workflowResult)
|
||||
},
|
||||
new WriteLine(context => $"Subroutine output: {JsonSerializer.Serialize(workflowResult.Get(context))}")
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.Management.Activities.SetOutput;
|
||||
using Hangfire.Annotations;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.ExecuteWorkflows.Workflows;
|
||||
|
||||
[UsedImplicitly]
|
||||
public class SubroutineWorkflow : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = Guid.NewGuid().ToString();
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.WithDefinitionId(DefinitionId);
|
||||
var valueInput = builder.WithInput<double>("Value");
|
||||
var output = builder.WithOutput<double>("Output");
|
||||
|
||||
builder.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new WriteLine(context => $"Running subroutine on value {context.GetInput<double>(valueInput)}..."),
|
||||
new SetOutput
|
||||
{
|
||||
OutputName = new(output.Name),
|
||||
OutputValue = new(context => context.GetInput<double>(valueInput) * 2)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.HttpWorkflows;
|
||||
|
||||
public class HttpHelloWorldTests(App app) : AppComponentTest(app)
|
||||
{
|
||||
[Fact]
|
||||
public async Task HelloWorldWorkflow_ShouldRespondWithHelloWorld()
|
||||
{
|
||||
var client = WorkflowServer.CreateHttpWorkflowClient();
|
||||
var response = await client.GetStringAsync("hello-world");
|
||||
Assert.Equal("Hello World!", response);
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using System.Net;
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.HttpWorkflows;
|
||||
|
||||
public class ResumeSpecificHttpWorkflowInstanceTests(App app) : AppComponentTest(app)
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("workflowInstanceId")]
|
||||
[InlineData("correlationId")]
|
||||
public async Task ResumingSpecificWorkflow_ShouldResumeSpecifiedWorkflow(string identifierKey)
|
||||
{
|
||||
var client = WorkflowServer.CreateHttpWorkflowClient();
|
||||
|
||||
// Start 3 instances.
|
||||
var workflowInstanceId1 = await StartWorkflowAsync(client, identifierKey);
|
||||
var workflowInstanceId2 = await StartWorkflowAsync(client, identifierKey);
|
||||
var workflowInstanceId3 = await StartWorkflowAsync(client, identifierKey);
|
||||
|
||||
// Resume the 2nd instance.
|
||||
var response = await ResumeWorkflowAsync(client, identifierKey, workflowInstanceId2);
|
||||
|
||||
// Response should be OK.
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
}
|
||||
|
||||
private async Task<string> StartWorkflowAsync(HttpClient client, string identifierKey)
|
||||
{
|
||||
var identityGenerator = Scope.ServiceProvider.GetRequiredService<IIdentityGenerator>();
|
||||
var identifierValue = identityGenerator.GenerateId();
|
||||
await client.GetStringAsync($"simple-http-api/start?{identifierKey}={identifierValue}");
|
||||
return identifierValue;
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> ResumeWorkflowAsync(HttpClient client, string identifierKey, string identifierValue)
|
||||
{
|
||||
return await client.GetAsync($"simple-http-api/resume?{identifierKey}={identifierValue}");
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.HttpWorkflows;
|
||||
|
||||
public class RouteDataTests(App app) : AppComponentTest(app)
|
||||
{
|
||||
[Fact]
|
||||
public async Task RouteDataWorkflow_ShouldRespondWithRouteData()
|
||||
{
|
||||
var client = WorkflowServer.CreateHttpWorkflowClient();
|
||||
var response = await client.GetStringAsync("orders/42");
|
||||
Assert.Equal("42", response);
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
using Elsa.Http;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.HttpWorkflows.Workflows;
|
||||
|
||||
public class RouteDataWorkflow : WorkflowBase
|
||||
{
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
var routeDataVariable = builder.WithVariable<IDictionary<string, object>>();
|
||||
|
||||
builder.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
[
|
||||
new HttpEndpoint
|
||||
{
|
||||
Path = new("orders/{id}"),
|
||||
SupportedMethods = new([HttpMethods.Get]),
|
||||
CanStartWorkflow = true,
|
||||
RouteData = new(routeDataVariable)
|
||||
},
|
||||
new WriteHttpResponse
|
||||
{
|
||||
Content = new(context => routeDataVariable.Get(context)!["id"].ToString()),
|
||||
ContentType = new("text/plain")
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
using Elsa.Http;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.HttpWorkflows.Workflows;
|
||||
|
||||
public class SimpleHttpApiWorkflow : WorkflowBase
|
||||
{
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
[
|
||||
new HttpEndpoint
|
||||
{
|
||||
Path = new("simple-http-api/start"),
|
||||
SupportedMethods = new([HttpMethods.Get]),
|
||||
CanStartWorkflow = true
|
||||
},
|
||||
new HttpEndpoint
|
||||
{
|
||||
Path = new("simple-http-api/resume"),
|
||||
SupportedMethods = new([HttpMethods.Get])
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
{
|
||||
"id": "a86a9f668817dcb6",
|
||||
"definitionId": "f926beb0afc8505e",
|
||||
"name": "HTTP Hello World",
|
||||
"createdAt": "2024-04-20T17:14:34.118307+00:00",
|
||||
"version": 2,
|
||||
"toolVersion": "3.2.0.0",
|
||||
"variables": [],
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"outcomes": [],
|
||||
"customProperties": {
|
||||
"Elsa:WorkflowContextProviderTypes": []
|
||||
},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"options": {
|
||||
"autoUpdateConsumingWorkflows": false
|
||||
},
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "95a12c8ee4e8c5fc",
|
||||
"nodeId": "Workflow1:95a12c8ee4e8c5fc",
|
||||
"metadata": {},
|
||||
"customProperties": {
|
||||
"source": "FlowchartJsonConverter.cs:45",
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"path": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "hello-world"
|
||||
}
|
||||
},
|
||||
"supportedMethods": {
|
||||
"typeName": "String[]",
|
||||
"expression": {
|
||||
"type": "Object",
|
||||
"value": "[\u0022GET\u0022]"
|
||||
}
|
||||
},
|
||||
"authorize": {
|
||||
"typeName": "Boolean",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
"policy": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal"
|
||||
}
|
||||
},
|
||||
"requestTimeout": null,
|
||||
"requestSizeLimit": null,
|
||||
"fileSizeLimit": null,
|
||||
"allowedFileExtensions": null,
|
||||
"blockedFileExtensions": null,
|
||||
"allowedMimeTypes": null,
|
||||
"exposeRequestTooLargeOutcome": false,
|
||||
"exposeFileTooLargeOutcome": false,
|
||||
"exposeInvalidFileExtensionOutcome": false,
|
||||
"exposeInvalidFileMimeTypeOutcome": false,
|
||||
"parsedContent": null,
|
||||
"files": null,
|
||||
"routeData": null,
|
||||
"queryStringData": null,
|
||||
"headers": null,
|
||||
"result": null,
|
||||
"id": "90c3d3b2ea1f6ddc",
|
||||
"nodeId": "Workflow1:95a12c8ee4e8c5fc:90c3d3b2ea1f6ddc",
|
||||
"name": "HttpEndpoint1",
|
||||
"type": "Elsa.HttpEndpoint",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": true,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -262.796875,
|
||||
"y": 264
|
||||
},
|
||||
"size": {
|
||||
"width": 176.390625,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"statusCode": {
|
||||
"typeName": "System.Net.HttpStatusCode, System.Net.Primitives",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "OK"
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"typeName": "Object",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Hello World!"
|
||||
}
|
||||
},
|
||||
"contentType": null,
|
||||
"responseHeaders": {
|
||||
"typeName": "Elsa.Http.Models.HttpHeaders, Elsa.Http",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "{}"
|
||||
}
|
||||
},
|
||||
"id": "3048105347912af5",
|
||||
"nodeId": "Workflow1:95a12c8ee4e8c5fc:3048105347912af5",
|
||||
"name": "WriteHttpResponse1",
|
||||
"type": "Elsa.WriteHttpResponse",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": 35.203125,
|
||||
"y": 264
|
||||
},
|
||||
"size": {
|
||||
"width": 183.640625,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"source": {
|
||||
"activity": "90c3d3b2ea1f6ddc",
|
||||
"port": "Done"
|
||||
},
|
||||
"target": {
|
||||
"activity": "3048105347912af5",
|
||||
"port": "In"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Elsa.Workflows.Management;
|
||||
using Elsa.Workflows.Runtime;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.InputOutput;
|
||||
|
||||
public class InputOutputTests(App app) : AppComponentTest(app)
|
||||
{
|
||||
[Fact(DisplayName = "Input should be received by the Provider and should in turn be able to send it back to the Consumer.")]
|
||||
public async Task InputShouldBeOutput()
|
||||
{
|
||||
const string workflowDefinitionVersionId = "4e993ab1b3d4e4a1";
|
||||
var workflowDefinitionService = Scope.ServiceProvider.GetRequiredService<IWorkflowDefinitionService>();
|
||||
var workflowInvoker = Scope.ServiceProvider.GetRequiredService<IWorkflowInvoker>();
|
||||
var variableManager = Scope.ServiceProvider.GetRequiredService<IWorkflowInstanceVariableManager>();
|
||||
var workflowGraph = await workflowDefinitionService.FindWorkflowGraphAsync(workflowDefinitionVersionId);
|
||||
var result = await workflowInvoker.InvokeAsync(workflowGraph!);
|
||||
var variables = await variableManager.GetVariablesAsync(result.WorkflowState.Id);
|
||||
var variableValue = variables.First().Value;
|
||||
|
||||
Assert.Equal("Hello World!", variableValue);
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"$schema": "https://elsaworkflows.io/schemas/workflow-definition/v3.0.0/schema.json",
|
||||
"id": "4e993ab1b3d4e4a1",
|
||||
"definitionId": "58dbd7bffa85a761",
|
||||
"name": "Consumer",
|
||||
"createdAt": "2024-12-04T16:49:20.593932\u002B00:00",
|
||||
"version": 1,
|
||||
"toolVersion": "3.3.0.0",
|
||||
"variables": [
|
||||
{
|
||||
"id": "67fcb9870af4c28e",
|
||||
"name": "Variable1",
|
||||
"typeName": "String",
|
||||
"isArray": false,
|
||||
"value": "",
|
||||
"storageDriverTypeName": "Elsa.Workflows.WorkflowInstanceStorageDriver, Elsa.Workflows.Core"
|
||||
}
|
||||
],
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"outcomes": [],
|
||||
"customProperties": {
|
||||
"logPersistenceConfig": {
|
||||
"default": {
|
||||
"evaluationMode": "Strategy",
|
||||
"strategyType": "Elsa.Workflows.LogPersistence.Strategies.Inherit, Elsa.Workflows.Core",
|
||||
"expression": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"options": {
|
||||
"autoUpdateConsumingWorkflows": false
|
||||
},
|
||||
"root": {
|
||||
"id": "938f2c91bb759a6a",
|
||||
"nodeId": "Workflow2:938f2c91bb759a6a",
|
||||
"name": "Flowchart1",
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {},
|
||||
"activities": [
|
||||
{
|
||||
"workflowDefinitionId": "780fbf96f1bb1e7e",
|
||||
"workflowDefinitionVersionId": "640867552fc865d",
|
||||
"latestAvailablePublishedVersion": 3,
|
||||
"latestAvailablePublishedVersionId": "640867552fc865d",
|
||||
"id": "8c1127e1193cd6e8",
|
||||
"nodeId": "Workflow2:938f2c91bb759a6a:8c1127e1193cd6e8",
|
||||
"name": "Output1",
|
||||
"type": "Provider",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -160,
|
||||
"y": -280
|
||||
},
|
||||
"size": {
|
||||
"width": 89.6953125,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
},
|
||||
"input1": {
|
||||
"typeName": "Object",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Hello World!"
|
||||
},
|
||||
"memoryReference": {
|
||||
"id": "8c1127e1193cd6e8:input-input-1"
|
||||
}
|
||||
},
|
||||
"output1": {
|
||||
"typeName": "Object",
|
||||
"memoryReference": {
|
||||
"id": "67fcb9870af4c28e"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"variables": [],
|
||||
"connections": []
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"$schema": "https://elsaworkflows.io/schemas/workflow-definition/v3.0.0/schema.json",
|
||||
"id": "5ba5a7e36d60549b",
|
||||
"definitionId": "780fbf96f1bb1e7e",
|
||||
"name": "Provider",
|
||||
"createdAt": "2024-12-04T16:22:39.064075\u002B00:00",
|
||||
"version": 1,
|
||||
"toolVersion": "3.3.0.0",
|
||||
"variables": [],
|
||||
"inputs": [
|
||||
{
|
||||
"uiHint": "singleline",
|
||||
"storageDriverType": "Elsa.Workflows.WorkflowInstanceStorageDriver, Elsa.Workflows.Core",
|
||||
"type": "Object",
|
||||
"name": "Input1",
|
||||
"displayName": "Input 1",
|
||||
"description": "",
|
||||
"category": "Primitives",
|
||||
"isArray": false
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"type": "Object",
|
||||
"name": "Output1",
|
||||
"displayName": "Output 1",
|
||||
"description": "",
|
||||
"category": "Primitives",
|
||||
"isArray": false
|
||||
}
|
||||
],
|
||||
"outcomes": [],
|
||||
"customProperties": {
|
||||
"logPersistenceConfig": {
|
||||
"default": {
|
||||
"evaluationMode": "Strategy",
|
||||
"strategyType": "Elsa.Workflows.LogPersistence.Strategies.Inherit, Elsa.Workflows.Core",
|
||||
"expression": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": false,
|
||||
"options": {
|
||||
"usableAsActivity": true,
|
||||
"autoUpdateConsumingWorkflows": true
|
||||
},
|
||||
"root": {
|
||||
"id": "85a5e1d9e487bfa4",
|
||||
"nodeId": "Workflow1:85a5e1d9e487bfa4",
|
||||
"name": "Flowchart1",
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {},
|
||||
"activities": [
|
||||
{
|
||||
"outputName": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Output1"
|
||||
}
|
||||
},
|
||||
"outputValue": {
|
||||
"typeName": "Object",
|
||||
"expression": {
|
||||
"type": "JavaScript",
|
||||
"value": "return getInput1()"
|
||||
}
|
||||
},
|
||||
"id": "985eb3bfb8d4dd3f",
|
||||
"nodeId": "Workflow1:85a5e1d9e487bfa4:985eb3bfb8d4dd3f",
|
||||
"name": "SetOutput1",
|
||||
"type": "Elsa.SetOutput",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -380,
|
||||
"y": -220
|
||||
},
|
||||
"size": {
|
||||
"width": 107.3203125,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"variables": [],
|
||||
"connections": []
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using Elsa.Extensions;
|
||||
using Elsa.JavaScript.Activities;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.JavaScriptVariables;
|
||||
|
||||
public class JavaScriptVariablesWorkflow1 : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = Guid.NewGuid().ToString();
|
||||
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.WithDefinitionId(DefinitionId);
|
||||
builder.WithVariable("MagicNumber", 3).WithWorkflowStorage();
|
||||
builder.Root = new RunJavaScript("setMagicNumber(42)", null, null);
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using Elsa.Extensions;
|
||||
using Elsa.JavaScript.Activities;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.JavaScriptVariables;
|
||||
|
||||
public class JavaScriptVariablesWorkflow2 : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = Guid.NewGuid().ToString();
|
||||
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.WithDefinitionId(DefinitionId);
|
||||
builder.WithVariable("MagicNumber", 3).WithWorkflowStorage();
|
||||
builder.Root = new RunJavaScript("variables.MagicNumber = 42", null, null);
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using Elsa.Extensions;
|
||||
using Elsa.JavaScript.Activities;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.JavaScriptVariables;
|
||||
|
||||
public class JavaScriptVariablesWorkflow3 : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = Guid.NewGuid().ToString();
|
||||
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.WithDefinitionId(DefinitionId);
|
||||
builder.WithVariable("MagicNumber", 3).WithWorkflowStorage();
|
||||
builder.Root = new RunJavaScript("setVariable('MagicNumber', 42)", null, null);
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
using Elsa.Expressions.Helpers;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Elsa.Workflows.Management;
|
||||
using Elsa.Workflows.Models;
|
||||
using Elsa.Workflows.Runtime;
|
||||
using Elsa.Workflows.Runtime.Messages;
|
||||
using Elsa.Workflows.State;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.JavaScriptVariables;
|
||||
|
||||
public class JavaScriptVariablesWorkflowTests(App app) : AppComponentTest(app)
|
||||
{
|
||||
[Theory(DisplayName = "SetVariable JS function sets a variable and does not get overridden by variables API")]
|
||||
[MemberData(nameof(GetWorkflowDefinitions))]
|
||||
public async Task SetVariableRetainsValue(string workflowDefinitionId)
|
||||
{
|
||||
var workflowRuntime = Scope.ServiceProvider.GetRequiredService<IWorkflowRuntime>();
|
||||
var workflowInstanceStore = Scope.ServiceProvider.GetRequiredService<IWorkflowInstanceStore>();
|
||||
var workflowClient = await workflowRuntime.CreateClientAsync();
|
||||
var runAndCreateRequest = new CreateAndRunWorkflowInstanceRequest
|
||||
{
|
||||
WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionId(workflowDefinitionId)
|
||||
};
|
||||
var runResponse = await workflowClient.CreateAndRunInstanceAsync(runAndCreateRequest);
|
||||
var workflowInstanceId = runResponse.WorkflowInstanceId;
|
||||
var workflowInstance = await workflowInstanceStore.FindAsync(workflowInstanceId);
|
||||
var workflowState = workflowInstance!.WorkflowState;
|
||||
var rootWorkflowActivityExecutionContext = workflowState.ActivityExecutionContexts.Single(x => x.ParentContextId == null);
|
||||
var variables = GetVariablesDictionary(rootWorkflowActivityExecutionContext);
|
||||
var magicNumber = variables["magicNumberVariable"].ConvertTo<int>();
|
||||
Assert.Equal(42, magicNumber);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetWorkflowDefinitions()
|
||||
{
|
||||
return
|
||||
[
|
||||
[JavaScriptVariablesWorkflow1.DefinitionId],
|
||||
[JavaScriptVariablesWorkflow2.DefinitionId],
|
||||
[JavaScriptVariablesWorkflow3.DefinitionId]
|
||||
];
|
||||
}
|
||||
|
||||
private VariablesDictionary GetVariablesDictionary(ActivityExecutionContextState context)
|
||||
{
|
||||
return context.Properties.GetOrAdd(WorkflowInstanceStorageDriver.VariablesDictionaryStateKey, () => new VariablesDictionary());
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
using Elsa.Api.Client.Resources.WorkflowDefinitions.Contracts;
|
||||
using Elsa.Api.Client.Resources.WorkflowDefinitions.Responses;
|
||||
using Elsa.Api.Client.Resources.WorkflowInstances.Models;
|
||||
using Elsa.Common.Entities;
|
||||
using Elsa.Testing.Shared.Extensions;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Elsa.Workflows.Helpers;
|
||||
using Elsa.Workflows.Runtime;
|
||||
using Elsa.Workflows.Runtime.Entities;
|
||||
using Elsa.Workflows.Runtime.Filters;
|
||||
using Elsa.Workflows.Runtime.OrderDefinitions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Open.Linq.AsyncExtensions;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.LogPersistenceModes;
|
||||
|
||||
public class InputOutputLoggingTests(App app) : AppComponentTest(app)
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("input-output-logging-1", true, false, true, false)]
|
||||
[InlineData("input-output-logging-2", true, false, true, false)]
|
||||
public async Task Workflow_ShouldHonorSettings_WhenExecuting(string workflowDefinitionId, params bool[] shouldBeIncludedArray)
|
||||
{
|
||||
var workflowState = await ExecuteWorkflowAsync(workflowDefinitionId);
|
||||
var workflowInstanceId = workflowState.Id;
|
||||
var writeLineActivityTypeName = ActivityTypeNameHelper.GenerateTypeName<WriteLine>();
|
||||
var activityExecutionRecords = await GetActivityExecutionRecordsAsync(workflowInstanceId, x => x.ActivityType == writeLineActivityTypeName).ToList();
|
||||
|
||||
for (var i = 0; i < shouldBeIncludedArray.Length; i++)
|
||||
{
|
||||
var activityExecutionRecord = activityExecutionRecords[i];
|
||||
var shouldBeIncluded = shouldBeIncludedArray[i];
|
||||
var isIncluded = activityExecutionRecord.ActivityState?.ContainsKey(nameof(WriteLine.Text)) == true;
|
||||
Assert.Equal(shouldBeIncluded, isIncluded);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WorkflowAsActivity_ShouldHonorSettings_WhenExecuting()
|
||||
{
|
||||
await ExecuteWorkflowAsync("input-output-logging-3");
|
||||
var activityExecutionRecord = await GetRecordByActivityNameAsync("InputOutputLogging3-Child1");
|
||||
var output1IsIncluded = activityExecutionRecord.Outputs!.ContainsKey("Output1");
|
||||
var output2IsIncluded = activityExecutionRecord.Outputs!.ContainsKey("Output2");
|
||||
|
||||
Assert.True(output1IsIncluded);
|
||||
Assert.True(output2IsIncluded);
|
||||
}
|
||||
|
||||
[Fact(Skip = "Although this functionality works in practice, the component test fails from time to time for no clear reason (yet).")]
|
||||
public async Task WorkflowAsActivityInternal_ShouldHonorSettings_WhenExecuting()
|
||||
{
|
||||
await ExecuteWorkflowAsync("input-output-logging-3");
|
||||
var setOutput1Record = await GetRecordByActivityNameAsync("SetOutput1");
|
||||
var setOutput2Record = await GetRecordByActivityNameAsync("SetOutput2");
|
||||
var output1IsIncluded = setOutput1Record.ActivityState?.ContainsKey("OutputName") == true;
|
||||
var output2IsIncluded = setOutput2Record.ActivityState?.ContainsKey("OutputName") == true;
|
||||
|
||||
Assert.False(output1IsIncluded);
|
||||
Assert.True(output2IsIncluded);
|
||||
}
|
||||
|
||||
private async Task<WorkflowState> ExecuteWorkflowAsync(string workflowDefinitionId)
|
||||
{
|
||||
var client = WorkflowServer.CreateApiClient<IExecuteWorkflowApi>();
|
||||
using var response = await client.ExecuteAsync(workflowDefinitionId);
|
||||
var model = await response.ReadAsJsonAsync<ExecuteWorkflowDefinitionResponse>(WorkflowServer.Services);
|
||||
return model.WorkflowState;
|
||||
}
|
||||
|
||||
private Task<IEnumerable<ActivityExecutionRecord>> GetActivityExecutionRecordsAsync(string workflowInstanceId, Func<ActivityExecutionRecord, bool> predicate)
|
||||
{
|
||||
var activityExecutionRecordFilter = new ActivityExecutionRecordFilter
|
||||
{
|
||||
WorkflowInstanceId = workflowInstanceId,
|
||||
};
|
||||
return GetActivityExecutionRecordsAsync(activityExecutionRecordFilter, predicate);
|
||||
}
|
||||
|
||||
private Task<ActivityExecutionRecord> GetRecordByActivityNameAsync(string name)
|
||||
{
|
||||
var filter = new ActivityExecutionRecordFilter
|
||||
{
|
||||
Name = name
|
||||
};
|
||||
return GetActivityExecutionRecordAsync(filter);
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<ActivityExecutionRecord>> GetActivityExecutionRecordsAsync(ActivityExecutionRecordFilter filter, Func<ActivityExecutionRecord, bool>? predicate = null)
|
||||
{
|
||||
var activityExecutionStore = Scope.ServiceProvider.GetRequiredService<IActivityExecutionStore>();
|
||||
var orderBy = new ActivityExecutionRecordOrder<DateTimeOffset>(x => x.StartedAt, OrderDirection.Ascending);
|
||||
var activityExecutionRecords = await activityExecutionStore.FindManyAsync(filter, orderBy);
|
||||
return predicate == null ? activityExecutionRecords : activityExecutionRecords.Where(predicate);
|
||||
}
|
||||
|
||||
private async Task<ActivityExecutionRecord> GetActivityExecutionRecordAsync(ActivityExecutionRecordFilter filter)
|
||||
{
|
||||
var activityExecutionStore = Scope.ServiceProvider.GetRequiredService<IActivityExecutionStore>();
|
||||
var result = await activityExecutionStore.FindAsync(filter);
|
||||
|
||||
if(result == null)
|
||||
throw new InvalidOperationException($"Activity execution record not found.");
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
{
|
||||
"id": "94f11e583299383b",
|
||||
"definitionId": "input-output-logging-1",
|
||||
"name": "Input Output Logging 1",
|
||||
"createdAt": "2024-04-21T11:34:17.423214+00:00",
|
||||
"version": 1,
|
||||
"toolVersion": "3.2.0.0",
|
||||
"variables": [],
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"outcomes": [],
|
||||
"customProperties": {
|
||||
"Elsa:WorkflowContextProviderTypes": []
|
||||
},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"options": {
|
||||
"autoUpdateConsumingWorkflows": false
|
||||
},
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "482993d55270a5e7",
|
||||
"nodeId": "Workflow1:482993d55270a5e7",
|
||||
"metadata": {},
|
||||
"customProperties": {
|
||||
"source": "FlowchartJsonConverter.cs:45",
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "This input should be stored"
|
||||
}
|
||||
},
|
||||
"id": "ea21b3863867ebb3",
|
||||
"nodeId": "Workflow1:482993d55270a5e7:ea21b3863867ebb3",
|
||||
"name": "WriteLine1",
|
||||
"type": "Elsa.WriteLine",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -440,
|
||||
"y": -140
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "This input should not be stored"
|
||||
}
|
||||
},
|
||||
"id": "4726389b3d516c40",
|
||||
"nodeId": "Workflow1:482993d55270a5e7:4726389b3d516c40",
|
||||
"name": "WriteLine2",
|
||||
"type": "Elsa.WriteLine",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"logPersistenceMode": {
|
||||
"default": "Exclude",
|
||||
"inputs": {
|
||||
"text": "Inherit"
|
||||
},
|
||||
"outputs": {}
|
||||
},
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -220,
|
||||
"y": -140
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "This input should be stored"
|
||||
}
|
||||
},
|
||||
"id": "e13b09839d4244b9",
|
||||
"nodeId": "Workflow1:482993d55270a5e7:e13b09839d4244b9",
|
||||
"name": "WriteLine3",
|
||||
"type": "Elsa.WriteLine",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"logPersistenceMode": {
|
||||
"default": "Exclude",
|
||||
"inputs": {
|
||||
"text": "Include"
|
||||
},
|
||||
"outputs": {}
|
||||
},
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -16,
|
||||
"y": -140
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "This input should not be stored"
|
||||
}
|
||||
},
|
||||
"id": "1ba2922db9052ce9",
|
||||
"nodeId": "Workflow1:482993d55270a5e7:1ba2922db9052ce9",
|
||||
"name": "WriteLine4",
|
||||
"type": "Elsa.WriteLine",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"logPersistenceMode": {
|
||||
"default": "Include",
|
||||
"inputs": {
|
||||
"text": "Exclude"
|
||||
},
|
||||
"outputs": {}
|
||||
},
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": 188,
|
||||
"y": -140
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"source": {
|
||||
"activity": "ea21b3863867ebb3",
|
||||
"port": "Done"
|
||||
},
|
||||
"target": {
|
||||
"activity": "4726389b3d516c40",
|
||||
"port": "In"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": {
|
||||
"activity": "4726389b3d516c40",
|
||||
"port": "Done"
|
||||
},
|
||||
"target": {
|
||||
"activity": "e13b09839d4244b9",
|
||||
"port": "In"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": {
|
||||
"activity": "e13b09839d4244b9",
|
||||
"port": "Done"
|
||||
},
|
||||
"target": {
|
||||
"activity": "1ba2922db9052ce9",
|
||||
"port": "In"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
{
|
||||
"id": "a7499c4c7888fafb",
|
||||
"definitionId": "input-output-logging-2",
|
||||
"name": "Input Output Logging 2",
|
||||
"createdAt": "2024-04-21T11:39:57.182662+00:00",
|
||||
"version": 2,
|
||||
"toolVersion": "3.2.0.0",
|
||||
"variables": [],
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"outcomes": [],
|
||||
"customProperties": {
|
||||
"Elsa:WorkflowContextProviderTypes": [],
|
||||
"logPersistenceMode": {
|
||||
"default": "Exclude"
|
||||
}
|
||||
},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"options": {
|
||||
"autoUpdateConsumingWorkflows": false
|
||||
},
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "4dce3806d9c7af92",
|
||||
"nodeId": "Workflow1:4dce3806d9c7af92",
|
||||
"metadata": {},
|
||||
"customProperties": {
|
||||
"source": "FlowchartJsonConverter.cs:45",
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "This should be logged"
|
||||
}
|
||||
},
|
||||
"id": "30d32d634c1ddbfa",
|
||||
"nodeId": "Workflow1:4dce3806d9c7af92:30d32d634c1ddbfa",
|
||||
"name": "WriteLine1",
|
||||
"type": "Elsa.WriteLine",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"logPersistenceMode": {
|
||||
"default": "Include",
|
||||
"inputs": {
|
||||
"text": "Inherit"
|
||||
},
|
||||
"outputs": {}
|
||||
},
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -380,
|
||||
"y": -120
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "This should not be logged"
|
||||
}
|
||||
},
|
||||
"id": "50fcb804a7ace442",
|
||||
"nodeId": "Workflow1:4dce3806d9c7af92:50fcb804a7ace442",
|
||||
"name": "WriteLine2",
|
||||
"type": "Elsa.WriteLine",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -156,
|
||||
"y": -120
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "This should be logged"
|
||||
}
|
||||
},
|
||||
"id": "3f00f1de3ec2862b",
|
||||
"nodeId": "Workflow1:4dce3806d9c7af92:3f00f1de3ec2862b",
|
||||
"name": "WriteLine3",
|
||||
"type": "Elsa.WriteLine",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"logPersistenceMode": {
|
||||
"default": "Inherit",
|
||||
"inputs": {
|
||||
"text": "Include"
|
||||
},
|
||||
"outputs": {}
|
||||
},
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": 60,
|
||||
"y": -120
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "This should not be logged"
|
||||
}
|
||||
},
|
||||
"id": "3dcff307781b3707",
|
||||
"nodeId": "Workflow1:4dce3806d9c7af92:3dcff307781b3707",
|
||||
"name": "WriteLine4",
|
||||
"type": "Elsa.WriteLine",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"logPersistenceMode": {
|
||||
"default": "Inherit",
|
||||
"inputs": {
|
||||
"text": "Exclude"
|
||||
},
|
||||
"outputs": {}
|
||||
},
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": 264,
|
||||
"y": -120
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"source": {
|
||||
"activity": "3f00f1de3ec2862b",
|
||||
"port": "Done"
|
||||
},
|
||||
"target": {
|
||||
"activity": "3dcff307781b3707",
|
||||
"port": "In"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": {
|
||||
"activity": "30d32d634c1ddbfa",
|
||||
"port": "Done"
|
||||
},
|
||||
"target": {
|
||||
"activity": "50fcb804a7ace442",
|
||||
"port": "In"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": {
|
||||
"activity": "50fcb804a7ace442",
|
||||
"port": "Done"
|
||||
},
|
||||
"target": {
|
||||
"activity": "3f00f1de3ec2862b",
|
||||
"port": "In"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"id": "4cf7c10c7a2901d6",
|
||||
"definitionId": "input-output-logging-3-child",
|
||||
"name": "Input Output Logging 3 - Child",
|
||||
"createdAt": "2024-04-22T13:11:07.146222+00:00",
|
||||
"version": 4,
|
||||
"toolVersion": "3.2.0.0",
|
||||
"variables": [],
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"type": "Object",
|
||||
"name": "Output1",
|
||||
"displayName": "Output 1",
|
||||
"category": "Primitives",
|
||||
"isArray": false
|
||||
},
|
||||
{
|
||||
"type": "Object",
|
||||
"name": "Output2",
|
||||
"displayName": "Output 2",
|
||||
"category": "Primitives",
|
||||
"isArray": false
|
||||
}
|
||||
],
|
||||
"outcomes": [],
|
||||
"customProperties": {
|
||||
"logPersistenceMode": {
|
||||
"default": "Exclude"
|
||||
},
|
||||
"Elsa:WorkflowContextProviderTypes": []
|
||||
},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"options": {
|
||||
"usableAsActivity": true,
|
||||
"autoUpdateConsumingWorkflows": true
|
||||
},
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "5f72efc9043753fa",
|
||||
"nodeId": "Workflow1:5f72efc9043753fa",
|
||||
"metadata": {},
|
||||
"customProperties": {
|
||||
"source": "FlowchartJsonConverter.cs:45",
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"outputName": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Output1"
|
||||
}
|
||||
},
|
||||
"outputValue": {
|
||||
"typeName": "Object",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Output value 1"
|
||||
}
|
||||
},
|
||||
"id": "402076f12c1220a9",
|
||||
"nodeId": "Workflow1:5f72efc9043753fa:402076f12c1220a9",
|
||||
"name": "SetOutput1",
|
||||
"type": "Elsa.SetOutput",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false,
|
||||
"logPersistenceMode": {
|
||||
"default": "Inherit",
|
||||
"inputs": {
|
||||
"outputName": "Inherit",
|
||||
"outputValue": "Inherit"
|
||||
},
|
||||
"outputs": {}
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -221.4757080078125,
|
||||
"y": 264.861083984375
|
||||
},
|
||||
"size": {
|
||||
"width": 107.734375,
|
||||
"height": 50.2256965637207
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"outputName": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Output2"
|
||||
}
|
||||
},
|
||||
"outputValue": {
|
||||
"typeName": "Object",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Output value 2"
|
||||
}
|
||||
},
|
||||
"id": "db1d1c02cbfac590",
|
||||
"nodeId": "Workflow1:5f72efc9043753fa:db1d1c02cbfac590",
|
||||
"name": "SetOutput2",
|
||||
"type": "Elsa.SetOutput",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false,
|
||||
"logPersistenceMode": {
|
||||
"default": "Inherit",
|
||||
"inputs": {
|
||||
"outputName": "Include",
|
||||
"outputValue": "Include"
|
||||
},
|
||||
"outputs": {}
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": 58.5242919921875,
|
||||
"y": 264.861083984375
|
||||
},
|
||||
"size": {
|
||||
"width": 107.734375,
|
||||
"height": 50.2256965637207
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"source": {
|
||||
"activity": "402076f12c1220a9",
|
||||
"port": "Done"
|
||||
},
|
||||
"target": {
|
||||
"activity": "db1d1c02cbfac590",
|
||||
"port": "In"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"id": "6da18530316aee2e",
|
||||
"definitionId": "input-output-logging-3",
|
||||
"name": "Input Output Logging 3",
|
||||
"createdAt": "2024-04-21T20:08:17.495551+00:00",
|
||||
"version": 1,
|
||||
"toolVersion": "3.2.0.0",
|
||||
"variables": [],
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"outcomes": [],
|
||||
"customProperties": {
|
||||
"Elsa:WorkflowContextProviderTypes": []
|
||||
},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"options": {
|
||||
"autoUpdateConsumingWorkflows": false
|
||||
},
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "9aec3fea5a39cccd",
|
||||
"nodeId": "Workflow2:9aec3fea5a39cccd",
|
||||
"metadata": {},
|
||||
"customProperties": {
|
||||
"source": "FlowchartJsonConverter.cs:45",
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"workflowDefinitionId": "input-output-logging-3-child",
|
||||
"workflowDefinitionVersionId": "4cf7c10c7a2901d6",
|
||||
"latestAvailablePublishedVersion": 4,
|
||||
"latestAvailablePublishedVersionId": "4cf7c10c7a2901d6",
|
||||
"id": "6bf4016d6074831f",
|
||||
"nodeId": "Workflow2:9aec3fea5a39cccd:6bf4016d6074831f",
|
||||
"name": "InputOutputLogging3-Child1",
|
||||
"type": "InputOutputLogging3-Child",
|
||||
"version": 4,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -360,
|
||||
"y": -320
|
||||
},
|
||||
"size": {
|
||||
"width": 246.375,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": []
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
using Elsa.Common.Models;
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Elsa.Workflows.Management;
|
||||
using Elsa.Workflows.Management.Filters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.Multitenancy;
|
||||
|
||||
public class MultitenancyTests(App app) : AppComponentTest(app)
|
||||
{
|
||||
[Fact(Skip = "Multitenancy disabled. This test doesn't work because not all workflows are assigned the Tenant1 tenant.")]
|
||||
public async Task LoadingWorkflows_ShouldReturnWorkflows_FromCurrentTenant()
|
||||
{
|
||||
var store = Scope.ServiceProvider.GetRequiredService<IWorkflowDefinitionStore>();
|
||||
var filter = new WorkflowDefinitionFilter
|
||||
{
|
||||
IsSystem = false,
|
||||
VersionOptions = VersionOptions.Latest
|
||||
};
|
||||
var workflows = await store.FindManyAsync(filter);
|
||||
Assert.All(workflows, workflow => Assert.Equal("Tenant1", workflow.TenantId));
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using Elsa.Workflows.Activities;
|
||||
using Hangfire.Annotations;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.Multitenancy.Workflows;
|
||||
|
||||
[UsedImplicitly]
|
||||
public class WorkflowForTenant1 : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = Guid.NewGuid().ToString();
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.WithDefinitionId(DefinitionId);
|
||||
builder.WithTenantId("Tenant1");
|
||||
builder.Root = new WriteLine("Tenant1");
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using Elsa.Workflows.Activities;
|
||||
using Hangfire.Annotations;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.Multitenancy.Workflows;
|
||||
|
||||
[UsedImplicitly]
|
||||
public class WorkflowForTenant2 : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = Guid.NewGuid().ToString();
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.WithDefinitionId(DefinitionId);
|
||||
builder.WithTenantId("Tenant2");
|
||||
builder.Root = new WriteLine("Tenant2");
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
using Elsa.Extensions;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.Variables.Activities;
|
||||
|
||||
public class CountdownStep : Activity
|
||||
{
|
||||
protected override void Execute(ActivityExecutionContext context)
|
||||
{
|
||||
var counter = context.GetVariable<int>("Counter");
|
||||
context.SetVariable("Counter", counter - 1);
|
||||
context?.CreateBookmark();
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
using Elsa.Common.Models;
|
||||
using Elsa.Expressions.Helpers;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Elsa.Workflows.ComponentTests.Scenarios.Variables.Workflows;
|
||||
using Elsa.Workflows.Management;
|
||||
using Elsa.Workflows.Models;
|
||||
using Elsa.Workflows.Runtime;
|
||||
using Elsa.Workflows.Runtime.Entities;
|
||||
using Elsa.Workflows.Runtime.Filters;
|
||||
using Elsa.Workflows.Runtime.Messages;
|
||||
using Elsa.Workflows.State;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.Variables;
|
||||
|
||||
public class CountdownWorkflowTests(App app) : AppComponentTest(app)
|
||||
{
|
||||
[Fact(DisplayName = "Variable is persisted across workflow runs")]
|
||||
public async Task VariableIsPersistedAcrossWorkflowRuns()
|
||||
{
|
||||
var workflowRuntime = Scope.ServiceProvider.GetRequiredService<IWorkflowRuntime>();
|
||||
var workflowClient = await workflowRuntime.CreateClientAsync();
|
||||
var workflowInstanceStore = Scope.ServiceProvider.GetRequiredService<IWorkflowInstanceStore>();
|
||||
var bookmarkStore = Scope.ServiceProvider.GetRequiredService<IBookmarkStore>();
|
||||
var runAndCreateRequest = new CreateAndRunWorkflowInstanceRequest
|
||||
{
|
||||
WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionId(CountdownWorkflow.DefinitionId, VersionOptions.Latest),
|
||||
};
|
||||
var runResponse = await workflowClient.CreateAndRunInstanceAsync(runAndCreateRequest);
|
||||
var workflowInstanceId = runResponse.WorkflowInstanceId;
|
||||
var createdBookmarks = await bookmarkStore.FindManyAsync(new()
|
||||
{
|
||||
WorkflowInstanceId = workflowInstanceId
|
||||
});
|
||||
var bookmarks = new Stack<StoredBookmark>(createdBookmarks);
|
||||
var expectedCounter = 3;
|
||||
|
||||
while (bookmarks.Any())
|
||||
{
|
||||
var workflowInstance = await workflowInstanceStore.FindAsync(workflowInstanceId);
|
||||
var workflowState = workflowInstance!.WorkflowState;
|
||||
var rootWorkflowActivityExecutionContext = workflowState.ActivityExecutionContexts.Single(x => x.ParentContextId == null);
|
||||
var variables = GetVariablesDictionary(rootWorkflowActivityExecutionContext);
|
||||
var actualCounter = variables["counterVariable"].ConvertTo<int>();
|
||||
Assert.Equal(--expectedCounter, actualCounter);
|
||||
|
||||
var bookmark = bookmarks.Pop();
|
||||
var runRequest = new RunWorkflowInstanceRequest
|
||||
{
|
||||
BookmarkId = bookmark.Id,
|
||||
};
|
||||
|
||||
await workflowClient.RunInstanceAsync(runRequest);
|
||||
|
||||
createdBookmarks = await bookmarkStore.FindManyAsync(new()
|
||||
{
|
||||
WorkflowInstanceId = workflowInstanceId
|
||||
});
|
||||
|
||||
foreach (var newBookmark in createdBookmarks)
|
||||
bookmarks.Push(newBookmark);
|
||||
}
|
||||
}
|
||||
|
||||
private VariablesDictionary GetVariablesDictionary(ActivityExecutionContextState context)
|
||||
{
|
||||
return context.Properties.GetOrAdd(WorkflowInstanceStorageDriver.VariablesDictionaryStateKey, () => new VariablesDictionary());
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.ComponentTests.Scenarios.Variables.Activities;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.Variables.Workflows;
|
||||
|
||||
public class CountdownWorkflow : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = Guid.NewGuid().ToString();
|
||||
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.WithDefinitionId(DefinitionId);
|
||||
var counter = builder.WithVariable("Counter", 3).WithWorkflowStorage();
|
||||
|
||||
builder.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new While(context => counter.Get(context) > 0)
|
||||
{
|
||||
Body = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new WriteLine(context => $"Counter: {counter.Get(context)}"),
|
||||
new CountdownStep()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
using Elsa.Http;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Testing.Shared.Services;
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Elsa.Workflows.Management;
|
||||
using Elsa.Workflows.Management.Filters;
|
||||
using Elsa.Workflows.Runtime.Filters;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.WorkflowActivities;
|
||||
|
||||
public class AutoUpdateTests : AppComponentTest
|
||||
{
|
||||
private const string ParentDefinitionVersionId = "4b584e249fdca951";
|
||||
private const string ParentDefinitionId = "878770f04439a55d";
|
||||
private const string ChildDefinitionId = "f353742a9ef6af4";
|
||||
|
||||
private static readonly object HttpChangeTokenSignal = new();
|
||||
private static readonly object TriggerChangeTokenSignal = new();
|
||||
private static readonly object GraphChangeTokenSignal = new();
|
||||
private readonly IMemoryCache _cache;
|
||||
private readonly TriggerChangeTokenSignalEvents _changeTokenEvents;
|
||||
private readonly IWorkflowDefinitionCacheManager _definitionCacheManager;
|
||||
private readonly IHasher _hasher;
|
||||
private readonly IHttpWorkflowsCacheManager _httpCacheManager;
|
||||
private readonly IWorkflowDefinitionPublisher _publisher;
|
||||
private readonly SignalManager _signalManager;
|
||||
private readonly IWorkflowDefinitionCacheManager _workflowCacheManager;
|
||||
private string? _graphChangeToken;
|
||||
|
||||
private string? _httpChangeToken;
|
||||
private string? _triggerChangeToken;
|
||||
|
||||
public AutoUpdateTests(App app) : base(app)
|
||||
{
|
||||
_cache = Scope.ServiceProvider.GetRequiredService<IMemoryCache>();
|
||||
_hasher = Scope.ServiceProvider.GetRequiredService<IHasher>();
|
||||
_definitionCacheManager = Scope.ServiceProvider.GetRequiredService<IWorkflowDefinitionCacheManager>();
|
||||
_publisher = Scope.ServiceProvider.GetRequiredService<IWorkflowDefinitionPublisher>();
|
||||
|
||||
_httpCacheManager = Scope.ServiceProvider.GetRequiredService<IHttpWorkflowsCacheManager>();
|
||||
_workflowCacheManager = Scope.ServiceProvider.GetRequiredService<IWorkflowDefinitionCacheManager>();
|
||||
|
||||
_signalManager = Scope.ServiceProvider.GetRequiredService<SignalManager>();
|
||||
_changeTokenEvents = Scope.ServiceProvider.GetRequiredService<TriggerChangeTokenSignalEvents>();
|
||||
_changeTokenEvents.ChangeTokenSignalTriggered += OnChangeTokenSignalTriggered;
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Updating a workflow with `auto update consuming workflows` should invalidate consuming workflows from cache")]
|
||||
public async Task UpdateWorkflowWithAutoUpdate()
|
||||
{
|
||||
// Run workflow to make sure the all required items for running the workflow are in the cache.
|
||||
var client = WorkflowServer.CreateHttpWorkflowClient();
|
||||
await client.GetStringAsync("test-cache-invalidation");
|
||||
|
||||
// Make sure the items are in the cache.
|
||||
var hash = _httpCacheManager.ComputeBookmarkHash("/test-cache-invalidation", "get");
|
||||
Assert.True(_cache.TryGetValue($"http-workflow:{hash}", out _));
|
||||
|
||||
var filter = new TriggerFilter
|
||||
{
|
||||
Hash = hash
|
||||
};
|
||||
var hashedFilter = _hasher.Hash(filter);
|
||||
Assert.True(_cache.TryGetValue($"IEnumerable`1:{hashedFilter}", out _));
|
||||
|
||||
var parentWorkflowDefinitionFilter = new WorkflowDefinitionFilter
|
||||
{
|
||||
Id = ParentDefinitionVersionId
|
||||
};
|
||||
var parentVersionCacheKey = _definitionCacheManager.CreateWorkflowFilterCacheKey(parentWorkflowDefinitionFilter);
|
||||
Assert.True(_cache.TryGetValue(parentVersionCacheKey, out _));
|
||||
|
||||
// Set change tokens.
|
||||
_httpChangeToken = _workflowCacheManager.CreateWorkflowDefinitionChangeTokenKey(ParentDefinitionId);
|
||||
_triggerChangeToken = _httpCacheManager.GetTriggerChangeTokenKey(hash);
|
||||
_graphChangeToken = _workflowCacheManager.CreateWorkflowDefinitionChangeTokenKey(ParentDefinitionId);
|
||||
|
||||
// (Act) Save the draft version of the child workflow and update the references.
|
||||
await _publisher.PublishAsync(ChildDefinitionId);
|
||||
|
||||
// Wait until the notifications for updating the cache have been send and check the cache.
|
||||
await _signalManager.WaitAsync<TriggerChangeTokenSignalEventArgs>(HttpChangeTokenSignal);
|
||||
await _signalManager.WaitAsync<TriggerChangeTokenSignalEventArgs>(TriggerChangeTokenSignal);
|
||||
await _signalManager.WaitAsync<TriggerChangeTokenSignalEventArgs>(GraphChangeTokenSignal);
|
||||
|
||||
Assert.False(_cache.TryGetValue($"http-workflow:{hash}", out _));
|
||||
Assert.False(_cache.TryGetValue($"IEnumerable`1:{hashedFilter}", out _));
|
||||
Assert.False(_cache.TryGetValue(parentVersionCacheKey, out _));
|
||||
}
|
||||
|
||||
private void OnChangeTokenSignalTriggered(object? sender, TriggerChangeTokenSignalEventArgs args)
|
||||
{
|
||||
if (args.Key == _httpChangeToken) _signalManager.Trigger(HttpChangeTokenSignal, args);
|
||||
if (args.Key == _triggerChangeToken) _signalManager.Trigger(TriggerChangeTokenSignal, args);
|
||||
if (args.Key == _graphChangeToken) _signalManager.Trigger(GraphChangeTokenSignal, args);
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Testing.Shared.Services;
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Elsa.Workflows.ComponentTests.Scenarios.WorkflowActivities.Workflows;
|
||||
using Elsa.Workflows.Management;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.WorkflowActivities;
|
||||
|
||||
public class DeleteWorkflowTests : AppComponentTest
|
||||
{
|
||||
private static readonly object WorkflowDeletedSignal = new();
|
||||
private readonly IServiceScope _scope1;
|
||||
// private readonly IServiceScope _scope2;
|
||||
// private readonly IServiceScope _scope3;
|
||||
private readonly SignalManager _signalManager;
|
||||
private readonly WorkflowDefinitionEvents _workflowDefinitionEvents;
|
||||
|
||||
public DeleteWorkflowTests(App app) : base(app)
|
||||
{
|
||||
_scope1 = app.Cluster.Pod1.Services.CreateScope();
|
||||
// Disabled these scope creations since this prevents events from firing in other tests.
|
||||
// _scope2 = app.Cluster.Pod2.Services.CreateScope();
|
||||
// _scope3 = app.Cluster.Pod3.Services.CreateScope();
|
||||
_signalManager = Scope.ServiceProvider.GetRequiredService<SignalManager>();
|
||||
_workflowDefinitionEvents = Scope.ServiceProvider.GetRequiredService<WorkflowDefinitionEvents>();
|
||||
_workflowDefinitionEvents.WorkflowDefinitionDeleted += OnWorkflowDefinitionDeleted;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteWorkflow()
|
||||
{
|
||||
EnsureWorkflowInRegistry(_scope1, Workflows.DeleteWorkflow.Type);
|
||||
|
||||
var workflowDefinitionManager = _scope1.ServiceProvider.GetRequiredService<IWorkflowDefinitionManager>();
|
||||
await workflowDefinitionManager.DeleteByDefinitionIdAsync(Workflows.DeleteWorkflow.DefinitionId);
|
||||
|
||||
WorkflowTypeDeletedFromRegistry(_scope1, Workflows.DeleteWorkflow.Type);
|
||||
}
|
||||
|
||||
[Fact(Skip = "Clustered tests are interfering with other event driven tests")]
|
||||
public async Task DeleteWorkflow_Clustered()
|
||||
{
|
||||
EnsureWorkflowInRegistry(_scope1, DeleteWorkflowClustered.Type);
|
||||
// EnsureWorkflowInRegistry(_scope2, DeleteWorkflowClustered.Type);
|
||||
// EnsureWorkflowInRegistry(_scope3, DeleteWorkflowClustered.Type);
|
||||
|
||||
var workflowDefinitionManager = _scope1.ServiceProvider.GetRequiredService<IWorkflowDefinitionManager>();
|
||||
await workflowDefinitionManager.DeleteByDefinitionIdAsync(DeleteWorkflowClustered.DefinitionId);
|
||||
|
||||
WorkflowTypeDeletedFromRegistry(_scope1, DeleteWorkflowClustered.Type);
|
||||
|
||||
await _signalManager.WaitAsync<WorkflowDefinitionDeletedEventArgs>(WorkflowDeletedSignal);
|
||||
// WorkflowTypeDeletedFromRegistry(_scope2, DeleteWorkflowClustered.Type);
|
||||
// WorkflowTypeDeletedFromRegistry(_scope3, DeleteWorkflowClustered.Type);
|
||||
}
|
||||
|
||||
private static void EnsureWorkflowInRegistry(IServiceScope scope, string type)
|
||||
{
|
||||
var activityRegistry = scope.ServiceProvider.GetRequiredService<IActivityRegistry>();
|
||||
var descriptor = activityRegistry.Find(type);
|
||||
Assert.NotNull(descriptor);
|
||||
}
|
||||
|
||||
private static void WorkflowTypeDeletedFromRegistry(IServiceScope scope, string type)
|
||||
{
|
||||
var activityRegistry = scope.ServiceProvider.GetRequiredService<IActivityRegistry>();
|
||||
var descriptor = activityRegistry.Find(type);
|
||||
|
||||
Assert.Null(descriptor);
|
||||
}
|
||||
|
||||
private void OnWorkflowDefinitionDeleted(object? sender, WorkflowDefinitionDeletedEventArgs args)
|
||||
{
|
||||
if (args.DefinitionId == Workflows.DeleteWorkflow.DefinitionId)
|
||||
_signalManager.Trigger(WorkflowDeletedSignal, args);
|
||||
}
|
||||
|
||||
protected override void OnDispose()
|
||||
{
|
||||
_workflowDefinitionEvents.WorkflowDefinitionDeleted -= OnWorkflowDefinitionDeleted;
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Elsa.Workflows.Management;
|
||||
using Elsa.Workflows.Management.Activities.WorkflowDefinitionActivity;
|
||||
using Elsa.Workflows.Management.Models;
|
||||
using Elsa.Workflows.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.WorkflowActivities;
|
||||
|
||||
public class SaveWorkflowTests(App app) : AppComponentTest(app)
|
||||
{
|
||||
[Theory(DisplayName = "Saving workflows updates ActivityRegistry")]
|
||||
[InlineData("Save1", true, true, true, true)]
|
||||
[InlineData("Save2", true, false, false, false)]
|
||||
[InlineData("Save3", false, true, false, false)]
|
||||
[InlineData("Save4", false, false, false, false)]
|
||||
public async Task ActivityRegistry(string name, bool usableAsActivity, bool publish, bool expectedInRegistry, bool isBrowsable)
|
||||
{
|
||||
var activityRegistry = Scope.ServiceProvider.GetRequiredService<IActivityRegistry>();
|
||||
|
||||
var descriptor = activityRegistry.Find(name);
|
||||
if (descriptor is not null)
|
||||
activityRegistry.Remove(typeof(WorkflowDefinitionActivityProvider), descriptor);
|
||||
|
||||
var importer = Scope.ServiceProvider.GetRequiredService<IWorkflowDefinitionImporter>();
|
||||
var request = new SaveWorkflowDefinitionRequest
|
||||
{
|
||||
Model = new WorkflowDefinitionModel
|
||||
{
|
||||
Name = name,
|
||||
DefinitionId = name,
|
||||
Options = new WorkflowOptions
|
||||
{
|
||||
UsableAsActivity = usableAsActivity,
|
||||
AutoUpdateConsumingWorkflows = true
|
||||
}
|
||||
},
|
||||
Publish = publish
|
||||
};
|
||||
await importer.ImportAsync(request);
|
||||
|
||||
descriptor = activityRegistry.Find(name);
|
||||
|
||||
if (expectedInRegistry)
|
||||
{
|
||||
Assert.NotNull(descriptor);
|
||||
Assert.Equal(isBrowsable, descriptor.IsBrowsable);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Null(descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"id": "23d18a46dc3000fd",
|
||||
"definitionId": "f353742a9ef6af4",
|
||||
"name": "CacheInvalidation_Child",
|
||||
"createdAt": "2024-05-28T08:10:31.5429337+00:00",
|
||||
"version": 1,
|
||||
"toolVersion": "3.2.0.0",
|
||||
"variables": [],
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"outcomes": [],
|
||||
"customProperties": {},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"options": {
|
||||
"usableAsActivity": true,
|
||||
"autoUpdateConsumingWorkflows": true
|
||||
},
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "5d146ccb42668b6b",
|
||||
"nodeId": "Workflow1:5d146ccb42668b6b",
|
||||
"metadata": {},
|
||||
"customProperties": {
|
||||
"source": "FlowchartJsonConverter.cs:45",
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"correlationId": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Child version 1"
|
||||
}
|
||||
},
|
||||
"id": "d719079757f5d1bf",
|
||||
"nodeId": "Workflow1:5d146ccb42668b6b:d719079757f5d1bf",
|
||||
"name": "Correlate1",
|
||||
"type": "Elsa.Correlate",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -127.40211486816406,
|
||||
"y": 526.2000122070312
|
||||
},
|
||||
"size": {
|
||||
"width": 132.7375030517578,
|
||||
"height": 49.60000228881836
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": []
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"id": "3da9c310a071ac7b",
|
||||
"definitionId": "f353742a9ef6af4",
|
||||
"name": "CacheInvalidation_Child",
|
||||
"createdAt": "2024-05-28T11:00:47.8186457+00:00",
|
||||
"version": 2,
|
||||
"toolVersion": "3.2.0.0",
|
||||
"variables": [],
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"outcomes": [],
|
||||
"customProperties": {},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": false,
|
||||
"options": {
|
||||
"usableAsActivity": true,
|
||||
"autoUpdateConsumingWorkflows": true
|
||||
},
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "5d146ccb42668b6b",
|
||||
"nodeId": "Workflow1:5d146ccb42668b6b",
|
||||
"metadata": {},
|
||||
"customProperties": {
|
||||
"source": "FlowchartJsonConverter.cs:45",
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"correlationId": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Child version 2"
|
||||
}
|
||||
},
|
||||
"id": "d719079757f5d1bf",
|
||||
"nodeId": "Workflow1:5d146ccb42668b6b:d719079757f5d1bf",
|
||||
"name": "Correlate1",
|
||||
"type": "Elsa.Correlate",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -127.40211486816406,
|
||||
"y": 526.2000122070312
|
||||
},
|
||||
"size": {
|
||||
"width": 132.7375030517578,
|
||||
"height": 49.60000228881836
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": []
|
||||
}
|
||||
}
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
{
|
||||
"id": "4b584e249fdca951",
|
||||
"definitionId": "878770f04439a55d",
|
||||
"name": "CacheInvalidation_Parent",
|
||||
"createdAt": "2024-05-28T08:11:06.1123531+00:00",
|
||||
"version": 1,
|
||||
"toolVersion": "3.2.0.0",
|
||||
"variables": [
|
||||
{
|
||||
"id": "47ab4de953db266",
|
||||
"name": "CorrId",
|
||||
"typeName": "String",
|
||||
"isArray": false,
|
||||
"storageDriverTypeName": "Elsa.Workflows.Services.WorkflowStorageDriver, Elsa.Workflows.Core"
|
||||
}
|
||||
],
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"outcomes": [],
|
||||
"customProperties": {},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"options": {
|
||||
"autoUpdateConsumingWorkflows": false
|
||||
},
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "76f03074bbc8cff3",
|
||||
"nodeId": "Workflow2:76f03074bbc8cff3",
|
||||
"metadata": {},
|
||||
"customProperties": {
|
||||
"source": "FlowchartJsonConverter.cs:45",
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"workflowDefinitionId": "f353742a9ef6af4",
|
||||
"workflowDefinitionVersionId": "23d18a46dc3000fd",
|
||||
"latestAvailablePublishedVersion": 1,
|
||||
"latestAvailablePublishedVersionId": "23d18a46dc3000fd",
|
||||
"id": "b8fe7116016141f",
|
||||
"nodeId": "Workflow2:76f03074bbc8cff3:b8fe7116016141f",
|
||||
"name": "CacheInvalidationChild1",
|
||||
"type": "CacheInvalidationChild",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -40,
|
||||
"y": 320
|
||||
},
|
||||
"size": {
|
||||
"width": 203.9499969482422,
|
||||
"height": 49.60000228881836
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Parent version 1"
|
||||
}
|
||||
},
|
||||
"id": "ad9ee49c807b852e",
|
||||
"nodeId": "Workflow2:76f03074bbc8cff3:ad9ee49c807b852e",
|
||||
"name": "SetName1",
|
||||
"type": "Elsa.SetName",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -303.3261413574219,
|
||||
"y": 320
|
||||
},
|
||||
"size": {
|
||||
"width": 137.6354217529297,
|
||||
"height": 49.60000228881836
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "test-cache-invalidation"
|
||||
}
|
||||
},
|
||||
"supportedMethods": {
|
||||
"typeName": "String[]",
|
||||
"expression": {
|
||||
"type": "Object",
|
||||
"value": "[\u0022GET\u0022]"
|
||||
}
|
||||
},
|
||||
"authorize": {
|
||||
"typeName": "Boolean",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
"policy": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal"
|
||||
}
|
||||
},
|
||||
"requestTimeout": null,
|
||||
"requestSizeLimit": null,
|
||||
"fileSizeLimit": null,
|
||||
"allowedFileExtensions": null,
|
||||
"blockedFileExtensions": null,
|
||||
"allowedMimeTypes": null,
|
||||
"exposeRequestTooLargeOutcome": false,
|
||||
"exposeFileTooLargeOutcome": false,
|
||||
"exposeInvalidFileExtensionOutcome": false,
|
||||
"exposeInvalidFileMimeTypeOutcome": false,
|
||||
"parsedContent": null,
|
||||
"files": null,
|
||||
"routeData": null,
|
||||
"queryStringData": null,
|
||||
"headers": null,
|
||||
"result": null,
|
||||
"id": "109b9c25e55375ba",
|
||||
"nodeId": "Workflow2:76f03074bbc8cff3:109b9c25e55375ba",
|
||||
"name": "HttpEndpoint1",
|
||||
"type": "Elsa.HttpEndpoint",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": true,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -622.4391784667969,
|
||||
"y": 320
|
||||
},
|
||||
"size": {
|
||||
"width": 176.0625,
|
||||
"height": 49.60000228881836
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"source": {
|
||||
"activity": "ad9ee49c807b852e",
|
||||
"port": "Done"
|
||||
},
|
||||
"target": {
|
||||
"activity": "b8fe7116016141f",
|
||||
"port": "In"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": {
|
||||
"activity": "109b9c25e55375ba",
|
||||
"port": "Done"
|
||||
},
|
||||
"target": {
|
||||
"activity": "ad9ee49c807b852e",
|
||||
"port": "In"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using Elsa.Scheduling.Activities;
|
||||
using Elsa.Workflows.Activities;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.WorkflowActivities.Workflows;
|
||||
|
||||
public class DeleteWorkflow : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = Guid.NewGuid().ToString();
|
||||
public static readonly string Type = nameof(DeleteWorkflow);
|
||||
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.Name = Type;
|
||||
builder.WithDefinitionId(DefinitionId);
|
||||
builder.WorkflowOptions.UsableAsActivity = true;
|
||||
builder.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new Delay(TimeSpan.FromMilliseconds(250)),
|
||||
new WriteLine("This workflow will be deleted!")
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using Elsa.Scheduling.Activities;
|
||||
using Elsa.Workflows.Activities;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.WorkflowActivities.Workflows;
|
||||
|
||||
public class DeleteWorkflowClustered : WorkflowBase
|
||||
{
|
||||
public static readonly string DefinitionId = Guid.NewGuid().ToString();
|
||||
public static readonly string Type = nameof(DeleteWorkflowClustered);
|
||||
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder.Name = Type;
|
||||
builder.WithDefinitionId(DefinitionId);
|
||||
builder.WorkflowOptions.UsableAsActivity = true;
|
||||
builder.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new Delay(TimeSpan.FromMilliseconds(250)),
|
||||
new WriteLine("This workflow will be deleted!")
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
using System.Net;
|
||||
using Elsa.Api.Client.Resources.WorkflowDefinitions.Contracts;
|
||||
using Elsa.Testing.Shared.Extensions;
|
||||
using Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.Execute;
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.WorkflowCompletion;
|
||||
|
||||
public class WorkflowCompletionTests(App app) : AppComponentTest(app)
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("2630068018ac1f0a")]
|
||||
[InlineData("5590069018aa4f0e")]
|
||||
public async Task Workflow_ShouldComplete(string workflowDefinitionId)
|
||||
{
|
||||
var client = WorkflowServer.CreateApiClient<IExecuteWorkflowApi>();
|
||||
using var response = await client.ExecuteAsync(workflowDefinitionId);
|
||||
var model = await response.ReadAsJsonAsync<Response>(WorkflowServer.Services);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal(WorkflowSubStatus.Finished, model.WorkflowState.SubStatus);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
{
|
||||
"id": "5590069018aa4f0e",
|
||||
"definitionId": "5590069018aa4f0e",
|
||||
"name": "Fork",
|
||||
"createdAt": "2024-04-20T20:52:32.29327+00:00",
|
||||
"version": 2,
|
||||
"toolVersion": "3.2.0.0",
|
||||
"variables": [],
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"outcomes": [],
|
||||
"customProperties": {
|
||||
"Elsa:WorkflowContextProviderTypes": []
|
||||
},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"options": {
|
||||
"autoUpdateConsumingWorkflows": false
|
||||
},
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "c74056c7f1f4a3",
|
||||
"nodeId": "Workflow1:c74056c7f1f4a3",
|
||||
"metadata": {},
|
||||
"customProperties": {
|
||||
"source": "FlowchartJsonConverter.cs:45",
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"branches": {
|
||||
"typeName": "String[]",
|
||||
"expression": {
|
||||
"type": "Object",
|
||||
"value": "[\u0022One\u0022,\u0022Two\u0022,\u0022Three\u0022]"
|
||||
}
|
||||
},
|
||||
"id": "c0e09e3ac3cfdba7",
|
||||
"nodeId": "Workflow1:c74056c7f1f4a3:c0e09e3ac3cfdba7",
|
||||
"name": "FlowFork1",
|
||||
"type": "Elsa.FlowFork",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -340,
|
||||
"y": -197
|
||||
},
|
||||
"size": {
|
||||
"width": 145.5625,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "One"
|
||||
}
|
||||
},
|
||||
"id": "e6f0cddb1cdbdd7c",
|
||||
"nodeId": "Workflow1:c74056c7f1f4a3:e6f0cddb1cdbdd7c",
|
||||
"name": "WriteLine1",
|
||||
"type": "Elsa.WriteLine",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -80,
|
||||
"y": -300
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Two"
|
||||
}
|
||||
},
|
||||
"id": "3ece093dfc20c752",
|
||||
"nodeId": "Workflow1:c74056c7f1f4a3:3ece093dfc20c752",
|
||||
"name": "WriteLine2",
|
||||
"type": "Elsa.WriteLine",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -80,
|
||||
"y": -197
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Three"
|
||||
}
|
||||
},
|
||||
"id": "ca9fcd1d6069df9",
|
||||
"nodeId": "Workflow1:c74056c7f1f4a3:ca9fcd1d6069df9",
|
||||
"name": "WriteLine3",
|
||||
"type": "Elsa.WriteLine",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -80,
|
||||
"y": -102
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"source": {
|
||||
"activity": "c0e09e3ac3cfdba7",
|
||||
"port": "One"
|
||||
},
|
||||
"target": {
|
||||
"activity": "e6f0cddb1cdbdd7c",
|
||||
"port": "In"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": {
|
||||
"activity": "c0e09e3ac3cfdba7",
|
||||
"port": "Two"
|
||||
},
|
||||
"target": {
|
||||
"activity": "3ece093dfc20c752",
|
||||
"port": "In"
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": {
|
||||
"activity": "c0e09e3ac3cfdba7",
|
||||
"port": "Three"
|
||||
},
|
||||
"target": {
|
||||
"activity": "ca9fcd1d6069df9",
|
||||
"port": "In"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"id": "2630068018ac1f0a",
|
||||
"definitionId": "2630068018ac1f0a",
|
||||
"name": "Hello World",
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "969b0703a9379c3b",
|
||||
"nodeId": "Workflow1:969b0703a9379c3b",
|
||||
"activities": [
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Hello World!"
|
||||
}
|
||||
},
|
||||
"id": "b039045bb7443e57",
|
||||
"nodeId": "Workflow1:969b0703a9379c3b:b039045bb7443e57",
|
||||
"name": "WriteLine1",
|
||||
"type": "Elsa.WriteLine",
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -231.796875,
|
||||
"y": 269
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": []
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
using System.Net;
|
||||
using Elsa.Testing.Shared.Services;
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Elsa.Workflows.Runtime;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.WorkflowDefinitionRefresh;
|
||||
|
||||
public class DynamicEndpointTests : AppComponentTest
|
||||
{
|
||||
private readonly IWorkflowDefinitionsRefresher _workflowDefinitionsRefresher;
|
||||
|
||||
public DynamicEndpointTests(App app) : base(app)
|
||||
{
|
||||
StaticValueHolder.Value = "first-value";
|
||||
_workflowDefinitionsRefresher = Scope.ServiceProvider.GetRequiredService<IWorkflowDefinitionsRefresher>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ChangingEndpointValueThenRefresh_WorkflowShouldRespondToTheNewValue()
|
||||
{
|
||||
var client = WorkflowServer.CreateHttpWorkflowClient();
|
||||
|
||||
var firstResponse = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "first-value"));
|
||||
|
||||
StaticValueHolder.Value = "second-value";
|
||||
var _ = await _workflowDefinitionsRefresher.RefreshWorkflowDefinitionsAsync(
|
||||
new Runtime.Requests.RefreshWorkflowDefinitionsRequest() { DefinitionIds = ["f69f061159adc3ae"] }, CancellationToken.None);
|
||||
|
||||
var secondResponse = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "second-value"));
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, firstResponse.StatusCode);
|
||||
Assert.Equal(HttpStatusCode.OK, secondResponse.StatusCode);
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"id": "69cb6deaa8e20599",
|
||||
"definitionId": "f69f061159adc3ae",
|
||||
"name": "Workflow 1",
|
||||
"createdAt": "2024-07-04T13:41:33.1905134+00:00",
|
||||
"version": 1,
|
||||
"toolVersion": "3.3.0.0",
|
||||
"variables": [],
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"outcomes": [],
|
||||
"customProperties": {
|
||||
"Elsa:WorkflowContextProviderTypes": []
|
||||
},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"options": {
|
||||
"autoUpdateConsumingWorkflows": false
|
||||
},
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "1bac532ee993dfdc",
|
||||
"nodeId": "Workflow1:1bac532ee993dfdc",
|
||||
"metadata": {},
|
||||
"customProperties": {
|
||||
"source": "FlowchartJsonConverter.cs:45",
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"path": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "JavaScript",
|
||||
"value": "return getStaticValue() ?? \u0022first-value\u0022;"
|
||||
}
|
||||
},
|
||||
"supportedMethods": {
|
||||
"typeName": "String[]",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "[\u0022GET\u0022]"
|
||||
}
|
||||
},
|
||||
"authorize": {
|
||||
"typeName": "Boolean",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
"policy": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal"
|
||||
}
|
||||
},
|
||||
"requestTimeout": null,
|
||||
"requestSizeLimit": null,
|
||||
"fileSizeLimit": null,
|
||||
"allowedFileExtensions": null,
|
||||
"blockedFileExtensions": null,
|
||||
"allowedMimeTypes": null,
|
||||
"exposeRequestTooLargeOutcome": false,
|
||||
"exposeFileTooLargeOutcome": false,
|
||||
"exposeInvalidFileExtensionOutcome": false,
|
||||
"exposeInvalidFileMimeTypeOutcome": false,
|
||||
"parsedContent": null,
|
||||
"files": null,
|
||||
"routeData": null,
|
||||
"queryStringData": null,
|
||||
"headers": null,
|
||||
"result": null,
|
||||
"id": "25d3a6465094a796",
|
||||
"nodeId": "Workflow1:1bac532ee993dfdc:25d3a6465094a796",
|
||||
"name": "HttpEndpoint1",
|
||||
"type": "Elsa.HttpEndpoint",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": true,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -260,
|
||||
"y": 460
|
||||
},
|
||||
"size": {
|
||||
"width": 176.390625,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": []
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
using System.Net;
|
||||
using Elsa.Common.Models;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
using Elsa.Workflows.ComponentTests.Materializers;
|
||||
using Elsa.Workflows.ComponentTests.WorkflowProviders;
|
||||
using Elsa.Workflows.Management;
|
||||
using Elsa.Workflows.Runtime;
|
||||
using Humanizer;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.WorkflowDefinitionReload;
|
||||
|
||||
public class ReloadWorkflowTests : AppComponentTest
|
||||
{
|
||||
private readonly IActivityRegistry _activityRegistry;
|
||||
private readonly TestWorkflowProvider _testWorkflowProvider;
|
||||
private readonly IWorkflowBuilderFactory _workflowBuilderFactory;
|
||||
private readonly IWorkflowDefinitionManager _workflowDefinitionManager;
|
||||
private readonly IWorkflowDefinitionService _workflowDefinitionService;
|
||||
private readonly IWorkflowDefinitionsReloader _workflowDefinitionsReloader;
|
||||
|
||||
public ReloadWorkflowTests(App app) : base(app)
|
||||
{
|
||||
_workflowDefinitionManager = Scope.ServiceProvider.GetRequiredService<IWorkflowDefinitionManager>();
|
||||
_workflowDefinitionsReloader = Scope.ServiceProvider.GetRequiredService<IWorkflowDefinitionsReloader>();
|
||||
_workflowBuilderFactory = Scope.ServiceProvider.GetRequiredService<IWorkflowBuilderFactory>();
|
||||
_workflowDefinitionService = Scope.ServiceProvider.GetRequiredService<IWorkflowDefinitionService>();
|
||||
_activityRegistry = Scope.ServiceProvider.GetRequiredService<IActivityRegistry>();
|
||||
var workflowsProviders = Scope.ServiceProvider.GetRequiredService<IEnumerable<IWorkflowsProvider>>();
|
||||
_testWorkflowProvider = (TestWorkflowProvider)workflowsProviders.First(x => x is TestWorkflowProvider);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Reloading_AfterRemovingTheWorkflow_ShouldMakeWorkflowReachableAgain()
|
||||
{
|
||||
var client = WorkflowServer.CreateHttpWorkflowClient();
|
||||
await _workflowDefinitionManager.DeleteByDefinitionIdAsync("f68b09bc-2013-4617-b82f-d76b6819a624", CancellationToken.None);
|
||||
var firstResponse = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "reload-test"));
|
||||
await _workflowDefinitionsReloader.ReloadWorkflowDefinitionsAsync();
|
||||
var secondResponse = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "reload-test"));
|
||||
Assert.Equal(HttpStatusCode.NotFound, firstResponse.StatusCode);
|
||||
Assert.Equal(HttpStatusCode.OK, secondResponse.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Reloading_AfterUpdatingSourceProvider_ShouldRefreshCaches()
|
||||
{
|
||||
var definitionId = Guid.NewGuid().ToString();
|
||||
var definitionVersionId1 = Guid.NewGuid().ToString();
|
||||
var workflowV1 = await BuildWorkflowAsync(definitionId, definitionVersionId1, 1);
|
||||
|
||||
// Set up the initial workflow version.
|
||||
_testWorkflowProvider.MaterializedWorkflows = [workflowV1];
|
||||
await _workflowDefinitionsReloader.ReloadWorkflowDefinitionsAsync();
|
||||
var definitionV1 = await _workflowDefinitionService.FindWorkflowGraphAsync(definitionId, VersionOptions.Latest);
|
||||
Assert.Equal(definitionVersionId1, definitionV1!.Workflow.Identity.Id);
|
||||
|
||||
// Simulate the workflow provider to have a new version available.
|
||||
var definitionVersionId2 = Guid.NewGuid().ToString();
|
||||
var workflowV2 = await BuildWorkflowAsync(definitionId, definitionVersionId2, 2);
|
||||
_testWorkflowProvider.MaterializedWorkflows = [workflowV1, workflowV2];
|
||||
|
||||
// Reload the workflow definitions.
|
||||
await _workflowDefinitionsReloader.ReloadWorkflowDefinitionsAsync();
|
||||
|
||||
// Assert that the workflow definition service finds the updated workflow version.
|
||||
var definitionV2 = await _workflowDefinitionService.FindWorkflowGraphAsync(definitionId, VersionOptions.Latest);
|
||||
Assert.Equal(definitionVersionId2, definitionV2!.Workflow.Identity.Id);
|
||||
|
||||
// Cleanup: Delete the workflow definition and its versions.
|
||||
await _workflowDefinitionManager.DeleteByDefinitionIdAsync(definitionId, CancellationToken.None);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Reloading_AfterUpdatingSourceProvider_ShouldRefreshActivityRegistry()
|
||||
{
|
||||
var definitionId = Guid.NewGuid().ToString();
|
||||
var definitionVersionId1 = Guid.NewGuid().ToString();
|
||||
var workflowV1 = await BuildWorkflowAsync(definitionId, definitionVersionId1, 1);
|
||||
|
||||
// Set up the initial workflow version.
|
||||
_testWorkflowProvider.MaterializedWorkflows = [workflowV1];
|
||||
await _workflowDefinitionsReloader.ReloadWorkflowDefinitionsAsync();
|
||||
var activityTypeName = workflowV1.Workflow.Name.Pascalize();
|
||||
var activityV1 = _activityRegistry.Find(activityTypeName);
|
||||
Assert.Equal(1, activityV1!.Version);
|
||||
|
||||
// Simulate the workflow provider to have a new version available.
|
||||
var definitionVersionId2 = Guid.NewGuid().ToString();
|
||||
var workflowV2 = await BuildWorkflowAsync(definitionId, definitionVersionId2, 2);
|
||||
_testWorkflowProvider.MaterializedWorkflows = [workflowV1, workflowV2];
|
||||
|
||||
// Reload the workflow definitions.
|
||||
await _workflowDefinitionsReloader.ReloadWorkflowDefinitionsAsync();
|
||||
|
||||
// Assert that the activity registry contains a new activity descriptor representing the new workflow version.
|
||||
var activityV2 = _activityRegistry.Find(activityTypeName)!;
|
||||
Assert.Equal(2, activityV2.Version);
|
||||
|
||||
// Cleanup: Delete the workflow definition and its versions.
|
||||
await _workflowDefinitionManager.DeleteByDefinitionIdAsync(definitionId, CancellationToken.None);
|
||||
}
|
||||
|
||||
private async Task<MaterializedWorkflow> BuildWorkflowAsync(string definitionId, string definitionVersionId, int version)
|
||||
{
|
||||
var builder = _workflowBuilderFactory.CreateBuilder();
|
||||
builder.DefinitionId = definitionId;
|
||||
builder.Id = definitionVersionId;
|
||||
builder.Version = version;
|
||||
builder.Name = definitionId;
|
||||
builder.Root = new WriteLine($"Version {version}");
|
||||
builder.WorkflowOptions.UsableAsActivity = true;
|
||||
var workflow = await builder.BuildWorkflowAsync();
|
||||
workflow.Name = definitionId;
|
||||
return new MaterializedWorkflow(workflow, _testWorkflowProvider.Name, TestWorkflowMaterializer.MaterializerName);
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"id": "c2e19917-d03e-41ed-9d19-ef5c1ddc9871",
|
||||
"definitionId": "f68b09bc-2013-4617-b82f-d76b6819a624",
|
||||
"name": "Workflow 1",
|
||||
"createdAt": "2024-07-04T13:41:33.1905134+00:00",
|
||||
"version": 1,
|
||||
"toolVersion": "3.3.0.0",
|
||||
"variables": [],
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"outcomes": [],
|
||||
"customProperties": {
|
||||
"Elsa:WorkflowContextProviderTypes": []
|
||||
},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"options": {
|
||||
"autoUpdateConsumingWorkflows": false
|
||||
},
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "f8019bae-c506-4467-812b-e403e7aad8ff",
|
||||
"nodeId": "Workflow1:f8019bae-c506-4467-812b-e403e7aad8ff",
|
||||
"metadata": {},
|
||||
"customProperties": {
|
||||
"source": "FlowchartJsonConverter.cs:45",
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"path": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "reload-test"
|
||||
}
|
||||
},
|
||||
"supportedMethods": {
|
||||
"typeName": "String[]",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "[\u0022GET\u0022]"
|
||||
}
|
||||
},
|
||||
"authorize": {
|
||||
"typeName": "Boolean",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
"policy": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal"
|
||||
}
|
||||
},
|
||||
"requestTimeout": null,
|
||||
"requestSizeLimit": null,
|
||||
"fileSizeLimit": null,
|
||||
"allowedFileExtensions": null,
|
||||
"blockedFileExtensions": null,
|
||||
"allowedMimeTypes": null,
|
||||
"exposeRequestTooLargeOutcome": false,
|
||||
"exposeFileTooLargeOutcome": false,
|
||||
"exposeInvalidFileExtensionOutcome": false,
|
||||
"exposeInvalidFileMimeTypeOutcome": false,
|
||||
"parsedContent": null,
|
||||
"files": null,
|
||||
"routeData": null,
|
||||
"queryStringData": null,
|
||||
"headers": null,
|
||||
"result": null,
|
||||
"id": "cc196a5e-04f7-4794-b25c-1a0987632d8a",
|
||||
"nodeId": "Workflow1:f8019bae-c506-4467-812b-e403e7aad8ff:cc196a5e-04f7-4794-b25c-1a0987632d8a",
|
||||
"name": "HttpEndpoint1",
|
||||
"type": "Elsa.HttpEndpoint",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": true,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -260,
|
||||
"y": 460
|
||||
},
|
||||
"size": {
|
||||
"width": 176.390625,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": []
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
using Elsa.Api.Client.Resources.WorkflowDefinitions.Contracts;
|
||||
using Elsa.Workflows.ComponentTests.Abstractions;
|
||||
using Elsa.Workflows.ComponentTests.Fixtures;
|
||||
|
||||
namespace Elsa.Workflows.ComponentTests.Scenarios.WorkflowJsonStructures;
|
||||
|
||||
public class WorkflowJsonStructureTests(App app) : AppComponentTest(app)
|
||||
{
|
||||
private const string MainDefinitionId = "a417fadbbb7c417e";
|
||||
private const string Sub1NodeId = "Workflow2:4d5af7585eece1d7:f19ae76011a020f3";
|
||||
|
||||
[Fact]
|
||||
public async Task Workflow_ContainingWorkflowActivity_ShouldNotIncludeChildrenOfWorkflowActivity()
|
||||
{
|
||||
var client = WorkflowServer.CreateApiClient<IWorkflowDefinitionsApi>();
|
||||
var workflowDefinition = (await client.GetByIdAsync(MainDefinitionId))!;
|
||||
var root = workflowDefinition.Root;
|
||||
var subActivity = root["activities"]![0]!;
|
||||
|
||||
Assert.Null(subActivity["root"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Requesting_Subgraph_Returns_ExpectedSubgraph()
|
||||
{
|
||||
var client = WorkflowServer.CreateApiClient<IWorkflowDefinitionsApi>();
|
||||
var subgraph = (await client.GetSubgraphAsync(MainDefinitionId, Sub1NodeId))!;
|
||||
Assert.Equal(Sub1NodeId, subgraph.Activity["nodeId"]!.ToString());
|
||||
var writeLine = subgraph.Activity["root"]!["root"]!["activities"]![0]!;
|
||||
Assert.Equal("Elsa.WriteLine", writeLine["type"]!.ToString());
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"id": "a417fadbbb7c417e",
|
||||
"definitionId": "ebe87d0f1d1924ea",
|
||||
"name": "Main",
|
||||
"createdAt": "2024-06-20T15:08:33.469865+00:00",
|
||||
"version": 1,
|
||||
"toolVersion": "3.3.0.0",
|
||||
"variables": [],
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"outcomes": [],
|
||||
"customProperties": {
|
||||
"Elsa:WorkflowContextProviderTypes": []
|
||||
},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"options": {
|
||||
"autoUpdateConsumingWorkflows": false
|
||||
},
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "4d5af7585eece1d7",
|
||||
"nodeId": "Workflow2:4d5af7585eece1d7",
|
||||
"metadata": {},
|
||||
"customProperties": {
|
||||
"source": "FlowchartJsonConverter.cs:45",
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"workflowDefinitionId": "497be45e7d647e8f",
|
||||
"workflowDefinitionVersionId": "e91d58c7a7ca1861",
|
||||
"latestAvailablePublishedVersion": 1,
|
||||
"latestAvailablePublishedVersionId": "e91d58c7a7ca1861",
|
||||
"id": "f19ae76011a020f3",
|
||||
"nodeId": "Workflow2:4d5af7585eece1d7:f19ae76011a020f3",
|
||||
"name": "Sub1",
|
||||
"type": "Sub",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -235.796875,
|
||||
"y": -74
|
||||
},
|
||||
"size": {
|
||||
"width": 58.7421875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"workflowDefinitionId": "497be45e7d647e8f",
|
||||
"workflowDefinitionVersionId": "e91d58c7a7ca1861",
|
||||
"latestAvailablePublishedVersion": 1,
|
||||
"latestAvailablePublishedVersionId": "e91d58c7a7ca1861",
|
||||
"id": "ad4d54104a443b5b",
|
||||
"nodeId": "Workflow2:4d5af7585eece1d7:ad4d54104a443b5b",
|
||||
"name": "Sub2",
|
||||
"type": "Sub",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -101.796875,
|
||||
"y": -74
|
||||
},
|
||||
"size": {
|
||||
"width": 58.7421875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"source": {
|
||||
"activity": "f19ae76011a020f3",
|
||||
"port": "Done"
|
||||
},
|
||||
"target": {
|
||||
"activity": "ad4d54104a443b5b",
|
||||
"port": "In"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"id": "e91d58c7a7ca1861",
|
||||
"definitionId": "497be45e7d647e8f",
|
||||
"name": "Sub",
|
||||
"createdAt": "2024-06-20T15:08:33.474045+00:00",
|
||||
"version": 1,
|
||||
"toolVersion": "3.3.0.0",
|
||||
"variables": [],
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"outcomes": [],
|
||||
"customProperties": {
|
||||
"Elsa:WorkflowContextProviderTypes": []
|
||||
},
|
||||
"isReadonly": false,
|
||||
"isSystem": false,
|
||||
"isLatest": true,
|
||||
"isPublished": true,
|
||||
"options": {
|
||||
"usableAsActivity": true,
|
||||
"autoUpdateConsumingWorkflows": true
|
||||
},
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "dc487ec63d2e8520",
|
||||
"nodeId": "Workflow1:dc487ec63d2e8520",
|
||||
"metadata": {},
|
||||
"customProperties": {
|
||||
"source": "FlowchartJsonConverter.cs:45",
|
||||
"notFoundConnections": [],
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Test"
|
||||
}
|
||||
},
|
||||
"id": "f0f7dfe68eda4744",
|
||||
"nodeId": "Workflow1:dc487ec63d2e8520:f0f7dfe68eda4744",
|
||||
"name": "WriteLine1",
|
||||
"type": "Elsa.WriteLine",
|
||||
"version": 1,
|
||||
"customProperties": {
|
||||
"canStartWorkflow": false,
|
||||
"runAsynchronously": false
|
||||
},
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -55.796875,
|
||||
"y": -171
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
|
||||
"parallelizeTestCollections": true
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\common\Elsa.Testing.Shared.Integration\Elsa.Testing.Shared.Integration.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\common\Elsa.Testing.Shared\Elsa.Testing.Shared.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.Workflows.Core\Elsa.Workflows.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,27 @@
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.Models;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Activities.IntegrationTests;
|
||||
|
||||
public class SetNameTests
|
||||
{
|
||||
private readonly CapturingTextWriter _capturingTextWriter = new();
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public SetNameTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
_serviceProvider = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "SetName sets the workflow instance name.")]
|
||||
public async Task Test1()
|
||||
{
|
||||
const string expectedName = "Foo";
|
||||
var setName = new SetName(new Input<string>(expectedName));
|
||||
var result = await _serviceProvider.RunActivityAsync(setName);
|
||||
var actualName = result.WorkflowState.Name;
|
||||
Assert.Equal(expectedName, actualName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
global using Xunit;
|
||||
@@ -0,0 +1,25 @@
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Activities.IntegrationTests;
|
||||
|
||||
public class WriteLineTests
|
||||
{
|
||||
private readonly CapturingTextWriter _capturingTextWriter = new();
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public WriteLineTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
_serviceProvider = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "WriteLine prints the expected line to the console.")]
|
||||
public async Task Test1()
|
||||
{
|
||||
const string expectedLine = "Hello world!";
|
||||
var writeLine = new WriteLine(expectedLine);
|
||||
await _serviceProvider.RunActivityAsync(writeLine);
|
||||
Assert.Equal(expectedLine, _capturingTextWriter.Lines.Single());
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\common\Elsa.Testing.Shared.Integration\Elsa.Testing.Shared.Integration.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.Alterations\Elsa.Alterations.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.Workflows.Runtime\Elsa.Workflows.Runtime.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Workflows\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Workflows\version-1.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Workflows\version-2.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,73 @@
|
||||
using Elsa.Alterations.AlterationTypes;
|
||||
using Elsa.Alterations.Core.Contracts;
|
||||
using Elsa.Alterations.Extensions;
|
||||
using Elsa.Common.Models;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.Management;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Alterations.IntegrationTests;
|
||||
|
||||
/// <summary>
|
||||
/// Contains tests for migration.
|
||||
/// </summary>
|
||||
public class MigrationTests
|
||||
{
|
||||
private readonly CapturingTextWriter _capturingTextWriter = new();
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly IAlterationRunner _alterationRunner;
|
||||
private readonly IWorkflowInstanceStore _workflowInstanceStore;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MigrationTests"/> class.
|
||||
/// </summary>
|
||||
public MigrationTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
_services = new TestApplicationBuilder(testOutputHelper)
|
||||
.WithCapturingTextWriter(_capturingTextWriter)
|
||||
.ConfigureElsa(elsa => elsa.UseAlterations())
|
||||
.Build();
|
||||
_alterationRunner = _services.GetRequiredService<IAlterationRunner>();
|
||||
_workflowInstanceStore = _services.GetRequiredService<IWorkflowInstanceStore>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method tests the migration from version 1 to version 2.
|
||||
/// </summary>
|
||||
[Fact(DisplayName = "Migrating from version 1 to 2 succeeds")]
|
||||
public async Task Test1()
|
||||
{
|
||||
// Populate registries.
|
||||
await _services.PopulateRegistriesAsync();
|
||||
|
||||
// Import workflows.
|
||||
await _services.ImportWorkflowDefinitionAsync("Workflows/version-1.json");
|
||||
await _services.ImportWorkflowDefinitionAsync("Workflows/version-2.json");
|
||||
|
||||
// Run version 1 workflow.
|
||||
var workflowState = await _services.RunWorkflowUntilEndAsync("my-workflow", versionOptions: VersionOptions.SpecificVersion(1));
|
||||
|
||||
// Migrate to version 2.
|
||||
var alterations = new IAlteration[]
|
||||
{
|
||||
new Migrate
|
||||
{
|
||||
TargetVersion = 2
|
||||
}
|
||||
};
|
||||
var instanceIds = new[]
|
||||
{
|
||||
workflowState.Id
|
||||
};
|
||||
var migrationResult = await _alterationRunner.RunAsync(instanceIds, alterations);
|
||||
|
||||
// Assert success.
|
||||
Assert.True(migrationResult.All(x => x.IsSuccessful));
|
||||
|
||||
// Assert that the workflow instance is now at version 2.
|
||||
var workflowState2 = await _workflowInstanceStore.FindAsync(workflowState.Id);
|
||||
Assert.Equal(2, workflowState2!.Version);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
global using Xunit;
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"id": "b760f121e7dc5dae",
|
||||
"definitionId": "my-workflow",
|
||||
"name": "Version 1",
|
||||
"version": 1,
|
||||
"toolVersion": "3.0.0.0",
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "52ae1d65f7896a9e",
|
||||
"nodeId": "Workflow1:52ae1d65f7896a9e",
|
||||
"activities": [
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Version 1"
|
||||
}
|
||||
},
|
||||
"id": "3a1ab61a351fe232",
|
||||
"nodeId": "Workflow1:52ae1d65f7896a9e:3a1ab61a351fe232",
|
||||
"name": "WriteLine1",
|
||||
"type": "Elsa.WriteLine",
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -121.796875,
|
||||
"y": 272
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"id": "4aca2e3aa79475d3",
|
||||
"definitionId": "my-workflow",
|
||||
"name": "Version 2",
|
||||
"version": 2,
|
||||
"toolVersion": "3.0.0.0",
|
||||
"root": {
|
||||
"type": "Elsa.Flowchart",
|
||||
"version": 1,
|
||||
"id": "52ae1d65f7896a9e",
|
||||
"nodeId": "Workflow1:52ae1d65f7896a9e",
|
||||
"activities": [
|
||||
{
|
||||
"text": {
|
||||
"typeName": "String",
|
||||
"expression": {
|
||||
"type": "Literal",
|
||||
"value": "Version 2"
|
||||
}
|
||||
},
|
||||
"id": "3a1ab61a351fe232",
|
||||
"nodeId": "Workflow1:52ae1d65f7896a9e:3a1ab61a351fe232",
|
||||
"name": "WriteLine1",
|
||||
"type": "Elsa.WriteLine",
|
||||
"metadata": {
|
||||
"designer": {
|
||||
"position": {
|
||||
"x": -121.796875,
|
||||
"y": 272
|
||||
},
|
||||
"size": {
|
||||
"width": 139.296875,
|
||||
"height": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": []
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\common\Elsa.Testing.Shared.Integration\Elsa.Testing.Shared.Integration.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\common\Elsa.Testing.Shared\Elsa.Testing.Shared.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.JavaScript\Elsa.JavaScript.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Dynamic;
|
||||
using System.Numerics;
|
||||
using System.Text.Json;
|
||||
using Elsa.Common.Converters;
|
||||
using Elsa.Expressions.Models;
|
||||
using Elsa.JavaScript.Contracts;
|
||||
using Elsa.Testing.Shared;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.JavaScript.IntegrationTests;
|
||||
public class JsonConverterTest(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider = new TestApplicationBuilder(testOutputHelper).Build();
|
||||
|
||||
[Fact(DisplayName = "JavaScript BigInt mapping to BigInteger serialization")]
|
||||
public async Task Test1()
|
||||
{
|
||||
var javaScriptEvaluator = _serviceProvider.GetRequiredService<IJavaScriptEvaluator>();
|
||||
var script = @"return {
|
||||
'BigNumber': BigInt('7239948466988781569')
|
||||
}";
|
||||
var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, new MemoryRegister());
|
||||
var result = (await javaScriptEvaluator.EvaluateAsync(script, typeof(ExpandoObject), expressionExecutionContext))!;
|
||||
var options = new JsonSerializerOptions()
|
||||
{
|
||||
Converters = { new BigIntegerJsonConverter() }
|
||||
};
|
||||
var serializedText = JsonSerializer.Serialize(result, options);
|
||||
|
||||
Assert.Equal("{\"BigNumber\":7239948466988781569}", serializedText);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "JavaScript BigInt mapping to BigInteger serialization")]
|
||||
public async Task Test2()
|
||||
{
|
||||
var javaScriptEvaluator = _serviceProvider.GetRequiredService<IJavaScriptEvaluator>();
|
||||
var script = @"return {
|
||||
'BigNumber': BigInt('7239948466988781569')
|
||||
}";
|
||||
var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, new MemoryRegister());
|
||||
var result = (await javaScriptEvaluator.EvaluateAsync(script, typeof(ExpandoObject), expressionExecutionContext))!;
|
||||
var serializedText = JsonSerializer.Serialize(result);
|
||||
|
||||
Assert.Equal("{\"BigNumber\":{\"IsPowerOfTwo\":false,\"IsZero\":false,\"IsOne\":false,\"IsEven\":false,\"Sign\":1}}", serializedText);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "BigIntegerJsonConverter Deserialize")]
|
||||
public async Task Test3()
|
||||
{
|
||||
var options = new JsonSerializerOptions()
|
||||
{
|
||||
Converters = { new BigIntegerJsonConverter() }
|
||||
};
|
||||
|
||||
BigInteger bigInteger = JsonSerializer.Deserialize<BigInteger>("7239948466988781569", options);
|
||||
|
||||
Assert.Equal(7239948466988781569, bigInteger);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
using System.Text.Json;
|
||||
using Elsa.Expressions.Models;
|
||||
using Elsa.JavaScript.Contracts;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.JavaScript.IntegrationTests;
|
||||
|
||||
public class JsonElementConverterTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider = new TestApplicationBuilder(testOutputHelper).Build();
|
||||
|
||||
[Fact(DisplayName = "JsonElement JsonObject can be passed to JavaScript")]
|
||||
public async Task TestJsonObjectPassedAsJsonElement()
|
||||
{
|
||||
var javaScriptEvaluator = _serviceProvider.GetRequiredService<IJavaScriptEvaluator>();
|
||||
var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, new());
|
||||
var jsonVariable = new Variable<object>("JsonVariable", "");
|
||||
var jsonString = "{\"name\": \"John\", \"age\": 30}";
|
||||
var jsonElement = JsonSerializer.Deserialize<JsonElement>(jsonString);
|
||||
|
||||
jsonVariable.Set(expressionExecutionContext, jsonElement);
|
||||
var script = "getVariable('JsonVariable').age";
|
||||
var result = await javaScriptEvaluator.EvaluateAsync(script, typeof(int), expressionExecutionContext);
|
||||
Assert.Equal(30, result);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "JsonElement JsonArray can be passed to JavaScript")]
|
||||
public async Task TestJsonArrayPassedAsJsonElement()
|
||||
{
|
||||
var javaScriptEvaluator = _serviceProvider.GetRequiredService<IJavaScriptEvaluator>();
|
||||
var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, new());
|
||||
var jsonVariable = new Variable<object>("JsonVariable", "");
|
||||
var jsonString = "[1, 2, 3, 4, 5, 6]";
|
||||
var jsonElement = JsonSerializer.Deserialize<JsonElement>(jsonString);
|
||||
|
||||
jsonVariable.Set(expressionExecutionContext, jsonElement);
|
||||
var script = "getVariable('JsonVariable')[3]";
|
||||
var result = await javaScriptEvaluator.EvaluateAsync(script, typeof(int), expressionExecutionContext);
|
||||
Assert.Equal(4, result);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "JsonElement string can be passed to JavaScript")]
|
||||
public async Task TestStringPassedAsJsonElement()
|
||||
{
|
||||
var javaScriptEvaluator = _serviceProvider.GetRequiredService<IJavaScriptEvaluator>();
|
||||
var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, new());
|
||||
var jsonVariable = new Variable<object>("JsonVariable", "");
|
||||
var jsonString = "\"I'm just a string\"";
|
||||
var jsonElement = JsonSerializer.Deserialize<JsonElement>(jsonString);
|
||||
|
||||
jsonVariable.Set(expressionExecutionContext, jsonElement);
|
||||
var script = "getVariable('JsonVariable')";
|
||||
var result = await javaScriptEvaluator.EvaluateAsync(script, typeof(string), expressionExecutionContext);
|
||||
Assert.Equal("I'm just a string", result);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "JsonElement boolean can be passed to JavaScript")]
|
||||
public async Task TestBooleanPassedAsJsonElement()
|
||||
{
|
||||
var javaScriptEvaluator = _serviceProvider.GetRequiredService<IJavaScriptEvaluator>();
|
||||
var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, new());
|
||||
var jsonVariable = new Variable<object>("JsonVariable", "");
|
||||
var jsonString = "false";
|
||||
var jsonElement = JsonSerializer.Deserialize<JsonElement>(jsonString);
|
||||
|
||||
jsonVariable.Set(expressionExecutionContext, jsonElement);
|
||||
var script = "getVariable('JsonVariable')";
|
||||
var result = await javaScriptEvaluator.EvaluateAsync(script, typeof(bool), expressionExecutionContext);
|
||||
Assert.Equal(false, result);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "JsonElement containing nested Json objects and arrays be passed to JavaScript")]
|
||||
public async Task TestNestedJsonPassedAsJsonElement()
|
||||
{
|
||||
var javaScriptEvaluator = _serviceProvider.GetRequiredService<IJavaScriptEvaluator>();
|
||||
var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, new());
|
||||
var jsonVariable = new Variable<object>("JsonVariable", "");
|
||||
|
||||
var jsonString = @"
|
||||
{
|
||||
""name"": ""John Doe"",
|
||||
""age"": 35,
|
||||
""address"": {
|
||||
""street"": ""123 Main St"",
|
||||
""city"": ""Springfield"",
|
||||
""zip"": ""12345"",
|
||||
""coordinates"": { ""lat"": 40.7128, ""lng"": -74.006 }
|
||||
},
|
||||
""skills"": [
|
||||
{ ""name"": ""Programming"", ""level"": ""Advanced"" },
|
||||
{ ""name"": ""Writing"", ""level"": ""Intermediate"" }
|
||||
],
|
||||
""projects"": [
|
||||
{
|
||||
""title"": ""Project A"",
|
||||
""status"": ""Completed"",
|
||||
""team"": [""Alice"", ""Bob""]
|
||||
},
|
||||
{
|
||||
""title"": ""Project B"",
|
||||
""status"": ""In Progress"",
|
||||
""team"": [""Charlie"", ""David"", ""Eve""]
|
||||
}
|
||||
],
|
||||
""isEmployed"": true,
|
||||
""contact"": { ""email"": ""john.doe@example.com"", ""phone"": ""555-1234"" }
|
||||
}";
|
||||
var jsonElement = JsonSerializer.Deserialize<JsonElement>(jsonString);
|
||||
|
||||
jsonVariable.Set(expressionExecutionContext, jsonElement);
|
||||
|
||||
var script = "getVariable('JsonVariable').projects[1].team[0]";
|
||||
var result = await javaScriptEvaluator.EvaluateAsync(script, typeof(string), expressionExecutionContext);
|
||||
Assert.Equal("Charlie", result);
|
||||
|
||||
var script2 = "getVariable('JsonVariable').isEmployed";
|
||||
var result2 = await javaScriptEvaluator.EvaluateAsync(script2, typeof(bool), expressionExecutionContext);
|
||||
Assert.Equal(true, result2);
|
||||
|
||||
var script3 = "getVariable('JsonVariable').skills[0].level";
|
||||
var result3 = await javaScriptEvaluator.EvaluateAsync(script3, typeof(string), expressionExecutionContext);
|
||||
Assert.Equal("Advanced", result3);
|
||||
|
||||
var script4 = "getVariable('JsonVariable').address.coordinates.lat";
|
||||
var result4 = await javaScriptEvaluator.EvaluateAsync(script4, typeof(double), expressionExecutionContext);
|
||||
Assert.Equal(40.7128, result4);
|
||||
|
||||
var script5 = "getVariable('JsonVariable').age";
|
||||
var result5 = await javaScriptEvaluator.EvaluateAsync(script5, typeof(int), expressionExecutionContext);
|
||||
Assert.Equal(35, result5);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Text.Json;
|
||||
using Elsa.Expressions.Models;
|
||||
using Elsa.JavaScript.Contracts;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.JavaScript.IntegrationTests;
|
||||
|
||||
public class ToJsonTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider = new TestApplicationBuilder(testOutputHelper).Build();
|
||||
|
||||
[Fact(DisplayName = "Serialize large unicode string using JavaScript's toJson function")]
|
||||
public async Task Test1()
|
||||
{
|
||||
var javaScriptEvaluator = _serviceProvider.GetRequiredService<IJavaScriptEvaluator>();
|
||||
var unicodeString = UnicodeRangeGenerator.GenerateUnicodeString();
|
||||
var script = $"toJson({{ text: '{unicodeString}' }})";
|
||||
var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, new MemoryRegister());
|
||||
var result = (string)(await javaScriptEvaluator.EvaluateAsync(script, typeof(string), expressionExecutionContext))!;
|
||||
var serializedText = JsonDocument.Parse(result).RootElement.GetProperty("text").GetString();
|
||||
Assert.Equal(unicodeString, serializedText);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Serialize large unicode string using JavaScript's toJson function from a workflow variable")]
|
||||
public async Task Test2()
|
||||
{
|
||||
var javaScriptEvaluator = _serviceProvider.GetRequiredService<IJavaScriptEvaluator>();
|
||||
var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, new MemoryRegister());
|
||||
var unicodeString = UnicodeRangeGenerator.GenerateUnicodeString();
|
||||
var payloadVariable = new Variable<object>("Payload", null!);
|
||||
var payload = new
|
||||
{
|
||||
Text = unicodeString
|
||||
};
|
||||
payloadVariable.Set(expressionExecutionContext, payload);
|
||||
var script = "toJson(getPayload())";
|
||||
var result = (string)(await javaScriptEvaluator.EvaluateAsync(script, typeof(string), expressionExecutionContext))!;
|
||||
var serializedText = JsonDocument.Parse(result).RootElement.GetProperty("Text").GetString();
|
||||
Assert.Equal(unicodeString, serializedText);
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\common\Elsa.Testing.Shared.Integration\Elsa.Testing.Shared.Integration.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\common\Elsa.Testing.Shared\Elsa.Testing.Shared.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.Http\Elsa.Http.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.Resilience\Elsa.Resilience.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.Workflows.Core\Elsa.Workflows.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
using System.Net;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Http;
|
||||
using Elsa.Resilience.Extensions;
|
||||
using Elsa.Resilience.Models;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Resilience.IntegrationTests;
|
||||
|
||||
public class FlowSendHttpRequestResilienceTests
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly TestRetryAttemptRecorder _recorder = new();
|
||||
|
||||
public FlowSendHttpRequestResilienceTests(ITestOutputHelper output)
|
||||
{
|
||||
var handler = new SequentialStatusHandler([HttpStatusCode.TooManyRequests, HttpStatusCode.ServiceUnavailable, HttpStatusCode.OK]);
|
||||
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["Resilience:Strategies:0:$type"] = "HttpResilienceStrategy",
|
||||
["Resilience:Strategies:0:Id"] = "test",
|
||||
["Resilience:Strategies:0:DisplayName"] = "Test",
|
||||
["Resilience:Strategies:0:MaxRetryAttempts"] = "5",
|
||||
["Resilience:Strategies:0:Delay"] = "00:00:00.001"
|
||||
})
|
||||
.Build();
|
||||
|
||||
_services = new TestApplicationBuilder(output)
|
||||
.ConfigureServices(s =>
|
||||
{
|
||||
s.AddSingleton<IConfiguration>(configuration);
|
||||
})
|
||||
.ConfigureElsa(module =>
|
||||
{
|
||||
module.UseHttp(http =>
|
||||
{
|
||||
http.HttpClientBuilder = builder => builder.ConfigurePrimaryHttpMessageHandler(() => handler);
|
||||
});
|
||||
|
||||
module.UseResilience(resilience =>
|
||||
{
|
||||
resilience.WithRetryAttemptRecorder(_ => _recorder);
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "FlowSendHttpRequest retries using selected resilience strategy")]
|
||||
public async Task InvokesResilienceStrategy()
|
||||
{
|
||||
var activity = new FlowSendHttpRequest
|
||||
{
|
||||
Url = new(new Uri("http://localhost/test")),
|
||||
Method = new("GET"),
|
||||
ExpectedStatusCodes = new([200]),
|
||||
CustomProperties =
|
||||
{
|
||||
["resilienceStrategy"] = new ResilienceStrategyConfig
|
||||
{
|
||||
Mode = ResilienceStrategyConfigMode.Identifier,
|
||||
StrategyId = "test"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var result = await _services.RunActivityAsync(activity);
|
||||
var statusCode = result.GetActivityOutput<int>(activity, nameof(SendHttpRequestBase.StatusCode));
|
||||
|
||||
Assert.Equal(200, statusCode);
|
||||
Assert.Equal(2, _recorder.Attempts.Count);
|
||||
Assert.Collection(_recorder.Attempts,
|
||||
first => Assert.Equal(0, first.AttemptNumber),
|
||||
second => Assert.Equal(1, second.AttemptNumber));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
|
||||
namespace Elsa.Resilience.IntegrationTests;
|
||||
|
||||
public class SequentialStatusHandler(IEnumerable<HttpStatusCode> codes) : HttpMessageHandler
|
||||
{
|
||||
private readonly Queue<HttpStatusCode> _codes = new(codes);
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var code = _codes.Count > 0 ? _codes.Dequeue() : _codes.Last();
|
||||
var response = new HttpResponseMessage(code)
|
||||
{
|
||||
Content = new StringContent(string.Empty)
|
||||
};
|
||||
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Elsa.Resilience.Entities;
|
||||
using Elsa.Resilience.Models;
|
||||
|
||||
namespace Elsa.Resilience.IntegrationTests;
|
||||
|
||||
public class TestRetryAttemptRecorder : IRetryAttemptRecorder
|
||||
{
|
||||
public IList<RetryAttemptRecord> Attempts { get; } = new List<RetryAttemptRecord>();
|
||||
|
||||
public Task RecordAsync(RecordRetryAttemptsContext context)
|
||||
{
|
||||
foreach (var record in context.Attempts)
|
||||
Attempts.Add(record);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
global using Xunit;
|
||||
@@ -0,0 +1,64 @@
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.IntegrationTests.Activities.Workflows;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Activities;
|
||||
|
||||
public class BreakTests
|
||||
{
|
||||
private readonly IWorkflowRunner _workflowRunner;
|
||||
private readonly CapturingTextWriter _capturingTextWriter = new();
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public BreakTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
_services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
|
||||
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Break exits out of ForEach")]
|
||||
public async Task Test1()
|
||||
{
|
||||
await _services.PopulateRegistriesAsync();
|
||||
await _workflowRunner.RunAsync<BreakForEachWorkflow>();
|
||||
var lines = _capturingTextWriter.Lines.ToList();
|
||||
Assert.Equal(new[] { "Start", "C#", "End" }, lines);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Break exits out of immediate ForEach only")]
|
||||
public async Task Test2()
|
||||
{
|
||||
await _services.PopulateRegistriesAsync();
|
||||
await _workflowRunner.RunAsync<NestedForEachWithBreakWorkflow>();
|
||||
var lines = _capturingTextWriter.Lines.ToList();
|
||||
Assert.Equal(new[] { "C#", "Classes", "Rust", "Classes", "Go", "Classes" }, lines);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Break exits out of For")]
|
||||
public async Task Test3()
|
||||
{
|
||||
await _services.PopulateRegistriesAsync();
|
||||
await _workflowRunner.RunAsync<BreakForWorkflow>();
|
||||
var lines = _capturingTextWriter.Lines.ToList();
|
||||
Assert.Equal(new[] { "Start", "0", "1", "End" }, lines);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Break exits out of While")]
|
||||
public async Task Test4()
|
||||
{
|
||||
await _services.PopulateRegistriesAsync();
|
||||
await _workflowRunner.RunAsync<BreakWhileWorkflow>();
|
||||
var lines = _capturingTextWriter.Lines.ToList();
|
||||
Assert.Equal(new[] { "Start", "1", "2", "End" }, lines);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Break removes the right bookmarks")]
|
||||
public async Task Test5()
|
||||
{
|
||||
await _services.PopulateRegistriesAsync();
|
||||
await _workflowRunner.RunAsync<BreakWhileWorkflow>();
|
||||
var lines = _capturingTextWriter.Lines.ToList();
|
||||
Assert.Equal(new[] { "Start", "1", "2", "End" }, lines);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.Memory;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Activities.Workflows;
|
||||
|
||||
class BreakForEachWorkflow : WorkflowBase
|
||||
{
|
||||
protected override void Build(IWorkflowBuilder workflow)
|
||||
{
|
||||
var items = new[] { "C#", "Rust", "Go" };
|
||||
var currentItem = new Variable<string>("CurrentItem", "");
|
||||
|
||||
workflow.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new WriteLine("Start"),
|
||||
new ForEach<string>
|
||||
{
|
||||
Items = new(items),
|
||||
CurrentValue = new (currentItem),
|
||||
Body = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new If(context => currentItem.Get(context) == "Rust")
|
||||
{
|
||||
Then = new Break()
|
||||
},
|
||||
new WriteLine(currentItem)
|
||||
}
|
||||
}
|
||||
},
|
||||
new WriteLine("End"),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.Memory;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Activities.Workflows;
|
||||
|
||||
class BreakForWorkflow : WorkflowBase
|
||||
{
|
||||
protected override void Build(IWorkflowBuilder workflow)
|
||||
{
|
||||
var currentValue = new Variable<int?>("CurrentValue", null);
|
||||
|
||||
workflow.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new WriteLine("Start"),
|
||||
new For(0, 3, 1)
|
||||
{
|
||||
CurrentValue = new (currentValue),
|
||||
Body = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new If(context => currentValue.Get(context) == 2)
|
||||
{
|
||||
Then = new Break()
|
||||
},
|
||||
new WriteLine(context => currentValue.Get(context).ToString()),
|
||||
}
|
||||
}
|
||||
},
|
||||
new WriteLine("End"),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.Memory;
|
||||
using Elsa.Workflows.Runtime.Activities;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Activities.Workflows;
|
||||
|
||||
public class BreakWhileForkWorkflow : WorkflowBase
|
||||
{
|
||||
protected override void Build(IWorkflowBuilder workflow)
|
||||
{
|
||||
var currentValue = new Variable<int?>("CurrentValue", 0);
|
||||
|
||||
workflow.Root = new Sequence
|
||||
{
|
||||
Variables = { currentValue },
|
||||
Activities =
|
||||
{
|
||||
While.True(new Fork
|
||||
{
|
||||
Branches =
|
||||
{
|
||||
new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new SetVariable
|
||||
{
|
||||
Variable = currentValue,
|
||||
Value = new (context => currentValue.Get(context) + 1)
|
||||
},
|
||||
new If(context => currentValue.Get(context) == 3)
|
||||
{
|
||||
Then = new Break()
|
||||
}
|
||||
}
|
||||
},
|
||||
new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
new WriteLine("Waiting for event..."),
|
||||
new Event("Some event") { Id = "SomeEvent" },
|
||||
new WriteLine("Resuming"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user