diff --git a/test/integration/Elsa.Core.IntegrationTests/Autofixture/HostBuilderWithDuplicateActivitiesWorkflowAttributes.cs b/test/integration/Elsa.Core.IntegrationTests/Autofixture/HostBuilderWithDuplicateActivitiesWorkflowAttributes.cs new file mode 100644 index 000000000..48ef3dddc --- /dev/null +++ b/test/integration/Elsa.Core.IntegrationTests/Autofixture/HostBuilderWithDuplicateActivitiesWorkflowAttributes.cs @@ -0,0 +1,80 @@ +using System.Reflection; +using AutoFixture; +using AutoFixture.Xunit2; +using Elsa.Core.IntegrationTests.Workflows; +using Elsa.Persistence.MongoDb.Extensions; +using Elsa.Testing.Shared.AutoFixture.Customizations; +using Microsoft.Extensions.DependencyInjection; +using Elsa.Persistence.EntityFramework.Core.Extensions; +using Microsoft.EntityFrameworkCore; +using Elsa.Persistence.EntityFramework.Sqlite; +using Elsa.Persistence.YesSql; +using YesSql.Provider.Sqlite; +using System.Data; + +namespace Elsa.Core.IntegrationTests.Autofixture +{ + public class HostBuilderWithDuplicateActivitiesWorkflowAttribute : CustomizeAttribute + { + public override ICustomization GetCustomization(ParameterInfo parameter) + { + return new HostBubilderUsingServicesCustomization(services => { + services + .AddElsa(elsa => { + elsa.AddWorkflow(); + }); + }, parameter); + } + } + + public class HostBuilderWithDuplicateActivitiesWorkflowAndMongoDbAttribute : CustomizeAttribute + { + public override ICustomization GetCustomization(ParameterInfo parameter) + { + return new HostBubilderUsingServicesCustomization(services => { + services + .AddElsa(elsa => { + elsa.AddWorkflow(); + elsa.UseMongoDbPersistence(opts => { + opts.ConnectionString = "mongodb://localhost:27017"; + opts.DatabaseName = "IntegrationTests"; + }); + }); + }, parameter); + } + } + + public class HostBuilderWithDuplicateActivitiesWorkflowAndEntityFrameworkAttribute : CustomizeAttribute + { + public override ICustomization GetCustomization(ParameterInfo parameter) + { + return new HostBubilderUsingServicesCustomization(services => { + services + .AddElsa(elsa => { + elsa + .AddWorkflow() + .UseEntityFrameworkPersistence(opts => { + opts.UseSqlite("Data Source=elsa.db;", db => db.MigrationsAssembly(typeof(SqliteElsaContextFactory).Assembly.GetName().Name)); + }); + }); + }, parameter); + } + } + + public class HostBuilderWithDuplicateActivitiesWorkflowAndYesSqlAttribute : CustomizeAttribute + { + public override ICustomization GetCustomization(ParameterInfo parameter) + { + return new HostBubilderUsingServicesCustomization(services => { + services + .AddElsa(elsa => { + elsa + .AddWorkflow() + .UseYesSqlPersistence(config => { + config.UseSqLite("Data Source=elsa-sqlite.db;", IsolationLevel.ReadUncommitted); + }); + }); + }, parameter); + } + } +} \ No newline at end of file diff --git a/test/integration/Elsa.Core.IntegrationTests/Elsa.Core.IntegrationTests.csproj b/test/integration/Elsa.Core.IntegrationTests/Elsa.Core.IntegrationTests.csproj index 58e23c58f..081da3096 100644 --- a/test/integration/Elsa.Core.IntegrationTests/Elsa.Core.IntegrationTests.csproj +++ b/test/integration/Elsa.Core.IntegrationTests/Elsa.Core.IntegrationTests.csproj @@ -23,11 +23,12 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + @@ -46,6 +47,9 @@ + + + diff --git a/test/integration/Elsa.Core.IntegrationTests/Persistence/WorkflowMayContainDuplicateActivitiesIntegrationTests.cs b/test/integration/Elsa.Core.IntegrationTests/Persistence/WorkflowMayContainDuplicateActivitiesIntegrationTests.cs new file mode 100644 index 000000000..498565d53 --- /dev/null +++ b/test/integration/Elsa.Core.IntegrationTests/Persistence/WorkflowMayContainDuplicateActivitiesIntegrationTests.cs @@ -0,0 +1,80 @@ +using Xunit; +using System.Threading.Tasks; +using Elsa.Core.IntegrationTests.Autofixture; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using System.Threading; +using Elsa.Services; +using Elsa.Core.IntegrationTests.Workflows; +using Elsa.Persistence; + +namespace Elsa.Core.IntegrationTests.Persistence +{ + public class WorkflowMayContainDuplicateActivitiesIntegrationTests + { + /* Please note that these tests might not represent _actually desired behaviour_. + * The tests do prove that issue #683 is no longer a problem, but it still does not seem logical + * that Elsa should want to allow duplicate activity IDs in a Workflow (definition or instance). + * + * It would be reasonable to remove these tests if they begin to "get in the way". + */ + + [Theory(DisplayName = "A workflow that contains duplicate activities may be run & persisted to an in-memory store"), AutoMoqData] + public async Task ADuplicateActivitiesWorkflowInstanceShouldBeRoundTrippableInMemory([HostBuilderWithDuplicateActivitiesWorkflow] IHostBuilder hostBuilder) + { + hostBuilder.ConfigureServices((ctx, services) => { + services.AddHostedService>(); + }); + var host = await hostBuilder.StartAsync(); + } + + [Theory(DisplayName = "A workflow that contains duplicate activities may be run & persisted to an EF Sqlite store"), AutoMoqData] + public async Task ADuplicateActivitiesWorkflowInstanceShouldBeRoundTrippableWithEntityFramework([HostBuilderWithDuplicateActivitiesWorkflowAndEntityFramework] IHostBuilder hostBuilder) + { + hostBuilder.ConfigureServices((ctx, services) => { + services.AddHostedService>(); + }); + var host = await hostBuilder.StartAsync(); + } + + [Theory(DisplayName = "A workflow that contains duplicate activities may be run & persisted to a MongoDb store"), AutoMoqData] + public async Task ADuplicateActivitiesWorkflowInstanceShouldBeRoundTrippableWithMongoDb([HostBuilderWithDuplicateActivitiesWorkflowAndMongoDb] IHostBuilder hostBuilder) + { + hostBuilder.ConfigureServices((ctx, services) => { + services.AddHostedService>(); + }); + var host = await hostBuilder.StartAsync(); + } + + [Theory(DisplayName = "A workflow that contains duplicate activities may be run & persisted to a YesSQL store"), AutoMoqData] + public async Task ADuplicateActivitiesWorkflowInstanceShouldBeRoundTrippableWithYesSql([HostBuilderWithDuplicateActivitiesWorkflowAndYesSql] IHostBuilder hostBuilder) + { + hostBuilder.ConfigureServices((ctx, services) => { + services.AddHostedService>(); + }); + var host = await hostBuilder.StartAsync(); + } + + class HostedWorkflowRunner : IHostedService where TWorkflow : DuplicateActivitiesWorkflow + { + readonly IWorkflowRunner workflowRunner; + readonly IWorkflowInstanceStore instanceStore; + + public async Task StartAsync(CancellationToken cancellationToken) + { + var instance = await workflowRunner.RunWorkflowAsync(); + var retrievedInstance = await instanceStore.FindByIdAsync(instance.Id); + + Assert.NotNull(retrievedInstance); + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + + public HostedWorkflowRunner(IWorkflowRunner workflowRunner, IWorkflowInstanceStore instanceStore) + { + this.workflowRunner = workflowRunner ?? throw new System.ArgumentNullException(nameof(workflowRunner)); + this.instanceStore = instanceStore ?? throw new System.ArgumentNullException(nameof(instanceStore)); + } + } + } +} \ No newline at end of file diff --git a/test/integration/Elsa.Core.IntegrationTests/Workflows/DuplicateActivitiesWorkflow.cs b/test/integration/Elsa.Core.IntegrationTests/Workflows/DuplicateActivitiesWorkflow.cs new file mode 100644 index 000000000..00c8742c8 --- /dev/null +++ b/test/integration/Elsa.Core.IntegrationTests/Workflows/DuplicateActivitiesWorkflow.cs @@ -0,0 +1,19 @@ +using Elsa.Activities.Primitives; +using Elsa.Builders; + +namespace Elsa.Core.IntegrationTests.Workflows +{ + public class DuplicateActivitiesWorkflow : IWorkflow + { + const string duplicateId = "Duplicate"; + + public static readonly object Result = new object(); + + public virtual void Build(IWorkflowBuilder builder) + { + builder + .StartWith(a => a.Set(x => x.VariableName, "Unused").Set(x => x.Value, "Unused").Set(x => x.Id, duplicateId)) + .Then(a => a.Set(x => x.VariableName, "AlsoUnused").Set(x => x.Value, "Unused").Set(x => x.Id, duplicateId)); + } + } +} \ No newline at end of file