From e82720b421e70ae60d04eddbe9dc63fbf72d559e Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Tue, 27 Feb 2024 09:32:08 +0500 Subject: [PATCH 1/2] Add abstract class CrudWellRelatedClient --- .../CrudWellRelatedRepositoryBase.cs | 1 - .../Clients/ICrudWellRelatedClient.cs | 29 ++++ .../Controllers/AdminDepositControllerTest.cs | 2 +- .../ProcessMapPlanDrillingControllerTest.cs | 2 +- .../Controllers/ScheduleControllerTest.cs | 159 ++++++++++++++++++ .../Controllers/SlipsStatControllerTest.cs | 2 +- .../WellOperationControllerTest.cs | 2 +- .../WebAppFactoryFixture.cs | 6 +- 8 files changed, 196 insertions(+), 7 deletions(-) create mode 100644 AsbCloudWebApi.IntegrationTests/Clients/ICrudWellRelatedClient.cs create mode 100644 AsbCloudWebApi.IntegrationTests/Controllers/ScheduleControllerTest.cs diff --git a/AsbCloudInfrastructure/Repository/CrudWellRelatedRepositoryBase.cs b/AsbCloudInfrastructure/Repository/CrudWellRelatedRepositoryBase.cs index d788f3d6..d6b267a8 100644 --- a/AsbCloudInfrastructure/Repository/CrudWellRelatedRepositoryBase.cs +++ b/AsbCloudInfrastructure/Repository/CrudWellRelatedRepositoryBase.cs @@ -43,5 +43,4 @@ namespace AsbCloudInfrastructure.Repository return dtos; } } - } diff --git a/AsbCloudWebApi.IntegrationTests/Clients/ICrudWellRelatedClient.cs b/AsbCloudWebApi.IntegrationTests/Clients/ICrudWellRelatedClient.cs new file mode 100644 index 00000000..7f99ba5f --- /dev/null +++ b/AsbCloudWebApi.IntegrationTests/Clients/ICrudWellRelatedClient.cs @@ -0,0 +1,29 @@ +using AsbCloudApp.Data; +using Refit; + +namespace AsbCloudWebApi.IntegrationTests.Clients; + +public interface ICrudWellRelatedClient + where TDto : IId, IWellRelated +{ + [Post("/")] + Task> InsertAsync([Body] TDto dto); + + [Post("/range")] + Task> InsertRangeAsync([Body] IEnumerable dtos); + + [Get("/")] + Task>> GetAllAsync(); + + [Get("/well/{idWell}")] + Task>> GetByIdWellAsync(int idWell); + + [Get("/{id}")] + Task> GetOrDefaultAsync(int id); + + [Put("/")] + Task> UpdateAsync([Body] TDto dto); + + [Delete("/{id}")] + Task> DeleteAsync(int id); +} diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/AdminDepositControllerTest.cs b/AsbCloudWebApi.IntegrationTests/Controllers/AdminDepositControllerTest.cs index 1d2a2c3c..331c7b7d 100644 --- a/AsbCloudWebApi.IntegrationTests/Controllers/AdminDepositControllerTest.cs +++ b/AsbCloudWebApi.IntegrationTests/Controllers/AdminDepositControllerTest.cs @@ -26,7 +26,7 @@ public class AdminDepositControllerTest : BaseIntegrationTest public AdminDepositControllerTest(WebAppFactoryFixture factory) : base(factory) { - client = factory.GetAuthorizedHttpClient(); + client = factory.GetAuthorizedHttpClient(string.Empty); dbContext.CleanupDbSet(); } diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/ProcessMapPlanDrillingControllerTest.cs b/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/ProcessMapPlanDrillingControllerTest.cs index f06e2133..f04ba9ca 100644 --- a/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/ProcessMapPlanDrillingControllerTest.cs +++ b/AsbCloudWebApi.IntegrationTests/Controllers/ProcessMapPlan/ProcessMapPlanDrillingControllerTest.cs @@ -82,7 +82,7 @@ public class ProcessMapPlanDrillingControllerTest: BaseIntegrationTest public ProcessMapPlanDrillingControllerTest(WebAppFactoryFixture factory) : base(factory) { dbContext.CleanupDbSet(); - client = factory.GetAuthorizedHttpClient(); + client = factory.GetAuthorizedHttpClient(string.Empty); } [Fact] diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/ScheduleControllerTest.cs b/AsbCloudWebApi.IntegrationTests/Controllers/ScheduleControllerTest.cs new file mode 100644 index 00000000..ebc52258 --- /dev/null +++ b/AsbCloudWebApi.IntegrationTests/Controllers/ScheduleControllerTest.cs @@ -0,0 +1,159 @@ +using AsbCloudApp.Data; +using AsbCloudDb.Model; +using AsbCloudInfrastructure; +using AsbCloudWebApi.IntegrationTests.Clients; +using Mapster; +using Microsoft.EntityFrameworkCore; +using System.Net; +using Xunit; + +namespace AsbCloudWebApi.IntegrationTests.Controllers +{ + public abstract class CrudWellRelatedClient : BaseIntegrationTest + where TDto : AsbCloudApp.Data.IId, AsbCloudApp.Data.IWellRelated + where TEntity : class, AsbCloudDb.Model.IId, AsbCloudDb.Model.IWellRelated + { + public abstract IEnumerable ValidDtos { get; } + public abstract IEnumerable InvalidDtos { get; } + protected List ExcludeProps { get; set; } = new() { "Id" }; + + protected ICrudWellRelatedClient client; + + public CrudWellRelatedClient(WebAppFactoryFixture factory, string uriSuffix) + : base(factory) + { + client = factory.GetAuthorizedHttpClient>(uriSuffix); + } + + protected async Task> GetCleanDbSet() + { + var dbset = dbContext.Set(); + dbset.RemoveRange(dbset); + await dbContext.SaveChangesAsync(CancellationToken.None); + return dbset; + } + + [Fact] + public async Task Insert_returns_success() + { + foreach (var validDto in ValidDtos) + await _Insert_returns_success(validDto); + } + + private async Task _Insert_returns_success(TDto validDto) + { + var dbset = await GetCleanDbSet(); + + //act + var response = await client.InsertAsync(validDto); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.True(response.Content > 0); + + var entity = dbset.First(); + var fromDbDto = Convert(entity); + MatchHelper.Match(validDto, fromDbDto, ExcludeProps); + } + + [Fact] + public async Task Insert_fails() + { + foreach (var inValidDto in InvalidDtos) + await _Insert_fails(inValidDto); + } + + private async Task _Insert_fails(TDto invalidDto) + { + var dbset = await GetCleanDbSet(); + //act + var response = await client.InsertAsync(invalidDto); + + //assert + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } + + protected virtual TDto Convert(TEntity entity) + { + var dto = entity.Adapt(); + return dto; + } + } + + public class ScheduleControllerTest : CrudWellRelatedClient + { + static DrillerDto driller = new (){ + Id = 2 , + Name = "Name" , + Patronymic = "Patronymic", + Surname = "Surname", + }; + static Well well = Data.Defaults.Wells.First(); + + public override IEnumerable ValidDtos { get; } = new ScheduleDto[] + { + new() { + Id = 1, + IdWell = well.Id, + Driller = driller, + IdDriller = driller.Id, + DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), + DrillEnd = new DateTime(2024, 1, 2, 0, 0, 0, DateTimeKind.Unspecified), + ShiftStart = new TimeDto(8, 0, 0), + ShiftEnd = new TimeDto(20, 0, 0), + }, + new() { + Id = 1, + IdWell = well.Id, + Driller = driller, + IdDriller = driller.Id, + DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), + DrillEnd = new DateTime(2024, 1, 2, 0, 0, 0, DateTimeKind.Unspecified), + ShiftStart = new TimeDto(20, 0, 0), + ShiftEnd = new TimeDto(8, 0, 0), + } + }; + + public override IEnumerable InvalidDtos { get; } = new ScheduleDto[] + { + new() { + Id = 1, + IdWell = well.Id, + Driller = driller, + IdDriller = driller.Id, + DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), + DrillEnd = new DateTime(2022, 1, 2, 0, 0, 0, DateTimeKind.Unspecified), + ShiftStart = new TimeDto(8, 0, 0), + ShiftEnd = new TimeDto(20, 0, 0), + }, + new() { + Id = 1, + IdWell = well.Id, + Driller = driller, + IdDriller = int.MaxValue - 1, + DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), + DrillEnd = new DateTime(2024, 1, 2, 0, 0, 0, DateTimeKind.Unspecified), + ShiftStart = new TimeDto(8, 0, 0), + ShiftEnd = new TimeDto(20, 0, 0), + } + }; + + public ScheduleControllerTest(WebAppFactoryFixture factory) + : base(factory, "api/schedule") + { + var dbDriller = driller.Adapt(); + dbContext.Set().Add(dbDriller); + dbContext.SaveChanges(); + + ExcludeProps.Add(nameof(ScheduleDto.Driller)); + } + + protected override ScheduleDto Convert(Schedule entity) + { + var dto = base.Convert(entity); + dto.DrillStart = entity.DrillStart.ToRemoteDateTime(well.Timezone.Hours); + dto.DrillEnd = entity.DrillEnd.ToRemoteDateTime(well.Timezone.Hours); + return dto; + } + } +} diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/SlipsStatControllerTest.cs b/AsbCloudWebApi.IntegrationTests/Controllers/SlipsStatControllerTest.cs index 908d5b95..57eacadf 100644 --- a/AsbCloudWebApi.IntegrationTests/Controllers/SlipsStatControllerTest.cs +++ b/AsbCloudWebApi.IntegrationTests/Controllers/SlipsStatControllerTest.cs @@ -66,7 +66,7 @@ public class SlipsStatControllerTest : BaseIntegrationTest wellOperations.Add(factWellOperation); dbContext.SaveChanges(); - client = factory.GetAuthorizedHttpClient(); + client = factory.GetAuthorizedHttpClient(string.Empty); } [Fact] diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/WellOperationControllerTest.cs b/AsbCloudWebApi.IntegrationTests/Controllers/WellOperationControllerTest.cs index a47d5f09..459e6b52 100644 --- a/AsbCloudWebApi.IntegrationTests/Controllers/WellOperationControllerTest.cs +++ b/AsbCloudWebApi.IntegrationTests/Controllers/WellOperationControllerTest.cs @@ -44,7 +44,7 @@ public class WellOperationControllerTest : BaseIntegrationTest public WellOperationControllerTest(WebAppFactoryFixture factory) : base(factory) { - client = factory.GetAuthorizedHttpClient(); + client = factory.GetAuthorizedHttpClient(string.Empty); } /// diff --git a/AsbCloudWebApi.IntegrationTests/WebAppFactoryFixture.cs b/AsbCloudWebApi.IntegrationTests/WebAppFactoryFixture.cs index aa8bd27e..44bd4b67 100644 --- a/AsbCloudWebApi.IntegrationTests/WebAppFactoryFixture.cs +++ b/AsbCloudWebApi.IntegrationTests/WebAppFactoryFixture.cs @@ -83,10 +83,12 @@ public class WebAppFactoryFixture : WebApplicationFactory, return httpClient; } - public T GetAuthorizedHttpClient() + public T GetAuthorizedHttpClient(string uriSuffix) { var httpClient = GetAuthorizedHttpClient(); + if (!string.IsNullOrEmpty(uriSuffix)) + if(httpClient.BaseAddress is not null) + httpClient.BaseAddress = new Uri(httpClient.BaseAddress, uriSuffix); return RestService.For(httpClient, refitSettings); } - } \ No newline at end of file From a9dc67f83fd123f11f08461641796939719a1688 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Tue, 27 Feb 2024 13:44:14 +0500 Subject: [PATCH 2/2] ScheduleDto Add IValidatableObject --- AsbCloudApp/Data/ScheduleDto.cs | 10 +- .../Controllers/ScheduleControllerTest.cs | 117 ++++++++++++++---- .../Data/Defaults.cs | 12 +- 3 files changed, 112 insertions(+), 27 deletions(-) diff --git a/AsbCloudApp/Data/ScheduleDto.cs b/AsbCloudApp/Data/ScheduleDto.cs index ea6d54b0..84cbe515 100644 --- a/AsbCloudApp/Data/ScheduleDto.cs +++ b/AsbCloudApp/Data/ScheduleDto.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace AsbCloudApp.Data @@ -6,7 +7,7 @@ namespace AsbCloudApp.Data /// /// Описание данных графика работ /// - public class ScheduleDto : IId, IWellRelated + public class ScheduleDto : IId, IWellRelated, IValidatableObject { /// [Required] @@ -50,5 +51,12 @@ namespace AsbCloudApp.Data /// Бурильщик /// public DrillerDto? Driller { get; set; } + + /// + public IEnumerable Validate(ValidationContext validationContext) + { + if(DrillStart >= DrillEnd) + yield return new ValidationResult($"DrillStart > DrillEnd"); + } } } diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/ScheduleControllerTest.cs b/AsbCloudWebApi.IntegrationTests/Controllers/ScheduleControllerTest.cs index ebc52258..a58e20f1 100644 --- a/AsbCloudWebApi.IntegrationTests/Controllers/ScheduleControllerTest.cs +++ b/AsbCloudWebApi.IntegrationTests/Controllers/ScheduleControllerTest.cs @@ -4,6 +4,7 @@ using AsbCloudInfrastructure; using AsbCloudWebApi.IntegrationTests.Clients; using Mapster; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; using System.Net; using Xunit; @@ -15,9 +16,10 @@ namespace AsbCloudWebApi.IntegrationTests.Controllers { public abstract IEnumerable ValidDtos { get; } public abstract IEnumerable InvalidDtos { get; } + public abstract IEnumerable ForbiddenDtos { get; } protected List ExcludeProps { get; set; } = new() { "Id" }; - protected ICrudWellRelatedClient client; + protected ICrudWellRelatedClient client; public CrudWellRelatedClient(WebAppFactoryFixture factory, string uriSuffix) : base(factory) @@ -34,13 +36,13 @@ namespace AsbCloudWebApi.IntegrationTests.Controllers } [Fact] - public async Task Insert_returns_success() + public async Task Insert_returns_success_for_validDtos() { foreach (var validDto in ValidDtos) - await _Insert_returns_success(validDto); + await Insert_returns_success(validDto); } - private async Task _Insert_returns_success(TDto validDto) + private async Task Insert_returns_success(TDto validDto) { var dbset = await GetCleanDbSet(); @@ -57,15 +59,16 @@ namespace AsbCloudWebApi.IntegrationTests.Controllers } [Fact] - public async Task Insert_fails() + public async Task Insert_returns_badRequest_for_invalidDtos() { foreach (var inValidDto in InvalidDtos) - await _Insert_fails(inValidDto); + await Insert_returns_badRequest(inValidDto); } - private async Task _Insert_fails(TDto invalidDto) + private async Task Insert_returns_badRequest(TDto invalidDto) { - var dbset = await GetCleanDbSet(); + await GetCleanDbSet(); + //act var response = await client.InsertAsync(invalidDto); @@ -73,21 +76,73 @@ namespace AsbCloudWebApi.IntegrationTests.Controllers Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); } + [Fact] + public async Task Insert_returns_forbidden_for_forbiddenDtos() + { + foreach (var forbiddenDto in ForbiddenDtos) + await Insert_returns_forbidden(forbiddenDto); + } + + private async Task Insert_returns_forbidden(TDto forbiddenDto) + { + await GetCleanDbSet(); + + //act + var response = await client.InsertAsync(forbiddenDto); + + //assert + Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); + } + + [Fact] + public async Task GetAllAsync_returns_data() + { + //arrange + var dbset = await GetCleanDbSet(); + var entries = new List<(EntityEntry, TDto)>(); + + foreach (var dto in ValidDtos) + { + var entity = Convert(dto); + entity.Id = 0; + var entry = dbset.Add(entity); + entries.Add((entry, dto)); + } + dbContext.SaveChanges(); + + //act + var response = await client.GetAllAsync(); + + //assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.NotNull(response.Content); + Assert.Equal(entries.Count, response.Content.Count()); + + foreach (var item in response.Content) + { + var entry = entries.First(e => e.Item1.Entity.Id == item.Id); + MatchHelper.Match(entry.Item2, item, ExcludeProps); + } + } + protected virtual TDto Convert(TEntity entity) { var dto = entity.Adapt(); return dto; } + + protected virtual TEntity Convert(TDto dto) + { + var entity = dto.Adapt(); + return entity; + } } public class ScheduleControllerTest : CrudWellRelatedClient { - static DrillerDto driller = new (){ - Id = 2 , - Name = "Name" , - Patronymic = "Patronymic", - Surname = "Surname", - }; + static Driller driller = Data.Defaults.Drillers.First(); + static DrillerDto drillerDto = driller.Adapt(); + static Well well = Data.Defaults.Wells.First(); public override IEnumerable ValidDtos { get; } = new ScheduleDto[] @@ -95,7 +150,7 @@ namespace AsbCloudWebApi.IntegrationTests.Controllers new() { Id = 1, IdWell = well.Id, - Driller = driller, + Driller = drillerDto, IdDriller = driller.Id, DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), DrillEnd = new DateTime(2024, 1, 2, 0, 0, 0, DateTimeKind.Unspecified), @@ -105,7 +160,7 @@ namespace AsbCloudWebApi.IntegrationTests.Controllers new() { Id = 1, IdWell = well.Id, - Driller = driller, + Driller = drillerDto, IdDriller = driller.Id, DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), DrillEnd = new DateTime(2024, 1, 2, 0, 0, 0, DateTimeKind.Unspecified), @@ -117,9 +172,8 @@ namespace AsbCloudWebApi.IntegrationTests.Controllers public override IEnumerable InvalidDtos { get; } = new ScheduleDto[] { new() { - Id = 1, IdWell = well.Id, - Driller = driller, + Driller = drillerDto, IdDriller = driller.Id, DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), DrillEnd = new DateTime(2022, 1, 2, 0, 0, 0, DateTimeKind.Unspecified), @@ -127,9 +181,8 @@ namespace AsbCloudWebApi.IntegrationTests.Controllers ShiftEnd = new TimeDto(20, 0, 0), }, new() { - Id = 1, IdWell = well.Id, - Driller = driller, + Driller = drillerDto, IdDriller = int.MaxValue - 1, DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), DrillEnd = new DateTime(2024, 1, 2, 0, 0, 0, DateTimeKind.Unspecified), @@ -138,13 +191,21 @@ namespace AsbCloudWebApi.IntegrationTests.Controllers } }; + public override IEnumerable ForbiddenDtos { get; } = new ScheduleDto[] { + new() { + IdWell = int.MaxValue - 1, + Driller = drillerDto, + IdDriller = driller.Id, + DrillStart = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), + DrillEnd = new DateTime(2022, 1, 2, 0, 0, 0, DateTimeKind.Unspecified), + ShiftStart = new TimeDto(8, 0, 0), + ShiftEnd = new TimeDto(20, 0, 0), + } + }; + public ScheduleControllerTest(WebAppFactoryFixture factory) : base(factory, "api/schedule") { - var dbDriller = driller.Adapt(); - dbContext.Set().Add(dbDriller); - dbContext.SaveChanges(); - ExcludeProps.Add(nameof(ScheduleDto.Driller)); } @@ -155,5 +216,13 @@ namespace AsbCloudWebApi.IntegrationTests.Controllers dto.DrillEnd = entity.DrillEnd.ToRemoteDateTime(well.Timezone.Hours); return dto; } + + protected override Schedule Convert(ScheduleDto dto) + { + var entity = base.Convert(dto); + entity.DrillStart = dto.DrillStart.FromTimeZoneOffsetHours(well.Timezone.Hours); + entity.DrillEnd = dto.DrillEnd.FromTimeZoneOffsetHours(well.Timezone.Hours); + return entity; + } } } diff --git a/AsbCloudWebApi.IntegrationTests/Data/Defaults.cs b/AsbCloudWebApi.IntegrationTests/Data/Defaults.cs index 45219449..4142705c 100644 --- a/AsbCloudWebApi.IntegrationTests/Data/Defaults.cs +++ b/AsbCloudWebApi.IntegrationTests/Data/Defaults.cs @@ -10,8 +10,16 @@ namespace AsbCloudWebApi.IntegrationTests.Data new() { Id = 1, - Name = "test", - Surname = "test" + Name = "test1", + Surname = "test1", + Patronymic = "test1" + }, + new() + { + Id = 2, + Name = "test2", + Surname = "test2", + Patronymic = "test2" } };