diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/StreetCode/Text/GetAllTextsHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/StreetCode/Text/GetAllTextsHandlerTests.cs new file mode 100644 index 0000000..753b913 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/StreetCode/Text/GetAllTextsHandlerTests.cs @@ -0,0 +1,149 @@ +// +// Copyright (c) PlaceholderCompany. All rights reserved. +// + +namespace Streetcode.XUnitTest.BLL.MediatR.StreetCode.Text +{ + using System.Linq.Expressions; + using AutoMapper; + using FluentAssertions; + using Microsoft.EntityFrameworkCore.Query; + using Moq; + using Streetcode.BLL.DTO.Streetcode.TextContent.Text; + using Streetcode.BLL.Interfaces.Logging; + using Streetcode.BLL.Mapping.Streetcode.TextContent; + using Streetcode.BLL.MediatR.Streetcode.Text.GetAll; + using Streetcode.DAL.Repositories.Interfaces.Base; + using Streetcode.DAL.Repositories.Interfaces.Streetcode.TextContent; + using Xunit; + using TextEntity = Streetcode.DAL.Entities.Streetcode.TextContent.Text; + + /// + /// Unit tests for . + /// + public class GetAllTextsHandlerTests + { + private readonly Mock repositoryWrapperMock; + private readonly Mock textRepositoryMock; + private readonly IMapper mapper; + private readonly Mock loggerMock; + private readonly GetAllTextsHandler handler; + + /// + /// Initializes a new instance of the class. + /// + public GetAllTextsHandlerTests() + { + this.repositoryWrapperMock = new Mock(); + this.textRepositoryMock = new Mock(); + this.mapper = new MapperConfiguration(cfg => cfg.AddProfile()).CreateMapper(); + this.loggerMock = new Mock(); + + this.repositoryWrapperMock + .Setup(w => w.TextRepository) + .Returns(this.textRepositoryMock.Object); + + this.handler = new GetAllTextsHandler( + this.repositoryWrapperMock.Object, + this.mapper, + this.loggerMock.Object); + } + + /// + /// Tests that the Handle method returns all texts when they exist in the repository. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task Handle_ReturnsAllTexts_WhenTextsExist() + { + // Arrange + var query = new GetAllTextsQuery(); + + var texts = new List + { + new TextEntity { Id = 1, Title = "Title 1", TextContent = "Content 1", StreetcodeId = 1 }, + new TextEntity { Id = 2, Title = "Title 2", TextContent = "Content 2", StreetcodeId = 2 }, + }; + + var textDtos = new List + { + new TextDTO { Id = 1, Title = "Title 1", TextContent = "Content 1", StreetcodeId = 1 }, + new TextDTO { Id = 2, Title = "Title 2", TextContent = "Content 2", StreetcodeId = 2 }, + }; + + this.textRepositoryMock + .Setup(repo => repo.GetAllAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>?>())) + .ReturnsAsync(texts); + + // Act + var result = await this.handler.Handle(query, CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value.Should().BeEquivalentTo(textDtos); + + this.loggerMock.Verify( + l => l.LogError(It.IsAny(), It.IsAny()), + Times.Never); + } + + /// + /// Tests that the Handle method returns an error when the repository returns null. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task Handle_ReturnsError_WhenRepositoryReturnsNull() + { + // Arrange + var query = new GetAllTextsQuery(); + + this.textRepositoryMock + .Setup(repo => repo.GetAllAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>?>())) + .ReturnsAsync((IEnumerable)null!); + + // Act + var result = await this.handler.Handle(query, CancellationToken.None); + + // Assert + result.IsFailed.Should().BeTrue(); + result.Errors[0].Message.Should().Be("Cannot find any text"); + + this.loggerMock.Verify( + l => l.LogError(query, "Cannot find any text"), + Times.Once); + } + + /// + /// Tests that the Handle method calls the mapper with the correct entity when texts are retrieved from the repository. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task Handle_CallsMapper_WithCorrectEntity() + { + // Arrange + var query = new GetAllTextsQuery(); + + var texts = new List + { + new TextEntity { Id = 1, Title = "Title 1", TextContent = "Content 1", StreetcodeId = 1 }, + }; + + this.textRepositoryMock + .Setup(repo => repo.GetAllAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>?>())) + .ReturnsAsync(texts); + + // Act + var result = await this.handler.Handle(query, CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value.Should().ContainSingle(dto => dto.Id == 1 && dto.Title == "Title 1"); + } + } +} diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/StreetCode/Text/GetParsedTextAdminPreviewHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/StreetCode/Text/GetParsedTextAdminPreviewHandlerTests.cs new file mode 100644 index 0000000..607543f --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/StreetCode/Text/GetParsedTextAdminPreviewHandlerTests.cs @@ -0,0 +1,99 @@ +// +// Copyright (c) PlaceholderCompany. All rights reserved. +// + +namespace Streetcode.XUnitTest.BLL.MediatR.StreetCode.Text +{ + using FluentAssertions; + using Moq; + using Streetcode.BLL.Interfaces.Text; + using Streetcode.BLL.MediatR.Streetcode.Text.GetParsed; + using Xunit; + + /// + /// Unit tests for . + /// + public class GetParsedTextAdminPreviewHandlerTests + { + private readonly Mock textServiceMock; + private readonly GetParsedTextAdminPreviewHandler handler; + + /// + /// Initializes a new instance of the class. + /// + public GetParsedTextAdminPreviewHandlerTests() + { + this.textServiceMock = new Mock(); + this.handler = new GetParsedTextAdminPreviewHandler(this.textServiceMock.Object); + } + + /// + /// Tests that the Handle method returns an error when the text service returns null, indicating that parsing was unsuccessful. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task Handle_ReturnsError_WhenServiceReturnsNull() + { + // Arrange + var command = new GetParsedTextForAdminPreviewCommand(textToParse: "some text"); + + this.textServiceMock + .Setup(s => s.AddTermsTag(command.textToParse)) + .ReturnsAsync((string?)null); + + // Act + var result = await this.handler.Handle(command, CancellationToken.None); + + // Assert + result.IsFailed.Should().BeTrue(); + result.Errors.Should().ContainSingle(e => e.Message == "text was not parsed successfully"); + } + + /// + /// Tests that the Handle method returns the processed text when the text service successfully parses the input text. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task Handle_ReturnsProcessedText_WhenServiceSucceeds() + { + // Arrange + const string parsedText = "МайданЦентральна площа"; + var command = new GetParsedTextForAdminPreviewCommand(textToParse: "some text"); + + this.textServiceMock + .Setup(s => s.AddTermsTag(command.textToParse)) + .ReturnsAsync(parsedText); + + // Act + var result = await this.handler.Handle(command, CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value.Should().Be(parsedText); + } + + /// + /// Tests that the Handle method calls the text service with the original input text, ensuring that the correct data is passed for processing. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task Handle_PassesOriginalText_ToService() + { + // Arrange + const string originalText = "raw input text"; + var command = new GetParsedTextForAdminPreviewCommand(textToParse: originalText); + + this.textServiceMock + .Setup(s => s.AddTermsTag(originalText)) + .ReturnsAsync("processed"); + + // Act + await this.handler.Handle(command, CancellationToken.None); + + // Assert + this.textServiceMock.Verify( + s => s.AddTermsTag(originalText), + Times.Once); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/StreetCode/Text/GetTextByIdHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/StreetCode/Text/GetTextByIdHandlerTests.cs new file mode 100644 index 0000000..824f353 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/StreetCode/Text/GetTextByIdHandlerTests.cs @@ -0,0 +1,126 @@ +// +// Copyright (c) PlaceholderCompany. All rights reserved. +// + +namespace Streetcode.XUnitTest.BLL.MediatR.StreetCode.Text +{ + using System.Linq.Expressions; + using AutoMapper; + using FluentAssertions; + using Microsoft.EntityFrameworkCore.Query; + using Moq; + using Streetcode.BLL.DTO.Streetcode.TextContent.Text; + using Streetcode.BLL.Interfaces.Logging; + using Streetcode.BLL.Mapping.Streetcode.TextContent; + using Streetcode.BLL.MediatR.Streetcode.Text.GetById; + using Streetcode.DAL.Repositories.Interfaces.Base; + using Streetcode.DAL.Repositories.Interfaces.Streetcode.TextContent; + using Xunit; + using TextEntity = Streetcode.DAL.Entities.Streetcode.TextContent.Text; + + /// + /// Unit tests for . + /// + public class GetTextByIdHandlerTests + { + private readonly Mock repositoryWrapperMock; + private readonly Mock textRepositoryMock; + private readonly IMapper mapper; + private readonly Mock loggerMock; + private readonly GetTextByIdHandler handler; + + /// + /// Initializes a new instance of the class. + /// + public GetTextByIdHandlerTests() + { + this.repositoryWrapperMock = new Mock(); + this.textRepositoryMock = new Mock(); + this.mapper = new MapperConfiguration(cfg => cfg.AddProfile()).CreateMapper(); + this.loggerMock = new Mock(); + + this.repositoryWrapperMock + .Setup(w => w.TextRepository) + .Returns(this.textRepositoryMock.Object); + + this.handler = new GetTextByIdHandler( + this.repositoryWrapperMock.Object, + this.mapper, + this.loggerMock.Object); + } + + /// + /// Tests that the Handle method returns a TextDTO when a text with the specified ID exists in the repository. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task Handle_ReturnsTextDTO_WhenTextExists() + { + // Arrange + const int textId = 1; + var query = new GetTextByIdQuery(Id: textId); + + var textEntity = new TextEntity + { + Id = textId, + Title = "Test Title", + TextContent = "Test Content", + StreetcodeId = 1, + }; + + var textDto = new TextDTO + { + Id = textId, + Title = "Test Title", + TextContent = "Test Content", + StreetcodeId = 1, + }; + + this.textRepositoryMock + .Setup(repo => repo.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>?>())) + .ReturnsAsync(textEntity); + + // Act + var result = await this.handler.Handle(query, CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value.Should().BeEquivalentTo(textDto); + + this.loggerMock.Verify( + l => l.LogError(It.IsAny(), It.IsAny()), + Times.Never); + } + + /// + /// Tests that the Handle method returns an error when no text with the specified ID exists in the repository. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task Handle_ReturnsError_WhenTextNotFound() + { + // Arrange + const int textId = 42; + var query = new GetTextByIdQuery(Id: textId); + + this.textRepositoryMock + .Setup(repo => repo.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>?>())) + .ReturnsAsync((TextEntity?)null); + + // Act + var result = await this.handler.Handle(query, CancellationToken.None); + + // Assert + result.IsFailed.Should().BeTrue(); + result.Errors[0].Message.Should().Be($"Cannot find any text with corresponding id: {textId}"); + + this.loggerMock.Verify( + l => l.LogError(query, $"Cannot find any text with corresponding id: {textId}"), + Times.Once); + } + } +} diff --git a/Streetcode/Streetcode.XUnitTest/BLL/MediatR/StreetCode/Text/GetTextByStreetcodeIdHandlerTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/StreetCode/Text/GetTextByStreetcodeIdHandlerTests.cs new file mode 100644 index 0000000..e8bf149 --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/MediatR/StreetCode/Text/GetTextByStreetcodeIdHandlerTests.cs @@ -0,0 +1,221 @@ +// +// Copyright (c) PlaceholderCompany. All rights reserved. +// + +namespace Streetcode.XUnitTest.BLL.MediatR.StreetCode.Text +{ + using System.Linq.Expressions; + using AutoMapper; + using FluentAssertions; + using Microsoft.EntityFrameworkCore.Query; + using Moq; + using Streetcode.BLL.DTO.Streetcode.TextContent.Text; + using Streetcode.BLL.Interfaces.Logging; + using Streetcode.BLL.Interfaces.Text; + using Streetcode.BLL.Mapping.Streetcode.TextContent; + using Streetcode.BLL.MediatR.Streetcode.Text.GetByStreetcodeId; + using Streetcode.DAL.Entities.Streetcode; + using Streetcode.DAL.Repositories.Interfaces.Base; + using Streetcode.DAL.Repositories.Interfaces.Streetcode; + using Streetcode.DAL.Repositories.Interfaces.Streetcode.TextContent; + using Xunit; + using TextEntity = Streetcode.DAL.Entities.Streetcode.TextContent.Text; + + /// + /// Unit tests for . + /// + public class GetTextByStreetcodeIdHandlerTests + { + private readonly Mock repositoryWrapperMock; + private readonly Mock textRepositoryMock; + private readonly Mock streetcodeRepositoryMock; + private readonly IMapper mapper; + private readonly Mock textServiceMock; + private readonly Mock loggerMock; + private readonly GetTextByStreetcodeIdHandler handler; + + /// + /// Initializes a new instance of the class. + /// + public GetTextByStreetcodeIdHandlerTests() + { + this.repositoryWrapperMock = new Mock(); + this.textRepositoryMock = new Mock(); + this.streetcodeRepositoryMock = new Mock(); + this.mapper = new MapperConfiguration(cfg => cfg.AddProfile()).CreateMapper(); + this.textServiceMock = new Mock(); + this.loggerMock = new Mock(); + + this.repositoryWrapperMock + .Setup(w => w.TextRepository) + .Returns(this.textRepositoryMock.Object); + + this.repositoryWrapperMock + .Setup(w => w.StreetcodeRepository) + .Returns(this.streetcodeRepositoryMock.Object); + + this.handler = new GetTextByStreetcodeIdHandler( + this.repositoryWrapperMock.Object, + this.mapper, + this.textServiceMock.Object, + this.loggerMock.Object); + } + + /// + /// Tests that the Handle method returns a TextDTO with processed text content when a text associated with the specified streetcode ID exists in the repository. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task Handle_ReturnsProcessedText_WhenTextExists() + { + // Arrange + const string originalContent = "Text about Maidan"; + const string taggedContent = "MaidanCentral square"; + var query = new GetTextByStreetcodeIdQuery(StreetcodeId: 1); + + var textEntity = new TextEntity + { + Id = 1, + StreetcodeId = 1, + TextContent = originalContent, + }; + + this.textRepositoryMock + .Setup(repo => repo.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>?>())) + .ReturnsAsync(textEntity); + + this.textServiceMock + .Setup(s => s.AddTermsTag(originalContent)) + .ReturnsAsync(taggedContent); + + // Act + var result = await this.handler.Handle(query, CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value.Should().NotBeNull(); + result.Value!.TextContent.Should().Be(taggedContent); + + this.textServiceMock.Verify( + s => s.AddTermsTag(originalContent), + Times.Once); + + this.loggerMock.Verify( + l => l.LogError(It.IsAny(), It.IsAny()), + Times.Never); + } + + /// + /// Tests that the Handle method returns a successful result with a null value when no text is found for the specified streetcode ID, but the streetcode itself exists in the repository. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task Handle_ReturnsNullResult_WhenTextNotFoundButStreetcodeExists() + { + // Arrange + var query = new GetTextByStreetcodeIdQuery(StreetcodeId: 1); + + this.textRepositoryMock + .Setup(repo => repo.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>?>())) + .ReturnsAsync((TextEntity?)null); + + this.streetcodeRepositoryMock + .Setup(repo => repo.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>?>())) + .ReturnsAsync(new StreetcodeContent { Id = 1 }); + + // Act + var result = await this.handler.Handle(query, CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value.Should().BeNull(); + + this.loggerMock.Verify( + l => l.LogError(It.IsAny(), It.IsAny()), + Times.Never); + + this.textServiceMock.Verify( + s => s.AddTermsTag(It.IsAny()), + Times.Never); + } + + /// + /// Tests that the Handle method returns an error when no streetcode is found for the specified streetcode ID, regardless of whether a text exists or not. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task Handle_ReturnsError_WhenStreetcodeDoesNotExist() + { + // Arrange + var query = new GetTextByStreetcodeIdQuery(StreetcodeId: 1); + + this.textRepositoryMock + .Setup(repo => repo.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>?>())) + .ReturnsAsync((TextEntity?)null); + + this.streetcodeRepositoryMock + .Setup(repo => repo.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>?>())) + .ReturnsAsync((StreetcodeContent?)null); + + // Act + var result = await this.handler.Handle(query, CancellationToken.None); + + // Assert + result.IsFailed.Should().BeTrue(); + + this.loggerMock.Verify( + l => l.LogError(query, It.Is(msg => msg.Contains("1"))), + Times.Once); + + this.textServiceMock.Verify( + s => s.AddTermsTag(It.IsAny()), + Times.Never); + } + + /// + /// Tests that the Handle method processes the text content using the text service and updates the TextDTO with the processed content before returning it in the result when a text is found for the specified streetcode ID. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task Handle_UsesAddTermsTagResult_AsTextContent() + { + // Arrange + const string originalContent = "Text about streetcode"; + const string taggedContent = "streetcodedescription"; + var query = new GetTextByStreetcodeIdQuery(StreetcodeId: 1); + var textEntity = new TextEntity + { + Id = 1, + StreetcodeId = 1, + TextContent = originalContent, + }; + + this.textRepositoryMock + .Setup(repo => repo.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>?>())) + .ReturnsAsync(textEntity); + + this.textServiceMock + .Setup(s => s.AddTermsTag(originalContent)) + .ReturnsAsync(taggedContent); + + // Act + var result = await this.handler.Handle(query, CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value!.TextContent.Should().Be(taggedContent); + } + } +} \ No newline at end of file diff --git a/Streetcode/Streetcode.XUnitTest/BLL/Services/Text/AddTermsToTextServiceTests.cs b/Streetcode/Streetcode.XUnitTest/BLL/Services/Text/AddTermsToTextServiceTests.cs new file mode 100644 index 0000000..fc5001b --- /dev/null +++ b/Streetcode/Streetcode.XUnitTest/BLL/Services/Text/AddTermsToTextServiceTests.cs @@ -0,0 +1,138 @@ +// +// Copyright (c) PlaceholderCompany. All rights reserved. +// + +namespace Streetcode.XUnitTest.BLL.Services.Text +{ + using System.Linq.Expressions; + using FluentAssertions; + using Microsoft.EntityFrameworkCore.Query; + using Moq; + using Streetcode.BLL.Services.Text; + using Streetcode.DAL.Entities.Streetcode.TextContent; + using Streetcode.DAL.Repositories.Interfaces.Base; + using Streetcode.DAL.Repositories.Interfaces.Streetcode.TextContent; + using Xunit; + + /// + /// Unit tests for . + /// + public class AddTermsToTextServiceTests + { + private readonly Mock repositoryWrapperMock; + private readonly Mock termRepositoryMock; + private readonly AddTermsToTextService service; + + /// + /// Initializes a new instance of the class. + /// + public AddTermsToTextServiceTests() + { + this.repositoryWrapperMock = new Mock(); + this.termRepositoryMock = new Mock(); + + this.repositoryWrapperMock + .Setup(w => w.TermRepository) + .Returns(this.termRepositoryMock.Object); + + this.service = new AddTermsToTextService(this.repositoryWrapperMock.Object); + } + + /// + /// Tests that the AddTermsTag method throws an ArgumentNullException when the input string is null. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task AddTermsTag_ThrowArgumentNullException_WhenInputIsNull() + { + // Arrange + string? input = null; + + // Act + var result = async () => await this.service.AddTermsTag(input!); + + // Assert + await result.Should().ThrowAsync(); + } + + /// + /// Tests that the AddTermsTag method wraps a word with a Popover tag when a direct term match is found in the repository. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task AddTermsTag_WrapsWord_WhenDirectTermMatchFound() + { + // Arrange + const string input = "hello"; + var term = new Term { Id = 1, Title = "hello", Description = "test description" }; + + this.termRepositoryMock + .Setup(repo => repo.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>?>())) + .ReturnsAsync(term); + + // Act + var result = await this.service.AddTermsTag(input); + + // Assert + result.Should().Contain("hellotest description"); + } + + /// + /// Tests that the AddTermsTag method wraps only the first occurrence of a word with a Popover tag when multiple occurrences of the same word are present in the input string, and skips subsequent occurrences. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task AddTermsTag_WrapsFirstOccurrence_AndSkipsSubsequent() + { + // Arrange + const string input = "hello hello"; + const string expectedTag = "hellotest description"; + var term = new Term { Id = 1, Title = "hello", Description = "test description" }; + + this.termRepositoryMock + .Setup(repo => repo.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>?>())) + .ReturnsAsync(term); + + // Act + var result = await this.service.AddTermsTag(input); + + // Assert + result.Should().Contain(expectedTag); + result.Split("").Should().HaveCount(2); + } + + /// + /// Tests that the AddTermsTag method does not wrap words that are part of HTML tags and does not query the repository for terms that are HTML tags, ensuring that HTML tags are skipped when splitting words. + /// + /// A representing the result of the asynchronous operation. + [Fact] + public async Task AddTermsTag_SkipsHTMLTags_WhenSplittingWords() + { + // Arrange + const string input = "hello"; + var term = new Term { Id = 1, Title = "strong", Description = "HTML element" }; + + this.termRepositoryMock + .Setup(repo => repo.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>?>())) + .ReturnsAsync(term); + + // Act + var result = await this.service.AddTermsTag(input); + + // Assert + result.Should().NotContain("strong"); + + this.termRepositoryMock.Verify( + repo => repo.GetFirstOrDefaultAsync( + It.IsAny>>(), + It.IsAny, IIncludableQueryable>?>()), + Times.Once); + } + } +}