elsa-core/test/unit/Elsa.Workflows.Management.UnitTests/Services/MaterializerRegistryTests.cs
Sipke Schoorstra ca88051573
Improves workflow materializer handling (#7195)
* Add tenant headers support to BackgroundWorkflowCancellationDispatcher (#7040)

* Add tenant headers support to BackgroundWorkflowCancellationDispatcher

* Fix 'CreateHeaders' call

* Fix memory leak: Dispose IronCompressResult in Zstd codec (#7193)

* Initial plan

* Fix memory leak: Dispose IronCompressResult in Zstd codec and add tests

Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com>

* Refactor tests to be more DRY using Theory and InlineData

Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com>

* Introduce `IMaterializerRegistry` to manage workflow materializers and ensure availability checks.

* Extend `IWorkflowDefinitionService` and `CachingWorkflowDefinitionService` with workflow graph lookup methods (`TryFindWorkflowGraphAsync`). Refactor caching and materialization logic for consistency.

* Refactor caching interface and implementation: add `FindOrCreateAsync`, update `GetOrCreateAsync` to ensure non-null results, and improve exception handling.

* Refactor `GetWorkflowGraphAsync` to use `TryFindWorkflowGraphAsync` and improve exception handling for missing workflow definitions and materializers.

* Refactor caching logic to replace `GetOrCreateAsync` with `FindOrCreateAsync` for improved clarity and consistency.

* Update workflow model, add event, and mark exception obsolete

Updated `TimestampFilter.Column` to use a `null!` default value for clarity. Added `Event1` in the `hello-world.elsa` workflow and removed an unused folder entry from the project. Marked `WorkflowGraphNotFoundException` as obsolete with guidance to use `WorkflowDefinitionNotFoundException` instead.

* Add new workflow files and exception classes for Elsa

Introduced a workflow definition file "eventing.json" and new exception classes (`WorkflowDefinitionNotFoundException` and `WorkflowMaterializerNotFoundException`) to enhance handling of workflow-related errors. Also added a `WorkflowGraphFindResult` model for better workflow graph management. These changes improve the structure and functionality of the workflow system.

* Add unit tests for `CachingWorkflowDefinitionService` and related helpers

Introduce comprehensive unit tests to validate caching logic, workflow graph/materialization behavior, and cache key generation in `CachingWorkflowDefinitionService`. Add `WorkflowDefinitionServiceTests` and helper methods for streamlined test setup.

* Enable `UseElsaScriptBlobStorage` in workflow server configuration

* Refactor `BackgroundWorkflowCancellationDispatcher` to simplify object initialization and clean up XML documentation comments

* Address PR #7195 review feedback: optimize caching, improve exceptions, add test coverage (#7196)

* Initial plan

* Apply PR review feedback: Fix exceptions, optimize caching, improve error handling

Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com>

* Add unit tests for MaterializerRegistry and LocalWorkflowClient exception handling

Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com>

* Add unit tests for BackgroundWorkflowCancellationDispatcher tenant headers

Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com>

* Refactor `WorkflowMaterializerNotFoundException` to improve structure and usability, update related references, and simplify object initialization in test cases.

* Update `WorkflowDefinitionServiceTests` to use `WorkflowMaterializerNotFoundException` in place of `InvalidOperationException` for materializer not found scenario

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com>
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>

* Potential fix for pull request finding 'Inefficient use of ContainsKey'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* Refactor tests and services: simplify object initialization, use target-typed `new()` syntax, and replace `CancellationToken` with `CancellationToken.None` where applicable.

* Refactor tests in `BackgroundWorkflowCancellationDispatcherTests`: improve tenant initialization and optimize header checks by replacing `TryGetValue` with `ContainsKey`.

---------

Co-authored-by: Sverre Winkelmans <69142682+Sverre-W@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-01-19 08:59:12 +01:00

180 lines
5.4 KiB
C#

using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Services;
using NSubstitute;
namespace Elsa.Workflows.Management.UnitTests.Services;
public class MaterializerRegistryTests
{
[Fact]
public void GetMaterializers_Should_Return_All_Materializers()
{
// Arrange
var materializer1 = CreateMaterializer("materializer1");
var materializer2 = CreateMaterializer("materializer2");
var registry = CreateRegistry(materializer1, materializer2);
// Act
var result = registry.GetMaterializers().ToList();
// Assert
Assert.Equal(2, result.Count);
Assert.Contains(result, m => m.Name == "materializer1");
Assert.Contains(result, m => m.Name == "materializer2");
}
[Fact]
public void GetMaterializer_Should_Return_Materializer_By_Name()
{
// Arrange
var materializer1 = CreateMaterializer("test-materializer");
var materializer2 = CreateMaterializer("other-materializer");
var registry = CreateRegistry(materializer1, materializer2);
// Act
var result = registry.GetMaterializer("test-materializer");
// Assert
Assert.NotNull(result);
Assert.Equal("test-materializer", result.Name);
}
[Theory]
[InlineData("non-existent")]
[InlineData("")]
[InlineData("wrong-name")]
public void GetMaterializer_Should_Return_Null_When_Not_Found(string name)
{
// Arrange
var materializer = CreateMaterializer("existing-materializer");
var registry = CreateRegistry(materializer);
// Act
var result = registry.GetMaterializer(name);
// Assert
Assert.Null(result);
}
[Fact]
public void IsMaterializerAvailable_Should_Return_True_When_Materializer_Exists()
{
// Arrange
var materializer = CreateMaterializer("available-materializer");
var registry = CreateRegistry(materializer);
// Act
var result = registry.IsMaterializerAvailable("available-materializer");
// Assert
Assert.True(result);
}
[Theory]
[InlineData("not-available")]
[InlineData("")]
[InlineData("wrong-name")]
public void IsMaterializerAvailable_Should_Return_False_When_Materializer_Does_Not_Exist(string name)
{
// Arrange
var materializer = CreateMaterializer("existing-materializer");
var registry = CreateRegistry(materializer);
// Act
var result = registry.IsMaterializerAvailable(name);
// Assert
Assert.False(result);
}
[Fact]
public void Should_Handle_Empty_Materializer_Collection()
{
// Arrange
var registry = CreateRegistry();
// Act
var allMaterializers = registry.GetMaterializers().ToList();
var foundMaterializer = registry.GetMaterializer("any-name");
var isAvailable = registry.IsMaterializerAvailable("any-name");
// Assert
Assert.Empty(allMaterializers);
Assert.Null(foundMaterializer);
Assert.False(isAvailable);
}
[Fact]
public void Should_Cache_Materializers_On_First_Access()
{
// Arrange
var callCount = 0;
IEnumerable<IWorkflowMaterializer> MaterializersFactory()
{
callCount++;
return new[] { CreateMaterializer("test") };
}
var registry = new MaterializerRegistry(MaterializersFactory);
// Act
var first = registry.GetMaterializers().ToList();
var second = registry.GetMaterializers().ToList();
var third = registry.GetMaterializer("test");
var fourth = registry.IsMaterializerAvailable("test");
// Assert
Assert.Single(first);
Assert.Single(second);
Assert.NotNull(third);
Assert.True(fourth);
Assert.Equal(1, callCount); // Factory should only be called once
}
[Fact]
public void GetMaterializers_Should_Allow_Multiple_Enumerations()
{
// Arrange
var materializer1 = CreateMaterializer("mat1");
var materializer2 = CreateMaterializer("mat2");
var registry = CreateRegistry(materializer1, materializer2);
// Act
var first = registry.GetMaterializers().ToList();
var second = registry.GetMaterializers().ToList();
// Assert
Assert.Equal(2, first.Count);
Assert.Equal(2, second.Count);
Assert.Equal(first.Count, second.Count);
}
[Fact]
public void GetMaterializer_Should_Return_First_When_Multiple_Materializers_With_Same_Name()
{
// Arrange - FirstOrDefault returns the first match
var materializer1 = CreateMaterializer("duplicate-name");
var materializer2 = CreateMaterializer("duplicate-name");
var registry = CreateRegistry(materializer1, materializer2);
// Act
var result = registry.GetMaterializer("duplicate-name");
// Assert
Assert.NotNull(result);
Assert.Same(materializer1, result); // FirstOrDefault returns the first match
}
private static IWorkflowMaterializer CreateMaterializer(string name)
{
var materializer = Substitute.For<IWorkflowMaterializer>();
materializer.Name.Returns(name);
return materializer;
}
private static MaterializerRegistry CreateRegistry(params IWorkflowMaterializer[] materializers)
{
return new(() => materializers);
}
}