From 8ea964cd2dbdc8244e97d5606b9f88b32d57dc61 Mon Sep 17 00:00:00 2001 From: fa0xh1 Date: Tue, 16 Jul 2024 15:09:34 +0700 Subject: [PATCH] Adding Unit Test --- .../LocalDatabase.db-shm | Bin 0 -> 32768 bytes .../LocalDatabase.db-wal | Bin 0 -> 131872 bytes .../Application.UnitTests.csproj | 30 ++++++++++ .../Common/Behaviours/RequestLoggerTests.cs | 45 ++++++++++++++ .../Exceptions/ValidationExceptionTests.cs | 55 ++++++++++++++++++ .../Common/Mappings/MappingTests.cs | 53 +++++++++++++++++ tests/Application.UnitTests/UnitTest1.cs | 16 +++++ 7 files changed, 199 insertions(+) create mode 100644 src/MiniSkeletonAPI.Presentation/LocalDatabase.db-shm create mode 100644 src/MiniSkeletonAPI.Presentation/LocalDatabase.db-wal create mode 100644 tests/Application.UnitTests/Application.UnitTests.csproj create mode 100644 tests/Application.UnitTests/Common/Behaviours/RequestLoggerTests.cs create mode 100644 tests/Application.UnitTests/Common/Exceptions/ValidationExceptionTests.cs create mode 100644 tests/Application.UnitTests/Common/Mappings/MappingTests.cs create mode 100644 tests/Application.UnitTests/UnitTest1.cs diff --git a/src/MiniSkeletonAPI.Presentation/LocalDatabase.db-shm b/src/MiniSkeletonAPI.Presentation/LocalDatabase.db-shm new file mode 100644 index 0000000000000000000000000000000000000000..7cc364fb1e96c2ec5f4374a10c3703c27fd3989e GIT binary patch literal 32768 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 RoZFihwm6%OSmWHm{{s+T-_rm9 literal 0 HcmV?d00001 diff --git a/tests/Application.UnitTests/Application.UnitTests.csproj b/tests/Application.UnitTests/Application.UnitTests.csproj new file mode 100644 index 0000000..581c183 --- /dev/null +++ b/tests/Application.UnitTests/Application.UnitTests.csproj @@ -0,0 +1,30 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Application.UnitTests/Common/Behaviours/RequestLoggerTests.cs b/tests/Application.UnitTests/Common/Behaviours/RequestLoggerTests.cs new file mode 100644 index 0000000..c641f7e --- /dev/null +++ b/tests/Application.UnitTests/Common/Behaviours/RequestLoggerTests.cs @@ -0,0 +1,45 @@ +using MiniSkeletonAPI.Application.Common.Behaviours; +using MiniSkeletonAPI.Application.Common.Interfaces; +using MiniSkeletonAPI.Application.TodoItems.Commands.CreateTodoItem; +using Microsoft.Extensions.Logging; +using Moq; +using NUnit.Framework; + +namespace Application.UnitTests.Common.Behaviours; + +public class RequestLoggerTests +{ + private Mock> _logger = null!; + private Mock _user = null!; + private Mock _identityService = null!; + + [SetUp] + public void Setup() + { + _logger = new Mock>(); + _user = new Mock(); + _identityService = new Mock(); + } + + [Test] + public async Task ShouldCallGetUserNameAsyncOnceIfAuthenticated() + { + _user.Setup(x => x.Id).Returns(Guid.NewGuid().ToString()); + + var requestLogger = new LoggingBehaviour(_logger.Object, _user.Object, _identityService.Object); + + await requestLogger.Process(new CreateTodoItemCommand { ListId = new Guid(), Title = "title" }, new CancellationToken()); + + _identityService.Verify(i => i.GetUserNameAsync(It.IsAny()), Times.Once); + } + + [Test] + public async Task ShouldNotCallGetUserNameAsyncOnceIfUnauthenticated() + { + var requestLogger = new LoggingBehaviour(_logger.Object, _user.Object, _identityService.Object); + + await requestLogger.Process(new CreateTodoItemCommand { ListId = new Guid(), Title = "title" }, new CancellationToken()); + + _identityService.Verify(i => i.GetUserNameAsync(It.IsAny()), Times.Never); + } +} diff --git a/tests/Application.UnitTests/Common/Exceptions/ValidationExceptionTests.cs b/tests/Application.UnitTests/Common/Exceptions/ValidationExceptionTests.cs new file mode 100644 index 0000000..ace0090 --- /dev/null +++ b/tests/Application.UnitTests/Common/Exceptions/ValidationExceptionTests.cs @@ -0,0 +1,55 @@ +using MiniSkeletonAPI.Application.Common.Exceptions; +using FluentAssertions; +using FluentValidation.Results; +using NUnit.Framework; + +namespace Application.UnitTests.Common.Exceptions; + +public class ValidationExceptionTests +{ + [Test] + public void DefaultConstructorCreatesAnEmptyErrorDictionary() + { + var actual = new ValidationException().Errors; + + actual.Keys.Should().BeEquivalentTo(Array.Empty()); + } + + [Test] + public void SingleValidationFailureCreatesASingleElementErrorDictionary() + { + var failures = new List + { + new ValidationFailure("Password", "must contain at least 8 characters"), + }; + + var actual = new ValidationException(failures).Errors; + + actual.Keys.Should().BeEquivalentTo(new string[] { "Password" }); + actual["Password"].Should().BeEquivalentTo(new string[] { "must contain at least 8 characters" }); + } + + [Test] + public void MulitpleValidationFailureForMultiplePropertiesCreatesAMultipleElementErrorDictionaryEachWithMultipleValues() + { + var failures = new List + { + new ValidationFailure("Password", "must contain at least 8 characters"), + new ValidationFailure("Password", "must contain a digit"), + new ValidationFailure("Password", "must contain upper case letter"), + new ValidationFailure("Password", "must contain lower case letter"), + }; + + var actual = new ValidationException(failures).Errors; + + actual.Keys.Should().BeEquivalentTo(new string[] { "Password" }); + + actual["Password"].Should().BeEquivalentTo(new string[] + { + "must contain lower case letter", + "must contain upper case letter", + "must contain at least 8 characters", + "must contain a digit", + }); + } +} diff --git a/tests/Application.UnitTests/Common/Mappings/MappingTests.cs b/tests/Application.UnitTests/Common/Mappings/MappingTests.cs new file mode 100644 index 0000000..36e0400 --- /dev/null +++ b/tests/Application.UnitTests/Common/Mappings/MappingTests.cs @@ -0,0 +1,53 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using AutoMapper; +using MiniSkeletonAPI.Application.Common.Interfaces; +using MiniSkeletonAPI.Application.Common.Models; +using MiniSkeletonAPI.Application.TodoItems.Queries.GetTodoItemsWithPagination; +using MiniSkeletonAPI.Application.TodoLists.Queries.GetTodos; +using MiniSkeletonAPI.Domain.Entities; +using NUnit.Framework; + +namespace Application.UnitTests.Common.Mappings; + +public class MappingTests +{ + private readonly IConfigurationProvider _configuration; + private readonly IMapper _mapper; + + public MappingTests() + { + _configuration = new MapperConfiguration(config => + config.AddMaps(Assembly.GetAssembly(typeof(IApplicationDbContext)))); + + _mapper = _configuration.CreateMapper(); + } + + [Test] + public void ShouldHaveValidConfiguration() + { + _configuration.AssertConfigurationIsValid(); + } + + [Test] + [TestCase(typeof(TodoList), typeof(TodoListDto))] + [TestCase(typeof(TodoItem), typeof(TodoItemDto))] + [TestCase(typeof(TodoList), typeof(LookupDto))] + [TestCase(typeof(TodoItem), typeof(LookupDto))] + [TestCase(typeof(TodoItem), typeof(TodoItemBriefDto))] + public void ShouldSupportMappingFromSourceToDestination(Type source, Type destination) + { + var instance = GetInstanceOf(source); + + _mapper.Map(instance, source, destination); + } + + private object GetInstanceOf(Type type) + { + if (type.GetConstructor(Type.EmptyTypes) != null) + return Activator.CreateInstance(type)!; + + // Type without parameterless constructor + return RuntimeHelpers.GetUninitializedObject(type); + } +} diff --git a/tests/Application.UnitTests/UnitTest1.cs b/tests/Application.UnitTests/UnitTest1.cs new file mode 100644 index 0000000..f582c5f --- /dev/null +++ b/tests/Application.UnitTests/UnitTest1.cs @@ -0,0 +1,16 @@ +namespace Application.UnitTests +{ + public class Tests + { + [SetUp] + public void Setup() + { + } + + [Test] + public void Test1() + { + Assert.Pass(); + } + } +} \ No newline at end of file