diff --git a/src/modules/Elsa.Connections.Core/Middleware/ConnectionMiddleware.cs b/src/modules/Elsa.Connections.Core/Middleware/ConnectionMiddleware.cs index 736470521..b48b2326c 100644 --- a/src/modules/Elsa.Connections.Core/Middleware/ConnectionMiddleware.cs +++ b/src/modules/Elsa.Connections.Core/Middleware/ConnectionMiddleware.cs @@ -50,14 +50,13 @@ public class ConnectionMiddleware(ActivityMiddlewareDelegate next LogConnectionExtensions.LogConnectionIsNull(logger); else { - //Get connection from store, if exist, - var connectionConfiguration = await connectionStore.FindAsync(new Persistence.Filters.ConnectionDefinitionFilter() { Name = connectionName }); + // Get connection from store, if exists. + var connectionConfiguration = await connectionStore.FindAsync(new() { Name = connectionName }); if (connectionConfiguration != null) { - dynamic deserializedjson = JsonSerializer.Deserialize(connectionConfiguration.ConnectionConfiguration, propertyType, SerializerOptions); - - inputValue.Properties = deserializedjson; + dynamic deserializedJson = connectionConfiguration.ConnectionConfiguration.Deserialize(propertyType, SerializerOptions)!; + inputValue.Properties = deserializedJson; input.ValueSetter(context.Activity, inputValue); } else diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionRecord.cs index 17b24a294..19c25c98d 100644 --- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionRecord.cs +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionRecord.cs @@ -10,22 +10,22 @@ internal class ActivityExecutionRecordRecord : Record /// /// Gets or sets the workflow instance ID. /// - public string WorkflowInstanceId { get; set; } = default!; + public string WorkflowInstanceId { get; set; } = null!; /// /// Gets or sets the activity ID. /// - public string ActivityId { get; set; } = default!; + public string ActivityId { get; set; } = null!; /// /// Gets or sets the activity node ID. /// - public string ActivityNodeId { get; set; } = default!; + public string ActivityNodeId { get; set; } = null!; /// /// The type of the activity. /// - public string ActivityType { get; set; } = default!; + public string ActivityType { get; set; } = null!; /// /// The version of the activity type. @@ -75,7 +75,7 @@ internal class ActivityExecutionRecordRecord : Record /// /// Gets or sets the status of the activity. /// - public string Status { get; set; } = default!; + public string Status { get; set; } = null!; /// /// Gets or sets the time at which the activity execution completed. diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionSummaryRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionSummaryRecord.cs index 0e389202b..228b82e47 100644 --- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionSummaryRecord.cs +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionSummaryRecord.cs @@ -10,22 +10,22 @@ internal class ActivityExecutionSummaryRecord : Record /// /// Gets or sets the workflow instance ID. /// - public string WorkflowInstanceId { get; set; } = default!; + public string WorkflowInstanceId { get; set; } = null!; /// /// Gets or sets the activity ID. /// - public string ActivityId { get; set; } = default!; + public string ActivityId { get; set; } = null!; /// /// Gets or sets the activity node ID. /// - public string ActivityNodeId { get; set; } = default!; + public string ActivityNodeId { get; set; } = null!; /// /// The type of the activity. /// - public string ActivityType { get; set; } = default!; + public string ActivityType { get; set; } = null!; /// /// The version of the activity type. @@ -50,7 +50,7 @@ internal class ActivityExecutionSummaryRecord : Record /// /// Gets or sets the status of the activity. /// - public string Status { get; set; } = default!; + public string Status { get; set; } = null!; /// /// Gets or sets the time at which the activity execution completed. diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/KeyValuePairRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/KeyValuePairRecord.cs index a1d97edff..5a276a042 100644 --- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/KeyValuePairRecord.cs +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/KeyValuePairRecord.cs @@ -4,5 +4,5 @@ namespace Elsa.Dapper.Modules.Runtime.Records; internal class KeyValuePairRecord : Record { - public string Value { get; set; } = default!; + public string Value { get; set; } = null!; } \ No newline at end of file diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredBookmarkRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredBookmarkRecord.cs index ecaf03652..4040bdc92 100644 --- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredBookmarkRecord.cs +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredBookmarkRecord.cs @@ -4,9 +4,9 @@ namespace Elsa.Dapper.Modules.Runtime.Records; internal class StoredBookmarkRecord : Record { - public string ActivityTypeName { get; set; } = default!; - public string Hash { get; set; } = default!; - public string WorkflowInstanceId { get; set; } = default!; + public string ActivityTypeName { get; set; } = null!; + public string Hash { get; set; } = null!; + public string WorkflowInstanceId { get; set; } = null!; public string? CorrelationId { get; set; } public string? ActivityInstanceId { get; set; } public string? SerializedPayload { get; set; } diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredTriggerRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredTriggerRecord.cs index c81a8318c..537c55620 100644 --- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredTriggerRecord.cs +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredTriggerRecord.cs @@ -4,10 +4,10 @@ namespace Elsa.Dapper.Modules.Runtime.Records; internal class StoredTriggerRecord : Record { - public string WorkflowDefinitionId { get; set; } = default!; - public string WorkflowDefinitionVersionId { get; set; } = default!; - public string Name { get; set; } = default!; - public string ActivityId { get; set; } = default!; + public string WorkflowDefinitionId { get; set; } = null!; + public string WorkflowDefinitionVersionId { get; set; } = null!; + public string Name { get; set; } = null!; + public string ActivityId { get; set; } = null!; public string? Hash { get; set; } public string? SerializedPayload { get; set; } } \ No newline at end of file diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/WorkflowExecutionLogRecordRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/WorkflowExecutionLogRecordRecord.cs index 60115f286..5b2b5c74a 100644 --- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/WorkflowExecutionLogRecordRecord.cs +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/WorkflowExecutionLogRecordRecord.cs @@ -4,18 +4,18 @@ namespace Elsa.Dapper.Modules.Runtime.Records; internal class WorkflowExecutionLogRecordRecord : Record { - public string Id { get; set; } = default!; - public string WorkflowDefinitionId { get; set; } = default!; - public string WorkflowDefinitionVersionId { get; set; } = default!; - public string WorkflowInstanceId { get; set; } = default!; + public string Id { get; set; } = null!; + public string WorkflowDefinitionId { get; set; } = null!; + public string WorkflowDefinitionVersionId { get; set; } = null!; + public string WorkflowInstanceId { get; set; } = null!; public int WorkflowVersion { get; set; } - public string ActivityInstanceId { get; set; } = default!; + public string ActivityInstanceId { get; set; } = null!; public string? ParentActivityInstanceId { get; set; } - public string ActivityId { get; set; } = default!; - public string ActivityType { get; set; } = default!; + public string ActivityId { get; set; } = null!; + public string ActivityType { get; set; } = null!; public int ActivityTypeVersion { get; set; } - public string? ActivityName { get; set; } = default!; - public string ActivityNodeId { get; set; } = default!; + public string? ActivityName { get; set; } + public string ActivityNodeId { get; set; } = null!; public DateTimeOffset Timestamp { get; set; } public long Sequence { get; set; } public string? EventName { get; set; } diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperActivityExecutionRecordStore.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperActivityExecutionRecordStore.cs index 622436450..37879078c 100644 --- a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperActivityExecutionRecordStore.cs +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperActivityExecutionRecordStore.cs @@ -109,7 +109,7 @@ internal class DapperActivityExecutionRecordStore(Store(source.Status), ActivityTypeVersion = source.ActivityTypeVersion, - ActivityState = source.SerializedActivityState != null ? payloadSerializer.Deserialize>(source.SerializedActivityState) : default, - Payload = source.SerializedPayload != null ? safeSerializer.Deserialize>(source.SerializedPayload) : default, - Outputs = source.SerializedOutputs != null ? safeSerializer.Deserialize>(source.SerializedOutputs) : default, - Exception = source.SerializedException != null ? payloadSerializer.Deserialize(source.SerializedException) : default, - Properties = source.SerializedProperties != null ? safeSerializer.Deserialize>(source.SerializedProperties) : new Dictionary(), + ActivityState = source.SerializedActivityState != null ? payloadSerializer.Deserialize>(source.SerializedActivityState) : null, + Payload = source.SerializedPayload != null ? safeSerializer.Deserialize>(source.SerializedPayload) : null, + Outputs = source.SerializedOutputs != null ? safeSerializer.Deserialize>(source.SerializedOutputs) : null, + Exception = source.SerializedException != null ? payloadSerializer.Deserialize(source.SerializedException) : null, + Properties = source.SerializedProperties != null ? safeSerializer.Deserialize>(source.SerializedProperties) : null, TenantId = source.TenantId }; } private ActivityExecutionRecordSummary MapSummary(ActivityExecutionSummaryRecord source) { - return new ActivityExecutionRecordSummary + return new() { Id = source.Id, ActivityId = source.ActivityId, diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/ActivityExecutionLogStore.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/ActivityExecutionLogStore.cs index 43cb9fec6..092781327 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/ActivityExecutionLogStore.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/ActivityExecutionLogStore.cs @@ -85,13 +85,13 @@ public class EFCoreActivityExecutionStore( { entity = entity.SanitizeLogMessage(); var compressionAlgorithm = options.Value.CompressionAlgorithm ?? nameof(None); - var serializedActivityState = entity.ActivityState != null ? safeSerializer.Serialize(entity.ActivityState) : null; + var serializedActivityState = entity.ActivityState?.Count > 0 ? safeSerializer.Serialize(entity.ActivityState) : null; var compressedSerializedActivityState = serializedActivityState != null ? await compressionCodecResolver.Resolve(compressionAlgorithm).CompressAsync(serializedActivityState, cancellationToken) : null; dbContext.Entry(entity).Property("SerializedActivityState").CurrentValue = compressedSerializedActivityState; dbContext.Entry(entity).Property("SerializedActivityStateCompressionAlgorithm").CurrentValue = compressionAlgorithm; dbContext.Entry(entity).Property("SerializedOutputs").CurrentValue = entity.Outputs?.Any() == true ? safeSerializer.Serialize(entity.Outputs) : null; - dbContext.Entry(entity).Property("SerializedProperties").CurrentValue = entity.Properties.Any() ? payloadSerializer.Serialize(entity.Properties) : null; + dbContext.Entry(entity).Property("SerializedProperties").CurrentValue = entity.Properties?.Any() == true ? payloadSerializer.Serialize(entity.Properties) : null; dbContext.Entry(entity).Property("SerializedException").CurrentValue = entity.Exception != null ? payloadSerializer.Serialize(entity.Exception) : null; dbContext.Entry(entity).Property("SerializedPayload").CurrentValue = entity.Payload?.Any() == true ? payloadSerializer.Serialize(entity.Payload) : null; } @@ -104,13 +104,13 @@ public class EFCoreActivityExecutionStore( entity.ActivityState = await DeserializeActivityState(dbContext, entity, cancellationToken); entity.Outputs = Deserialize>(dbContext, entity, "SerializedOutputs"); - entity.Properties = DeserializePayload?>(dbContext, entity, "SerializedProperties") ?? new Dictionary(); + entity.Properties = DeserializePayload?>(dbContext, entity, "SerializedProperties"); entity.Exception = DeserializePayload(dbContext, entity, "SerializedException"); entity.Payload = DeserializePayload>(dbContext, entity, "SerializedPayload"); } [RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.Deserialize(String, JsonSerializerOptions)")] - private async Task?> DeserializeActivityState(RuntimeElsaDbContext dbContext, ActivityExecutionRecord entity, CancellationToken cancellationToken) + private async Task?> DeserializeActivityState(RuntimeElsaDbContext dbContext, ActivityExecutionRecord entity, CancellationToken cancellationToken) { var json = dbContext.Entry(entity).Property("SerializedActivityState").CurrentValue; @@ -119,11 +119,11 @@ public class EFCoreActivityExecutionStore( var compressionAlgorithm = (string?)dbContext.Entry(entity).Property("SerializedActivityStateCompressionAlgorithm").CurrentValue ?? nameof(None); var compressionStrategy = compressionCodecResolver.Resolve(compressionAlgorithm); json = await compressionStrategy.DecompressAsync(json, cancellationToken); - var dictionary = JsonSerializer.Deserialize>(json); - return dictionary?.ToDictionary(x => x.Key, x => (object)x.Value); + var dictionary = JsonSerializer.Deserialize>(json); + return dictionary?.ToDictionary(x => x.Key, x => x.Value); } - return default; + return null; } [RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.Deserialize(String, JsonSerializerOptions)")] diff --git a/src/modules/Elsa.Workflows.Runtime/Entities/ActivityExecutionRecord.cs b/src/modules/Elsa.Workflows.Runtime/Entities/ActivityExecutionRecord.cs index 429516fa8..64d148618 100644 --- a/src/modules/Elsa.Workflows.Runtime/Entities/ActivityExecutionRecord.cs +++ b/src/modules/Elsa.Workflows.Runtime/Entities/ActivityExecutionRecord.cs @@ -57,7 +57,7 @@ public class ActivityExecutionRecord : Entity, ILogRecord /// /// Any properties provided by the activity. /// - public IDictionary Properties { get; set; } = new Dictionary(); + public IDictionary? Properties { get; set; } /// /// Gets or sets the exception that occurred during the activity execution. diff --git a/src/modules/Elsa.Workflows.Runtime/LogPersistence/Models/ActivityLogPersistenceModeMap.cs b/src/modules/Elsa.Workflows.Runtime/LogPersistence/Models/ActivityLogPersistenceModeMap.cs index a8472268c..5513a5617 100644 --- a/src/modules/Elsa.Workflows.Runtime/LogPersistence/Models/ActivityLogPersistenceModeMap.cs +++ b/src/modules/Elsa.Workflows.Runtime/LogPersistence/Models/ActivityLogPersistenceModeMap.cs @@ -6,4 +6,5 @@ public class ActivityLogPersistenceModeMap { public IDictionary Inputs { get; set; } = new Dictionary(); public IDictionary Outputs { get; set; } = new Dictionary(); + public LogPersistenceMode InternalState { get; set; } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/LogPersistence/Services/ActivityPropertyLogPersistenceEvaluator.cs b/src/modules/Elsa.Workflows.Runtime/LogPersistence/Services/ActivityPropertyLogPersistenceEvaluator.cs index 1f5e2c2e9..2113e290b 100644 --- a/src/modules/Elsa.Workflows.Runtime/LogPersistence/Services/ActivityPropertyLogPersistenceEvaluator.cs +++ b/src/modules/Elsa.Workflows.Runtime/LogPersistence/Services/ActivityPropertyLogPersistenceEvaluator.cs @@ -31,6 +31,7 @@ namespace Elsa.Workflows.Runtime; * "default": { "evaluationMode": "Strategy", "strategyType": "Elsa.Workflows.LogPersistence.Strategies.Inherit, Elsa.Workflows.Core", "expression": "..." }, * "inputs": { "input1" : { "evaluationMode": "Strategy", "strategyType": "Elsa.Workflows.LogPersistence.Strategies.Inherit, Elsa.Workflows.Core", "expression": "..." } }, * "outputs": { "output1" : { "evaluationMode": "Strategy", "strategyType": "Elsa.Workflows.LogPersistence.Strategies.Inherit, Elsa.Workflows.Core", "expression": "..." } } + * "internalState": { "evaluationMode": "Strategy", "strategyType": "Elsa.Workflows.LogPersistence.Strategies.Inherit, Elsa.Workflows.Core", "expression": "..." } * } * } */ @@ -73,6 +74,7 @@ public class ActivityPropertyLogPersistenceEvaluator : IActivityPropertyLogPersi await EvaluatePropertiesAsync(context, "inputs", context.ActivityDescriptor.Inputs, legacyProps, configProps, defaultMode, map.Inputs, cancellationToken); await EvaluatePropertiesAsync(context, "outputs", context.ActivityDescriptor.Outputs, legacyProps, configProps, defaultMode, map.Outputs, cancellationToken); + map.InternalState = await EvaluateInternalStateModeAsync(context.ExpressionExecutionContext, context.Activity.CustomProperties, defaultMode, cancellationToken); return map; } @@ -85,8 +87,7 @@ public class ActivityPropertyLogPersistenceEvaluator : IActivityPropertyLogPersi return await GetPersistablePropertiesAsync(context, outputs, "outputs", legacyProps, configProps, defaultMode, cancellationToken); } - private async Task<(IDictionary legacyProps, IDictionary configProps, LogPersistenceMode defaultMode)> - GetPersistenceDefaultsAsync(ActivityExecutionContext context, CancellationToken cancellationToken) + private async Task<(IDictionary legacyProps, IDictionary configProps, LogPersistenceMode defaultMode)> GetPersistenceDefaultsAsync(ActivityExecutionContext context, CancellationToken cancellationToken) { var legacyProps = context.Activity.CustomProperties.GetValueOrDefault>(LegacyKey, () => new Dictionary())!; var rootContext = context.WorkflowExecutionContext.ActivityExecutionContexts.First(x => x.ParentActivityExecutionContext == null); @@ -133,6 +134,18 @@ public class ActivityPropertyLogPersistenceEvaluator : IActivityPropertyLogPersi var mode = legacySection.GetValueOrDefault(key, () => defaultMode); return ResolveMode(mode, () => defaultMode); } + + private async Task EvaluateInternalStateModeAsync( + ExpressionExecutionContext executionContext, + IDictionary currentConfig, + LogPersistenceMode defaultMode, + CancellationToken cancellationToken) + { + var configObject = currentConfig.GetValueOrDefault("internalState", () => new Dictionary())!; + var config = ConvertToConfig(configObject); + if (config != null) return await EvaluateConfigAsync(config, executionContext, () => defaultMode, cancellationToken); + return LogPersistenceMode.Inherit; + } private async Task> GetPersistablePropertiesAsync( ActivityExecutionContext context, diff --git a/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs b/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs index 8a3b04c69..054da084f 100644 --- a/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs +++ b/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs @@ -13,8 +13,10 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper var outputs = source.GetOutputs(); var inputs = source.GetInputs(); var persistenceMap = source.GetLogPersistenceModeMap(); - var persistableInputs = GetPersistableProperties(inputs, persistenceMap.Inputs); - var persistableOutputs = GetPersistableProperties(outputs, persistenceMap.Outputs); + var persistableInputs = GetPersistableInputOutput(inputs, persistenceMap.Inputs); + var persistableOutputs = GetPersistableInputOutput(outputs, persistenceMap.Outputs); + var persistableProperties = GetPersistableDictionary(source.Properties!, persistenceMap.InternalState); + var persistablePayload = GetPersistableDictionary(payload!, persistenceMap.InternalState); return new() { @@ -26,8 +28,8 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper ActivityName = source.Activity.Name, ActivityState = persistableInputs, Outputs = persistableOutputs, - Properties = source.Properties, - Payload = payload, + Properties = persistableProperties, + Payload = persistablePayload!, Exception = ExceptionState.FromException(source.Exception), ActivityTypeVersion = source.Activity.Version, StartedAt = source.StartedAt, @@ -43,7 +45,7 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper return Task.FromResult(Map(source)); } - private IDictionary GetPersistableProperties(IDictionary state, IDictionary map) + private IDictionary GetPersistableInputOutput(IDictionary state, IDictionary map) { var result = new Dictionary(); foreach (var stateEntry in state) @@ -55,7 +57,12 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper return result; } - + + private IDictionary? GetPersistableDictionary(IDictionary dictionary, LogPersistenceMode mode) + { + return mode == LogPersistenceMode.Include ? dictionary : null; + } + private static IDictionary GetPayload(ActivityExecutionContext source) { var outcomes = source.JournalData.TryGetValue("Outcomes", out var resultValue) ? resultValue as string[] : null; diff --git a/test/component/Elsa.Workflows.ComponentTests/Scenarios/LogPersistenceModes/InputOutputLoggingTests.cs b/test/component/Elsa.Workflows.ComponentTests/Scenarios/LogPersistenceModes/InputOutputLoggingTests.cs index 4d2a5629e..ad6e60b29 100644 --- a/test/component/Elsa.Workflows.ComponentTests/Scenarios/LogPersistenceModes/InputOutputLoggingTests.cs +++ b/test/component/Elsa.Workflows.ComponentTests/Scenarios/LogPersistenceModes/InputOutputLoggingTests.cs @@ -32,7 +32,7 @@ public class InputOutputLoggingTests(App app) : AppComponentTest(app) { var activityExecutionRecord = activityExecutionRecords[i]; var shouldBeIncluded = shouldBeIncludedArray[i]; - var isIncluded = activityExecutionRecord.ActivityState!.ContainsKey(nameof(WriteLine.Text)); + var isIncluded = activityExecutionRecord.ActivityState?.ContainsKey(nameof(WriteLine.Text)) == true; Assert.Equal(shouldBeIncluded, isIncluded); } } @@ -55,8 +55,8 @@ public class InputOutputLoggingTests(App app) : AppComponentTest(app) await ExecuteWorkflowAsync("input-output-logging-3"); var setOutput1Record = await GetRecordByActivityNameAsync("SetOutput1"); var setOutput2Record = await GetRecordByActivityNameAsync("SetOutput2"); - var output1IsIncluded = setOutput1Record.ActivityState!.ContainsKey("OutputName"); - var output2IsIncluded = setOutput2Record.ActivityState!.ContainsKey("OutputName"); + var output1IsIncluded = setOutput1Record.ActivityState?.ContainsKey("OutputName") == true; + var output2IsIncluded = setOutput2Record.ActivityState?.ContainsKey("OutputName") == true; Assert.False(output1IsIncluded); Assert.True(output2IsIncluded);