diff --git a/src/Content/Backend/Solution/.template.config/template.json b/src/Content/Backend/Solution/.template.config/template.json index 1845bdc..4b8b6b4 100644 --- a/src/Content/Backend/Solution/.template.config/template.json +++ b/src/Content/Backend/Solution/.template.config/template.json @@ -159,6 +159,7 @@ "condition": "(!filesSupport)", "exclude": [ "Monaco.Template.Backend.Common.BlobStorage/**/*", + "Monaco.Template.Backend.Common.BlobStorage.Tests/**/*", "Monaco.Template.Backend.Api/Controllers/FilesController.cs", "Monaco.Template.Backend.Api/Controllers/ImagesController.cs", "Monaco.Template.Backend.Application/Features/File/**/*", @@ -547,6 +548,7 @@ "5a3893d1-1e36-310c-7633-8f36ffa26315", "a2689ae3-3643-6250-a748-8f055cc72da8", "be447a08-0a85-5779-8c65-cf15c2c9a5a8", - "c776f397-182b-6d0d-09f2-4e440dc093d3" + "c776f397-182b-6d0d-09f2-4e440dc093d3", + "d8623b90-59c1-4753-a0e6-f2dbd4305c9b" ] } \ No newline at end of file diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Api/Monaco.Template.Backend.Api.csproj b/src/Content/Backend/Solution/Monaco.Template.Backend.Api/Monaco.Template.Backend.Api.csproj index 36e3c8c..96b10c8 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Api/Monaco.Template.Backend.Api.csproj +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Api/Monaco.Template.Backend.Api.csproj @@ -10,8 +10,8 @@ - - + + all diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Commands/CompanyCommandsHandlersTests.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Commands/CompanyCommandsHandlersTests.cs index 3934406..c6501a5 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Commands/CompanyCommandsHandlersTests.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Commands/CompanyCommandsHandlersTests.cs @@ -16,9 +16,9 @@ namespace Monaco.Template.Backend.Application.Tests.Features.Company.Commands; [ExcludeFromCodeCoverage] +[Trait("Application Commands", "Company Commands")] public class CompanyCommandsHandlersTests { - [Trait("Application Commands", "Company Commands")] [Theory(DisplayName = "Create new company succeeds")] [AnonymousData] public async Task CreateNewCompanySucceeds(Domain.Model.Country country) @@ -48,7 +48,6 @@ public async Task CreateNewCompanySucceeds(Domain.Model.Country country) result.ItemNotFound.Should().BeFalse(); } - [Trait("Application Commands", "Company Commands")] [Theory(DisplayName = "Edit company succeeds")] [AnonymousData] public async Task EditCompanySucceeds(Domain.Model.Country country) diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Commands/Validators/CompanyCreateCommandValidatorTests.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Commands/Validators/CompanyCreateCommandValidatorTests.cs index 650143a..e7f74fa 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Commands/Validators/CompanyCreateCommandValidatorTests.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Commands/Validators/CompanyCreateCommandValidatorTests.cs @@ -17,9 +17,9 @@ namespace Monaco.Template.Backend.Application.Tests.Features.Company.Commands.Validators; [ExcludeFromCodeCoverage] +[Trait("Application Validators", "Company Validators")] public class CompanyCreateCommandValidatorTests { - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Validator's rule level cascade mode is 'Stop'")] public void ValidatorRuleLevelCascadeModeIsStop() { @@ -28,7 +28,6 @@ public void ValidatorRuleLevelCascadeModeIsStop() sut.RuleLevelCascadeMode.Should().Be(CascadeMode.Stop); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Name being valid does not generate validation error")] public async Task NameDoesNotGenerateErrorWhenValid() { @@ -52,7 +51,6 @@ public async Task NameDoesNotGenerateErrorWhenValid() validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.Name); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Name with empty value generates validation error")] public async Task NameIsEmptyGeneratesError() { @@ -74,7 +72,6 @@ public async Task NameIsEmptyGeneratesError() .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Name with long value generates validation error")] public async Task NameWithLongValueGeneratesError() { @@ -97,7 +94,6 @@ public async Task NameWithLongValueGeneratesError() .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Theory(DisplayName = "Name which already exists generates validation error")] [AnonymousData] public async Task NameAlreadyExistsGeneratesError(Domain.Model.Company company) @@ -125,7 +121,6 @@ public async Task NameAlreadyExistsGeneratesError(Domain.Model.Company company) .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Email being valid does not generate validation error")] public async Task EmailIsValidDoesNotGenerateError() { @@ -149,7 +144,6 @@ public async Task EmailIsValidDoesNotGenerateError() validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.Email); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Email with empty value generates validation error")] public async Task EmailIsEmptyGeneratesError() { @@ -171,7 +165,6 @@ public async Task EmailIsEmptyGeneratesError() .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Theory(DisplayName = "Email being invalid generates validation error")] [AnonymousData] public async Task EmailAddressIsInvalidGeneratesError(string email) @@ -194,7 +187,6 @@ public async Task EmailAddressIsInvalidGeneratesError(string email) .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Website URL with long value generates validation error")] public async Task WebsiteUrlWithLongValueGeneratesError() { @@ -217,7 +209,6 @@ public async Task WebsiteUrlWithLongValueGeneratesError() .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Website URL with empty value does not generate validation error")] public async Task WebsiteUrlWithEmptyValueDoesNotGenerateError() { @@ -236,9 +227,8 @@ public async Task WebsiteUrlWithEmptyValueDoesNotGenerateError() validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.WebSiteUrl); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Street with long value generates validation error")] - public async Task AddressWithLongValueGeneratesError() + public async Task StreetWithLongValueGeneratesError() { var cmdMock = new Mock(It.IsAny(), // Name It.IsAny(), // Email @@ -259,9 +249,8 @@ public async Task AddressWithLongValueGeneratesError() .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Street with empty value does not generate validation error")] - public async Task AddressWithEmptyValueDoesNotGenerateError() + public async Task StreetWithEmptyValueDoesNotGenerateError() { var cmdMock = new Mock(It.IsAny(), // Name It.IsAny(), // Email @@ -278,7 +267,6 @@ public async Task AddressWithEmptyValueDoesNotGenerateError() validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.Street); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "City with long value generates validation error")] public async Task CityWithLongValueGeneratesError() { @@ -301,7 +289,6 @@ public async Task CityWithLongValueGeneratesError() .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "City with empty value does not generate validation error")] public async Task CityWithEmptyValueDoesNotGenerateError() { @@ -320,7 +307,6 @@ public async Task CityWithEmptyValueDoesNotGenerateError() validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.City); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "County with long value generates validation error")] public async Task CountyWithLongValueGeneratesError() { @@ -343,7 +329,6 @@ public async Task CountyWithLongValueGeneratesError() .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "County with empty value does not generate validation error")] public async Task CountyWithEmptyValueDoesNotGenerateError() { @@ -362,7 +347,6 @@ public async Task CountyWithEmptyValueDoesNotGenerateError() validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.County); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Postcode with long value generates validation error")] public async Task PostcodeWithLongValueGeneratesError() { @@ -385,7 +369,6 @@ public async Task PostcodeWithLongValueGeneratesError() .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Postcode with empty value does not generate validation error")] public async Task PostcodeWithEmptyValueDoesNotGenerateError() { @@ -404,7 +387,6 @@ public async Task PostcodeWithEmptyValueDoesNotGenerateError() validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.PostCode); } - [Trait("Application Validators", "Company Validators")] [Theory(DisplayName = "Country being valid does not generate validation error")] [AnonymousData(true)] public async Task CountryIsValidDoesNotGenerateError(Domain.Model.Country country) @@ -436,7 +418,6 @@ public async Task CountryIsValidDoesNotGenerateError(Domain.Model.Country countr validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.CountryId); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Country with null value does not generate validation error when Address fields null")] public async Task CountryWithNullValueDoesNotGenerateErrorWhenAddressFieldsNull() { @@ -459,7 +440,6 @@ public async Task CountryWithNullValueDoesNotGenerateErrorWhenAddressFieldsNull( validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.CountryId); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Country with null value generates validation error when Address fields present")] public async Task CountryWithNullValueGeneratesErrorWhenAddressFieldsPresent() { @@ -482,7 +462,6 @@ public async Task CountryWithNullValueGeneratesErrorWhenAddressFieldsPresent() validationResult.ShouldHaveValidationErrorFor(cmd => cmd.CountryId); } - [Trait("Application Validators", "Company Validators")] [Theory(DisplayName = "Country that doesn't exist generates validation error")] [AnonymousData] public async Task CountryMustExistValidation(Domain.Model.Country country) diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Commands/Validators/CompanyDeleteCommandValidatorTests.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Commands/Validators/CompanyDeleteCommandValidatorTests.cs index 2c29733..3a7ba57 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Commands/Validators/CompanyDeleteCommandValidatorTests.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Commands/Validators/CompanyDeleteCommandValidatorTests.cs @@ -18,9 +18,9 @@ namespace Monaco.Template.Backend.Application.Tests.Features.Company.Commands.Validators; [ExcludeFromCodeCoverage] +[Trait("Application Validators", "Company Validators")] public class CompanyDeleteCommandValidatorTests { - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Validator's rule level cascade mode is 'Stop'")] public void ValidatorRuleLevelCascadeModeIsStop() { @@ -29,7 +29,6 @@ public void ValidatorRuleLevelCascadeModeIsStop() sut.RuleLevelCascadeMode.Should().Be(CascadeMode.Stop); } - [Trait("Application Validators", "Company Validators")] [Theory(DisplayName = "Existing company passes validation correctly")] [AnonymousData] public async Task ExistingCompanyPassesValidationCorrectly(Domain.Model.Company company) @@ -46,7 +45,6 @@ public async Task ExistingCompanyPassesValidationCorrectly(Domain.Model.Company validationResult.ShouldNotHaveAnyValidationErrors(); } - [Trait("Application Validators", "Company Validators")] [Theory(DisplayName = "Non existing company passes validation correctly")] [AnonymousData] public async Task NonExistingCompanyPassesValidationCorrectly(Domain.Model.Company company, Guid id) diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Commands/Validators/CompanyEditCommandValidatorTests.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Commands/Validators/CompanyEditCommandValidatorTests.cs index 7c1e5b9..1c8702f 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Commands/Validators/CompanyEditCommandValidatorTests.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Commands/Validators/CompanyEditCommandValidatorTests.cs @@ -18,9 +18,9 @@ namespace Monaco.Template.Backend.Application.Tests.Features.Company.Commands.Validators; [ExcludeFromCodeCoverage] +[Trait("Application Validators", "Company Validators")] public class CompanyEditCommandValidatorTests { - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Validator's rule level cascade mode is 'Stop'")] public void ValidatorRuleLevelCascadeModeIsStop() { @@ -29,7 +29,6 @@ public void ValidatorRuleLevelCascadeModeIsStop() sut.RuleLevelCascadeMode.Should().Be(CascadeMode.Stop); } - [Trait("Application Validators", "Company Validators")] [Theory(DisplayName = "Existing company passes validation correctly")] [AnonymousData] public async Task ExistingCompanyPassesValidationCorrectly(Domain.Model.Company company) @@ -54,7 +53,6 @@ public async Task ExistingCompanyPassesValidationCorrectly(Domain.Model.Company validationResult.ShouldNotHaveAnyValidationErrors(); } - [Trait("Application Validators", "Company Validators")] [Theory(DisplayName = "Non existing company passes validation correctly")] [AnonymousData] public async Task NonExistingCompanyPassesValidationCorrectly(Domain.Model.Company company, Guid id) @@ -79,7 +77,6 @@ public async Task NonExistingCompanyPassesValidationCorrectly(Domain.Model.Compa validationResult.ShouldHaveValidationErrorFor(x => x.Id); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Name being valid does not generate validation error")] public async Task NameDoesNotGenerateErrorWhenValid() { @@ -88,15 +85,15 @@ public async Task NameDoesNotGenerateErrorWhenValid() var companyDbSetMock = new List().AsQueryable().BuildMockDbSet(); dbContextMock.Setup(x => x.Set()).Returns(companyDbSetMock.Object); - var cmdMock = new Mock(It.IsAny(), + var cmdMock = new Mock(It.IsAny(), // Id new string(It.IsAny(), 100), // same Name as the already-existing Company - It.IsAny(), // Email - It.IsAny(), // WebSiteUrl - It.IsAny(), // Street - It.IsAny(), // City - It.IsAny(), // County - It.IsAny(), // PostCode - It.IsAny()); // country.Id + It.IsAny(), // Email + It.IsAny(), // WebSiteUrl + It.IsAny(), // Street + It.IsAny(), // City + It.IsAny(), // County + It.IsAny(), // PostCode + It.IsAny()); // country.Id var sut = new CompanyEditCommandValidator(dbContextMock.Object); var validationResult = await sut.TestValidateAsync(cmdMock.Object, strategy => strategy.IncludeProperties(cmd => cmd.Name)); @@ -104,19 +101,18 @@ public async Task NameDoesNotGenerateErrorWhenValid() validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.Name); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Name with empty value generates validation error")] public async Task NameIsEmptyGeneratesError() { - var cmdMock = new Mock(It.IsAny(), - string.Empty, // Name + var cmdMock = new Mock(It.IsAny(), // Id + string.Empty, // Name It.IsAny(), // Email It.IsAny(), // WebSiteUrl It.IsAny(), // Street It.IsAny(), // City It.IsAny(), // County It.IsAny(), // PostCode - It.IsAny()); // country.Id + It.IsAny()); // country.Id var sut = new CompanyEditCommandValidator(new Mock().Object); var validationResult = await sut.TestValidateAsync(cmdMock.Object, strategy => strategy.IncludeProperties(cmd => cmd.Name)); @@ -127,18 +123,17 @@ public async Task NameIsEmptyGeneratesError() .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Name with long value generates validation error")] public async Task NameWithLongValueGeneratesError() { - var cmdMock = new Mock(It.IsAny(), + var cmdMock = new Mock(It.IsAny(), // Id new string(It.IsAny(), 101), // Name - It.IsAny(), // Email - It.IsAny(), // WebSiteUrl - It.IsAny(), // Street - It.IsAny(), // City - It.IsAny(), // County - It.IsAny(), // PostCode + It.IsAny(), // Email + It.IsAny(), // WebSiteUrl + It.IsAny(), // Street + It.IsAny(), // City + It.IsAny(), // County + It.IsAny(), // PostCode It.IsAny()); // country.Id var sut = new CompanyEditCommandValidator(new Mock().Object); @@ -151,7 +146,6 @@ public async Task NameWithLongValueGeneratesError() .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Theory(DisplayName = "Name which already exists generates validation error")] [AnonymousData] public async Task NameAlreadyExistsGeneratesError(Domain.Model.Company company, Guid id) @@ -161,15 +155,15 @@ public async Task NameAlreadyExistsGeneratesError(Domain.Model.Company company, var companyDbSetMock = new List { company }.AsQueryable().BuildMockDbSet(); dbContextMock.Setup(x => x.Set()).Returns(companyDbSetMock.Object); - var cmdMock = new Mock(id, - company.Name, // same Name as the already-existing Company + var cmdMock = new Mock(id, // Id + company.Name, // same Name as the already-existing Company It.IsAny(), // Email It.IsAny(), // WebSiteUrl It.IsAny(), // Street It.IsAny(), // City - It.IsAny(), // County + It.IsAny(), // County It.IsAny(), // PostCode - It.IsAny()); // country.Id + It.IsAny()); // country.Id var sut = new CompanyEditCommandValidator(dbContextMock.Object); var validationResult = await sut.TestValidateAsync(cmdMock.Object, strategy => strategy.IncludeProperties(cmd => cmd.Name)); @@ -180,7 +174,6 @@ public async Task NameAlreadyExistsGeneratesError(Domain.Model.Company company, .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Email being valid does not generate validation error")] public async Task EmailIsValidDoesNotGenerateError() { @@ -189,15 +182,15 @@ public async Task EmailIsValidDoesNotGenerateError() var companyDbSetMock = new List().AsQueryable().BuildMockDbSet(); dbContextMock.Setup(x => x.Set()).Returns(companyDbSetMock.Object); - var cmdMock = new Mock(It.IsAny(), + var cmdMock = new Mock(It.IsAny(), // Id It.IsAny(), // same Name as the already-existing Company - "valid@email.com", // Email + "valid@email.com", // Email It.IsAny(), // WebSiteUrl It.IsAny(), // Street It.IsAny(), // City It.IsAny(), // County It.IsAny(), // PostCode - It.IsAny()); // country.Id + It.IsAny()); // country.Id var sut = new CompanyEditCommandValidator(dbContextMock.Object); var validationResult = await sut.TestValidateAsync(cmdMock.Object, strategy => strategy.IncludeProperties(cmd => cmd.Email)); @@ -205,19 +198,18 @@ public async Task EmailIsValidDoesNotGenerateError() validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.Email); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Email with empty value generates validation error")] public async Task EmailIsEmptyGeneratesError() { - var cmdMock = new Mock(It.IsAny(), + var cmdMock = new Mock(It.IsAny(), // Id It.IsAny(), // Name - string.Empty, // Email + string.Empty, // Email It.IsAny(), // WebSiteUrl It.IsAny(), // Street It.IsAny(), // City It.IsAny(), // County It.IsAny(), // PostCode - It.IsAny()); // country.Id + It.IsAny()); // country.Id var sut = new CompanyEditCommandValidator(new Mock().Object); var validationResult = await sut.TestValidateAsync(cmdMock.Object, strategy => strategy.IncludeProperties(cmd => cmd.Email)); @@ -228,20 +220,19 @@ public async Task EmailIsEmptyGeneratesError() .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Theory(DisplayName = "Email being invalid generates validation error")] [AnonymousData] public async Task EmailAddressIsInvalidGeneratesError(string email) { - var cmdMock = new Mock(It.IsAny(), + var cmdMock = new Mock(It.IsAny(), // Id It.IsAny(), // Name - email, // Email + email, // Email It.IsAny(), // WebSiteUrl It.IsAny(), // Street It.IsAny(), // City It.IsAny(), // County It.IsAny(), // PostCode - It.IsAny()); // country.Id + It.IsAny()); // country.Id var sut = new CompanyEditCommandValidator(new Mock().Object); var validationResult = await sut.TestValidateAsync(cmdMock.Object, strategy => strategy.IncludeProperties(cmd => cmd.Email)); @@ -252,18 +243,17 @@ public async Task EmailAddressIsInvalidGeneratesError(string email) .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Website URL with long value generates validation error")] public async Task WebsiteUrlWithLongValueGeneratesError() { - var cmdMock = new Mock(It.IsAny(), - It.IsAny(), // Name - It.IsAny(), // Email + var cmdMock = new Mock(It.IsAny(), // Id + It.IsAny(), // Name + It.IsAny(), // Email new string(It.IsAny(), 301), // WebSiteUrl - It.IsAny(), // Street - It.IsAny(), // City - It.IsAny(), // County - It.IsAny(), // PostCode + It.IsAny(), // Street + It.IsAny(), // City + It.IsAny(), // County + It.IsAny(), // PostCode It.IsAny()); // country.Id var sut = new CompanyEditCommandValidator(new Mock().Object); @@ -276,19 +266,18 @@ public async Task WebsiteUrlWithLongValueGeneratesError() .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Website URL with empty value does not generate validation error")] public async Task WebsiteUrlWithEmptyValueDoesNotGenerateError() { - var cmdMock = new Mock(It.IsAny(), - It.IsAny(), // Name + var cmdMock = new Mock(It.IsAny(), // Id + It.IsAny(), // Name It.IsAny(), // Email - string.Empty, // WebSiteUrl + string.Empty, // WebSiteUrl It.IsAny(), // Street It.IsAny(), // City It.IsAny(), // County It.IsAny(), // PostCode - It.IsAny()); // country.Id + It.IsAny()); // country.Id var sut = new CompanyEditCommandValidator(new Mock().Object); var validationResult = await sut.TestValidateAsync(cmdMock.Object, strategy => strategy.IncludeProperties(cmd => cmd.WebSiteUrl)); @@ -296,18 +285,17 @@ public async Task WebsiteUrlWithEmptyValueDoesNotGenerateError() validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.WebSiteUrl); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Street with long value generates validation error")] public async Task AddressWithLongValueGeneratesError() { - var cmdMock = new Mock(It.IsAny(), - It.IsAny(), // Name - It.IsAny(), // Email - It.IsAny(), // WebSiteUrl + var cmdMock = new Mock(It.IsAny(), // Id + It.IsAny(), // Name + It.IsAny(), // Email + It.IsAny(), // WebSiteUrl new string(It.IsAny(), 101), // Street - It.IsAny(), // City - It.IsAny(), // County - It.IsAny(), // PostCode + It.IsAny(), // City + It.IsAny(), // County + It.IsAny(), // PostCode It.IsAny()); // country.Id var validator = new CompanyEditCommandValidator(new Mock().Object); @@ -320,19 +308,18 @@ public async Task AddressWithLongValueGeneratesError() .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Street with empty value does not generate validation error")] public async Task AddressWithEmptyValueDoesNotGenerateError() { - var cmdMock = new Mock(It.IsAny(), - It.IsAny(), // Name + var cmdMock = new Mock(It.IsAny(), // Id + It.IsAny(), // Name It.IsAny(), // Email It.IsAny(), // WebSiteUrl - string.Empty, // Street + string.Empty, // Street It.IsAny(), // City It.IsAny(), // County It.IsAny(), // PostCode - It.IsAny()); // country.Id + It.IsAny()); // country.Id var sut = new CompanyEditCommandValidator(new Mock().Object); var validationResult = await sut.TestValidateAsync(cmdMock.Object, strategy => strategy.IncludeProperties(cmd => cmd.Street)); @@ -340,18 +327,17 @@ public async Task AddressWithEmptyValueDoesNotGenerateError() validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.Street); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "City with long value generates validation error")] public async Task CityWithLongValueGeneratesError() { - var cmdMock = new Mock(It.IsAny(), - It.IsAny(), // Name - It.IsAny(), // Email - It.IsAny(), // WebSiteUrl - It.IsAny(), // Street + var cmdMock = new Mock(It.IsAny(), // Id + It.IsAny(), // Name + It.IsAny(), // Email + It.IsAny(), // WebSiteUrl + It.IsAny(), // Street new string(It.IsAny(), 101), // City - It.IsAny(), // County - It.IsAny(), // PostCode + It.IsAny(), // County + It.IsAny(), // PostCode It.IsAny()); // country.Id var sut = new CompanyEditCommandValidator(new Mock().Object); @@ -364,19 +350,18 @@ public async Task CityWithLongValueGeneratesError() .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "City with empty value does not generate validation error")] public async Task CityWithEmptyValueDoesNotGenerateError() { - var cmdMock = new Mock(It.IsAny(), - It.IsAny(), // Name + var cmdMock = new Mock(It.IsAny(), // Id + It.IsAny(), // Name It.IsAny(), // Email It.IsAny(), // WebSiteUrl It.IsAny(), // Street - string.Empty, // City + string.Empty, // City It.IsAny(), // County It.IsAny(), // PostCode - It.IsAny()); // country.Id + It.IsAny()); // country.Id var sut = new CompanyEditCommandValidator(new Mock().Object); var validationResult = await sut.TestValidateAsync(cmdMock.Object, strategy => strategy.IncludeProperties(cmd => cmd.City)); @@ -384,18 +369,17 @@ public async Task CityWithEmptyValueDoesNotGenerateError() validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.City); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "County with long value generates validation error")] public async Task CountyWithLongValueGeneratesError() { - var cmdMock = new Mock(It.IsAny(), - It.IsAny(), // Name - It.IsAny(), // Email - It.IsAny(), // WebSiteUrl - It.IsAny(), // Street - It.IsAny(), // City + var cmdMock = new Mock(It.IsAny(), // Id + It.IsAny(), // Name + It.IsAny(), // Email + It.IsAny(), // WebSiteUrl + It.IsAny(), // Street + It.IsAny(), // City new string(It.IsAny(), 101), // County - It.IsAny(), // PostCode + It.IsAny(), // PostCode It.IsAny()); // country.Id var sut = new CompanyEditCommandValidator(new Mock().Object); @@ -408,19 +392,18 @@ public async Task CountyWithLongValueGeneratesError() .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "County with empty value does not generate validation error")] public async Task CountyWithEmptyValueDoesNotGenerateError() { - var cmdMock = new Mock(It.IsAny(), - It.IsAny(), // Name + var cmdMock = new Mock(It.IsAny(), // Id + It.IsAny(), // Name It.IsAny(), // Email It.IsAny(), // WebSiteUrl It.IsAny(), // Street It.IsAny(), // City - string.Empty, // County + string.Empty, // County It.IsAny(), // PostCode - It.IsAny()); // country.Id + It.IsAny()); // country.Id var sut = new CompanyEditCommandValidator(new Mock().Object); var validationResult = await sut.TestValidateAsync(cmdMock.Object, strategy => strategy.IncludeProperties(cmd => cmd.County)); @@ -428,18 +411,17 @@ public async Task CountyWithEmptyValueDoesNotGenerateError() validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.County); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Postcode with long value generates validation error")] public async Task PostcodeWithLongValueGeneratesError() { - var cmdMock = new Mock(It.IsAny(), - It.IsAny(), // Name - It.IsAny(), // Email - It.IsAny(), // WebSiteUrl - It.IsAny(), // Street - It.IsAny(), // City - It.IsAny(), // County - new string(It.IsAny(), 11), // PostCode + var cmdMock = new Mock(It.IsAny(), // Id + It.IsAny(), // Name + It.IsAny(), // Email + It.IsAny(), // WebSiteUrl + It.IsAny(), // Street + It.IsAny(), // City + It.IsAny(), // County + new string(It.IsAny(), 11), // PostCode It.IsAny()); // country.Id var sut = new CompanyEditCommandValidator(new Mock().Object); @@ -452,19 +434,18 @@ public async Task PostcodeWithLongValueGeneratesError() .HaveCount(1); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Postcode with empty value does not generate validation error")] public async Task PostcodeWithEmptyValueDoesNotGenerateError() { - var cmdMock = new Mock(It.IsAny(), - It.IsAny(), // Name + var cmdMock = new Mock(It.IsAny(), // Id + It.IsAny(), // Name It.IsAny(), // Email It.IsAny(), // WebSiteUrl It.IsAny(), // Street It.IsAny(), // City It.IsAny(), // County - string.Empty, // PostCode - It.IsAny()); // country.Id + string.Empty, // PostCode + It.IsAny()); // country.Id var sut = new CompanyEditCommandValidator(new Mock().Object); var validationResult = await sut.TestValidateAsync(cmdMock.Object, strategy => strategy.IncludeProperties(cmd => cmd.PostCode)); @@ -472,7 +453,6 @@ public async Task PostcodeWithEmptyValueDoesNotGenerateError() validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.PostCode); } - [Trait("Application Validators", "Company Validators")] [Theory(DisplayName = "Country being valid does not generate validation error")] [AnonymousData(true)] public async Task CountryIsValidDoesNotGenerateError(Domain.Model.Country country) @@ -485,7 +465,7 @@ public async Task CountryIsValidDoesNotGenerateError(Domain.Model.Country countr var countryDbSetMock = new List() { country }.AsQueryable().BuildMockDbSet(); dbContextMock.Setup(x => x.Set()).Returns(countryDbSetMock.Object); - var cmdMock = new Mock(It.IsAny(), + var cmdMock = new Mock(It.IsAny(), // Id It.IsAny(), // Name It.IsAny(), // Email It.IsAny(), // WebSiteUrl @@ -501,11 +481,10 @@ public async Task CountryIsValidDoesNotGenerateError(Domain.Model.Country countr validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.CountryId); } - [Trait("Application Validators", "Company Validators")] [Fact(DisplayName = "Country with null value does not generate validation error")] public async Task CountryWithNullValueDoesNotGenerateError() { - var cmdMock = new Mock(It.IsAny(), + var cmdMock = new Mock(It.IsAny(), // Id It.IsAny(), // Name It.IsAny(), // Email It.IsAny(), // WebSiteUrl @@ -513,7 +492,7 @@ public async Task CountryWithNullValueDoesNotGenerateError() It.IsAny(), // City It.IsAny(), // County It.IsAny(), // PostCode - null); // country.Id + null); // country.Id var sut = new CompanyEditCommandValidator(new Mock().Object); var validationResult = await sut.TestValidateAsync(cmdMock.Object, strategy => strategy.IncludeProperties(cmd => cmd.CountryId)); @@ -521,7 +500,6 @@ public async Task CountryWithNullValueDoesNotGenerateError() validationResult.ShouldNotHaveValidationErrorFor(cmd => cmd.CountryId); } - [Trait("Application Validators", "Company Validators")] [Theory(DisplayName = "Country that doesn't exist generates validation error")] [AnonymousData] public async Task CountryMustExistValidation(Domain.Model.Country country) @@ -534,14 +512,14 @@ public async Task CountryMustExistValidation(Domain.Model.Country country) var countryDbSetMock = new List() { country }.AsQueryable().BuildMockDbSet(); dbContextMock.Setup(x => x.Set()).Returns(countryDbSetMock.Object); - var cmdMock = new Mock(It.IsAny(), - It.IsAny(), // Name + var cmdMock = new Mock(It.IsAny(), // Id + It.IsAny(), // Name It.IsAny(), // Email It.IsAny(), // WebSiteUrl It.IsAny(), // Street It.IsAny(), // City It.IsAny(), // County - It.IsAny(), // PostCode + It.IsAny(), // PostCode Guid.NewGuid()); // country.Id var sut = new CompanyEditCommandValidator(dbContextMock.Object); diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Queries/CompanyQueriesHandlersTests.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Queries/CompanyQueriesHandlersTests.cs index 1821ef6..0fa30ec 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Queries/CompanyQueriesHandlersTests.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Company/Queries/CompanyQueriesHandlersTests.cs @@ -18,9 +18,9 @@ namespace Monaco.Template.Backend.Application.Tests.Features.Company.Queries; [ExcludeFromCodeCoverage] +[Trait("Application Queries", "Company Queries")] public class CompanyQueriesHandlersTests { - [Trait("Application Queries", "Company Queries")] [Theory(DisplayName = "Get company page without params succeeds")] [AnonymousData] public async Task GetCompanyPageWithoutParamsSucceeds(List companies) @@ -40,7 +40,6 @@ public async Task GetCompanyPageWithoutParamsSucceeds(List .BeInAscendingOrder(x => x.Name); } - [Trait("Application Queries", "Company Queries")] [Theory(DisplayName = "Get company page with params succeeds")] [AnonymousData] public async Task GetCompanyPageWithParamsSucceeds(List companies) @@ -82,9 +81,8 @@ public async Task GetExistingCompanyByIdSucceeds() result!.Name.Should().Be(company.Name); } - [Trait("Application Queries", "Company Queries")] [Fact(DisplayName = "Get non-existing company by Id fails")] - public async Task GetNonExistingCountryByIdFails() + public async Task GetNonExistingCompanyByIdFails() { var companies = CompanyFactory.CreateMany().ToList(); var dbContextMock = SetupMock(companies); @@ -103,7 +101,6 @@ private static Mock SetupMock(IEnumerable co dbContextMock.Setup(x => x.Set()) .Returns(dbSetMock.Object); - return dbContextMock; } } \ No newline at end of file diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Country/Queries/CountryQueriesHandlersTests.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Country/Queries/CountryQueriesHandlersTests.cs index 9267134..7d63b8c 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Country/Queries/CountryQueriesHandlersTests.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Features/Country/Queries/CountryQueriesHandlersTests.cs @@ -18,9 +18,9 @@ namespace Monaco.Template.Backend.Application.Tests.Features.Country.Queries; [ExcludeFromCodeCoverage] +[Trait("Application Queries", "Country Queries")] public class CountryQueriesHandlersTests { - [Trait("Application Queries", "Country Queries")] [Theory(DisplayName = "Get country list without params succeeds")] [AnonymousData] public async Task GetCountryListWithoutParamsSucceeds(List countries) @@ -37,7 +37,6 @@ public async Task GetCountryListWithoutParamsSucceeds(List .BeInAscendingOrder(x => x.Name); } - [Trait("Application Queries", "Country Queries")] [Theory(DisplayName = "Get country list with params succeeds")] [AnonymousData] public async Task GetCountryListWithParamsSucceeds(List countries) @@ -61,7 +60,6 @@ public async Task GetCountryListWithParamsSucceeds(List co .BeInDescendingOrder(x => x.Name); } - [Trait("Application Queries", "Country Queries")] [Fact(DisplayName = "Get existing country by Id succeeds")] public async Task GetExistingCountryByIdSucceeds() { @@ -77,7 +75,6 @@ public async Task GetExistingCountryByIdSucceeds() result!.Name.Should().Be(country.Name); } - [Trait("Application Queries", "Country Queries")] [Fact(DisplayName = "Get non-existing country by Id fails")] public async Task GetNonExistingCountryByIdFails() { diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Monaco.Template.Backend.Application.Tests.csproj b/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Monaco.Template.Backend.Application.Tests.csproj index 6b90d42..daf387f 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Monaco.Template.Backend.Application.Tests.csproj +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Monaco.Template.Backend.Application.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Services/FileServiceTests.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Services/FileServiceTests.cs index 77d838e..ab0d7c1 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Services/FileServiceTests.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application.Tests/Services/FileServiceTests.cs @@ -1,6 +1,7 @@ using MockQueryable.Moq; using Monaco.Template.Backend.Application.Infrastructure.Context; using Monaco.Template.Backend.Application.Services; +using Monaco.Template.Backend.Common.BlobStorage; using Monaco.Template.Backend.Common.BlobStorage.Contracts; using Monaco.Template.Backend.Domain.Model; using Moq; @@ -16,9 +17,9 @@ namespace Monaco.Template.Backend.Application.Tests.Services; [ExcludeFromCodeCoverage] +[Trait("Application Services", "File Service")] public class FileServiceTests { - [Trait("Application Services", "File Service")] [Fact(DisplayName = "Upload image succeeds")] public async Task UploadImageSucceeds() { @@ -40,4 +41,72 @@ public async Task UploadImageSucceeds() dbContextMock.Verify(x => x.Set().AddAsync(It.IsAny(), It.IsAny())); dbContextMock.Verify(x => x.SaveEntitiesAsync(It.IsAny())); } + + [Fact(DisplayName = "Upload document succeeds")] + public async Task UploadDocumentSucceeds() + { + var dbContextMock = new Mock(); + var documentDbSetMock = new List().AsQueryable().BuildMockDbSet(); + dbContextMock.Setup(x => x.Set()) + .Returns(documentDbSetMock.Object); + var blobStorageServiceMock = new Mock(); + + var sut = new FileService(dbContextMock.Object, blobStorageServiceMock.Object); + + const string txtBase64 = "TW9uYWNvIFVuaXQgVGVzdCBGaWxlIGZvciBVcGxvYWQgZG9jdW1lbnQgc3VjY2VlZHMu"; + + await using var stream = new MemoryStream(Convert.FromBase64String(txtBase64)); + await sut.UploadDocument(stream, "sample-text-file.txt", "text/plain", CancellationToken.None); + stream.Close(); + + blobStorageServiceMock.Verify(x => x.UploadTempFileAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(1)); + dbContextMock.Verify(x => x.Set().AddAsync(It.IsAny(), It.IsAny())); + dbContextMock.Verify(x => x.SaveEntitiesAsync(It.IsAny())); + } + + [Fact(DisplayName = "Uploading an image file uploads an image succeeds")] + public async Task UploadingImageFileUploadsImageSucceeds() + { + var dbContextMock = new Mock(); + var imageDbSetMock = new List().AsQueryable().BuildMockDbSet(); + dbContextMock.Setup(x => x.Set()) + .Returns(imageDbSetMock.Object); + var blobStorageServiceMock = new Mock(); + blobStorageServiceMock.Setup(x => x.GetFileType(It.IsAny())) + .Returns(FileTypeEnum.Image); + + var sut = new FileService(dbContextMock.Object, blobStorageServiceMock.Object); + + const string imgBase64 = "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"; + + await using var stream = new MemoryStream(Convert.FromBase64String(imgBase64)); + await sut.Upload(stream, "sample-image.png", "image/png", CancellationToken.None); + stream.Close(); + + blobStorageServiceMock.Verify(x => x.UploadTempFileAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(2)); + dbContextMock.Verify(x => x.Set().AddAsync(It.IsAny(), It.IsAny())); + dbContextMock.Verify(x => x.SaveEntitiesAsync(It.IsAny())); + } + + [Fact(DisplayName = "Uploading a text file uploads a document succeeds")] + public async Task UploadingTextFileUploadsDocumentSucceeds() + { + var dbContextMock = new Mock(); + var documentDbSetMock = new List().AsQueryable().BuildMockDbSet(); + dbContextMock.Setup(x => x.Set()) + .Returns(documentDbSetMock.Object); + var blobStorageServiceMock = new Mock(); + + var sut = new FileService(dbContextMock.Object, blobStorageServiceMock.Object); + + const string txtBase64 = "TW9uYWNvIFVuaXQgVGVzdCBGaWxlIGZvciBVcGxvYWQgZG9jdW1lbnQgc3VjY2VlZHMu"; + + await using var stream = new MemoryStream(Convert.FromBase64String(txtBase64)); + await sut.Upload(stream, "sample-text-file.txt", "text/plain", CancellationToken.None); + stream.Close(); + + blobStorageServiceMock.Verify(x => x.UploadTempFileAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(1)); + dbContextMock.Verify(x => x.Set().AddAsync(It.IsAny(), It.IsAny())); + dbContextMock.Verify(x => x.SaveEntitiesAsync(It.IsAny())); + } } \ No newline at end of file diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/CompanyDto.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/CompanyDto.cs index 2a267e1..ab22306 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/CompanyDto.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/CompanyDto.cs @@ -3,9 +3,9 @@ public class CompanyDto { public Guid Id { get; set; } - public string Name { get; set; } - public string Email { get; set; } - public string WebSiteUrl { get; set; } + public string Name { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public string WebSiteUrl { get; set; } = string.Empty; public string? Street { get; set; } public string? City { get; set; } public string? County { get; set; } diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/CountryDto.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/CountryDto.cs index 9794d16..b860e7b 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/CountryDto.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/CountryDto.cs @@ -3,5 +3,5 @@ public class CountryDto { public Guid Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; } \ No newline at end of file diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/Extensions/CompanyExtensions.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/Extensions/CompanyExtensions.cs index eee9209..d0108c8 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/Extensions/CompanyExtensions.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/Extensions/CompanyExtensions.cs @@ -1,5 +1,4 @@ -using Monaco.Template.Backend.Application.DTOs.Extensions; -using Monaco.Template.Backend.Application.Features.Company.Commands; +using Monaco.Template.Backend.Application.Features.Company.Commands; using Monaco.Template.Backend.Domain.Model; using System.Linq.Expressions; diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/Extensions/CountryExtensions.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/Extensions/CountryExtensions.cs index f0e415a..b9092ab 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/Extensions/CountryExtensions.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/Extensions/CountryExtensions.cs @@ -5,18 +5,14 @@ namespace Monaco.Template.Backend.Application.DTOs.Extensions; public static class CountryExtensions { - public static CountryDto? Map(this Country? value) - { - if (value == null) - return null; - - var dto = new CountryDto - { - Id = value.Id, - Name = value.Name - }; - return dto; - } + public static CountryDto? Map(this Country? value) => + value is null + ? null + : new() + { + Id = value.Id, + Name = value.Name + }; public static Dictionary>> GetMappedFields() => new() diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/Extensions/FileExtensions.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/Extensions/FileExtensions.cs index f962fe4..cdade0c 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/Extensions/FileExtensions.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/Extensions/FileExtensions.cs @@ -7,44 +7,38 @@ namespace Monaco.Template.Backend.Application.DTOs.Extensions; public static class FileExtensions { - public static FileDto? Map(this File? value) - { - if (value == null) - return null; + public static FileDto? Map(this File? value) => + value is null + ? null + : new() + { + Id = value.Id, + Name = value.Name, + Extension = value.Extension, + ContentType = value.ContentType, + Size = value.Size, + UploadedOn = value.UploadedOn, + IsTemp = value.IsTemp + }; - return new FileDto - { - Id = value.Id, - Name = value.Name, - Extension = value.Extension, - ContentType = value.ContentType, - Size = value.Size, - UploadedOn = value.UploadedOn, - IsTemp = value.IsTemp - }; - } - - public static ImageDto? Map(this Image? value) - { - if (value == null) - return null; - - return new ImageDto - { - DateTaken = value.DateTaken, - Width = value.Width, - Height = value.Height, - ThumbnailId = value.ThumbnailId, - Thumbnail = value.ThumbnailId.HasValue ? value.Thumbnail.Map() : null, - Id = value.Id, - Name = value.Name, - Extension = value.Extension, - ContentType = value.ContentType, - Size = value.Size, - UploadedOn = value.UploadedOn, - IsTemp = value.IsTemp - }; - } + public static ImageDto? Map(this Image? value) => + value is null + ? null + : new() + { + DateTaken = value.DateTaken, + Width = value.Width, + Height = value.Height, + ThumbnailId = value.ThumbnailId, + Thumbnail = value.ThumbnailId.HasValue ? value.Thumbnail.Map() : null, + Id = value.Id, + Name = value.Name, + Extension = value.Extension, + ContentType = value.ContentType, + Size = value.Size, + UploadedOn = value.UploadedOn, + IsTemp = value.IsTemp + }; public static File Map(this FileCreateCommand value, Guid id, FileTypeEnum fileType) => fileType switch diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/FileDownloadDto.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/FileDownloadDto.cs index 255fcfb..c3617e5 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/FileDownloadDto.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/FileDownloadDto.cs @@ -3,6 +3,6 @@ public class FileDownloadDto { public Stream FileContent { get; set; } - public string FileName { get; set; } - public string ContentType { get; set; } + public string FileName { get; set; } = string.Empty; + public string ContentType { get; set; } = string.Empty; } \ No newline at end of file diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/FileDto.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/FileDto.cs index 716b851..85ba975 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/FileDto.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/DTOs/FileDto.cs @@ -3,9 +3,9 @@ public class FileDto { public Guid Id { get; set; } - public string Name { get; set; } - public string Extension { get; set; } - public string ContentType { get; set; } + public string Name { get; set; } = string.Empty; + public string Extension { get; set; } = string.Empty; + public string ContentType { get; set; } = string.Empty; public long Size { get; set; } public DateTime UploadedOn { get; set; } public bool IsTemp { get; set; } diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/Monaco.Template.Backend.Application.csproj b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/Monaco.Template.Backend.Application.csproj index 43ba611..79556e3 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/Monaco.Template.Backend.Application.csproj +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/Monaco.Template.Backend.Application.csproj @@ -15,9 +15,9 @@ - + - + diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/Services/FileService.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/Services/FileService.cs index ea09305..044d6d2 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Application/Services/FileService.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Application/Services/FileService.cs @@ -5,7 +5,6 @@ using Monaco.Template.Backend.Common.BlobStorage.Contracts; using Monaco.Template.Backend.Domain.Model; using SkiaSharp; -using System.Drawing; using File = Monaco.Template.Backend.Domain.Model.File; using Image = Monaco.Template.Backend.Domain.Model.Image; @@ -66,7 +65,7 @@ public async Task UploadImage(Stream stream, string fileName, string cont var dateTaken = metadata.Get(ExifTag.DateTimeOriginal)?.Value; var gpsLat = metadata.Get(ExifTag.GPSLatitude)?.ToFloat(); var gpsLong = metadata.Get(ExifTag.GPSLongitude)?.ToFloat(); - stream.Position = 0; //Reset streams position to read from beginning + stream.Position = 0; // Reset streams position to read from beginning thumbStream.Position = 0; var imageIds = await Task.WhenAll(_blobStorageService.UploadTempFileAsync(stream, fileName, @@ -85,8 +84,8 @@ public async Task UploadImage(Stream stream, string fileName, string cont contentType, stream.Length, thumbStream.Length, - new Size(image.Width, image.Height), - new Size(thumb.Width, thumb.Height), + (image.Width, image.Height), + (thumb.Width, thumb.Height), dateTaken, gpsLat, gpsLong, @@ -178,9 +177,10 @@ public async Task CopyFile(Guid id, CancellationToken cancellationToken) switch (file) { case Image image: - Guid? thumbCopyId = null; - if (image.ThumbnailId.HasValue) - thumbCopyId = image.ThumbnailId.HasValue ? await _blobStorageService.CopyAsync(image.ThumbnailId.Value, image.IsTemp, cancellationToken) : null; + Guid? thumbCopyId = image.ThumbnailId.HasValue + ? await _blobStorageService.CopyAsync(image.ThumbnailId.Value, image.IsTemp, cancellationToken) + : null; + return await SaveImage(copyId, thumbCopyId, image.Name, @@ -188,8 +188,8 @@ public async Task CopyFile(Guid id, CancellationToken cancellationToken) image.ContentType, image.Size, image.Thumbnail?.Size, - new Size(image.Width, image.Height), - image.ThumbnailId.HasValue ? new Size(image.Thumbnail!.Width, image.Thumbnail.Height) : null, + (image.Width, image.Height), + image.ThumbnailId.HasValue ? (image.Thumbnail!.Width, image.Thumbnail.Height) : null, image.DateTaken, image.GpsLatitude, image.GpsLongitude, @@ -213,12 +213,12 @@ public ExifPropertyCollection GetMetadata(Stream stream) public SKImage GetThumbnail(SKImage image, int thumbnailWidth, int thumbnailHeight) { - //Calculates the proper scale to shrink the image so the aspect ratio remains the same for the thumbnail as well + // Calculates the proper scale to shrink the image so the aspect ratio remains the same for the thumbnail as well var scale = Math.Min(thumbnailWidth / (float)image.Height, thumbnailHeight / (float)image.Width); - if (scale > 1) //If scale is bigger than 1, it means that the thumbnail would end up being bigger than the original image - scale = 1; //So we reset it to 1 so both image and thumbnail are the same size at worst. - //Finally, we use the scale to calculate the final width and height to use for the thumbnail + if (scale > 1) // If scale is bigger than 1, it means that the thumbnail would end up being bigger than the original image + scale = 1; // So we reset it to 1 so both image and thumbnail are the same size at worst. + // Finally, we use the scale to calculate the final width and height to use for the thumbnail var sourceBitmap = SKBitmap.FromImage(image); using var scaledBitmap = sourceBitmap.Resize(new SKImageInfo((int)(image.Width * scale), (int)(image.Height * scale)), @@ -238,8 +238,8 @@ private async Task SaveImage(Guid imageId, string contentType, long imageSize, long? thumbSize, - Size imageDimentions, - Size? thumbDimentions, + (int Height, int Width) imageDimensions, + (int Height, int Width)? thumbDimensions, DateTime? dateTaken, float? gpsLatitude, float? gpsLongitude, @@ -252,20 +252,21 @@ private async Task SaveImage(Guid imageId, thumbSize!.Value, contentType, true, - thumbDimentions!.Value.Height, - thumbDimentions!.Value.Width, + thumbDimensions!.Value.Height, + thumbDimensions!.Value.Width, dateTaken, gpsLatitude, gpsLongitude) : null; + var item = new Image(imageId, name, extension, imageSize, contentType, true, - imageDimentions.Height, - imageDimentions.Width, + imageDimensions.Height, + imageDimensions.Width, dateTaken, gpsLatitude, gpsLongitude, @@ -287,6 +288,7 @@ private async Task SaveDocument(Guid id, size, contentType, true); + return await Save(item, cancellationToken); } diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Api.Application/MediatorExtensions.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Api.Application/MediatorExtensions.cs index fdfc167..9ba7dcd 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Api.Application/MediatorExtensions.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Api.Application/MediatorExtensions.cs @@ -18,15 +18,13 @@ public static class MediatorExtensions /// /// /// - public static async Task> ExecuteQueryAsync(this IMediator mediator, - QueryBase query) + public static async Task> ExecuteQueryAsync(this IMediator mediator, QueryBase query) { var result = await mediator.Send(query); - if (result == null) - return new NotFoundResult(); - - return new OkObjectResult(result); + return result is null + ? new NotFoundResult() + : new OkObjectResult(result); } /// @@ -36,15 +34,13 @@ public static async Task> ExecuteQueryAsync(this /// /// /// - public static async Task>> ExecuteQueryAsync(this IMediator mediator, - QueryPagedBase query) + public static async Task>> ExecuteQueryAsync(this IMediator mediator, QueryPagedBase query) { var result = await mediator.Send(query); - if (result == null) - return new NotFoundResult(); - - return new OkObjectResult(result); + return result is null + ? new NotFoundResult() + : new OkObjectResult(result); } /// @@ -54,15 +50,13 @@ public static async Task>> ExecuteQueryAsync /// /// /// - public static async Task> ExecuteQueryAsync(this IMediator mediator, - QueryByIdBase query) + public static async Task> ExecuteQueryAsync(this IMediator mediator, QueryByIdBase query) { - var item = await mediator.Send(query); - - if (item == null) - return new NotFoundResult(); + var result = await mediator.Send(query); - return new OkObjectResult(item); + return result is null + ? new NotFoundResult() + : new OkObjectResult(result); } /// diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Api.Application/Monaco.Template.Backend.Common.Api.Application.csproj b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Api.Application/Monaco.Template.Backend.Common.Api.Application.csproj index 9c56153..8d3f17d 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Api.Application/Monaco.Template.Backend.Common.Api.Application.csproj +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Api.Application/Monaco.Template.Backend.Common.Api.Application.csproj @@ -20,7 +20,7 @@ - + diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Api/Auth/AuthExtensions.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Api/Auth/AuthExtensions.cs index 23bf241..c9b4513 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Api/Auth/AuthExtensions.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Api/Auth/AuthExtensions.cs @@ -12,10 +12,10 @@ public static class AuthExtensions public static IServiceCollection AddAuthorizationWithPolicies(this IServiceCollection services, List scopes) => services.AddAuthorization(cfg => - { //DefaultPolicy will require at least authenticated user by default + { // DefaultPolicy will require at least authenticated user by default cfg.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme) .RequireAuthenticatedUser().Build(); - //Register all listed scopes as policies requiring the existance of such scope in User claims + // Register all listed scopes as policies requiring the existence of such scope in User claims scopes.ForEach(s => cfg.AddPolicy(s, p => p.RequireScope(s))); }); @@ -23,9 +23,9 @@ public static AuthenticationBuilder AddJwtBearerAuthentication(this IServiceColl string authority, string audience, bool requireHttpsMetadata) => - services.AddTransient() //Add transformer to map scopes correctly in ClaimsPrincipal/Identity + services.AddTransient() // Add transformer to map scopes correctly in ClaimsPrincipal/Identity .AddAuthentication() - .AddJwtBearer(options => //Configure validation settings for JWT bearer + .AddJwtBearer(options => // Configure validation settings for JWT bearer { options.Authority = authority; options.Audience = audience; diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Application/Commands/Behaviors/CommandValidationBehavior.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Application/Commands/Behaviors/CommandValidationBehavior.cs index 509febf..b64330f 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Application/Commands/Behaviors/CommandValidationBehavior.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Application/Commands/Behaviors/CommandValidationBehavior.cs @@ -55,7 +55,7 @@ public CommandValidationBehavior(IValidator validator) } /// -/// Behavior to validate the existance of the entity represented by the Command Id. +/// Behavior to validate the existence of the entity represented by the Command Id. /// /// The type of the Command to process public class CommandValidationExistsBehavior : IPipelineBehavior where TCommand : CommandBase @@ -78,7 +78,7 @@ public virtual async Task Handle(TCommand request, RequestHandle } /// -/// Behavior to validate the existance of the entity represented by the Command Id. +/// Behavior to validate the existence of the entity represented by the Command Id. /// /// The type of the Command to process /// The type of data to return along with the CommandResult diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Application/Monaco.Template.Backend.Common.Application.csproj b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Application/Monaco.Template.Backend.Common.Application.csproj index bc5a3fd..64e44d2 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Application/Monaco.Template.Backend.Common.Application.csproj +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Application/Monaco.Template.Backend.Common.Application.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Application/Validators/Extensions/ValidatorsExtensions.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Application/Validators/Extensions/ValidatorsExtensions.cs index 9b677ee..23b7ed7 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Application/Validators/Extensions/ValidatorsExtensions.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Application/Validators/Extensions/ValidatorsExtensions.cs @@ -13,11 +13,13 @@ public static class ValidatorsExtensions public static void CheckIfExists(this AbstractValidator validator, BaseDbContext dbContext) where TCommand : CommandBase where TEntity : Entity => - validator.RuleSet(ExistsRulesetName, () => validator.RuleFor(x => x.Id).MustExistAsync(dbContext)); + validator.RuleSet(ExistsRulesetName, () => validator.RuleFor(x => x.Id) + .MustExistAsync(dbContext)); public static void CheckIfExists(this AbstractValidator validator, BaseDbContext dbContext) where TCommand : CommandBase where TEntity : Entity => - validator.RuleSet(ExistsRulesetName, () => validator.RuleFor(x => x.Id).MustExistAsync(dbContext)); + validator.RuleSet(ExistsRulesetName, () => validator.RuleFor(x => x.Id) + .MustExistAsync(dbContext)); public static void CheckIfExists(this AbstractValidator validator, Func> predicate) where TCommand : CommandBase => @@ -45,9 +47,9 @@ public static void CheckIfExists(this AbstractValidator validator.RuleSet(ExistsRulesetName, () => validator.RuleFor(selector) .MustAsync(predicate)); - public static void CheckIfExists(this AbstractValidator validator, - Expression> selector, - Func> predicate) where TComomand : CommandBase => + public static void CheckIfExists(this AbstractValidator validator, + Expression> selector, + Func> predicate) where TCommand : CommandBase => validator.RuleSet(ExistsRulesetName, () => validator.RuleFor(selector) .MustAsync(predicate)); diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.BlobStorage.Tests/BlobStorageServiceTests.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.BlobStorage.Tests/BlobStorageServiceTests.cs new file mode 100644 index 0000000..b7e4388 --- /dev/null +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.BlobStorage.Tests/BlobStorageServiceTests.cs @@ -0,0 +1,51 @@ +using Azure.Storage.Blobs; +using FluentAssertions; +using Moq; +using Xunit; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Monaco.Template.Backend.Common.BlobStorage.Tests; + +[ExcludeFromCodeCoverage] +[Trait("Common Application Services", "Blob Storage Service")] +public class BlobStorageServiceTests +{ + private readonly Mock _blobServiceClientMock; + private readonly BlobStorageService _blobStorageService; + + public BlobStorageServiceTests() + { + _blobServiceClientMock = new Mock(); + _blobStorageService = new(_blobServiceClientMock.Object, It.IsAny()); + } + + [Theory(DisplayName = "Get file type succeeds")] + [MemberData(nameof(GetFileTypeTestData))] + public void GetFileTypeSucceeds(string fileExtension, FileTypeEnum expected) + { + var fileType = _blobStorageService.GetFileType(fileExtension); + fileType.Should().Be(expected); + } + + public static IEnumerable GetFileTypeTestData => + new List + { + new object[] { ".doc", FileTypeEnum.Document }, + new object[] { ".docx", FileTypeEnum.Document }, + new object[] { ".pdf", FileTypeEnum.Document }, + new object[] { ".rtf", FileTypeEnum.Document }, + new object[] { ".txt", FileTypeEnum.Document }, + new object[] { ".xls", FileTypeEnum.Document }, + new object[] { ".xlsx", FileTypeEnum.Document }, + new object[] { ".xlsm", FileTypeEnum.Document }, + new object[] { ".jpg", FileTypeEnum.Image }, + new object[] { ".jpeg", FileTypeEnum.Image }, + new object[] { ".png", FileTypeEnum.Image }, + new object[] { ".bmp", FileTypeEnum.Image }, + new object[] { ".gif", FileTypeEnum.Image }, + new object[] { ".tif", FileTypeEnum.Image }, + new object[] { ".tiff", FileTypeEnum.Image }, + new object[] { ".other", FileTypeEnum.Others } + }; +} \ No newline at end of file diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.BlobStorage.Tests/Monaco.Template.Backend.Common.BlobStorage.Tests.csproj b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.BlobStorage.Tests/Monaco.Template.Backend.Common.BlobStorage.Tests.csproj new file mode 100644 index 0000000..e1cec6b --- /dev/null +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.BlobStorage.Tests/Monaco.Template.Backend.Common.BlobStorage.Tests.csproj @@ -0,0 +1,28 @@ + + + + net7.0 + enable + false + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.BlobStorage/BlobStorageService.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.BlobStorage/BlobStorageService.cs index 051df50..197c87a 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.BlobStorage/BlobStorageService.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.BlobStorage/BlobStorageService.cs @@ -61,15 +61,13 @@ public async Task CopyAsync(Guid fileName, bool isTemp, CancellationToken return destId; } - public FileTypeEnum GetFileType(string fileExtension) - { - return fileExtension.Trim('.') switch + public FileTypeEnum GetFileType(string fileExtension) => + fileExtension.Trim('.').ToLower() switch { "doc" or "docx" or "pdf" or "rtf" or "txt" or "xls" or "xlsx" or "xlsm" => FileTypeEnum.Document, - "jpg" or "jpeg" or "png" or "bmp" or "gif" or "tiff" => FileTypeEnum.Image, + "jpg" or "jpeg" or "png" or "bmp" or "gif" or "tif" or "tiff" => FileTypeEnum.Image, _ => FileTypeEnum.Others, }; - } private static string GetTempPath(string blobFileName) => $"temp/{blobFileName}"; @@ -78,10 +76,10 @@ public FileTypeEnum GetFileType(string fileExtension) private Dictionary GetMetadata(string fileName, string contentType) => new() { - { BlobMetadata.Name, HttpUtility.UrlEncode(Path.GetFileNameWithoutExtension(fileName)) }, - { BlobMetadata.Extension, HttpUtility.UrlEncode(Path.GetExtension(fileName)) }, - { BlobMetadata.ContentType, contentType }, - { BlobMetadata.UploadedOn, DateTime.UtcNow.ToString("O") }, - { BlobMetadata.FileType, GetFileType(Path.GetExtension(fileName)).ToString()} + [BlobMetadata.Name] = HttpUtility.UrlEncode(Path.GetFileNameWithoutExtension(fileName)), + [BlobMetadata.Extension] = HttpUtility.UrlEncode(Path.GetExtension(fileName)), + [BlobMetadata.ContentType] = contentType, + [BlobMetadata.UploadedOn] = DateTime.UtcNow.ToString("O"), + [BlobMetadata.FileType] = GetFileType(Path.GetExtension(fileName)).ToString() }; } \ No newline at end of file diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.BlobStorage/Monaco.Template.Backend.Common.BlobStorage.csproj b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.BlobStorage/Monaco.Template.Backend.Common.BlobStorage.csproj index 2e50374..4edf12d 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.BlobStorage/Monaco.Template.Backend.Common.BlobStorage.csproj +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.BlobStorage/Monaco.Template.Backend.Common.BlobStorage.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/DomainEventTests.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/DomainEventTests.cs index a4400b1..678da62 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/DomainEventTests.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/DomainEventTests.cs @@ -8,12 +8,12 @@ namespace Monaco.Template.Backend.Common.Domain.Tests; [ExcludeFromCodeCoverage] +[Trait("Common Domain Entities", "Domain Event Entity")] public class DomainEventTests { - [Trait("Common Domain Entities", "Domain Event Entity")] [Theory(DisplayName = "New domain event succeeds")] [AnonymousData] - public void NewEntityWithoutParametersSucceeds(DomainEvent sut) + public void NewDomainEventWithoutParametersSucceeds(DomainEvent sut) { sut.DateOccurred.Should().BeCloseTo(DateTime.UtcNow, new TimeSpan(0, 0, 5)); } diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/EntityTests.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/EntityTests.cs index e9fb776..dbcd066 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/EntityTests.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/EntityTests.cs @@ -12,9 +12,9 @@ namespace Monaco.Template.Backend.Common.Domain.Tests; [ExcludeFromCodeCoverage] +[Trait("Common Domain Entities", "Base Entity")] public class EntityTests { - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "New entity without parameters succeeds")] [AnonymousData] public void NewEntityWithoutParametersSucceeds(Entity sut) @@ -23,7 +23,6 @@ public void NewEntityWithoutParametersSucceeds(Entity sut) sut.DomainEvents.Should().BeEmpty(); } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "New entity with parameters succeeds")] [AnonymousData] public void NewEntityWithParametersSucceeds(Guid id) @@ -37,7 +36,6 @@ public void NewEntityWithParametersSucceeds(Guid id) sut.DomainEvents.Should().BeEmpty(); } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "Add Domain Event succeeds")] [AnonymousData] public void AddDomainEventSucceeds(Entity sut, DomainEvent domainEvent) @@ -47,7 +45,6 @@ public void AddDomainEventSucceeds(Entity sut, DomainEvent domainEvent) sut.DomainEvents.Should().HaveCount(1).And.Contain(domainEvent); } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "Add duplicated domain event allowing duplicates succeeds")] [AnonymousData] public void AddDuplicatedDomainEventAllowingDuplicatesSucceeds(Entity sut, DomainEvent domainEvent) @@ -58,7 +55,6 @@ public void AddDuplicatedDomainEventAllowingDuplicatesSucceeds(Entity sut, Domai sut.DomainEvents.Should().HaveCount(2).And.Contain(new[] { domainEvent, domainEvent }); } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "Add duplicated domain event not allowing duplicates adds only one")] [AnonymousData] public void AddDuplicatedDomainEventNotAllowingDuplicatesAddsOnlyOne(Entity sut, DomainEvent domainEvent) @@ -69,7 +65,6 @@ public void AddDuplicatedDomainEventNotAllowingDuplicatesAddsOnlyOne(Entity sut, sut.DomainEvents.Should().HaveCount(1).And.Contain(domainEvent); } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "Add duplicated type of domain event not allowing duplicates adds only one")] [AnonymousData] public void AddDuplicatedTypeOfDomainEventNotAllowingDuplicatesAddsOnlyOne(Entity sut, DomainEvent domainEvent1, DomainEvent domainEvent2) @@ -80,7 +75,6 @@ public void AddDuplicatedTypeOfDomainEventNotAllowingDuplicatesAddsOnlyOne(Entit sut.DomainEvents.Should().HaveCount(1).And.Contain(domainEvent1); } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "Remove Domain Event succeeds")] [AnonymousData] public void RemoveDomainEventSucceeds(Entity sut, List domainEvents) @@ -92,7 +86,6 @@ public void RemoveDomainEventSucceeds(Entity sut, List domainEvents sut.DomainEvents.Should().HaveCount(2).And.Contain(new[] { domainEvents[1], domainEvents[2] }); } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "Remove Domain Event succeeds")] [AnonymousData] public void RemoveNonExistingDomainEventSucceeds(Entity sut, List domainEvents) @@ -107,7 +100,6 @@ public void RemoveNonExistingDomainEventSucceeds(Entity sut, List d sut.DomainEvents.Should().HaveCount(2).And.Contain(new[] { domainEvents[0], domainEvents[1] }); } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "Clear Domain Events succeeds")] [AnonymousData] public void ClearDomainEventsSucceeds(Entity sut, List domainEvents) @@ -121,7 +113,6 @@ public void ClearDomainEventsSucceeds(Entity sut, List domainEvents sut.DomainEvents.Should().BeEmpty(); } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "Entity instance equals (method) itself succeeds")] [AnonymousData] public void EntityInstanceEqualsMethodItselfSucceeds(Entity sut) @@ -129,7 +120,6 @@ public void EntityInstanceEqualsMethodItselfSucceeds(Entity sut) sut.Equals(sut).Should().BeTrue(); } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "Entity instance equals (method) another instance same Id as same succeeds")] [AnonymousData] public void EntityInstanceEqualsMethodAnotherInstanceSameIdAsSameSucceeds(Guid id) @@ -143,7 +133,6 @@ public void EntityInstanceEqualsMethodAnotherInstanceSameIdAsSameSucceeds(Guid i sut.Equals(instance).Should().BeTrue(); } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "Entity instance equals (method) another instance as different succeeds")] [AnonymousData] public void EntityInstanceEqualsMethodAnotherInstanceAsDifferentSucceeds(Entity sut, Entity instance) @@ -151,7 +140,6 @@ public void EntityInstanceEqualsMethodAnotherInstanceAsDifferentSucceeds(Entity sut.Equals(instance).Should().BeFalse(); } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "Entity instance equals (method) another different object as different succeeds ")] [AnonymousData] public void EntityInstanceEqualsMethodAnotherDifferentObjectAsDifferentSucceeds(Entity sut, object instance) @@ -159,7 +147,6 @@ public void EntityInstanceEqualsMethodAnotherDifferentObjectAsDifferentSucceeds( sut.Equals(instance).Should().BeFalse(); } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "Entity instance equals (method) another different not proxy object as different succeeds ")] [AnonymousData] public void EntityInstanceEqualsMethodAnotherDifferentNotProxyObjectAsDifferentSucceeds(Entity sut) @@ -167,7 +154,6 @@ public void EntityInstanceEqualsMethodAnotherDifferentNotProxyObjectAsDifferentS sut.Equals(new object()).Should().BeFalse(); } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "Entity instance equals (operator) itself succeeds")] [AnonymousData] public void EntityInstanceEqualsOperatorItselfSucceeds(Entity sut) @@ -178,7 +164,6 @@ public void EntityInstanceEqualsOperatorItselfSucceeds(Entity sut) #pragma warning restore CS1718 // Comparison made to same variable } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "Entity instance equals (operator) as different succeeds")] [AnonymousData] public void EntityInstanceEqualsOperatorNullAsDifferentSucceeds(Entity sut) @@ -186,14 +171,12 @@ public void EntityInstanceEqualsOperatorNullAsDifferentSucceeds(Entity sut) (sut == null).Should().BeFalse(); } - [Trait("Common Domain Entities", "Base Entity")] [Fact(DisplayName = "Entity instance null equals (operator) null as same succeeds")] public void EntityInstanceNullEqualsOperatorNullAsSameSucceeds() { (null as Entity == null).Should().BeTrue(); } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "Entity instance equals (operator) null as different succeeds")] [AnonymousData] public void EntityInstanceNotEqualsOperatorNullAsDifferentSucceeds(Entity? sut) @@ -201,7 +184,6 @@ public void EntityInstanceNotEqualsOperatorNullAsDifferentSucceeds(Entity? sut) (sut != null).Should().BeTrue(); } - [Trait("Common Domain Entities", "Base Entity")] [Theory(DisplayName = "Get Entity hash code succeeds")] [AnonymousData] public void EntityGetHashCodeSucceeds(Entity sut) diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/EnumerationTests.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/EnumerationTests.cs index a35924c..238d83a 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/EnumerationTests.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/EnumerationTests.cs @@ -10,9 +10,9 @@ namespace Monaco.Template.Backend.Common.Domain.Tests; [ExcludeFromCodeCoverage] +[Trait("Common Domain Entities", "Enumeration Entity")] public class EnumerationTests { - [Trait("Common Domain Entities", "Enumeration Entity")] [Theory(DisplayName = "New enumeration instance succeeds")] [AnonymousData] public void NewEnumerationInstanceSucceeds(Guid id, string name) @@ -26,7 +26,6 @@ public void NewEnumerationInstanceSucceeds(Guid id, string name) sut.Name.Should().Be(name); } - [Trait("Common Domain Entities", "Enumeration Entity")] [Theory(DisplayName = "Enumeration to string is name")] [AnonymousData] public void EnumerationToStringIsName(Guid id, string name) @@ -39,7 +38,6 @@ public void EnumerationToStringIsName(Guid id, string name) sut.ToString().Should().Be(name); } - [Trait("Common Domain Entities", "Enumeration Entity")] [Fact(DisplayName = "Get all items from Enumeration succeeds")] public void GetAllItemsFromEnumerationSucceeds() { @@ -47,7 +45,6 @@ public void GetAllItemsFromEnumerationSucceeds() .Should().HaveCount(2).And.Contain(new[] { DummyEnumerationDerived.Item3, DummyEnumerationDerived.Item4 }); } - [Trait("Common Domain Entities", "Enumeration Entity")] [Fact(DisplayName = "Get an enumeration item from its value succeeds")] public void GetAnEnumerationItemFromItsValueSucceeds() { @@ -58,7 +55,6 @@ public void GetAnEnumerationItemFromItsValueSucceeds() result.Should().Be(DummyEnumeration.Item1); } - [Trait("Common Domain Entities", "Enumeration Entity")] [Theory(DisplayName = "Get an enumeration item from invalid value fails")] [AnonymousData] public void GetAnEnumerationItemFromInvalidValueFails(Guid id) @@ -68,7 +64,6 @@ public void GetAnEnumerationItemFromInvalidValueFails(Guid id) action.Should().Throw().WithMessage($"'{id}' is not a valid value in {typeof(DummyEnumeration)}"); } - [Trait("Common Domain Entities", "Enumeration Entity")] [Fact(DisplayName = "Get an enumeration item from its name succeeds")] public void GetAnEnumerationItemFromItsNameSucceeds() { @@ -79,7 +74,6 @@ public void GetAnEnumerationItemFromItsNameSucceeds() result.Should().Be(DummyEnumeration.Item1); } - [Trait("Common Domain Entities", "Enumeration Entity")] [Theory(DisplayName = "Get an enumeration item from invalid value fails")] [AnonymousData] public void GetAnEnumerationItemFromInvalidNameFails(string name) @@ -89,7 +83,6 @@ public void GetAnEnumerationItemFromInvalidNameFails(string name) action.Should().Throw().WithMessage($"'{name}' is not a valid display name in {typeof(DummyEnumeration)}"); } - [Trait("Common Domain Entities", "Enumeration Entity")] [Fact(DisplayName = "Enumeration compare succeeds")] public void EnumerationCompareSucceds() { diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/Monaco.Template.Backend.Common.Domain.Tests.csproj b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/Monaco.Template.Backend.Common.Domain.Tests.csproj index 7ecb5d3..67f46b7 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/Monaco.Template.Backend.Common.Domain.Tests.csproj +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/Monaco.Template.Backend.Common.Domain.Tests.csproj @@ -8,10 +8,10 @@ - - + + - + diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/PageTests.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/PageTests.cs index c44de78..2065ccc 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/PageTests.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/PageTests.cs @@ -13,7 +13,7 @@ public class PageTests [Trait("Common Domain Entities", "Page Entity")] [Theory(DisplayName = "Create a new page succeeds")] [AnonymousData] - public void Create_a_new_page(List results, int offset, int limit, long count) + public void CreateNewPageSucceeds(List results, int offset, int limit, long count) { var sut = new Page(results, offset, limit, count); @@ -24,7 +24,7 @@ public void Create_a_new_page(List results, int offset, int limit, long [Trait("Common Domain Entities", "Pager Entity")] [Theory(DisplayName = "Create a new pager succeeds")] [AnonymousData] - public void Create_a_new_pager(int offset, int limit, long count) + public void CreateNewPagerSucceeds(int offset, int limit, long count) { var sut = new Pager(offset, limit, count); diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/ValueObjectTests.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/ValueObjectTests.cs index 1013a91..f265f79 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/ValueObjectTests.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain.Tests/ValueObjectTests.cs @@ -8,9 +8,9 @@ namespace Monaco.Template.Backend.Common.Domain.Tests; [ExcludeFromCodeCoverage] +[Trait("Common Domain entities", "ValueObject")] public class ValueObjectTests { - [Trait("Common Domain entities", "ValueObject")] [Theory(DisplayName = "New ValueObject instance succeeds")] [AnonymousData] public void NewValueObjectInstanceSucceeds(string field1, string? field2) @@ -21,7 +21,6 @@ public void NewValueObjectInstanceSucceeds(string field1, string? field2) sut.Field2.Should().Be(field2); } - [Trait("Common Domain entities", "ValueObject")] [Theory(DisplayName = "Different ValueObject instances with same values are equal")] [AnonymousData] public void DifferentValueObjectInstancesWithSameValuesAreEqual(string field1, string? field2) @@ -33,7 +32,6 @@ public void DifferentValueObjectInstancesWithSameValuesAreEqual(string field1, s val1.Equals(val2).Should().BeTrue(); } - [Trait("Common Domain entities", "ValueObject")] [Theory(DisplayName = "Different ValueObject instances with same values are not equal")] [AnonymousData] public void DifferentValueObjectInstancesWithDifferentValuesAreNotEqual(string field1, string? field2, string? field3) @@ -45,7 +43,6 @@ public void DifferentValueObjectInstancesWithDifferentValuesAreNotEqual(string f val1.Equals(val2).Should().BeFalse(); } - [Trait("Common Domain entities", "ValueObject")] [Theory(DisplayName = "Different ValueObject instances with same values are not equal")] [AnonymousData] public void ValueObjectComparedAgainstNullIsNotEqual(string field1, string? field2) diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain/Monaco.Template.Backend.Common.Domain.csproj b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain/Monaco.Template.Backend.Common.Domain.csproj index 2546c40..b1c3857 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain/Monaco.Template.Backend.Common.Domain.csproj +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Domain/Monaco.Template.Backend.Common.Domain.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Infrastructure/Context/Extensions/FilterExtensions.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Infrastructure/Context/Extensions/FilterExtensions.cs index 31ce890..2a4f247 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Infrastructure/Context/Extensions/FilterExtensions.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Infrastructure/Context/Extensions/FilterExtensions.cs @@ -59,13 +59,13 @@ public static IEnumerable ApplyFilter(this IEnumerable source, var (filterMapLower, filterList, predicate) = GetData(queryString, filterMap, defaultCondition); - foreach (var (key, values) in filterList) //and while looping through the list of valid ones to use + foreach (var (key, values) in filterList) // and while looping through the list of valid ones to use { - var predicateKey = PredicateBuilder.New(false); //Declare a PredicateBuilder for the current key values + var predicateKey = PredicateBuilder.New(false); // Declare a PredicateBuilder for the current key values predicateKey = values.Where(value => ValidateDataType(value, GetBodyExpression(filterMapLower[key]).Type)) - .Select(value => GetOperationExpression(key, filterMapLower[key], value, true)) //then generate the expresion for each value - .Aggregate(predicateKey, (current, expr) => current.Or(expr)); //and chain them all with an OR operator - predicate = allConditions ? predicate.And(predicateKey) : predicate.Or(predicateKey); //then add the resulting expresion to the more general predicate + .Select(value => GetOperationExpression(key, filterMapLower[key], value, true)) // then generate the expression for each value + .Aggregate(predicateKey, (current, expr) => current.Or(expr)); // and chain them all with an OR operator + predicate = allConditions ? predicate.And(predicateKey) : predicate.Or(predicateKey); // then add the resulting expression to the more general predicate } return source.Where(predicate); @@ -78,13 +78,13 @@ private static (Dictionary>> filterMapLower, Dictionary>> filterMap, bool defaultCondition) { - //convert to Dictionary with Keys in Lowercase to ease search + // convert to Dictionary with Keys in Lowercase to ease search var filterMapLower = filterMap.ToDictionary(x => x.Key.ToLower(), x => x.Value); - //convert field list to filter in a lowercase list, then filter the ones that don't exist and the null ones + // convert field list to filter in a lowercase list, then filter the ones that don't exist and the null ones var filterList = queryString.ToDictionary(q => q.Key.ToLower(), q => q.Value.Select(v => v?.ToLower())) .Where(x => filterMapLower.ContainsKey(x.Key.ToLower())); - var predicate = PredicateBuilder.New(defaultCondition); //Generate a PredicateBuilder (True to return all by default) + var predicate = PredicateBuilder.New(defaultCondition); // Generate a PredicateBuilder (True to return all by default) return (filterMapLower, filterList, predicate); } @@ -94,21 +94,21 @@ private static Expression GetBodyExpression(Expression> expre private static Expression> GetOperationExpression(string fieldKey, Expression> fieldMap, object? value, bool toLowerCase = false) { - //Obtain expression Type and based on it y choose the method for the operation on the DB depending on if it's a String or any other type + // Obtain expression Type and based on it choose the method for the operation on the DB depending on if it's a String or any other type var bodyExpression = GetBodyExpression(fieldMap); var type = bodyExpression.Type; - //Create the call to the method using the selected expression, the method and the value to search. If it's a string and it´s working on an IEnumerable, first apply ToLower + // Create the call to the method using the selected expression, the method and the value to search. If it's a string and it´s working on an IEnumerable, first apply ToLower Expression expression; - if ((value as string) is not { Length: > 0 }) //Handles comparison against null values + if ((value as string) is not { Length: > 0 }) // Handles comparison against null values expression = Expression.Equal(type == typeof(string) || type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) ? bodyExpression //for strings : Expression.Convert(bodyExpression, typeof(Nullable<>).MakeGenericType(type)), //for all others Expression.Constant(null)); - else if (type == typeof(string)) //Handles string values + else if (type == typeof(string)) // Handles string values { - expression = toLowerCase //If it's needed, applies lower case to entire string to ignore casing differences + expression = toLowerCase // If needed, applies lower case to entire string to ignore casing differences ? Expression.Call(bodyExpression, type.GetMethod(nameof(string.ToLower), Type.EmptyTypes)!) : bodyExpression; @@ -116,14 +116,14 @@ private static Expression> GetOperationExpression(string fieldK var not = strValue is ['!', ..]; if (not) strValue = strValue[1..]; - if (strValue is ['"', .., '"']) //quoted strings searches as exactly the same + if (strValue is ['"', .., '"']) // quoted strings searches as exactly the same { var constExpr = Expression.Constant(Convert.ChangeType(strValue[1..^1], type)); expression = not ? Expression.NotEqual(expression, constExpr) : Expression.Equal(expression, constExpr); } - else //otherwise searches with Contains + else // otherwise searches with Contains { expression = Expression.Call(expression, type.GetMethod(nameof(string.Contains), new[] { type })!, @@ -133,15 +133,15 @@ private static Expression> GetOperationExpression(string fieldK } else if (type.IsEnum) expression = Expression.Equal(bodyExpression, Expression.Constant(Enum.Parse(type, (string.IsNullOrWhiteSpace(value?.ToString()) ? 0.ToString() : value!.ToString()) ?? 0.ToString()))); - else if (type.IsAssignableFrom(typeof(Guid))) //Handles Guid values + else if (type.IsAssignableFrom(typeof(Guid))) // Handles Guid values expression = value.ToString() is ['!', ..] ? Expression.NotEqual(bodyExpression, Expression.Constant(Guid.Parse(value.ToString()![1..]), type)) : Expression.Equal(bodyExpression, Expression.Constant(Guid.Parse(value.ToString()!), type)); - else if (type.IsAssignableFrom(typeof(DateTime)) && fieldKey.EndsWith("from")) //Handles DateTime values whose param name ends with From (range start) + else if (type.IsAssignableFrom(typeof(DateTime)) && fieldKey.EndsWith("from")) // Handles DateTime values whose param name ends with From (range start) expression = Expression.GreaterThanOrEqual(bodyExpression, Expression.Constant(DateTime.Parse(value.ToString()!), type)); - else if (type.IsAssignableFrom(typeof(DateTime)) && fieldKey.EndsWith("to")) //Handles DateTime values whose param name ends with To (range end) + else if (type.IsAssignableFrom(typeof(DateTime)) && fieldKey.EndsWith("to")) // Handles DateTime values whose param name ends with To (range end) expression = Expression.LessThanOrEqual(bodyExpression, Expression.Constant(DateTime.Parse(value.ToString()!), type)); - else //Handles all other generic cases (numbers, booleans, etc) + else // Handles all other generic cases (numbers, booleans, etc) { var underlyingType = Nullable.GetUnderlyingType(type); expression = value.ToString() is ['!', ..] @@ -149,7 +149,7 @@ private static Expression> GetOperationExpression(string fieldK : Expression.Equal(Expression.Convert(bodyExpression, underlyingType ?? type), Expression.Constant(Convert.ChangeType(value, underlyingType ?? type))); } - //Create and return the lambda expression that represents the operation to run + // Create and return the lambda expression that represents the operation to run return Expression.Lambda>(expression, fieldMap.Parameters); } diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Infrastructure/Context/Extensions/OperationsExtensions.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Infrastructure/Context/Extensions/OperationsExtensions.cs index 3f587bf..2978a23 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Infrastructure/Context/Extensions/OperationsExtensions.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Infrastructure/Context/Extensions/OperationsExtensions.cs @@ -13,7 +13,7 @@ public static Task ExistsAsync(this DbContext dbContext, public static Task ExistsAsync(this DbContext dbContext, Expression> predicate, - CancellationToken cancellationToken) where T : class => + CancellationToken cancellationToken) where T : class => dbContext.Set().AnyAsync(predicate, cancellationToken); public static async Task GetAsync(this DbContext dbContext, @@ -33,4 +33,13 @@ public static IQueryable Set(this DbContext context, Type t) = .GetMethod("Set", Type.EmptyTypes)? .MakeGenericMethod(t) .Invoke(context, Array.Empty())!; + + public static async Task> GetListByIdsAsync(this DbContext dbContext, + IEnumerable items, + CancellationToken cancellationToken) where T : Entity => + items?.Any() == true + ? await dbContext.Set() + .Where(x => items.Contains(x.Id)) + .ToListAsync(cancellationToken) + : new(); } \ No newline at end of file diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Infrastructure/Monaco.Template.Backend.Common.Infrastructure.csproj b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Infrastructure/Monaco.Template.Backend.Common.Infrastructure.csproj index 85a7980..bf811e9 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Infrastructure/Monaco.Template.Backend.Common.Infrastructure.csproj +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Infrastructure/Monaco.Template.Backend.Common.Infrastructure.csproj @@ -27,7 +27,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Tests/Monaco.Template.Backend.Common.Tests.csproj b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Tests/Monaco.Template.Backend.Common.Tests.csproj index 279f811..bc3fb28 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Tests/Monaco.Template.Backend.Common.Tests.csproj +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Tests/Monaco.Template.Backend.Common.Tests.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Domain.Tests/AddressTests.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Domain.Tests/AddressTests.cs index f347dde..216e489 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Domain.Tests/AddressTests.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Domain.Tests/AddressTests.cs @@ -7,9 +7,9 @@ namespace Monaco.Template.Backend.Domain.Tests; [ExcludeFromCodeCoverage] +[Trait("Core Domain Entities", "Address Entity")] public class AddressTests { - [Trait("Core Domain Entities", "Address Entity")] [Theory(DisplayName = "New address succeeds")] [AnonymousData] public void NewAddressSucceeds(string? street, diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Domain.Tests/CompanyTests.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Domain.Tests/CompanyTests.cs index 11554fd..c15865b 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Domain.Tests/CompanyTests.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Domain.Tests/CompanyTests.cs @@ -7,9 +7,9 @@ namespace Monaco.Template.Backend.Domain.Tests; [ExcludeFromCodeCoverage] +[Trait("Core Domain Entities", "Company Entity")] public class CompanyTests { - [Trait("Core Domain Entities", "Company Entity")] [Theory(DisplayName = "New company succeeds")] [AnonymousData] public void NewCompanySucceeds(string name, @@ -29,7 +29,6 @@ public void NewCompanySucceeds(string name, sut.Version.Should().BeNull(); } - [Trait("Core Domain Entities", "Company Entity")] [Theory(DisplayName = "New company succeeds")] [AnonymousData] public void UpdateCompanySucceeds(Company sut, diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Domain.Tests/CountryTests.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Domain.Tests/CountryTests.cs index 86d5eb1..e13274f 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Domain.Tests/CountryTests.cs +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Domain.Tests/CountryTests.cs @@ -7,9 +7,9 @@ namespace Monaco.Template.Backend.Domain.Tests; [ExcludeFromCodeCoverage] +[Trait("Core Domain Entities", "Country Entity")] public class CountryTests { - [Trait("Core Domain Entities", "Country Entity")] [Theory(DisplayName = "New country succeeds")] [AnonymousData] public void NewCountrySucceeds(string name) diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Domain.Tests/Monaco.Template.Backend.Domain.Tests.csproj b/src/Content/Backend/Solution/Monaco.Template.Backend.Domain.Tests/Monaco.Template.Backend.Domain.Tests.csproj index 8b21c55..e5812c1 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Domain.Tests/Monaco.Template.Backend.Domain.Tests.csproj +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Domain.Tests/Monaco.Template.Backend.Domain.Tests.csproj @@ -8,7 +8,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.sln b/src/Content/Backend/Solution/Monaco.Template.Backend.sln index 2a93f46..702c527 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.sln +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.sln @@ -64,6 +64,8 @@ EndProject #if (filesSupport) Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monaco.Template.Backend.Common.BlobStorage", "Monaco.Template.Backend.Common.BlobStorage\Monaco.Template.Backend.Common.BlobStorage.csproj", "{42E51D47-B82F-4A92-B1E5-CD8BE44DE6F0}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monaco.Template.Backend.Common.BlobStorage.Tests", "Monaco.Template.Backend.Common.BlobStorage.Tests\Monaco.Template.Backend.Common.BlobStorage.Tests.csproj", "{D8623B90-59C1-4753-A0E6-F2DBD4305C9B}" +EndProject #endif Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -150,6 +152,10 @@ Global {42E51D47-B82F-4A92-B1E5-CD8BE44DE6F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {42E51D47-B82F-4A92-B1E5-CD8BE44DE6F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {42E51D47-B82F-4A92-B1E5-CD8BE44DE6F0}.Release|Any CPU.Build.0 = Release|Any CPU + {D8623B90-59C1-4753-A0E6-F2DBD4305C9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D8623B90-59C1-4753-A0E6-F2DBD4305C9B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D8623B90-59C1-4753-A0E6-F2DBD4305C9B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D8623B90-59C1-4753-A0E6-F2DBD4305C9B}.Release|Any CPU.Build.0 = Release|Any CPU #endif EndGlobalSection GlobalSection(SolutionProperties) = preSolution @@ -173,11 +179,11 @@ Global #if (apiGateway) {815EF0B0-5FDA-4D1E-BACE-DA7E440D5D34} = {8BFE9C37-2620-4156-88B8-286537954C5E} #endif - {426A6AB4-95F6-46AC-AB6D-98C4612F74E5} = {35484293-234F-40DA-B430-A95170EDE449} {E5781B96-E0CA-4762-9412-C4202E0CA08F} = {920A23AF-59DA-4453-B825-0549B1C04F5B} {B09E0906-8522-4B70-8C55-958415DAF21D} = {920A23AF-59DA-4453-B825-0549B1C04F5B} #if (filesSupport) {42E51D47-B82F-4A92-B1E5-CD8BE44DE6F0} = {8BFE9C37-2620-4156-88B8-286537954C5E} + {D8623B90-59C1-4753-A0E6-F2DBD4305C9B} = {35484293-234F-40DA-B430-A95170EDE449} #endif EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/src/Monaco.Template.nuspec b/src/Monaco.Template.nuspec index 25f21dd..68e0883 100644 --- a/src/Monaco.Template.nuspec +++ b/src/Monaco.Template.nuspec @@ -2,7 +2,7 @@ Monaco.Template - 2.0.2 + 2.0.3 Monaco Template Templates for .NET projects