Merge branch 'feature/elsa-2.0' into feature/unwind-any

This commit is contained in:
Sipke Schoorstra 2021-02-24 14:29:37 +01:00
commit df72728cc1
16 changed files with 341 additions and 3 deletions

5
.gitignore vendored
View file

@ -62,4 +62,7 @@ FodyWeavers.xsd
# Elsa Stencil Components
src/designer/elsa-workflows-studio/dist
src/designer/elsa-workflows-studio/www
src/designer/elsa-workflows-studio/loader
src/designer/elsa-workflows-studio/loader
# Created by developers using GNU/Linux
.directory

33
.vscode/tasks.json vendored Normal file
View file

@ -0,0 +1,33 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"/property:GenerateFullPaths=true"
],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": "$msCompile"
},
{
"label": "test",
"command": "dotnet",
"type": "process",
"args": [
"test",
"/property:GenerateFullPaths=true"
],
"group": {
"kind": "test",
"isDefault": true
},
"problemMatcher": "$msCompile"
}
]
}

8
Elsa.code-workspace Normal file
View file

@ -0,0 +1,8 @@
{
"folders": [
{
"path": "."
}
],
"settings": {}
}

View file

@ -233,6 +233,9 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.Timers.Quartz", "src\samples\worker\Elsa.Samples.Timers.Quartz\Elsa.Samples.Timers.Quartz.csproj", "{FE724143-B24C-43DA-BBF5-15431BCA6E27}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.AutoConnectNextActivityConsole", "src\samples\console\Elsa.Samples.AutoConnectNextActivityConsole\Elsa.Samples.AutoConnectNextActivityConsole.csproj", "{DB42A8DA-06CA-4402-9C87-8F7F055AB37E}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "unit", "unit", "{F471267A-DA3A-48C7-8784-F8E4E46203A2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.UnitTests", "test\unit\Elsa.UnitTests\Elsa.UnitTests.csproj", "{F822DE2F-A91D-416E-BF4A-A6C466C1BF0A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -560,6 +563,10 @@ Global
{DB42A8DA-06CA-4402-9C87-8F7F055AB37E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DB42A8DA-06CA-4402-9C87-8F7F055AB37E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DB42A8DA-06CA-4402-9C87-8F7F055AB37E}.Release|Any CPU.Build.0 = Release|Any CPU
{F822DE2F-A91D-416E-BF4A-A6C466C1BF0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F822DE2F-A91D-416E-BF4A-A6C466C1BF0A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F822DE2F-A91D-416E-BF4A-A6C466C1BF0A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F822DE2F-A91D-416E-BF4A-A6C466C1BF0A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -671,6 +678,8 @@ Global
{45A06CA3-199B-4650-8880-30DDF2E38E19} = {8B6B40A8-DF21-4CD2-BABD-474B79D0C3AF}
{FE724143-B24C-43DA-BBF5-15431BCA6E27} = {E42743A0-FBDD-4150-9D53-6000496D9B87}
{DB42A8DA-06CA-4402-9C87-8F7F055AB37E} = {FC9F520F-BA51-4AD2-BFEE-EF787798E734}
{F471267A-DA3A-48C7-8784-F8E4E46203A2} = {AB1AE008-6FD6-414C-8E88-D735F42E1FA6}
{F822DE2F-A91D-416E-BF4A-A6C466C1BF0A} = {F471267A-DA3A-48C7-8784-F8E4E46203A2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158}

View file

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
namespace Elsa.Models
@ -29,11 +30,24 @@ namespace Elsa.Models
return this;
}
/// <summary>
/// Removes a variable of the specified name if it is present.
/// </summary>
/// <param name="name">The variable name</param>
/// <returns>A reference to this same <see cref="Variables"/> instance, so calls may be chained.</returns>
public Variables Remove(string name)
{
if (Has(name))
Data.Remove(name);
Data.Remove(name);
return this;
}
/// <summary>
/// Removes all of the variables from the current instance, clearing it.
/// </summary>
/// <returns>A reference to this same <see cref="Variables"/> instance, so calls may be chained.</returns>
public Variables RemoveAll()
{
Data.Clear();
return this;
}

View file

@ -116,6 +116,14 @@ namespace Elsa.Services.Models
public object? GetVariable(string name) => WorkflowExecutionContext.GetVariable(name);
public T? GetVariable<T>(string name) => WorkflowExecutionContext.GetVariable<T>(name);
public T? GetVariable<T>() => GetVariable<T>(typeof(T).Name);
/// <summary>
/// Clears all of the variables associated with the current <see cref="Elsa.Models.WorkflowInstance"/>.
/// </summary>
/// <seealso cref="WorkflowExecutionContext.PurgeVariables"/>
/// <seealso cref="Variables.RemoveAll"/>
public void PurgeVariables() => WorkflowExecutionContext.PurgeVariables();
public void SetTransientVariable(string name, object? value) => WorkflowExecutionContext.SetTransientVariable(name, value);
public object? GetTransientVariable(string name) => WorkflowExecutionContext.GetTransientVariable(name);
public T? GetTransientVariable<T>(string name) => WorkflowExecutionContext.GetTransientVariable<T>(name);

View file

@ -146,6 +146,13 @@ namespace Elsa.Services.Models
/// </summary>
public object? GetWorkflowVariable(string name) => WorkflowInstance.Variables.Get(name);
/// <summary>
/// Clears all of the variables associated with the current <see cref="WorkflowInstance"/>.
/// </summary>
/// <seealso cref="Variables.RemoveAll"/>
public void PurgeVariables() => WorkflowInstance.Variables.RemoveAll();
public ActivityScope CurrentScope => WorkflowInstance.Scopes.Peek();
public ActivityScope GetScope(string activityId) => WorkflowInstance.Scopes.First(x => x.ActivityId == activityId);

View file

@ -0,0 +1,43 @@
using System;
using System.Reflection;
using AutoFixture;
using AutoFixture.Kernel;
using AutoFixture.Xunit2;
using Elsa.Testing.Shared.AutoFixture.SpecimenBuilders;
namespace Elsa.Testing.Shared.AutoFixture.Attributes
{
/// <summary>
/// Customizes the parameter so that it creates an <see cref="IServiceProvider"/>
/// using Moq. That mock service provider gets services by resolving them from Autofixture.
/// </summary>
/// <seealso cref="AutofixtureServiceProviderSpecimenBuilder"/>
public class AutofixtureServiceProviderAttribute : CustomizeAttribute
{
public override ICustomization GetCustomization(ParameterInfo parameter)
=> new AutofixtureServiceProviderCustomization(parameter);
class AutofixtureServiceProviderCustomization : ICustomization
{
readonly ParameterInfo parameter;
public void Customize(IFixture fixture)
{
fixture.Customizations.Insert(0, GetSpecimenBuilder());
}
ISpecimenBuilder GetSpecimenBuilder()
{
var paramSpec = new ParameterSpecification(parameter.ParameterType, parameter.Name);
var specimenBuilder = new AutofixtureServiceProviderSpecimenBuilder();
return new FilteringSpecimenBuilder(specimenBuilder, paramSpec);
}
public AutofixtureServiceProviderCustomization(ParameterInfo parameter)
{
this.parameter = parameter;
}
}
}
}

View file

@ -0,0 +1,25 @@
using System.Linq;
using System.Reflection;
using AutoFixture;
using AutoFixture.Xunit2;
namespace Elsa.Testing.Shared.AutoFixture.Attributes
{
public class OmitOnRecursionAttribute : CustomizeAttribute
{
public override ICustomization GetCustomization(ParameterInfo parameter)
=> new OmitOnRecursionCustomization();
class OmitOnRecursionCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
var throwingBehaviours = fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList();
foreach(var behaviour in throwingBehaviours)
fixture.Behaviors.Remove(behaviour);
fixture.Behaviors.Add(new OmitOnRecursionBehavior());
}
}
}
}

View file

@ -0,0 +1,36 @@
using System;
using System.Reflection;
using AutoFixture.Kernel;
using Moq;
namespace Elsa.Testing.Shared.AutoFixture.SpecimenBuilders
{
/// <summary>
/// Creates a mock <see cref="IServiceProvider"/> which gets services
/// by resolving them from Autofixture.
/// </summary>
public class AutofixtureServiceProviderSpecimenBuilder : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
if(Equals(request, typeof(IServiceProvider)))
return GetServiceProvider(context);
if(request is ParameterInfo paramInfo && paramInfo.ParameterType == typeof(IServiceProvider))
return GetServiceProvider(context);
return new NoSpecimen();
}
static object GetServiceProvider(ISpecimenContext context)
{
var provider = new Mock<IServiceProvider>();
provider.Name = $"Autofixture_{nameof(IServiceProvider)}-{Guid.NewGuid()}";
provider
.Setup(x => x.GetService(It.IsAny<Type>()))
.Returns((Type t) => context.Resolve(t));
return provider.Object;
}
}
}

View file

@ -13,6 +13,8 @@
<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.15.0" />
<PackageReference Include="Autofixture.AutoMoq" Version="4.15.0" />
<PackageReference Include="autofixture.xunit2" Version="4.15.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
<PackageReference Include="NodaTime" Version="3.0.3" />

View file

@ -0,0 +1,13 @@
using AutoFixture;
using AutoFixture.AutoMoq;
using AutoFixture.Xunit2;
namespace Elsa
{
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute() : base(() => new Fixture().Customize(new AutoMoqCustomization()))
{
}
}
}

View file

@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<RootNamespace>Elsa</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofixture" Version="4.15.0" />
<PackageReference Include="Autofixture.AutoMoq" Version="4.15.0" />
<PackageReference Include="autofixture.xunit2" Version="4.15.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="Moq" Version="4.16.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="1.3.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../../src/core/Elsa.Abstractions/Elsa.Abstractions.csproj" />
<ProjectReference Include="../../shared/Elsa.Testing.Shared/Elsa.Testing.Shared.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,60 @@
using System.Collections.Generic;
using Xunit;
namespace Elsa.Models
{
public class VariablesTests
{
[Theory(DisplayName = "The Remove method should remove a variable if such a variable exists"), AutoMoqData]
public void Remove_removes_a_variable_by_name_if_present(string variableName, object variableData)
{
var sut = new Variables(new Dictionary<string,object> { { variableName, variableData } });
sut.Remove(variableName);
Assert.Empty(sut.Data);
}
[Theory(DisplayName = "The Remove method should not throw if used with a non-existent variable"), AutoMoqData]
public void Remove_does_not_throw_when_trying_to_remove_a_variable_which_does_not_exist(string variableName)
{
var sut = new Variables();
sut.Remove(variableName);
// No assertion, if the line about doesn't throw then this test passed
}
[Theory(DisplayName = "The Remove method should support call-chaining by returning a self-reference"), AutoMoqData]
public void Remove_returns_a_reference_to_itself(Variables sut, string variableName)
{
var result = sut.Remove(variableName);
Assert.Same(sut, result);
}
[Theory(DisplayName = "The RemoveAll method should leave the variables collection empty"), AutoMoqData]
public void RemoveAll_clears_all_variables(string variableName1,
string variableName2,
string variableName3,
object variableData)
{
var sut = new Variables(new Dictionary<string,object> {
{ variableName1, variableData },
{ variableName2, variableData },
{ variableName3, variableData },
});
sut.RemoveAll();
Assert.Empty(sut.Data);
}
[Theory(DisplayName = "The RemoveAll method should support call-chaining by returning a self-reference"), AutoMoqData]
public void RemoveAll_returns_a_reference_to_itself(Variables sut)
{
var result = sut.RemoveAll();
Assert.Same(sut, result);
}
}
}

View file

@ -0,0 +1,23 @@
using Xunit;
using Elsa.Testing.Shared.AutoFixture.Attributes;
using AutoFixture.Xunit2;
using System;
namespace Elsa.Services.Models
{
public class ActivityExecutionContextTests
{
[Theory(DisplayName = "The PurgeVariables method should clear the Variables instance associated with the WorkflowInstance associated with the Workflow Execution Context"), AutoMoqData]
public void PurgeVariables_clears_workflow_execution_context_workflow_instance_variables([AutofixtureServiceProvider, Frozen] IServiceProvider serviceProvider,
[OmitOnRecursion,NoAutoProperties] ActivityExecutionContext sut,
string variableName,
object variableValue)
{
sut.WorkflowExecutionContext.WorkflowInstance.Variables.Set(variableName, variableValue);
sut.PurgeVariables();
Assert.Empty(sut.WorkflowExecutionContext.WorkflowInstance.Variables.Data);
}
}
}

View file

@ -0,0 +1,23 @@
using Xunit;
using Elsa.Testing.Shared.AutoFixture.Attributes;
using AutoFixture.Xunit2;
using System;
namespace Elsa.Services.Models
{
public class WorkflowExecutionContextTests
{
[Theory(DisplayName = "The PurgeVariables method should clear the Variables instance associated with the WorkflowInstance"), AutoMoqData]
public void PurgeVariables_clears_workflow_execution_context_workflow_instance_variables([AutofixtureServiceProvider, Frozen] IServiceProvider serviceProvider,
[OmitOnRecursion,NoAutoProperties] WorkflowExecutionContext sut,
string variableName,
object variableValue)
{
sut.WorkflowInstance.Variables.Set(variableName, variableValue);
sut.PurgeVariables();
Assert.Empty(sut.WorkflowInstance.Variables.Data);
}
}
}