diff --git a/tests/Trax.Effect.Tests.Integration/UnitTests/Attributes/InjectAttributeTests.cs b/tests/Trax.Effect.Tests.Integration/UnitTests/Attributes/InjectAttributeTests.cs new file mode 100644 index 0000000..26fff2e --- /dev/null +++ b/tests/Trax.Effect.Tests.Integration/UnitTests/Attributes/InjectAttributeTests.cs @@ -0,0 +1,82 @@ +using System.Reflection; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Trax.Effect.Attributes; +using Trax.Effect.Extensions; + +namespace Trax.Effect.Tests.Integration.UnitTests.Attributes; + +[TestFixture] +public class InjectAttributeTests +{ + [Test] + public void InjectAttribute_CanBeAppliedToProperty() + { + // Arrange & Act + var property = typeof(TestInjectable).GetProperty(nameof(TestInjectable.InjectedService)); + var attribute = property?.GetCustomAttribute(); + + // Assert + attribute.Should().NotBeNull(); + } + + [Test] + public void InjectAttribute_TargetsPropertyOnly() + { + // Arrange & Act + var attributeUsage = typeof(InjectAttribute).GetCustomAttribute(); + + // Assert + attributeUsage.Should().NotBeNull(); + attributeUsage!.ValidOn.Should().Be(AttributeTargets.Property); + } + + [Test] + public void InjectProperties_SetsMarkedProperties() + { + // Arrange + var services = new ServiceCollection(); + services.AddSingleton(new TestService()); + using var provider = services.BuildServiceProvider(); + var instance = new TestInjectable(); + + // Act + provider.InjectProperties(instance); + + // Assert + instance.InjectedService.Should().NotBeNull(); + instance.InjectedService.Should().BeOfType(); + } + + [Test] + public void InjectProperties_SkipsNonMarkedProperties() + { + // Arrange + var services = new ServiceCollection(); + services.AddSingleton(new TestService()); + using var provider = services.BuildServiceProvider(); + var instance = new TestInjectable(); + + // Act + provider.InjectProperties(instance); + + // Assert + instance.NonInjectedService.Should().BeNull(); + } + + #region Test helpers + + private interface ITestService { } + + private class TestService : ITestService { } + + private class TestInjectable + { + [Inject] + public ITestService? InjectedService { get; set; } + + public ITestService? NonInjectedService { get; set; } + } + + #endregion +} diff --git a/tests/Trax.Effect.Tests.Integration/UnitTests/Configuration/TraxEffectConfigurationBuilderTests.cs b/tests/Trax.Effect.Tests.Integration/UnitTests/Configuration/TraxEffectConfigurationBuilderTests.cs new file mode 100644 index 0000000..f7cf99a --- /dev/null +++ b/tests/Trax.Effect.Tests.Integration/UnitTests/Configuration/TraxEffectConfigurationBuilderTests.cs @@ -0,0 +1,71 @@ +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Trax.Effect.Configuration.TraxEffectBuilder; + +namespace Trax.Effect.Tests.Integration.UnitTests.Configuration; + +[TestFixture] +public class TraxEffectConfigurationBuilderTests +{ + [Test] + public void Constructor_DefaultValues_AreCorrect() + { + // Arrange & Act + var builder = new TraxEffectConfigurationBuilder(new ServiceCollection()); + + // Assert + builder.DataContextLoggingEffectEnabled.Should().BeFalse(); + builder.SerializeStepData.Should().BeFalse(); + builder.LogLevel.Should().Be(LogLevel.Debug); + } + + [Test] + public void ServiceCollection_ExposedFromConstructor() + { + // Arrange + var services = new ServiceCollection(); + + // Act + var builder = new TraxEffectConfigurationBuilder(services); + + // Assert + builder.ServiceCollection.Should().BeSameAs(services); + } + + [Test] + public void SetEffectLogLevel_UpdatesLogLevel() + { + // Arrange + var builder = new TraxEffectConfigurationBuilder(new ServiceCollection()); + + // Act + builder.LogLevel = LogLevel.Trace; + + // Assert + builder.LogLevel.Should().Be(LogLevel.Trace); + } + + [Test] + public void SerializeStepData_SetTrue_IsReflected() + { + // Arrange + var builder = new TraxEffectConfigurationBuilder(new ServiceCollection()); + + // Act + builder.SerializeStepData = true; + + // Assert + builder.SerializeStepData.Should().BeTrue(); + } + + [Test] + public void TrainParameterJsonSerializerOptions_DefaultIsNotNull() + { + // Arrange & Act + var builder = new TraxEffectConfigurationBuilder(new ServiceCollection()); + + // Assert + builder.TrainParameterJsonSerializerOptions.Should().NotBeNull(); + } +} diff --git a/tests/Trax.Effect.Tests.Integration/UnitTests/Extensions/ServiceExtensionsRegistrationTests.cs b/tests/Trax.Effect.Tests.Integration/UnitTests/Extensions/ServiceExtensionsRegistrationTests.cs new file mode 100644 index 0000000..b30eb0b --- /dev/null +++ b/tests/Trax.Effect.Tests.Integration/UnitTests/Extensions/ServiceExtensionsRegistrationTests.cs @@ -0,0 +1,127 @@ +using FluentAssertions; +using LanguageExt; +using Microsoft.Extensions.DependencyInjection; +using Trax.Core.Route; +using Trax.Core.Step; +using Trax.Effect.Extensions; + +namespace Trax.Effect.Tests.Integration.UnitTests.Extensions; + +[TestFixture] +public class ServiceExtensionsRegistrationTests +{ + [Test] + public void AddScopedTraxStep_Resolves_ViaInterface() + { + // Arrange + var services = new ServiceCollection(); + services.AddScopedTraxStep(); + using var provider = services.BuildServiceProvider(); + + // Act + var step = provider.GetService(); + + // Assert + step.Should().NotBeNull(); + step.Should().BeOfType(); + } + + [Test] + public void AddTransientTraxStep_Resolves_ViaInterface() + { + // Arrange + var services = new ServiceCollection(); + services.AddTransientTraxStep(); + using var provider = services.BuildServiceProvider(); + + // Act + var step = provider.GetService(); + + // Assert + step.Should().NotBeNull(); + step.Should().BeOfType(); + } + + [Test] + public void AddSingletonTraxStep_Resolves_ViaInterface() + { + // Arrange + var services = new ServiceCollection(); + services.AddSingletonTraxStep(); + using var provider = services.BuildServiceProvider(); + + // Act + var step = provider.GetService(); + + // Assert + step.Should().NotBeNull(); + step.Should().BeOfType(); + } + + [Test] + public void AddScopedTraxRoute_Resolves_ViaInterface() + { + // Arrange + var services = new ServiceCollection(); + services.AddScopedTraxRoute(); + using var provider = services.BuildServiceProvider(); + + // Act + var route = provider.GetService(); + + // Assert + route.Should().NotBeNull(); + route.Should().BeOfType(); + } + + [Test] + public void AddTransientTraxRoute_Resolves_ViaInterface() + { + // Arrange + var services = new ServiceCollection(); + services.AddTransientTraxRoute(); + using var provider = services.BuildServiceProvider(); + + // Act + var route = provider.GetService(); + + // Assert + route.Should().NotBeNull(); + route.Should().BeOfType(); + } + + [Test] + public void AddSingletonTraxRoute_Resolves_ViaInterface() + { + // Arrange + var services = new ServiceCollection(); + services.AddSingletonTraxRoute(); + using var provider = services.BuildServiceProvider(); + + // Act + var route = provider.GetService(); + + // Assert + route.Should().NotBeNull(); + route.Should().BeOfType(); + } + + #region Test helpers + + public interface IFakeStep : IStep { } + + public class FakeStep : Step, IFakeStep + { + public override Task Run(string input) => Task.FromResult(input.Length); + } + + public interface IFakeRoute : IRoute { } + + public class FakeRoute : IFakeRoute + { + public Task Run(string input, CancellationToken cancellationToken = default) => + Task.FromResult(input.Length); + } + + #endregion +} diff --git a/tests/Trax.Effect.Tests.Integration/UnitTests/Utils/DisposableConverterTests.cs b/tests/Trax.Effect.Tests.Integration/UnitTests/Utils/DisposableConverterTests.cs new file mode 100644 index 0000000..89210e7 --- /dev/null +++ b/tests/Trax.Effect.Tests.Integration/UnitTests/Utils/DisposableConverterTests.cs @@ -0,0 +1,50 @@ +using System.Text.Json; +using FluentAssertions; +using Trax.Effect.Utils; + +namespace Trax.Effect.Tests.Integration.UnitTests.Utils; + +[TestFixture] +public class DisposableConverterTests +{ + [Test] + public void CanConvert_DisposableType_ReturnsTrue() + { + // Arrange + var converter = new DisposableConverter(); + + // Act + var result = converter.CanConvert(typeof(MemoryStream)); + + // Assert + result.Should().BeTrue(); + } + + [Test] + public void CanConvert_NonDisposableType_ReturnsFalse() + { + // Arrange + var converter = new DisposableConverter(); + + // Act + var result = converter.CanConvert(typeof(string)); + + // Assert + result.Should().BeFalse(); + } + + [Test] + public void Write_DisposableObject_WritesPlaceholder() + { + // Arrange + var options = new JsonSerializerOptions(); + options.Converters.Add(new DisposableConverter()); + using var stream = new MemoryStream(); + + // Act + var json = JsonSerializer.Serialize(stream, options); + + // Assert + json.Should().Contain("IDisposable"); + } +} diff --git a/tests/Trax.Effect.Tests.Integration/UnitTests/Utils/ValueTupleConverterTests.cs b/tests/Trax.Effect.Tests.Integration/UnitTests/Utils/ValueTupleConverterTests.cs new file mode 100644 index 0000000..07cdf29 --- /dev/null +++ b/tests/Trax.Effect.Tests.Integration/UnitTests/Utils/ValueTupleConverterTests.cs @@ -0,0 +1,83 @@ +using System.Text.Json; +using FluentAssertions; +using Trax.Effect.Utils; + +namespace Trax.Effect.Tests.Integration.UnitTests.Utils; + +[TestFixture] +public class ValueTupleConverterTests +{ + private JsonSerializerOptions _options; + + [SetUp] + public void SetUp() + { + _options = new JsonSerializerOptions(); + _options.Converters.Add(new ValueTupleConverter()); + } + + [Test] + public void CanConvert_ValueTupleType_ReturnsTrue() + { + // Arrange + var converter = new ValueTupleConverter(); + + // Act + var result = converter.CanConvert(typeof((int, string))); + + // Assert + result.Should().BeTrue(); + } + + [Test] + public void CanConvert_NonTupleType_ReturnsFalse() + { + // Arrange + var converter = new ValueTupleConverter(); + + // Act + var result = converter.CanConvert(typeof(int)); + + // Assert + result.Should().BeFalse(); + } + + [Test] + public void Serialize_Tuple2_ProducesJsonArray() + { + // Arrange + var tuple = (1, "hello"); + + // Act + var json = JsonSerializer.Serialize(tuple, _options); + + // Assert + json.Should().Be("[1,\"hello\"]"); + } + + [Test] + public void Serialize_Tuple3_ProducesJsonArray() + { + // Arrange + var tuple = (1, 2, 3.0); + + // Act + var json = JsonSerializer.Serialize(tuple, _options); + + // Assert + json.Should().Be("[1,2,3]"); + } + + [Test] + public void CanConvert_NestedTupleType_ReturnsTrue() + { + // Arrange + var converter = new ValueTupleConverter(); + + // Act + var result = converter.CanConvert(typeof((int, int, int))); + + // Assert + result.Should().BeTrue(); + } +}