Add project files.
This commit is contained in:
+197
@@ -0,0 +1,197 @@
|
||||
using Elsa.Expressions.Models;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.Activities.Flowchart.Activities;
|
||||
using Elsa.Workflows.Activities.Flowchart.Models;
|
||||
using Elsa.Workflows.Memory;
|
||||
using Elsa.Workflows.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Serialization.ContainerSerialization;
|
||||
|
||||
public class Tests
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly IActivitySerializer _activitySerializer;
|
||||
|
||||
public Tests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
_services = new TestApplicationBuilder(testOutputHelper).Build();
|
||||
_activitySerializer = _services.GetService<IActivitySerializer>()!;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void SerializeFlowchartContainerTest()
|
||||
{
|
||||
await _services.PopulateRegistriesAsync();
|
||||
|
||||
// Arrange
|
||||
|
||||
var start = new Start()
|
||||
{
|
||||
Id = "start",
|
||||
Name = "Start",
|
||||
};
|
||||
var writeLine = new WriteLine(new Input<string>(new Expression("JavaScript", "getVariable('TextVar')")))
|
||||
{
|
||||
Id = "writeLine",
|
||||
Name = "WriteLine",
|
||||
Version = 3,
|
||||
};
|
||||
var end = new End()
|
||||
{
|
||||
Id = "end",
|
||||
Name = "end",
|
||||
};
|
||||
var container = new Flowchart()
|
||||
{
|
||||
Id = "flowchart",
|
||||
Name = "Flowchart",
|
||||
Type = "Elsa.Flowchart",
|
||||
Version = 42,
|
||||
CustomProperties = new Dictionary<string, object>()
|
||||
{
|
||||
{ "purpose", "somePurpose" }
|
||||
},
|
||||
Metadata = new Dictionary<string, object>()
|
||||
{
|
||||
{ "int", 10 },
|
||||
{ "bool", false },
|
||||
{ "string", "str" },
|
||||
},
|
||||
Activities = new List<IActivity>() {
|
||||
start,
|
||||
writeLine,
|
||||
end
|
||||
},
|
||||
Variables = new List<Variable>() {
|
||||
new Variable<string>("TextVar", "This is the text to write")
|
||||
},
|
||||
Connections = new List<Connection>()
|
||||
{
|
||||
new Connection(start, writeLine),
|
||||
new Connection(writeLine, end),
|
||||
},
|
||||
};
|
||||
|
||||
// Act
|
||||
|
||||
var serialized = _activitySerializer.Serialize(container);
|
||||
var deserializedContainer = _activitySerializer.Deserialize(serialized) as Container;
|
||||
|
||||
// Assert
|
||||
|
||||
ValidateContainer(container, deserializedContainer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void SerializeSequenceContainerTest()
|
||||
{
|
||||
await _services.PopulateRegistriesAsync();
|
||||
|
||||
// Arrange
|
||||
|
||||
var container = new Sequence()
|
||||
{
|
||||
Id = "sequence",
|
||||
Name = "Sequence",
|
||||
Type = "Elsa.Sequence",
|
||||
Version = 42,
|
||||
Variables = new List<Variable>() {
|
||||
new Variable<string>("TextVar", "This is the text to write")
|
||||
},
|
||||
Activities = new List<IActivity>() {
|
||||
new WriteLine(new Input<string>(new Expression("JavaScript", "getVariable('TextVar')")))
|
||||
{
|
||||
Id = "writeLine",
|
||||
Name = "WriteLine",
|
||||
CanStartWorkflow = true,
|
||||
},
|
||||
},
|
||||
CustomProperties = new Dictionary<string, object>()
|
||||
{
|
||||
{ "purpose", "somePurpose" }
|
||||
},
|
||||
Metadata = new Dictionary<string, object>()
|
||||
{
|
||||
{ "int", 10 },
|
||||
{ "bool", false },
|
||||
{ "string", "str"},
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
|
||||
var serialized = _activitySerializer.Serialize(container);
|
||||
var deserializedContainer = _activitySerializer.Deserialize(serialized) as Container;
|
||||
|
||||
// Assert
|
||||
|
||||
ValidateContainer(container, deserializedContainer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void SerializeParallelContainerTest()
|
||||
{
|
||||
await _services.PopulateRegistriesAsync();
|
||||
|
||||
// Arrange
|
||||
|
||||
var container = new Workflows.Activities.Parallel()
|
||||
{
|
||||
Id = "parallel",
|
||||
Name = "Parallel",
|
||||
Type = "Elsa.Parallel",
|
||||
Version = 42,
|
||||
Variables = new List<Variable>() {
|
||||
new Variable<string>("TextVar", "This is the text to write")
|
||||
},
|
||||
Activities = new List<IActivity>() {
|
||||
new WriteLine(new Input<string>(new Expression("JavaScript", "getVariable('TextVar')")))
|
||||
{
|
||||
Id = "writeLine",
|
||||
Name = "WriteLine",
|
||||
CanStartWorkflow = true,
|
||||
},
|
||||
},
|
||||
CustomProperties = new Dictionary<string, object>()
|
||||
{
|
||||
{ "purpose", "somePurpose" }
|
||||
},
|
||||
Metadata = new Dictionary<string, object>()
|
||||
{
|
||||
{ "int", 10 },
|
||||
{ "bool", false },
|
||||
{ "string", "str"},
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
|
||||
var serialized = _activitySerializer.Serialize(container);
|
||||
var deserializedContainer = _activitySerializer.Deserialize(serialized) as Container;
|
||||
|
||||
// Assert
|
||||
|
||||
ValidateContainer(container, deserializedContainer);
|
||||
}
|
||||
|
||||
private static void ValidateContainer(Container container, Container? deserializedContainer)
|
||||
{
|
||||
if (deserializedContainer == null)
|
||||
throw new ArgumentNullException(nameof(deserializedContainer));
|
||||
|
||||
// Assert.Equivalent has trouble with the Behavior.Owner reference - since these aren't serialzied anyway, ignore them
|
||||
deserializedContainer.Behaviors.Clear();
|
||||
container.Behaviors.Clear();
|
||||
foreach (Activity activity in deserializedContainer.Activities)
|
||||
activity.Behaviors.Clear();
|
||||
foreach (Activity activity in container.Activities)
|
||||
activity.Behaviors.Clear();
|
||||
|
||||
// strict:false here allows "actual" to have extra public members that aren't part of "expected", and collection
|
||||
// comparison allows "actual" to have more data in it than is present in "expected".
|
||||
Assert.Equivalent(container, deserializedContainer, strict: false);
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"StatusCode": "Created",
|
||||
"Content": {
|
||||
"_island": "[\r\n {\r\n \u0022path\u0022: \u0022folder1\u0022,\r\n \u0022command\u0022: \u0022add\u0022\r\n }\r\n]",
|
||||
"_type": "Newtonsoft.Json.Linq.JArray, Newtonsoft.Json"
|
||||
},
|
||||
"_type": "ObjectMap"
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"StatusCode": "Created",
|
||||
"Content": [
|
||||
{
|
||||
"path": "folder1",
|
||||
"command": "add"
|
||||
}
|
||||
],
|
||||
"_type": "ObjectMap"
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"StatusCode": "Created",
|
||||
"Content": {
|
||||
"_island": "{\r\n \u0022file1\u0022: {\r\n \u0022script\u0022: [\r\n {\r\n \u0022path\u0022: \u0022folder1\u0022,\r\n \u0022command\u0022: \u0022add\u0022\r\n }\r\n ]\r\n }\r\n}",
|
||||
"_type": "Newtonsoft.Json.Linq.JObject, Newtonsoft.Json"
|
||||
},
|
||||
"_type": "ObjectMap"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"StatusCode": "Created",
|
||||
"Content": {
|
||||
"file1": {
|
||||
"script": [
|
||||
{
|
||||
"path": "folder1",
|
||||
"command": "add"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"_type": "ObjectMap"
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"StatusCode": "Created",
|
||||
"Content": {
|
||||
"_island": "[\r\n {\r\n \u0022path\u0022: \u0022folder1\u0022,\r\n \u0022command\u0022: \u0022add\u0022\r\n }\r\n]",
|
||||
"_type": "System.Text.Json.Nodes.JsonArray, System.Text.Json"
|
||||
},
|
||||
"_type": "ObjectMap"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"path": "folder1",
|
||||
"command": "add"
|
||||
}
|
||||
]
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"StatusCode": "Created",
|
||||
"Content": {
|
||||
"_island": "{\r\n \u0022file1\u0022: {\r\n \u0022script\u0022: [\r\n {\r\n \u0022path\u0022: \u0022folder1\u0022,\r\n \u0022command\u0022: \u0022add\u0022\r\n }\r\n ]\r\n }\r\n}",
|
||||
"_type": "System.Text.Json.Nodes.JsonObject, System.Text.Json"
|
||||
},
|
||||
"_type": "ObjectMap"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"StatusCode": "Created",
|
||||
"Content": {
|
||||
"file1": {
|
||||
"script": [
|
||||
{
|
||||
"path": "folder1",
|
||||
"command": "add"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"_type": "ObjectMap"
|
||||
}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Elsa.Testing.Shared;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Serialization.JsonSerialization;
|
||||
|
||||
public class SerializationTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
private readonly IServiceProvider _services = new TestApplicationBuilder(testOutputHelper).Build();
|
||||
|
||||
[Theory(DisplayName = "write")]
|
||||
[InlineData(typeof(JsonObject), "JsonObjectIsland")]
|
||||
[InlineData(typeof(JObject), "JObjectIsland")]
|
||||
[InlineData(typeof(JsonArray), "JsonArrayIsland")]
|
||||
[InlineData(typeof(JArray), "JArrayIsland")]
|
||||
public void Test_Serialization_create_Island(Type type, string fileName)
|
||||
{
|
||||
var dict = GetContent(type);
|
||||
|
||||
var result = SerializeUsingPayloadSerializer(dict);
|
||||
|
||||
var expected = File.ReadAllText($"Serialization/JsonSerialization/{fileName}.json");
|
||||
|
||||
CompareJsonsObjects(expected, result);
|
||||
}
|
||||
|
||||
[Theory(DisplayName = "roundtrip")]
|
||||
[InlineData(typeof(JsonObject))]
|
||||
[InlineData(typeof(JObject))]
|
||||
[InlineData(typeof(JsonArray))]
|
||||
[InlineData(typeof(JArray))]
|
||||
public void Test_Serialization_roundtrip(Type type)
|
||||
{
|
||||
var dict = GetContent(type);
|
||||
var jsonSerialized = SerializeUsingPayloadSerializer(dict);
|
||||
var transformationModel = DeSerializeDictionaryUsingPayloadSerializer(jsonSerialized);
|
||||
var result = transformationModel["Content"].ToString()!;
|
||||
var expected = GetExpected(type);
|
||||
|
||||
CompareJsonsObjects(expected, result);
|
||||
}
|
||||
|
||||
[Theory(DisplayName = "read")]
|
||||
[InlineData(typeof(JsonObject), "JsonObjectIsland", "JsonObjectWithoutType")]
|
||||
[InlineData(typeof(JObject), "JObjectIsland", "JObjectWithoutType")]
|
||||
[InlineData(typeof(JsonArray), "JsonArrayIsland", "JsonArrayWithoutType")]
|
||||
[InlineData(typeof(JArray), "JArrayIsland", "JArrayWithoutType")]
|
||||
public void Test_Serialization_read(Type type, string fileName, string compareFileName)
|
||||
{
|
||||
var jsonContent = File.ReadAllText(@$"Serialization/JsonSerialization/{fileName}.json");
|
||||
|
||||
var transformationModel = DeSerializeDictionaryUsingPayloadSerializer(jsonContent);
|
||||
|
||||
string result;
|
||||
if (type == typeof(JObject) || type == typeof(JArray))
|
||||
{
|
||||
result = Newtonsoft.Json.JsonConvert.SerializeObject(transformationModel);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = type == typeof(JsonObject) ? JsonObject.Create(JsonSerializer.SerializeToElement(transformationModel))!.ToString() : JsonArray.Create(JsonSerializer.SerializeToElement(transformationModel["Content"]))!.ToString();
|
||||
}
|
||||
|
||||
var expected = File.ReadAllText(@$"Serialization/JsonSerialization/{compareFileName}.json");
|
||||
|
||||
CompareJsonsObjects(expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoundtripComplexEnumerableObject()
|
||||
{
|
||||
var dict = new Dictionary<string, object>
|
||||
{
|
||||
{
|
||||
"Content", new List<TestObject>()
|
||||
{
|
||||
new()
|
||||
{
|
||||
Data = "Hello World"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
var jsonSerialized = SerializeUsingPayloadSerializer(dict);
|
||||
var transformationModel = DeSerializeDictionaryUsingPayloadSerializer(jsonSerialized);
|
||||
var result = transformationModel["Content"];
|
||||
Assert.Equal(typeof(List<TestObject>), result.GetType());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoundtripPrimitiveCollections()
|
||||
{
|
||||
var dict = new Dictionary<string, object>
|
||||
{
|
||||
{
|
||||
"Content", new List<Guid>
|
||||
{
|
||||
Guid.NewGuid()
|
||||
}
|
||||
}
|
||||
};
|
||||
var jsonSerialized = SerializeUsingPayloadSerializer(dict);
|
||||
var transformationModel = DeSerializeDictionaryUsingPayloadSerializer(jsonSerialized);
|
||||
var result = transformationModel["Content"];
|
||||
Assert.Equal(typeof(List<Guid>), result.GetType());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoundtripPrimitiveArrays()
|
||||
{
|
||||
var dict = new Dictionary<string, object>
|
||||
{
|
||||
{
|
||||
"Content", new[]
|
||||
{
|
||||
Guid.NewGuid()
|
||||
}
|
||||
}
|
||||
};
|
||||
var jsonSerialized = SerializeUsingPayloadSerializer(dict);
|
||||
var transformationModel = DeSerializeDictionaryUsingPayloadSerializer(jsonSerialized);
|
||||
var result = transformationModel["Content"];
|
||||
Assert.Equal(typeof(Guid[]), result.GetType());
|
||||
}
|
||||
|
||||
private string SerializeUsingPayloadSerializer(object obj)
|
||||
{
|
||||
var payloadSerializer = _services.GetRequiredService<IPayloadSerializer>();
|
||||
return payloadSerializer.Serialize(obj);
|
||||
}
|
||||
|
||||
private IDictionary<string, object> DeSerializeDictionaryUsingPayloadSerializer(string jsonString)
|
||||
{
|
||||
var payloadSerializer = _services.GetRequiredService<IPayloadSerializer>();
|
||||
return payloadSerializer.Deserialize<IDictionary<string, object>>(jsonString);
|
||||
}
|
||||
|
||||
private void CompareJsonsObjects(string expected, string actual)
|
||||
{
|
||||
var jsonActual = NormalizeNewlines(JsonNode.Parse(actual!)?.ToString());
|
||||
var jsonExpected = NormalizeNewlines(JsonNode.Parse(expected!)?.ToString());
|
||||
|
||||
Assert.Equal(jsonExpected, jsonActual);
|
||||
}
|
||||
|
||||
private IDictionary<string, object> GetContent(Type type)
|
||||
{
|
||||
var isArray = type == typeof(JsonArray) || type == typeof(JArray);
|
||||
|
||||
var jsonContent = isArray ? "[{\"path\":\"folder1\",\"command\":\"add\"}]" : "{\"file1\":{\"script\":[{\"path\":\"folder1\",\"command\":\"add\"}]} }";
|
||||
|
||||
var dict = new Dictionary<string, object>
|
||||
{
|
||||
{
|
||||
"StatusCode", "Created"
|
||||
},
|
||||
{
|
||||
"Content", isArray ? (type == typeof(JArray) ? JArray.Parse(jsonContent) : JsonArray.Parse(jsonContent)) : (type == typeof(JObject) ? JObject.Parse(jsonContent) : JsonObject.Parse(jsonContent))
|
||||
}
|
||||
};
|
||||
return dict;
|
||||
}
|
||||
|
||||
private string GetExpected(Type type)
|
||||
{
|
||||
if (type == typeof(JsonArray) || type == typeof(JArray))
|
||||
{
|
||||
return "[{\"path\":\"folder1\",\"command\":\"add\"}]";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "{\"file1\":{\"script\":[{\"path\":\"folder1\",\"command\":\"add\"}]} }";
|
||||
}
|
||||
}
|
||||
|
||||
private static string? NormalizeNewlines(string? input) => input?.Replace("\r\n", "\n").Replace("\\r\\n", "\\n");
|
||||
}
|
||||
|
||||
public class TestObject
|
||||
{
|
||||
public string? Data { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Elsa.Extensions;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Serialization.Polymorphism;
|
||||
|
||||
/// <summary>
|
||||
/// A model that contains a mixed bag of primitive, complex and expando objects.
|
||||
/// </summary>
|
||||
public record Model(
|
||||
string? Text = default,
|
||||
long Number = default,
|
||||
bool Flag = default,
|
||||
ICollection<Model>? Items = default,
|
||||
object? Metadata = default,
|
||||
object? Payload = default,
|
||||
object? HashSet = default,
|
||||
IDictionary<string, Model>? Properties = default)
|
||||
{
|
||||
public Model() : this(null, default, default, null, default, default, default, default)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomDictionary : Dictionary<string, string[]>
|
||||
{
|
||||
public string? ContentType => this.GetValue("content-type")?[0];
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using System.Dynamic;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
using Elsa.Expressions.Services;
|
||||
using Elsa.Workflows.Serialization.Converters;
|
||||
using Elsa.Workflows.Serialization.ReferenceHandlers;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Serialization.Polymorphism;
|
||||
|
||||
public class Tests
|
||||
{
|
||||
[Fact(DisplayName = "Objects mixed with primitive, complex, expando objects and arrays of these can be serialized")]
|
||||
public void Test1()
|
||||
{
|
||||
var model = CreateModel();
|
||||
var expectedJson = File.ReadAllText("Serialization/Polymorphism/data.json");
|
||||
var actualJson = JsonSerializer.Serialize(model, GetSerializerOptions());
|
||||
Assert.Equal(expectedJson, actualJson);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Objects mixed with primitive, complex, expando objects and arrays of these can be round-tripped")]
|
||||
public void Test2()
|
||||
{
|
||||
var model = CreateModel();
|
||||
var json = JsonSerializer.Serialize(model, GetSerializerOptions());
|
||||
var deserializedModel = JsonSerializer.Deserialize<Model>(json, GetSerializerOptions());
|
||||
var roundTrippedJson = JsonSerializer.Serialize(deserializedModel, GetSerializerOptions());
|
||||
Assert.Equal(json, roundTrippedJson);
|
||||
}
|
||||
|
||||
private Model CreateModel()
|
||||
{
|
||||
// Create a model with various nested properties
|
||||
var metadata = new ExpandoObject() as IDictionary<string, object>;
|
||||
|
||||
// Initialize metadata with test data.
|
||||
metadata["Foo"] = "Bar";
|
||||
metadata["Number"] = 123;
|
||||
metadata["Flag"] = true;
|
||||
metadata["Models"] = new List<Model>
|
||||
{
|
||||
new(Text: "Hello", Number: 1, Flag: true, Metadata: metadata),
|
||||
new(Text: "World", Number: 2)
|
||||
};
|
||||
metadata["CustomDictionary"] = new CustomDictionary { ["content-type"] = new[] { "application/json" } };
|
||||
|
||||
return new Model(
|
||||
"Hello World",
|
||||
123,
|
||||
true,
|
||||
new List<Model>
|
||||
{
|
||||
new("Hello", 1, true, new List<Model>() { new(Metadata: metadata) }),
|
||||
new("World", 2)
|
||||
},
|
||||
metadata,
|
||||
new Model("Payload"),
|
||||
new HashSet<Model>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Text = "I'm a model in a set!"
|
||||
}
|
||||
},
|
||||
new Dictionary<string, Model>
|
||||
{
|
||||
["Hello"] = new("Hello", 1, true),
|
||||
["World"] = new("World", 2),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private JsonSerializerOptions GetSerializerOptions()
|
||||
{
|
||||
var referenceHandler = new CrossScopedReferenceHandler();
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
ReferenceHandler = referenceHandler,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
PropertyNameCaseInsensitive = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
};
|
||||
|
||||
options.Converters.Add(new JsonStringEnumConverter());
|
||||
options.Converters.Add(JsonMetadataServices.TimeSpanConverter);
|
||||
options.Converters.Add(new PolymorphicObjectConverterFactory(new WellKnownTypeRegistry()));
|
||||
return options;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"$id":"1","text":"Hello World","number":123,"flag":true,"items":{"$id":"2","$values":[{"$id":"3","text":"Hello","number":1,"flag":true,"items":{"$id":"4","$values":[{"$id":"5","number":0,"flag":false,"metadata":{"Foo":"Bar","Number":123,"Flag":true,"Models":{"$id":"6","$values":[{"$id":"7","text":"Hello","number":1,"flag":true,"metadata":{"Foo":"Bar","Number":123,"Flag":true,"Models":{"$ref":"6"},"CustomDictionary":{"$id":"8","content-type":["application/json"],"_type":"Elsa.Workflows.IntegrationTests.Serialization.Polymorphism.CustomDictionary, Elsa.Workflows.IntegrationTests"}}},{"$id":"9","text":"World","number":2,"flag":false}],"_type":"System.Collections.Generic.List\u00601[[Elsa.Workflows.IntegrationTests.Serialization.Polymorphism.Model, Elsa.Workflows.IntegrationTests]], System.Private.CoreLib"},"CustomDictionary":{"$ref":"8"}}}]}},{"$id":"10","text":"World","number":2,"flag":false}]},"metadata":{"Foo":"Bar","Number":123,"Flag":true,"Models":{"$ref":"6"},"CustomDictionary":{"$ref":"8"}},"payload":{"$id":"11","text":"Payload","number":0,"flag":false,"_type":"Elsa.Workflows.IntegrationTests.Serialization.Polymorphism.Model, Elsa.Workflows.IntegrationTests"},"hashSet":{"$id":"12","$values":[{"$id":"13","text":"I\u0027m a model in a set!","number":0,"flag":false}],"_type":"System.Collections.Generic.HashSet\u00601[[Elsa.Workflows.IntegrationTests.Serialization.Polymorphism.Model, Elsa.Workflows.IntegrationTests]], System.Private.CoreLib"},"properties":{"$id":"14","Hello":{"$id":"15","text":"Hello","number":1,"flag":true},"World":{"$id":"16","text":"World","number":2,"flag":false}}}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Testing.Shared;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Serialization.ProgrammaticWorkflowsAndDelegates;
|
||||
|
||||
/// <summary>
|
||||
/// Contains tests for variable expressions serialization.
|
||||
/// </summary>
|
||||
public class Tests
|
||||
{
|
||||
private readonly IActivitySerializer _activitySerializer;
|
||||
private readonly IWorkflowBuilder _workflowBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Tests"/> class.
|
||||
/// </summary>
|
||||
public Tests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
var serviceProvider = new TestApplicationBuilder(testOutputHelper).Build();
|
||||
_activitySerializer = serviceProvider.GetRequiredService<IActivitySerializer>();
|
||||
var workflowBuilderFactory = serviceProvider.GetRequiredService<IWorkflowBuilderFactory>();
|
||||
_workflowBuilder = workflowBuilderFactory.CreateBuilder();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that programmatic workflows with inputs using delegates do not throw exceptions when serialized.
|
||||
/// </summary>
|
||||
[Fact(DisplayName = "Programmatic workflows with inputs using delegates do not throw exceptions when serialized")]
|
||||
public async Task Test1()
|
||||
{
|
||||
var workflow = await _workflowBuilder.BuildWorkflowAsync<GreeterWorkflow>();
|
||||
var serializedWorkflow = _activitySerializer.Serialize(workflow);
|
||||
|
||||
// If it reached here, the test passed
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Workflows.Activities;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Serialization.ProgrammaticWorkflowsAndDelegates;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class GreeterWorkflow : WorkflowBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
var messageInput = builder.WithInput<string>("Message", "The message to write to the console.");
|
||||
|
||||
builder.Name = "Greeter Workflow";
|
||||
builder.Root = new WriteLine(context => context.GetInput<string>(messageInput));
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
using Elsa.Testing.Shared;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Text.Json.Nodes;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Serialization.SpecialPropertySerialization
|
||||
{
|
||||
public class Tests
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public Tests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
_services = new TestApplicationBuilder(testOutputHelper).Build();
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Serialize and deserialize properties with $ prefix")]
|
||||
public void Test_Special_Properties_Serialization_roundtrip()
|
||||
{
|
||||
var payloadSerializer = _services.GetRequiredService<IPayloadSerializer>();
|
||||
var jsonContent = "{\"prop1\":{\"script\":[{\"$id\":\"someid\"}]} }";
|
||||
|
||||
var dict = new Dictionary<string, object>
|
||||
{
|
||||
{ "Content", JsonNode.Parse(jsonContent)! }
|
||||
};
|
||||
|
||||
var jsonSerialized = payloadSerializer.Serialize(dict);
|
||||
var transformationModel = payloadSerializer.Deserialize<IDictionary<string, object>>(jsonSerialized);
|
||||
var result = transformationModel["Content"].ToString()!;
|
||||
var expected = "{\"prop1\":{\"script\":[{\"$id\":\"someid\"}]} }";
|
||||
var jsonResult = JsonNode.Parse(result)?.ToString();
|
||||
var jsonExpected = JsonNode.Parse(expected)?.ToString();
|
||||
|
||||
Assert.Equal(jsonExpected, jsonResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.UIHints;
|
||||
using Elsa.Workflows.UIHints.Dropdown;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Serialization.UIHintSerializiation;
|
||||
|
||||
public class Tests
|
||||
{
|
||||
private readonly CapturingTextWriter _capturingTextWriter = new();
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MigrationTests"/> class.
|
||||
/// </summary>
|
||||
public Tests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
_services = new TestApplicationBuilder(testOutputHelper)
|
||||
.WithCapturingTextWriter(_capturingTextWriter)
|
||||
.ConfigureElsa(elsa =>
|
||||
{
|
||||
elsa.AddActivity<TestActivity>();
|
||||
})
|
||||
.Build();
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Enum input types get a dropdown UIHint by default")]
|
||||
public async Task Test1()
|
||||
{
|
||||
var activityDescriber = _services.GetRequiredService<IActivityDescriber>();
|
||||
|
||||
var description = await activityDescriber.DescribeActivityAsync(typeof(TestActivity));
|
||||
|
||||
var inputDescription = description.Inputs.First();
|
||||
Assert.Equal(InputUIHints.DropDown, inputDescription.UIHint);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Enum input types get a dropdown UIHint by default")]
|
||||
public async Task Test2()
|
||||
{
|
||||
var activityDescriber = _services.GetRequiredService<IActivityDescriber>();
|
||||
|
||||
var description = await activityDescriber.DescribeActivityAsync(typeof(TestActivity));
|
||||
|
||||
var inputDescription = description.Inputs.First();
|
||||
Assert.True(inputDescription.UISpecifications.ContainsKey(InputUIHints.DropDown));
|
||||
Assert.True(inputDescription.UISpecifications[InputUIHints.DropDown] is DropDownProps);
|
||||
var dropDownProperties = (DropDownProps) inputDescription.UISpecifications[InputUIHints.DropDown];
|
||||
|
||||
Assert.Collection(dropDownProperties.SelectList.Items,
|
||||
item => { Assert.Equal("OptionsAreNice", item.Text); Assert.Equal("OptionsAreNice", item.Value); },
|
||||
item => { Assert.Equal("ToHave", item.Text); Assert.Equal("ToHave", item.Value); },
|
||||
item => { Assert.Equal("IfYouCanChooseThem", item.Text); Assert.Equal("IfYouCanChooseThem", item.Value); });
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json.Serialization;
|
||||
using Elsa.Workflows.Attributes;
|
||||
using Elsa.Workflows.Models;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Serialization.UIHintSerializiation;
|
||||
|
||||
/// <summary>
|
||||
/// Write a line of text to the console.
|
||||
/// </summary>
|
||||
[Activity("Elsa", "Test", "Used in Testing - not a real activity")]
|
||||
public class TestActivity : CodeActivity
|
||||
{
|
||||
/// <summary>
|
||||
/// The text to write.
|
||||
/// </summary>
|
||||
[Description("The text to write.")]
|
||||
public Input<TestEnumType> Option { get; set; } = null!;
|
||||
|
||||
[JsonConstructor]
|
||||
private TestActivity(string? source = null, int? line = null) : base(source, line)
|
||||
{
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public TestActivity(Input<TestEnumType> option, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(source, line) => Option = option;
|
||||
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
namespace Elsa.Workflows.IntegrationTests.Serialization.UIHintSerializiation;
|
||||
|
||||
public enum TestEnumType
|
||||
{
|
||||
OptionsAreNice,
|
||||
ToHave,
|
||||
IfYouCanChooseThem
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Elsa.Expressions.Models;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Workflows.Memory;
|
||||
using Elsa.Workflows.Models;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Serialization.VariableExpressions;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class NumberActivity : CodeActivity
|
||||
{
|
||||
/// <inheritdoc />
|
||||
[JsonConstructor]
|
||||
public NumberActivity()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public NumberActivity(Variable<int> variable)
|
||||
{
|
||||
Number = new(variable);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public NumberActivity(Literal<int> literal)
|
||||
{
|
||||
Number = new(literal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number.
|
||||
/// </summary>
|
||||
public Input<int> Number { get; set; } = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Execute(ActivityExecutionContext context)
|
||||
{
|
||||
var number = Number.Get(context);
|
||||
Console.WriteLine(number.ToString());
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.Management;
|
||||
using Elsa.Workflows.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Serialization.VariableExpressions;
|
||||
|
||||
/// <summary>
|
||||
/// Contains tests for variable expressions serialization.
|
||||
/// </summary>
|
||||
public class Tests
|
||||
{
|
||||
private readonly IWorkflowSerializer _workflowSerializer;
|
||||
private readonly IWorkflowBuilder _workflowBuilder;
|
||||
private readonly IWorkflowRunner _workflowRunner;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Tests"/> class.
|
||||
/// </summary>
|
||||
public Tests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
var serviceProvider = new TestApplicationBuilder(testOutputHelper).Build();
|
||||
_workflowSerializer = serviceProvider.GetRequiredService<IWorkflowSerializer>();
|
||||
IWorkflowBuilderFactory workflowBuilderFactory = serviceProvider.GetRequiredService<IWorkflowBuilderFactory>();
|
||||
_workflowBuilder = workflowBuilderFactory.CreateBuilder();
|
||||
_workflowRunner = serviceProvider.GetRequiredService<IWorkflowRunner>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Variable types remain intact after serialization.
|
||||
/// </summary>
|
||||
[Fact(DisplayName = "Variable types remain intact after serialization")]
|
||||
public async Task Test1()
|
||||
{
|
||||
var workflow = await _workflowBuilder.BuildWorkflowAsync<SampleWorkflow>();
|
||||
var serialized = _workflowSerializer.Serialize(workflow);
|
||||
var deserializedWorkflow = _workflowSerializer.Deserialize(serialized);
|
||||
var rehydratedWriteLine1 = (WriteLine)((Sequence)deserializedWorkflow.Root).Activities.ElementAt(0);
|
||||
var rehydratedNumberActivity1 = (NumberActivity)((Sequence)deserializedWorkflow.Root).Activities.ElementAt(2);
|
||||
|
||||
Assert.IsType<Variable<string>>(rehydratedWriteLine1.Text.Expression!.Value);
|
||||
Assert.IsType<Variable<int>>(rehydratedNumberActivity1.Number.Expression!.Value);
|
||||
|
||||
await _workflowRunner.RunAsync(workflow);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
using Elsa.Expressions.Models;
|
||||
using Elsa.Workflows.Activities;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Serialization.VariableExpressions;
|
||||
|
||||
class SampleWorkflow : WorkflowBase
|
||||
{
|
||||
protected override void Build(IWorkflowBuilder workflow)
|
||||
{
|
||||
var variable1 = workflow.WithVariable("Some variable");
|
||||
var variable2 = workflow.WithVariable(42);
|
||||
var literal1 = new Literal<string>("Some literal");
|
||||
var literal2 = new Literal<int>(84);
|
||||
var writeLine1 = new WriteLine(variable1);
|
||||
var writeLine2 = new WriteLine(literal1);
|
||||
var numberActivity1 = new NumberActivity(variable2);
|
||||
var numberActivity2 = new NumberActivity(literal2);
|
||||
|
||||
workflow.Root = new Sequence
|
||||
{
|
||||
Activities =
|
||||
{
|
||||
writeLine1,
|
||||
writeLine2,
|
||||
numberActivity1,
|
||||
numberActivity2
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
using Elsa.Workflows.Memory;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Serialization.VariableTypes;
|
||||
|
||||
internal record VariablesContainer(ICollection<Variable> Variables);
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Serialization.VariableTypes;
|
||||
|
||||
public class Tests
|
||||
{
|
||||
private readonly IPayloadSerializer _payloadSerializer;
|
||||
|
||||
public Tests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
var services = new TestApplicationBuilder(testOutputHelper).Build();
|
||||
_payloadSerializer = services.GetRequiredService<IPayloadSerializer>();
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Variable types remain intact after serialization")]
|
||||
public void Test1()
|
||||
{
|
||||
// Create collection of variables to serialize.
|
||||
var variables = new Collection<Variable>();
|
||||
var model = new VariablesContainer(variables);
|
||||
|
||||
// Create a typed variable.
|
||||
var variable = new Variable<bool>("Variable", false);
|
||||
|
||||
// Add variable to collection.
|
||||
variables.Add(variable);
|
||||
|
||||
// Serialize collection.
|
||||
var json = _payloadSerializer.Serialize(model);
|
||||
|
||||
// Deserialize collection.
|
||||
var deserializedModel = _payloadSerializer.Deserialize<VariablesContainer>(json);
|
||||
|
||||
// Get the first variable.
|
||||
var deserializedVariable = deserializedModel.Variables.First();
|
||||
|
||||
// Assert that the variable is of the correct type.
|
||||
Assert.IsType<Variable<bool>>(deserializedVariable);
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
namespace Elsa.Workflows.IntegrationTests.Serialization.VariableTypes;
|
||||
|
||||
class SampleWorkflow : WorkflowBase
|
||||
{
|
||||
protected override void Build(IWorkflowBuilder workflow)
|
||||
{
|
||||
workflow.WithVariable<bool>();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user