From c02aa8abfc6add20200cb59b1f26c30fe0c7478f Mon Sep 17 00:00:00 2001 From: Arjen Kroezen Date: Wed, 20 Sep 2023 15:55:50 +0200 Subject: [PATCH] test: increase test coverage and fix some discovered issues --- ...eDataFactory.TestingFramework.Tests.csproj | 5 +- .../Base/ActivitiesEvaluatorTests.cs | 116 ++++++++++++++++++ .../Activities/Base/PipelineActivityTests.cs | 46 +++++++ .../ControlActivities/ForEachActivityTests.cs | 46 +++++++ .../IfConditionActivityTests.cs | 47 +++++++ .../Models/Activities/SetVariableTests.cs | 28 +++++ .../Models/DataFactoryEntityTests.cs | 35 ++++++ .../Models/Pipelines/PipelineRunStateTests.cs | 108 ++++++++++++++++ .../Models/Pipelines/PipelineTests.cs | 39 ++++++ .../AzureDataFactory.TestingFramework.csproj | 2 +- ...tiesEvaluatorInvalidDependencyException.cs | 9 ++ ...lineDuplicateParameterProvidedException.cs | 8 ++ .../PipelineParameterNotProvidedException.cs | 8 ++ .../Functions/FunctionPart.cs | 2 +- .../Functions/FunctionsRepository.cs | 1 + .../Activities/Base/ActivitiesEvaluator.cs | 3 +- .../SetVariableActivity.cs | 0 .../Models/DataFactoryDatasetProperties.cs | 7 -- .../DataFactoryLinkedServiceProperties.cs | 7 -- .../Models/Pipelines/Pipeline.cs | 21 ++-- .../Shared/DataFactoryElement.cs | 6 +- .../Shared/DataFactoryElementKind.cs | 82 +++++++++++++ 22 files changed, 590 insertions(+), 36 deletions(-) create mode 100644 src/AzureDataFactory.TestingFramework.Tests/Models/Activities/Base/ActivitiesEvaluatorTests.cs create mode 100644 src/AzureDataFactory.TestingFramework.Tests/Models/Activities/Base/PipelineActivityTests.cs create mode 100644 src/AzureDataFactory.TestingFramework.Tests/Models/Activities/ControlActivities/ForEachActivityTests.cs create mode 100644 src/AzureDataFactory.TestingFramework.Tests/Models/Activities/ControlActivities/IfConditionActivityTests.cs create mode 100644 src/AzureDataFactory.TestingFramework.Tests/Models/Activities/SetVariableTests.cs create mode 100644 src/AzureDataFactory.TestingFramework.Tests/Models/DataFactoryEntityTests.cs create mode 100644 src/AzureDataFactory.TestingFramework.Tests/Models/Pipelines/PipelineRunStateTests.cs create mode 100644 src/AzureDataFactory.TestingFramework.Tests/Models/Pipelines/PipelineTests.cs create mode 100644 src/AzureDataFactory.TestingFramework/Exceptions/ActivitiesEvaluatorInvalidDependencyException.cs create mode 100644 src/AzureDataFactory.TestingFramework/Exceptions/PipelineDuplicateParameterProvidedException.cs create mode 100644 src/AzureDataFactory.TestingFramework/Exceptions/PipelineParameterNotProvidedException.cs rename src/AzureDataFactory.TestingFramework/Models/Activities/{SetVariableActivity => }/SetVariableActivity.cs (100%) delete mode 100644 src/AzureDataFactory.TestingFramework/Models/DataFactoryDatasetProperties.cs delete mode 100644 src/AzureDataFactory.TestingFramework/Models/DataFactoryLinkedServiceProperties.cs create mode 100644 src/AzureDataFactory.TestingFramework/Shared/DataFactoryElementKind.cs diff --git a/src/AzureDataFactory.TestingFramework.Tests/AzureDataFactory.TestingFramework.Tests.csproj b/src/AzureDataFactory.TestingFramework.Tests/AzureDataFactory.TestingFramework.Tests.csproj index b5760c1a..454414e2 100644 --- a/src/AzureDataFactory.TestingFramework.Tests/AzureDataFactory.TestingFramework.Tests.csproj +++ b/src/AzureDataFactory.TestingFramework.Tests/AzureDataFactory.TestingFramework.Tests.csproj @@ -20,6 +20,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + @@ -32,8 +33,4 @@ - - - - diff --git a/src/AzureDataFactory.TestingFramework.Tests/Models/Activities/Base/ActivitiesEvaluatorTests.cs b/src/AzureDataFactory.TestingFramework.Tests/Models/Activities/Base/ActivitiesEvaluatorTests.cs new file mode 100644 index 00000000..091183e1 --- /dev/null +++ b/src/AzureDataFactory.TestingFramework.Tests/Models/Activities/Base/ActivitiesEvaluatorTests.cs @@ -0,0 +1,116 @@ +using Azure.Core.Expressions.DataFactory; +using AzureDataFactory.TestingFramework.Exceptions; +using AzureDataFactory.TestingFramework.Functions; +using AzureDataFactory.TestingFramework.Models; +using AzureDataFactory.TestingFramework.Models.Activities.Base; +using AzureDataFactory.TestingFramework.Models.Pipelines; + +namespace AzureDataFactory.TestingFramework.Tests.Models.Activities.Base; + +public class ActivitiesEvaluatorTests +{ + private readonly List _activities; + private readonly WebActivity _webActivity; + private readonly SetVariableActivity _setVariableActivity; + + public ActivitiesEvaluatorTests() + { + _activities = new List(); + _webActivity = new WebActivity("webActivity", WebActivityMethod.Get, "https://www.example.com") + { + DependsOn = + { + new PipelineActivityDependency("setVariableActivity", new[] { DependencyCondition.Succeeded }) + } + }; + _setVariableActivity = new SetVariableActivity("setVariableActivity") + { + VariableName = "variable1", + Value = "value1" + }; + _activities.Add(_webActivity); + _activities.Add(_setVariableActivity); + } + + [Fact] + public void EvaluateWithoutIterationActivities_ShouldEvaluateAccordingToDependencies() + { + // Act + var state = new PipelineRunState(); + state.Variables.Add(new PipelineRunVariable("variable1", string.Empty)); + var evaluatedActivities = ActivitiesEvaluator.Evaluate(_activities, state).ToList(); + + // Assert + Assert.NotNull(evaluatedActivities); + Assert.Equal(2, evaluatedActivities.Count()); + Assert.Equal("setVariableActivity", evaluatedActivities.First().Name); + Assert.Equal("webActivity", evaluatedActivities.Last().Name); + } + + [Fact] + public void EvaluateWithCircularDependencies_ShouldThrowActivitiesEvaluatorInvalidDependencyException() + { + // Arrange + _setVariableActivity.DependsOn.Add(new PipelineActivityDependency("webActivity", new[] { DependencyCondition.Succeeded })); + + // Assert + Assert.Throws(() => ActivitiesEvaluator.Evaluate(_activities, new PipelineRunState()).ToList()); + } + + [Fact] + public void EvaluateWithForeachActivities_ShouldEvaluateAccordingToDependencies() + { + // Arrange + var state = new PipelineRunState(); + state.Variables.Add(new PipelineRunVariable("variable1", string.Empty)); + state.Variables.Add(new PipelineRunVariable("iterationItems", "item1,item2,item3")); + var foreachActivity = new ForEachActivity("foreachActivity", + new DataFactoryExpression(DataFactoryExpressionType.Expression, "@split(variables('iterationItems'), ',')"), + _activities); + _webActivity.Uri = new DataFactoryElement("@concat('https://www.example.com/', item())", DataFactoryElementKind.Expression); + + // Act + var evaluatedActivities = ActivitiesEvaluator.Evaluate(new List { foreachActivity }, state); + + // Assert + using var enumerator = evaluatedActivities.GetEnumerator(); + Assert.True(enumerator.MoveNext()); + Assert.Equal("setVariableActivity", enumerator.Current.Name); + Assert.True(enumerator.MoveNext()); + Assert.Equal("webActivity", enumerator.Current.Name); + Assert.Equal("https://www.example.com/item1", ((WebActivity)enumerator.Current).Uri); + Assert.True(enumerator.MoveNext()); + Assert.Equal("setVariableActivity", enumerator.Current.Name); + Assert.True(enumerator.MoveNext()); + Assert.Equal("webActivity", enumerator.Current.Name); + Assert.Equal("https://www.example.com/item2", ((WebActivity)enumerator.Current).Uri); + Assert.True(enumerator.MoveNext()); + Assert.Equal("setVariableActivity", enumerator.Current.Name); + Assert.True(enumerator.MoveNext()); + Assert.Equal("webActivity", enumerator.Current.Name); + Assert.Equal("https://www.example.com/item3", ((WebActivity)enumerator.Current).Uri); + Assert.False(enumerator.MoveNext()); + } + + [Fact] + public void EvaluateWithUntilActivities_ShouldEvaluateAccordingToDependencies() + { + // Arrange + var state = new PipelineRunState(); + state.Variables.Add(new PipelineRunVariable("variable1", string.Empty)); + var untilActivity = new UntilActivity("untilActivity", + new DataFactoryExpression(DataFactoryExpressionType.Expression, "@equals(variables('variable1'), 'value1')"), + _activities); + + // Act + var evaluatedActivities = ActivitiesEvaluator.Evaluate(new List { untilActivity }, state); + + // Assert + using var enumerator = evaluatedActivities.GetEnumerator(); + Assert.True(enumerator.MoveNext()); + Assert.Equal("setVariableActivity", enumerator.Current.Name); + Assert.True(enumerator.MoveNext()); + Assert.Equal("webActivity", enumerator.Current.Name); + Assert.False(enumerator.MoveNext()); + } +} \ No newline at end of file diff --git a/src/AzureDataFactory.TestingFramework.Tests/Models/Activities/Base/PipelineActivityTests.cs b/src/AzureDataFactory.TestingFramework.Tests/Models/Activities/Base/PipelineActivityTests.cs new file mode 100644 index 00000000..9dc52521 --- /dev/null +++ b/src/AzureDataFactory.TestingFramework.Tests/Models/Activities/Base/PipelineActivityTests.cs @@ -0,0 +1,46 @@ +using AzureDataFactory.TestingFramework.Models; +using AzureDataFactory.TestingFramework.Models.Pipelines; + +namespace AzureDataFactory.TestingFramework.Tests.Models.Activities.Base; + +public class PipelineActivityTests +{ + [Theory] + [InlineData("Succeeded", "Succeeded", true)] + [InlineData("Failed", "Succeeded", false)] + [InlineData("Skipped", "Succeeded", false)] + [InlineData("Completed", "Succeeded", false)] + [InlineData("Failed", "Failed", true)] + [InlineData("Skipped", "Failed", false)] + [InlineData("Completed", "Failed", false)] + [InlineData("Skipped", "Skipped", true)] + [InlineData("Completed", "Skipped", false)] + [InlineData("Completed", "Completed", true)] + public void DependencyConditions_WhenCalled_ReturnsExpected(string requiredCondition, string actualCondition, bool expected) + { + // Arrange + var pipelineActivity = new PipelineActivity("activity") + { + DependsOn = { new PipelineActivityDependency("otherActivity", new[] { new DependencyCondition(requiredCondition) }) } + }; + var state = new PipelineRunState(); + state.AddActivityResult(new TestActivityResult("otherActivity", new DependencyCondition(actualCondition))); + + // Assert + Assert.Equal(expected, pipelineActivity.AreDependencyConditionMet(state)); + } + + [Fact] + public void EvaluateWhenNoStatusIsSet_ShouldSetStatusToSucceeded() + { + // Arrange + var pipelineActivity = new PipelineActivity("activity"); + var state = new PipelineRunState(); + + // Act + pipelineActivity.Evaluate(state); + + // Assert + Assert.Equal(DependencyCondition.Succeeded, pipelineActivity.Status); + } +} \ No newline at end of file diff --git a/src/AzureDataFactory.TestingFramework.Tests/Models/Activities/ControlActivities/ForEachActivityTests.cs b/src/AzureDataFactory.TestingFramework.Tests/Models/Activities/ControlActivities/ForEachActivityTests.cs new file mode 100644 index 00000000..c8acfc6c --- /dev/null +++ b/src/AzureDataFactory.TestingFramework.Tests/Models/Activities/ControlActivities/ForEachActivityTests.cs @@ -0,0 +1,46 @@ +using Azure.Core.Expressions.DataFactory; +using AzureDataFactory.TestingFramework.Models; +using AzureDataFactory.TestingFramework.Models.Pipelines; + +namespace AzureDataFactory.TestingFramework.Tests.Models.Activities.ControlActivities; + +public class ForEachActivityTests +{ + [Fact] + public void WhenEvaluateChildActivities_ThenShouldReturnTheActivityWithItemExpressionEvaluated() + { + // Arrange + var forEachActivity = new ForEachActivity("ForEachActivity", + new DataFactoryExpression(DataFactoryExpressionType.Expression, "@split('a,b,c', ',')"), + new List() + { + new SetVariableActivity("setVariable") + { + VariableName = "variable", + Value = new DataFactoryElement("item()", DataFactoryElementKind.Expression) + } + }); + var state = new PipelineRunState(); + state.Variables.Add(new PipelineRunVariable("variable", string.Empty)); + + // Act + forEachActivity.Evaluate(state); + var childActivities = forEachActivity.EvaluateChildActivities(state); + + // Assert + using var enumarator = childActivities.GetEnumerator(); + Assert.True(enumarator.MoveNext()); + var setVariableActivity = enumarator.Current as SetVariableActivity; + Assert.NotNull(setVariableActivity); + Assert.Equal("a", setVariableActivity.Value); + Assert.True(enumarator.MoveNext()); + setVariableActivity = enumarator.Current as SetVariableActivity; + Assert.NotNull(setVariableActivity); + Assert.Equal("b", setVariableActivity.Value); + Assert.True(enumarator.MoveNext()); + setVariableActivity = enumarator.Current as SetVariableActivity; + Assert.NotNull(setVariableActivity); + Assert.Equal("c", setVariableActivity.Value); + Assert.False(enumarator.MoveNext()); + } +} \ No newline at end of file diff --git a/src/AzureDataFactory.TestingFramework.Tests/Models/Activities/ControlActivities/IfConditionActivityTests.cs b/src/AzureDataFactory.TestingFramework.Tests/Models/Activities/ControlActivities/IfConditionActivityTests.cs new file mode 100644 index 00000000..012a75d6 --- /dev/null +++ b/src/AzureDataFactory.TestingFramework.Tests/Models/Activities/ControlActivities/IfConditionActivityTests.cs @@ -0,0 +1,47 @@ +using Azure.Core.Expressions.DataFactory; +using AzureDataFactory.TestingFramework.Models; +using AzureDataFactory.TestingFramework.Models.Pipelines; + +namespace AzureDataFactory.TestingFramework.Tests.Models.Activities.ControlActivities; + +public class IfConditionActivityTests +{ + [Fact] + public void WhenEvaluated_ShouldEvaluateExpression() + { + // Arrange + var activity = new IfConditionActivity("IfConditionActivity", + new DataFactoryExpression(DataFactoryExpressionType.Expression, "@equals(1, 1)")); + + // Act + activity.Evaluate(new PipelineRunState()); + + // Assert + Assert.True(activity.EvaluatedExpression); + } + + [Theory] + [InlineData(true, "setVariableActivity1")] + [InlineData(false, "setVariableActivity2")] + public void WhenEvaluated_ShouldEvaluateCorrectChildActivities(bool expressionOutcome, string expectedActivityName) + { + // Arrange + var expression = expressionOutcome ? "@equals(1, 1)" : "@equals(1, 2)"; + var activity = new IfConditionActivity("IfConditionActivity", + new DataFactoryExpression(DataFactoryExpressionType.Expression, expression)) + { + IfTrueActivities = { new SetVariableActivity("setVariableActivity1") { VariableName = "variable", Value = "dummy" } }, + IfFalseActivities = { new SetVariableActivity("setVariableActivity2") { VariableName = "variable", Value = "dummy" } } + }; + var state = new PipelineRunState(); + state.Variables.Add(new PipelineRunVariable("variable", string.Empty)); + activity.Evaluate(state); + + // Act + var childActivities = activity.EvaluateChildActivities(state).ToList(); + + // Assert + Assert.Single(childActivities); + Assert.Equal(expectedActivityName, childActivities.First().Name); + } +} \ No newline at end of file diff --git a/src/AzureDataFactory.TestingFramework.Tests/Models/Activities/SetVariableTests.cs b/src/AzureDataFactory.TestingFramework.Tests/Models/Activities/SetVariableTests.cs new file mode 100644 index 00000000..52fcf490 --- /dev/null +++ b/src/AzureDataFactory.TestingFramework.Tests/Models/Activities/SetVariableTests.cs @@ -0,0 +1,28 @@ +using AzureDataFactory.TestingFramework.Models; +using AzureDataFactory.TestingFramework.Models.Pipelines; + +namespace AzureDataFactory.TestingFramework.Tests.Models.Activities; + +public class SetVariableTests +{ + [Fact] + public void WhenStringVariableEvaluated_ThenStateVariableShouldBeSet() + { + // Arrange + var variableName = "TestVariable"; + var variable = new PipelineRunVariable(variableName, string.Empty); + var setVariable = new SetVariableActivity("TestSetVariable") + { + VariableName = variableName, + Value = "value1" + }; + var state = new PipelineRunState(); + state.Variables.Add(variable); + + // Act + setVariable.Evaluate(state); + + // Assert + Assert.Equal("value1", variable.Value); + } +} \ No newline at end of file diff --git a/src/AzureDataFactory.TestingFramework.Tests/Models/DataFactoryEntityTests.cs b/src/AzureDataFactory.TestingFramework.Tests/Models/DataFactoryEntityTests.cs new file mode 100644 index 00000000..65928588 --- /dev/null +++ b/src/AzureDataFactory.TestingFramework.Tests/Models/DataFactoryEntityTests.cs @@ -0,0 +1,35 @@ +using AzureDataFactory.TestingFramework.Models; +using Azure.Core.Expressions.DataFactory; +using AzureDataFactory.TestingFramework.Models.Pipelines; + +namespace AzureDataFactory.TestingFramework.Tests.Models; + +public class DataFactoryEntityTests +{ + [Fact] + public void WhenEvaluatingEntity_ShouldEvaluateAllProperties() + { + // Arrange + var entity = new WebActivity("TestActivity", WebActivityMethod.Get, new DataFactoryElement("@concat('https://example.com', '/123')", DataFactoryElementKind.Expression)); + + // Act + entity.Evaluate(new PipelineRunState()); + + // Assert + Assert.NotNull(entity); + Assert.Equal("https://example.com/123", entity.Uri); + } + + [Fact] + public void WhenNotEvaluatingEntity_ShouldNotEvaluateAllProperties() + { + // Arrange + var entity = new WebActivity("TestActivity", WebActivityMethod.Get, new DataFactoryElement("@concat('https://example.com', '/123')", DataFactoryElementKind.Expression)); + + // Act + + // Assert + Assert.NotNull(entity); + Assert.Throws(() => entity.Uri.Value); + } +} \ No newline at end of file diff --git a/src/AzureDataFactory.TestingFramework.Tests/Models/Pipelines/PipelineRunStateTests.cs b/src/AzureDataFactory.TestingFramework.Tests/Models/Pipelines/PipelineRunStateTests.cs new file mode 100644 index 00000000..84e6d8e4 --- /dev/null +++ b/src/AzureDataFactory.TestingFramework.Tests/Models/Pipelines/PipelineRunStateTests.cs @@ -0,0 +1,108 @@ +using AzureDataFactory.TestingFramework.Models; +using AzureDataFactory.TestingFramework.Models.Base; +using AzureDataFactory.TestingFramework.Models.Pipelines; + +namespace AzureDataFactory.TestingFramework.Tests.Models.Pipelines; + +public class PipelineRunStateTests +{ + [Fact] + public void WhenPipelineRunStateIsInitialized_ShouldBeInitializedCorrectly() + { + // Act + var state = new PipelineRunState( + new List() + { + new RunParameter(ParameterType.Dataset, "datasetKey", "datasetValue"), + new RunParameter(ParameterType.LinkedService, "linkedServiceKey", "linkedServiceValue"), + new RunParameter(ParameterType.Parameter, "parameterKey", "parameterValue"), + new RunParameter(ParameterType.Global, "globalParameterKey", "globalParameterValue") + }, + new Dictionary() + { + { "stringVariableKey", new PipelineVariableSpecification(PipelineVariableType.String) }, + { "boolVariableKey", new PipelineVariableSpecification(PipelineVariableType.Bool) } + }); + + // Assert + Assert.NotNull(state); + Assert.NotNull(state.Parameters); + Assert.Equal(4, state.Parameters.Count); + var datasetParameter = state.Parameters.Single(p => p.Name == "datasetKey"); + Assert.Equal(ParameterType.Dataset, datasetParameter.Type); + + var linkedServiceParameter = state.Parameters.Single(p => p.Name == "linkedServiceKey"); + Assert.Equal(ParameterType.LinkedService, linkedServiceParameter.Type); + + var parameterParameter = state.Parameters.Single(p => p.Name == "parameterKey"); + Assert.Equal(ParameterType.Parameter, parameterParameter.Type); + + var globalParameterParameter = state.Parameters.Single(p => p.Name == "globalParameterKey"); + Assert.Equal(ParameterType.Global, globalParameterParameter.Type); + + Assert.NotNull(state.Variables); + Assert.Equal(2, state.Variables.Count); + var stringVariable = state.Variables.OfType>().Single(v => v.Name == "stringVariableKey"); + Assert.Null(stringVariable.Value); + + var boolVariable = state.Variables.OfType>().Single(v => v.Name == "boolVariableKey"); + Assert.False(boolVariable.Value); + + Assert.Empty(state.PipelineActivityResults); + Assert.Empty(state.ScopedPipelineActivityResults); + Assert.Null(state.IterationItem); + } + + [Fact] + public void WhenActivityResultAreAdded_ShouldBeAddedCorrectly() + { + // Arrange + var state = new PipelineRunState(); + var activityResult = new TestActivityResult("activityName", DependencyCondition.Succeeded); + + // Act + state.AddActivityResult(activityResult); + + // Assert + Assert.Single(state.PipelineActivityResults); + Assert.Equal(activityResult, state.PipelineActivityResults.Single()); + Assert.Single(state.ScopedPipelineActivityResults); + Assert.Equal(activityResult, state.ScopedPipelineActivityResults.Single()); + } + + [Fact] + public void WhenAddScopedActivityResultsFromScopedState_ShouldRegisterScopedResults() + { + // Arrange + var scopedState = new PipelineRunState(); + scopedState.AddActivityResult(new TestActivityResult("activityName", DependencyCondition.Succeeded)); + var state = new PipelineRunState(); + + // Act + state.AddScopedActivityResultsFromScopedState(scopedState); + + // Assert + Assert.Single(state.PipelineActivityResults); + Assert.Equal(scopedState.ScopedPipelineActivityResults.Single(), state.PipelineActivityResults.Single()); + } + + [Fact] + public void WhenIterationScopedIsCreated_ShouldReturnNewScopeWithIterationItem() + { + // Arrange + var state = new PipelineRunState(); + state.AddActivityResult(new TestActivityResult("activityName", DependencyCondition.Succeeded)); + state.Parameters.Add(new RunParameter(ParameterType.Parameter, "parameterKey", "parameterValue")); + state.Variables.Add(new PipelineRunVariable("variableKey", "variableValue")); + + // Act + var scopedState = state.CreateIterationScope("iterationItem"); + + // Assert + Assert.Equal("iterationItem", scopedState.IterationItem); + Assert.Equal(state.Parameters, scopedState.Parameters); + Assert.Equal(state.Variables, scopedState.Variables); + Assert.Equal(state.PipelineActivityResults, scopedState.PipelineActivityResults); + Assert.Empty(scopedState.ScopedPipelineActivityResults); + } +} \ No newline at end of file diff --git a/src/AzureDataFactory.TestingFramework.Tests/Models/Pipelines/PipelineTests.cs b/src/AzureDataFactory.TestingFramework.Tests/Models/Pipelines/PipelineTests.cs new file mode 100644 index 00000000..4ff32913 --- /dev/null +++ b/src/AzureDataFactory.TestingFramework.Tests/Models/Pipelines/PipelineTests.cs @@ -0,0 +1,39 @@ +using Azure.ResourceManager.DataFactory; +using AzureDataFactory.TestingFramework.Exceptions; +using AzureDataFactory.TestingFramework.Models; +using AzureDataFactory.TestingFramework.Models.Base; + +namespace AzureDataFactory.TestingFramework.Tests.Models.Pipelines; + +public class PipelineTests +{ + [Fact] + public void WhenEvaluatingPipelineWithMissingParameters_ShouldThrowException() + { + // Arrange + var pipeline = new Pipeline(); + pipeline.Parameters.Add("key1", new EntityParameterSpecification(EntityParameterType.String)); + pipeline.Parameters.Add("key2", new EntityParameterSpecification(EntityParameterType.String)); + + // Assert + Assert.Throws(() => pipeline.Evaluate(new List()).ToList()); + } + + [Fact] + public void WhenEvaluatingPipeline_ShouldReturnActivities() + { + // Arrange + var pipeline = new Pipeline(); + pipeline.Parameters.Add("key1", new EntityParameterSpecification(EntityParameterType.String)); + + // Act + var activities = pipeline.Evaluate(new List() + { + new RunParameter(ParameterType.Parameter, "key1", "value1") + }); + + // Assert + Assert.NotNull(activities); + Assert.Empty(activities); + } +} \ No newline at end of file diff --git a/src/AzureDataFactory.TestingFramework/AzureDataFactory.TestingFramework.csproj b/src/AzureDataFactory.TestingFramework/AzureDataFactory.TestingFramework.csproj index 4867975e..bca14b6b 100644 --- a/src/AzureDataFactory.TestingFramework/AzureDataFactory.TestingFramework.csproj +++ b/src/AzureDataFactory.TestingFramework/AzureDataFactory.TestingFramework.csproj @@ -16,7 +16,7 @@ AzureDataFactory.TestingFramework - 0.1.4-alpha + 0.1.5-alpha arjendev true diff --git a/src/AzureDataFactory.TestingFramework/Exceptions/ActivitiesEvaluatorInvalidDependencyException.cs b/src/AzureDataFactory.TestingFramework/Exceptions/ActivitiesEvaluatorInvalidDependencyException.cs new file mode 100644 index 00000000..7485cf24 --- /dev/null +++ b/src/AzureDataFactory.TestingFramework/Exceptions/ActivitiesEvaluatorInvalidDependencyException.cs @@ -0,0 +1,9 @@ +namespace AzureDataFactory.TestingFramework.Exceptions; + +public class ActivitiesEvaluatorInvalidDependencyException : Exception +{ + public ActivitiesEvaluatorInvalidDependencyException(string message) : base(message) + { + + } +} \ No newline at end of file diff --git a/src/AzureDataFactory.TestingFramework/Exceptions/PipelineDuplicateParameterProvidedException.cs b/src/AzureDataFactory.TestingFramework/Exceptions/PipelineDuplicateParameterProvidedException.cs new file mode 100644 index 00000000..2af700a6 --- /dev/null +++ b/src/AzureDataFactory.TestingFramework/Exceptions/PipelineDuplicateParameterProvidedException.cs @@ -0,0 +1,8 @@ +namespace AzureDataFactory.TestingFramework.Exceptions; + +public class PipelineDuplicateParameterProvidedException : Exception +{ + public PipelineDuplicateParameterProvidedException(string message) : base(message) + { + } +} \ No newline at end of file diff --git a/src/AzureDataFactory.TestingFramework/Exceptions/PipelineParameterNotProvidedException.cs b/src/AzureDataFactory.TestingFramework/Exceptions/PipelineParameterNotProvidedException.cs new file mode 100644 index 00000000..d3a1dfa9 --- /dev/null +++ b/src/AzureDataFactory.TestingFramework/Exceptions/PipelineParameterNotProvidedException.cs @@ -0,0 +1,8 @@ +namespace AzureDataFactory.TestingFramework.Exceptions; + +public class PipelineParameterNotProvidedException : Exception +{ + public PipelineParameterNotProvidedException(string message) : base(message) + { + } +} \ No newline at end of file diff --git a/src/AzureDataFactory.TestingFramework/Functions/FunctionPart.cs b/src/AzureDataFactory.TestingFramework/Functions/FunctionPart.cs index 21d2646f..18c4e276 100644 --- a/src/AzureDataFactory.TestingFramework/Functions/FunctionPart.cs +++ b/src/AzureDataFactory.TestingFramework/Functions/FunctionPart.cs @@ -13,7 +13,7 @@ public static IFunctionPart Parse(string expression) return new FunctionArgument(expression); var functionName = match.Groups[1].Value.Trim(); - if (functionName is "variables" or "activity" or "pipeline") + if (functionName is "variables" or "activity" or "pipeline" or "item") return new FunctionArgument(expression); var functionArgumentsExpression = match.Groups[2].Value; diff --git a/src/AzureDataFactory.TestingFramework/Functions/FunctionsRepository.cs b/src/AzureDataFactory.TestingFramework/Functions/FunctionsRepository.cs index 3b2a74a9..04aa0035 100644 --- a/src/AzureDataFactory.TestingFramework/Functions/FunctionsRepository.cs +++ b/src/AzureDataFactory.TestingFramework/Functions/FunctionsRepository.cs @@ -25,6 +25,7 @@ public static class FunctionsRepository { "greaterOrEquals", (long a, long b) => a >= b }, { "not", (bool value) => !value }, { "empty", (object[] array) => array.Length == 0 }, + { "split", (string input, string delimiter) => input.Split(delimiter).ToList() } }; public static void Register(string functionName, Delegate function) diff --git a/src/AzureDataFactory.TestingFramework/Models/Activities/Base/ActivitiesEvaluator.cs b/src/AzureDataFactory.TestingFramework/Models/Activities/Base/ActivitiesEvaluator.cs index 3a3b95fc..7ca4baf2 100644 --- a/src/AzureDataFactory.TestingFramework/Models/Activities/Base/ActivitiesEvaluator.cs +++ b/src/AzureDataFactory.TestingFramework/Models/Activities/Base/ActivitiesEvaluator.cs @@ -1,3 +1,4 @@ +using AzureDataFactory.TestingFramework.Exceptions; using AzureDataFactory.TestingFramework.Models.Pipelines; namespace AzureDataFactory.TestingFramework.Models.Activities.Base; @@ -40,7 +41,7 @@ public static IEnumerable Evaluate(List acti if (!anyActivityEvaluated) { - throw new Exception("Dependencies could not be evaluated"); + throw new ActivitiesEvaluatorInvalidDependencyException("Validate that there are no circular dependencies or whether activity results were not set correctly."); } } } diff --git a/src/AzureDataFactory.TestingFramework/Models/Activities/SetVariableActivity/SetVariableActivity.cs b/src/AzureDataFactory.TestingFramework/Models/Activities/SetVariableActivity.cs similarity index 100% rename from src/AzureDataFactory.TestingFramework/Models/Activities/SetVariableActivity/SetVariableActivity.cs rename to src/AzureDataFactory.TestingFramework/Models/Activities/SetVariableActivity.cs diff --git a/src/AzureDataFactory.TestingFramework/Models/DataFactoryDatasetProperties.cs b/src/AzureDataFactory.TestingFramework/Models/DataFactoryDatasetProperties.cs deleted file mode 100644 index 9c0cae05..00000000 --- a/src/AzureDataFactory.TestingFramework/Models/DataFactoryDatasetProperties.cs +++ /dev/null @@ -1,7 +0,0 @@ -using AzureDataFactory.TestingFramework.Models.Base; - -namespace AzureDataFactory.TestingFramework.Models; - -public partial class DataFactoryDatasetProperties : DataFactoryEntity -{ -} \ No newline at end of file diff --git a/src/AzureDataFactory.TestingFramework/Models/DataFactoryLinkedServiceProperties.cs b/src/AzureDataFactory.TestingFramework/Models/DataFactoryLinkedServiceProperties.cs deleted file mode 100644 index b6a7b9d9..00000000 --- a/src/AzureDataFactory.TestingFramework/Models/DataFactoryLinkedServiceProperties.cs +++ /dev/null @@ -1,7 +0,0 @@ -using AzureDataFactory.TestingFramework.Models.Base; - -namespace AzureDataFactory.TestingFramework.Models; - -public partial class DataFactoryLinkedServiceProperties : DataFactoryEntity -{ -} \ No newline at end of file diff --git a/src/AzureDataFactory.TestingFramework/Models/Pipelines/Pipeline.cs b/src/AzureDataFactory.TestingFramework/Models/Pipelines/Pipeline.cs index 46037a5d..5dafe696 100644 --- a/src/AzureDataFactory.TestingFramework/Models/Pipelines/Pipeline.cs +++ b/src/AzureDataFactory.TestingFramework/Models/Pipelines/Pipeline.cs @@ -1,4 +1,5 @@ +using AzureDataFactory.TestingFramework.Exceptions; using AzureDataFactory.TestingFramework.Models; using AzureDataFactory.TestingFramework.Models.Activities.Base; using AzureDataFactory.TestingFramework.Models.Base; @@ -17,7 +18,12 @@ public IEnumerable Evaluate(List parameters) { //Check if all parameters are provided foreach (var parameter in Parameters.Where(parameter => parameters.All(p => p.Name != parameter.Key))) - throw new Exception($"Parameter {parameter.Key} is not provided"); + throw new PipelineParameterNotProvidedException($"Parameter {parameter.Key} is not provided"); + + // Check if no duplicate parameters are provided + var duplicateParameters = parameters.GroupBy(x => new { x.Name, x.Type }).Where(g => g.Count() > 1).Select(y => y.Key).ToList(); + if (duplicateParameters.Any()) + throw new PipelineDuplicateParameterProvidedException($"Duplicate parameters provided: {string.Join(", ", duplicateParameters.Select(x => $"{x.Name} ({x.Type})"))}"); var state = new PipelineRunState(parameters, Variables); foreach (var activity in ActivitiesEvaluator.Evaluate(Activities.ToList(), state)) @@ -26,16 +32,7 @@ public IEnumerable Evaluate(List parameters) public ActivityEnumerator EvaluateWithActivityEnumerator(List parameters) { - // Check if all parameters are provided - foreach (var parameter in Parameters.Where(parameter => parameters.All(p => p.Name != parameter.Key))) - throw new Exception($"Parameter {parameter.Key} is not provided"); - - // Check if no duplicate parameters are provided - var duplicateParameters = parameters.GroupBy(x => new { x.Name, x.Type }).Where(g => g.Count() > 1).Select(y => y.Key).ToList(); - if (duplicateParameters.Any()) - throw new Exception($"Duplicate parameters provided: {string.Join(", ", duplicateParameters.Select(x => $"{x.Name} ({x.Type})"))}"); - - var state = new PipelineRunState(parameters, Variables); - return new ActivityEnumerator(ActivitiesEvaluator.Evaluate(Activities.ToList(), state)); + var activities = Evaluate(parameters); + return new ActivityEnumerator(activities); } } \ No newline at end of file diff --git a/src/AzureDataFactory.TestingFramework/Shared/DataFactoryElement.cs b/src/AzureDataFactory.TestingFramework/Shared/DataFactoryElement.cs index b99cf098..2142c85b 100644 --- a/src/AzureDataFactory.TestingFramework/Shared/DataFactoryElement.cs +++ b/src/AzureDataFactory.TestingFramework/Shared/DataFactoryElement.cs @@ -26,7 +26,7 @@ public sealed class DataFactoryElement internal string? ExpressionString { get; } private T? _expressionValue; - internal DataFactoryElement(T? literal) + public DataFactoryElement(T? literal) { _kind = DataFactoryElementKind.Literal; _literal = literal; @@ -71,13 +71,13 @@ public T Value } } - internal DataFactoryElement(string? expressionString, DataFactoryElementKind kind) + public DataFactoryElement(string? expressionString, DataFactoryElementKind kind) { _kind = kind; ExpressionString = expressionString; } - internal DataFactoryElement(DataFactorySecretBaseDefinition secret, DataFactoryElementKind kind) + public DataFactoryElement(DataFactorySecretBaseDefinition secret, DataFactoryElementKind kind) { _kind = kind; Secret = secret; diff --git a/src/AzureDataFactory.TestingFramework/Shared/DataFactoryElementKind.cs b/src/AzureDataFactory.TestingFramework/Shared/DataFactoryElementKind.cs new file mode 100644 index 00000000..e689ead8 --- /dev/null +++ b/src/AzureDataFactory.TestingFramework/Shared/DataFactoryElementKind.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +namespace Azure.Core.Expressions.DataFactory +{ + /// + /// Represents the kind of data factory element. + /// + public readonly struct DataFactoryElementKind : IEquatable + { + private readonly string _kind; + + /// + /// A literal element. + /// + public static DataFactoryElementKind Literal { get; } = new DataFactoryElementKind("Literal"); + + /// + /// An expression element. + /// + public static DataFactoryElementKind Expression { get; } = new DataFactoryElementKind("Expression"); + + /// + /// A Secret string element. + /// + public static DataFactoryElementKind SecretString { get; } = new DataFactoryElementKind("SecureString"); + + /// + /// A KeyVaultSecretReference element. + /// + public static DataFactoryElementKind KeyVaultSecretReference { get; } = new DataFactoryElementKind("AzureKeyVaultSecret"); + + /// + /// Creates an instance of . + /// + /// The element kind. + public DataFactoryElementKind(string kind) + { + Argument.AssertNotNull(kind, nameof(kind)); + + _kind = kind; + } + + /// + public bool Equals(DataFactoryElementKind other) + { + return string.Equals(_kind, other._kind, StringComparison.Ordinal); + } + + /// + public override bool Equals(object? obj) + => (obj is DataFactoryElementKind other && Equals(other)) || + (obj is string str && str.Equals(_kind, StringComparison.Ordinal)); + + /// + public override int GetHashCode() + => _kind?.GetHashCode() ?? 0; + + /// + /// Compares equality of two instances. + /// + /// The kind to compare. + /// The kind to compare against. + /// true if values are equal for and , otherwise false. + public static bool operator ==(DataFactoryElementKind left, DataFactoryElementKind right) + => left.Equals(right); + + /// + /// Compares inequality of two instances. + /// + /// The kind to compare. + /// The kind to compare against. + /// true if values are equal for and , otherwise false. + public static bool operator !=(DataFactoryElementKind left, DataFactoryElementKind right) + => !left.Equals(right); + + /// + public override string ToString() => _kind ?? ""; + } +} \ No newline at end of file