WIP #738 - Add test coverage to replace big test

The large/complex test is now removed, replaced by
smaller/simpler unit tests for the individual units in
the overall process of getting triggers.
This commit is contained in:
Craig Fowler 2021-03-19 19:45:16 +00:00
parent 6700ac2402
commit 3943e64c70
7 changed files with 201 additions and 134 deletions

View file

@ -14,7 +14,6 @@ namespace Elsa.Services
public ActivityExecutionContextForActivityBlueprintFactory(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
}
/// <summary>

View file

@ -16,16 +16,32 @@ namespace Elsa.Services
/// <returns>A collection of the blueprint's starting activities</returns>
public IEnumerable<IActivityBlueprint> GetStartActivities(ICompositeActivityBlueprint compositeActivityBlueprint)
{
var targetActivityIds = compositeActivityBlueprint.Connections
.Select(x => x.Target.Activity?.Id)
.Distinct()
.ToLookup(x => x);
var activityIdsThatAreNotStartingActivities = GetAllActivityIdsWhichHaveInboundConnections(compositeActivityBlueprint);
var query = from activity in compositeActivityBlueprint.Activities
where !targetActivityIds.Contains(activity.Id)
where !activityIdsThatAreNotStartingActivities.Contains(activity.Id)
select activity;
return query;
}
/// <summary>
/// This method gets activities that have inbound connections.
/// </summary>
/// <remarks>
/// <para>
/// "Start activities" are those with no inbound connections; IE no workflow connection-target will point to a start activity.
/// What this method returns is essentially a blacklist of activity IDs which are not starting activities.
/// </para>
/// </remarks>
/// <param name="compositeActivityBlueprint">A composite activity blueprint</param>
/// <returns>A lookup of activity IDs which are not starting activities</returns>
ILookup<string?,string?> GetAllActivityIdsWhichHaveInboundConnections(ICompositeActivityBlueprint compositeActivityBlueprint)
{
return compositeActivityBlueprint.Connections
.Select(x => x.Target.Activity?.Id)
.Distinct()
.ToLookup(x => x);
}
}
}

View file

@ -0,0 +1,27 @@
using System;
using System.Threading;
using Elsa.Services.Models;
using Elsa.Testing.Shared.AutoFixture.Attributes;
using Xunit;
namespace Elsa.Services
{
public class ActivityExecutionContextForActivityBlueprintFactoryTests
{
[Theory(DisplayName = "The CreateActivityExecutionContext method should create a context using the activity blueprint, the workflow execution context, cancellation token and injected service provider."), AutoMoqData]
public void CreateActivityExecutionContextCreatesContextUsingBlueprintExecutionContextCancellationTokenAndServiceProvider([AutofixtureServiceProvider] IServiceProvider serviceProvider,
IActivityBlueprint activityBlueprint,
[OmitOnRecursion] WorkflowExecutionContext workflowExecutionContext,
CancellationToken cancellationToken)
{
var sut = new ActivityExecutionContextForActivityBlueprintFactory(serviceProvider);
var result = sut.CreateActivityExecutionContext(activityBlueprint, workflowExecutionContext, cancellationToken);
Assert.True(ReferenceEquals(activityBlueprint, result.ActivityBlueprint), "The activity blueprint should be the same");
Assert.True(ReferenceEquals(workflowExecutionContext, result.WorkflowExecutionContext), "The workflow execution context should be the same");
Assert.True(Equals(cancellationToken, result.CancellationToken), "The cancellation token should be equal");
Assert.True(ReferenceEquals(serviceProvider, result.ServiceProvider), "The service provider should be the same");
}
}
}

View file

@ -0,0 +1,51 @@
using System.Collections.Generic;
using System.Linq;
using Elsa.Services.Models;
using Moq;
using Xunit;
namespace Elsa.Services
{
public class StartActivitiesForCompositeActivityBlueprintProviderTests
{
[Theory(DisplayName = "The GetStartActivities returns only activities that have no inbound workflow connections"), AutoMoqData]
public void GetStartActivitiesReturnsAllActivitiesWhichHaveNoInboundConnections(StartActivitiesForCompositeActivityBlueprintProvider sut,
IWorkflowBlueprint workflowBlueprint,
IActivityBlueprint activityBlueprint1,
IActivityBlueprint activityBlueprint2,
IActivityBlueprint activityBlueprint3,
IActivityBlueprint activityBlueprint4,
string activityBlueprintId1,
string activityBlueprintId2,
string activityBlueprintId3,
string activityBlueprintId4,
IConnection connection1,
IConnection connection2,
ITargetEndpoint endpoint1,
ITargetEndpoint endpoint2)
{
SetupActivitiesWithIds(new () {
{activityBlueprintId1, activityBlueprint1},
{activityBlueprintId2, activityBlueprint2},
{activityBlueprintId3, activityBlueprint3},
{activityBlueprintId4, activityBlueprint4},
});
Mock.Get(workflowBlueprint).SetupGet(x => x.Connections).Returns(new [] { connection1, connection2 });
Mock.Get(workflowBlueprint).SetupGet(x => x.Activities).Returns(new [] { activityBlueprint1, activityBlueprint2, activityBlueprint3, activityBlueprint4 });
Mock.Get(connection1).SetupGet(x => x.Target).Returns(endpoint1);
Mock.Get(connection2).SetupGet(x => x.Target).Returns(endpoint2);
Mock.Get(endpoint1).SetupGet(x => x.Activity).Returns(activityBlueprint2);
Mock.Get(endpoint2).SetupGet(x => x.Activity).Returns(activityBlueprint4);
var result = sut.GetStartActivities(workflowBlueprint).ToArray();
Assert.Equal(new [] { activityBlueprint1, activityBlueprint3 }, result);
}
void SetupActivitiesWithIds(Dictionary<string,IActivityBlueprint> idsToBlueprints)
{
foreach(var kvp in idsToBlueprints)
Mock.Get(kvp.Value).SetupGet(x => x.Id).Returns(kvp.Key);
}
}
}

View file

@ -0,0 +1,31 @@
using System;
using System.Threading.Tasks;
using Elsa.Models;
using Elsa.Services.Models;
using Elsa.Testing.Shared.AutoFixture.Attributes;
using Moq;
using Xunit;
namespace Elsa.Services
{
public class WorkflowExecutionContextForWorkflowBlueprintFactoryTests
{
[Theory(DisplayName = "The CreateWorkflowExecutionContextAsync method returns an execution context using the blueprint, an instance and the service provider"), AutoMoqData]
public async Task CreateWorkflowExecutionContextAsyncReturnsContextWithBlueprintInstanceAndServiceProvider([AutofixtureServiceProvider] IServiceProvider serviceProvider,
IWorkflowFactory workflowFactory,
IWorkflowBlueprint workflowBlueprint,
[OmitOnRecursion] WorkflowInstance instance)
{
var sut = new WorkflowExecutionContextForWorkflowBlueprintFactory(serviceProvider, workflowFactory);
Mock.Get(workflowFactory)
.Setup(x => x.InstantiateAsync(workflowBlueprint, default, default, default))
.Returns(() => Task.FromResult(instance));
var result = await sut.CreateWorkflowExecutionContextAsync(workflowBlueprint);
Assert.Same(serviceProvider, result.ServiceProvider);
Assert.Same(workflowBlueprint, result.WorkflowBlueprint);
Assert.Same(instance, result.WorkflowInstance);
}
}
}

View file

@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Elsa.ActivityProviders;
using Elsa.Bookmarks;
using Elsa.Models;
using Elsa.Services;
using Elsa.Services.Models;
using Elsa.Testing.Shared.AutoFixture.Attributes;
using Moq;
using Xunit;
namespace Elsa.Triggers
{
public class TriggersForActivityBlueprintAndWorkflowProviderTests
{
[Theory(DisplayName = "The GetTriggersForActivityBlueprintAsync returns a trigger for every bookmark in the bookmark providers that support the activity"), AutoMoqData]
public async Task GetTriggersForActivityBlueprintAsyncReturnsTriggersForEachBookmarkInSupportedBookmarkProviders(IBookmarkHasher bookmarkHasher,
IBookmarkProvider bookmarkProvider1,
IBookmarkProvider unsupportedBookmarkProvider,
IBookmarkProvider bookmarkProvider2,
ICreatesActivityExecutionContextForActivityBlueprint activityExecutionContextFactory,
IActivityBlueprint activityBlueprint,
[AutofixtureServiceProvider] IServiceProvider serviceProvider,
IWorkflowBlueprint workflowBlueprint,
[OmitOnRecursion] WorkflowInstance workflowInstance,
ActivityType activityType,
IBookmark bookmark1,
IBookmark bookmark2,
IBookmark bookmark3,
IBookmark bookmark4)
{
var sut = new TriggersForActivityBlueprintAndWorkflowProvider(bookmarkHasher,
new[] { bookmarkProvider1, unsupportedBookmarkProvider, bookmarkProvider2 },
activityExecutionContextFactory);
var workflowExecutionContext = new WorkflowExecutionContext(serviceProvider, workflowBlueprint, workflowInstance);
var activityExecutionContext = new ActivityExecutionContext(serviceProvider, workflowExecutionContext, activityBlueprint, default, default, default);
Mock.Get(activityExecutionContextFactory)
.Setup(x => x.CreateActivityExecutionContext(activityBlueprint, workflowExecutionContext, default))
.Returns(activityExecutionContext);
Mock.Get(activityBlueprint).SetupGet(x => x.Type).Returns(activityType.TypeName);
Mock.Get(bookmarkProvider1)
.Setup(x => x.SupportsActivityAsync(It.Is<BookmarkProviderContext>(c => c.ActivityType == activityType), default))
.Returns(() => ValueTask.FromResult(true));
Mock.Get(unsupportedBookmarkProvider)
.Setup(x => x.SupportsActivityAsync(It.Is<BookmarkProviderContext>(c => c.ActivityType == activityType), default))
.Returns(() => ValueTask.FromResult(false));
Mock.Get(bookmarkProvider2)
.Setup(x => x.SupportsActivityAsync(It.Is<BookmarkProviderContext>(c => c.ActivityType == activityType), default))
.Returns(() => ValueTask.FromResult(true));
Mock.Get(bookmarkProvider1)
.Setup(x => x.GetBookmarksAsync(It.IsAny<BookmarkProviderContext>(), default))
.Returns(() => ValueTask.FromResult<IEnumerable<IBookmark>>(new [] { bookmark1, bookmark2 }));
Mock.Get(bookmarkProvider2)
.Setup(x => x.GetBookmarksAsync(It.IsAny<BookmarkProviderContext>(), default))
.Returns(() => ValueTask.FromResult<IEnumerable<IBookmark>>(new [] { bookmark3, bookmark4 }));
var result = await sut.GetTriggersForActivityBlueprintAsync(activityBlueprint,
workflowExecutionContext,
new Dictionary<string, ActivityType> { { activityType.TypeName, activityType } });
Assert.True(result.Any(x => x.Bookmark == bookmark1), "Result contains a trigger for bookmark 1");
Assert.True(result.Any(x => x.Bookmark == bookmark2), "Result contains a trigger for bookmark 2");
Assert.True(result.Any(x => x.Bookmark == bookmark3), "Result contains a trigger for bookmark 3");
Assert.True(result.Any(x => x.Bookmark == bookmark4), "Result contains a trigger for bookmark 4");
Assert.True(result.Count() == 4, "Result has 4 items");
}
}
}

View file

@ -18,134 +18,6 @@ namespace Elsa.UnitTests.Triggers
{
public class TriggersForBlueprintsProviderTests
{
// [Theory(DisplayName = "The GetTriggersAsync method should return all triggers for the compatible bookmarks of the start activities of the blueprints. This is actually an integration test."), AutoMoqData]
// public async Task GetTriggersAsyncGetsAllTriggersForAllBlueprintsStartActivitiesAndCompatibleBookmarks(IActivityTypeService activityTypeService,
// IBookmarkHasher bookmarkHasher,
// IBookmarkProvider provider1,
// IBookmarkProvider provider2,
// IBookmarkProvider provider3,
// [AutofixtureServiceProvider] IServiceProvider serviceProvider,
// IWorkflowFactory workflowFactory,
// ActivityType activityType1,
// ActivityType activityType2,
// ActivityType activityType3,
// IWorkflowBlueprint workflowBlueprint1,
// IWorkflowBlueprint workflowBlueprint2,
// IActivityBlueprint activityBlueprint1,
// IActivityBlueprint activityBlueprint2,
// IActivityBlueprint activityBlueprint3,
// IActivityBlueprint activityBlueprint4,
// IActivityBlueprint activityBlueprint5,
// IActivityBlueprint activityBlueprint6,
// string activityBlueprintId1,
// string activityBlueprintId2,
// string activityBlueprintId3,
// string activityBlueprintId4,
// string activityBlueprintId5,
// string activityBlueprintId6,
// IConnection connection1,
// IConnection connection2,
// IConnection connection3,
// ITargetEndpoint endpoint1,
// ITargetEndpoint endpoint2,
// ITargetEndpoint endpoint3,
// [OmitOnRecursion] WorkflowInstance workflow1,
// [OmitOnRecursion] WorkflowInstance workflow2,
// IBookmark bookmark1,
// IBookmark bookmark2,
// IBookmark bookmark3,
// IBookmark bookmark4,
// IBookmark bookmark5,
// IBookmark bookmark6,
// string bookmarkHash1,
// string bookmarkHash2,
// string bookmarkHash3,
// string bookmarkHash4,
// string bookmarkHash5,
// string bookmarkHash6)
// {
// var workflowExecutionContextFactory = new WorkflowExecutionContextForWorkflowBlueprintFactory(serviceProvider, workflowFactory);
// var activityExecutionContextFactory = new ActivityExecutionContextForActivityBlueprintFactory(serviceProvider);
// var triggersForActivityProvider = new TriggersForActivityBlueprintAndWorkflowProvider(bookmarkHasher,
// new[] { provider1, provider2, provider3 },
// activityExecutionContextFactory);
// var startingActivitiesProvider = new StartActivitiesForCompositeActivityBlueprintProvider();
// var sut = new TriggersForBlueprintsProvider(activityTypeService,
// workflowExecutionContextFactory,
// triggersForActivityProvider,
// startingActivitiesProvider);
// Mock.Get(workflowBlueprint1).SetupGet(x => x.Connections).Returns(new [] { connection1 });
// Mock.Get(workflowBlueprint2).SetupGet(x => x.Connections).Returns(new [] { connection2, connection3 });
// Mock.Get(workflowBlueprint1).SetupGet(x => x.Activities).Returns(new [] { activityBlueprint1, activityBlueprint2, activityBlueprint4 });
// Mock.Get(workflowBlueprint2).SetupGet(x => x.Activities).Returns(new [] { activityBlueprint3, activityBlueprint5, activityBlueprint6 });
// Mock.Get(workflowFactory)
// .Setup(x => x.InstantiateAsync(workflowBlueprint1, null, null, default))
// .Returns(Task.FromResult(workflow1));
// Mock.Get(workflowFactory)
// .Setup(x => x.InstantiateAsync(workflowBlueprint2, null, null, default))
// .Returns(Task.FromResult(workflow2));
// Mock.Get(connection1).SetupGet(x => x.Target).Returns(endpoint1);
// Mock.Get(connection2).SetupGet(x => x.Target).Returns(endpoint2);
// Mock.Get(connection3).SetupGet(x => x.Target).Returns(endpoint3);
// Mock.Get(endpoint1).SetupGet(x => x.Activity).Returns(activityBlueprint4);
// Mock.Get(endpoint2).SetupGet(x => x.Activity).Returns(activityBlueprint5);
// Mock.Get(endpoint3).SetupGet(x => x.Activity).Returns(activityBlueprint6);
// Mock.Get(activityBlueprint1).SetupGet(x => x.Type).Returns(activityType1.TypeName);
// Mock.Get(activityBlueprint2).SetupGet(x => x.Type).Returns(activityType2.TypeName);
// Mock.Get(activityBlueprint3).SetupGet(x => x.Type).Returns(activityType3.TypeName);
// Mock.Get(activityBlueprint1).SetupGet(x => x.Id).Returns(activityBlueprintId1);
// Mock.Get(activityBlueprint2).SetupGet(x => x.Id).Returns(activityBlueprintId2);
// Mock.Get(activityBlueprint3).SetupGet(x => x.Id).Returns(activityBlueprintId3);
// Mock.Get(activityBlueprint4).SetupGet(x => x.Id).Returns(activityBlueprintId4);
// Mock.Get(activityBlueprint5).SetupGet(x => x.Id).Returns(activityBlueprintId5);
// Mock.Get(activityBlueprint6).SetupGet(x => x.Id).Returns(activityBlueprintId6);
// SetupProviderToBeCompatibleWithActivityType(provider1, activityType1);
// SetupProviderToBeCompatibleWithActivityType(provider2, activityType2);
// SetupProviderToBeCompatibleWithActivityType(provider3, activityType3);
// Mock.Get(provider1)
// .Setup(x => x.GetBookmarksAsync(It.IsAny<BookmarkProviderContext>(), default))
// .Returns(ValueTask.FromResult<IEnumerable<IBookmark>>(new [] { bookmark1, bookmark2 }));
// Mock.Get(provider2)
// .Setup(x => x.GetBookmarksAsync(It.IsAny<BookmarkProviderContext>(), default))
// .Returns(ValueTask.FromResult<IEnumerable<IBookmark>>(new [] { bookmark3, bookmark4 }));
// Mock.Get(provider3)
// .Setup(x => x.GetBookmarksAsync(It.IsAny<BookmarkProviderContext>(), default))
// .Returns(ValueTask.FromResult<IEnumerable<IBookmark>>(new [] { bookmark5, bookmark6 }));
// Mock.Get(bookmarkHasher).Setup(x => x.Hash(bookmark1)).Returns(bookmarkHash1);
// Mock.Get(bookmarkHasher).Setup(x => x.Hash(bookmark2)).Returns(bookmarkHash2);
// Mock.Get(bookmarkHasher).Setup(x => x.Hash(bookmark3)).Returns(bookmarkHash3);
// Mock.Get(bookmarkHasher).Setup(x => x.Hash(bookmark4)).Returns(bookmarkHash4);
// Mock.Get(bookmarkHasher).Setup(x => x.Hash(bookmark5)).Returns(bookmarkHash5);
// Mock.Get(bookmarkHasher).Setup(x => x.Hash(bookmark6)).Returns(bookmarkHash6);
// var result = await sut.GetTriggersAsync(new [] { workflowBlueprint1, workflowBlueprint2 });
// Assert.Equal(6, result.Count());
// Assert.True(ContainsTriggerWithHash(result, bookmarkHash1)
// && ContainsTriggerWithHash(result, bookmarkHash2)
// && ContainsTriggerWithHash(result, bookmarkHash3)
// && ContainsTriggerWithHash(result, bookmarkHash4)
// && ContainsTriggerWithHash(result, bookmarkHash5)
// && ContainsTriggerWithHash(result, bookmarkHash6),
// "The returned triggers should contain a trigger with every one of the six expected hashes");
// }
// void SetupProviderToBeCompatibleWithActivityType(IBookmarkProvider provider, ActivityType activityType)
// {
// Mock.Get(provider)
// .Setup(x => x.SupportsActivityAsync(It.IsAny<BookmarkProviderContext>(), default))
// .Returns(ValueTask.FromResult(false));
// Mock.Get(provider)
// .Setup(x => x.SupportsActivityAsync(It.Is<BookmarkProviderContext>(c => c.ActivityType == activityType), default))
// .Returns(ValueTask.FromResult(true));
// }
// bool ContainsTriggerWithHash(IEnumerable<WorkflowTrigger> triggers, string hash)
// => triggers.Any(x => Equals(x.BookmarkHash, hash));
[Theory(DisplayName = "The GetTriggersAsync method should return all triggers for start activities of the workflow blueprints"), AutoMoqData]
public async Task GetTriggersAsyncGetsAllTriggersForAllBlueprintsStartActivitiesAndCompatibleBookmarks([Frozen] IActivityTypeService activityTypeService,
[Frozen] ICreatesWorkflowExecutionContextForWorkflowBlueprint workflowExecutionContextFactory,