From c1729d06c7edc092916072a213432e1bbf49d8ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Mon, 22 Jan 2024 17:22:11 +0500 Subject: [PATCH] =?UTF-8?q?=D0=A3=D0=B4=D0=B5=D1=80=D0=B6=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=B2=20=D0=BA=D0=BB=D0=B8=D0=BD=D1=8C=D1=8F?= =?UTF-8?q?=D1=85=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=20=D1=81=D1=82=D0=B0=D1=82=D0=B8=D1=81=D1=82=D0=B8=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Добавлены интеграционные тесты 2. Добавлены TODO дял дальнейшего рефакторинга 3. Сделан небольшой рефакторинг метода получения статистики по удержанию в клиньях --- AsbCloudApp/Requests/OperationStatRequest.cs | 4 + .../Services/SlipsStatService.cs | 58 +++++++----- .../Clients/ISlipsTimeClient.cs | 13 +++ .../Controllers/SlipsStatControllerTest.cs | 94 +++++++++++++++++++ .../Data/Defaults.cs | 27 +++++- .../TestFakers/EntitiesFaker.cs | 1 + .../WebAppFactoryFixture.cs | 4 +- 7 files changed, 172 insertions(+), 29 deletions(-) create mode 100644 AsbCloudWebApi.IntegrationTests/Clients/ISlipsTimeClient.cs create mode 100644 AsbCloudWebApi.IntegrationTests/Controllers/SlipsStatControllerTest.cs diff --git a/AsbCloudApp/Requests/OperationStatRequest.cs b/AsbCloudApp/Requests/OperationStatRequest.cs index d92d803d..ac6b50c4 100644 --- a/AsbCloudApp/Requests/OperationStatRequest.cs +++ b/AsbCloudApp/Requests/OperationStatRequest.cs @@ -7,21 +7,25 @@ namespace AsbCloudApp.Requests /// public class OperationStatRequest { + //TODO: использовать DateOnly. Поправить naming /// /// Дата начала операции в UTC /// public DateTime? DateStartUTC { get; set; } + //TODO: использовать DateOnly. Поправить naming /// /// Дата окончания операции в UTC /// public DateTime? DateEndUTC { get; set; } + //TODO: поправить naming /// /// Минимальная продолжительность операции, мин /// public double? DurationMinutesMin { get; set; } + //TODO: поправить naming /// /// Максимальная продолжительность операции, мин /// diff --git a/AsbCloudInfrastructure/Services/SlipsStatService.cs b/AsbCloudInfrastructure/Services/SlipsStatService.cs index 2458da93..aaa85bd5 100644 --- a/AsbCloudInfrastructure/Services/SlipsStatService.cs +++ b/AsbCloudInfrastructure/Services/SlipsStatService.cs @@ -78,11 +78,14 @@ public class SlipsStatService : ISlipsStatService .Where(o => o.IdCategory == WellOperationCategory.IdSlipsTime) .AsNoTracking(); - if (request.DateStartUTC.HasValue && request.DateEndUTC.HasValue) + if (request.DateStartUTC.HasValue) detectedOperationsQuery = detectedOperationsQuery - .Where(o => o.DateStart < request.DateEndUTC) .Where(o => o.DateEnd > request.DateStartUTC); + if (request.DateEndUTC.HasValue) + detectedOperationsQuery = detectedOperationsQuery + .Where(o => o.DateStart < request.DateEndUTC); + if (request.DurationMinutesMin.HasValue) { var durationMinutesMin = TimeSpan.FromMinutes(request.DurationMinutesMin.Value); @@ -101,21 +104,23 @@ public class SlipsStatService : ISlipsStatService .ToArrayAsync(token); var detectedOperationsGroupedByDrillerAndSection = detectedOperations.Select(o => new - { - Operation = o, - IdWell = telemetries[o.IdTelemetry], - schedules.FirstOrDefault(s => - s.IdWell == telemetries[o.IdTelemetry] - && s.DrillStart <= o.DateStart - && s.DrillEnd >= o.DateStart - && new TimeDto(s.ShiftStart) <= new TimeDto(o.DateStart.DateTime) - && new TimeDto(s.ShiftEnd) >= new TimeDto(o.DateStart.DateTime)) - ?.Driller, - Section = sections.FirstOrDefault(s => - s.IdWell == telemetries[o.IdTelemetry] - && s.DepthStart <= o.DepthStart - && s.DepthEnd >= o.DepthStart) - }) + { + Operation = o, + IdWell = telemetries[o.IdTelemetry], + schedules.FirstOrDefault(s => + s.IdWell == telemetries[o.IdTelemetry] && + s.DrillStart <= o.DateStart && + s.DrillEnd > o.DateStart && ( + s.ShiftStart > s.ShiftEnd + ) ^ (s.ShiftStart <= new TimeDto(o.DateStart.DateTime).MakeTimeOnly() && + s.ShiftEnd > new TimeDto(o.DateStart.DateTime).MakeTimeOnly() + )) + ?.Driller, + Section = sections.FirstOrDefault(s => + s.IdWell == telemetries[o.IdTelemetry] + && s.DepthStart <= o.DepthStart + && s.DepthEnd >= o.DepthStart) + }) .Where(o => o.Driller != null) .Where(o => o.Section != null) .Select(o => new @@ -133,20 +138,21 @@ public class SlipsStatService : ISlipsStatService { Operation = o, schedules.FirstOrDefault(s => - s.IdWell == o.IdWell - && s.DrillStart <= o.DateStart - && s.DrillEnd >= o.DateStart - && new TimeDto(s.ShiftStart) <= new TimeDto(o.DateStart.DateTime) - && new TimeDto(s.ShiftEnd) >= new TimeDto(o.DateStart.DateTime)) - ?.Driller, + s.IdWell == o.IdWell && + s.DrillStart <= o.DateStart && + s.DrillEnd > o.DateStart && ( + s.ShiftStart > s.ShiftEnd + ) ^ (s.ShiftStart <= new TimeDto(o.DateStart.DateTime).MakeTimeOnly() && + s.ShiftEnd > new TimeDto(o.DateStart.DateTime).MakeTimeOnly() + )) + ?.Driller, }) .Where(o => o.Driller != null) .GroupBy(o => new { o.Driller!.Id, o.Operation.IdWellSectionType }); - - + var stats = detectedOperationsGroupedByDrillerAndSection.Select(group => new SlipsStatDto { - DrillerName = $"{group.First().Driller!.Name} {group.First().Driller!.Patronymic} {group.First().Driller!.Surname}", + DrillerName = $"{group.First().Driller!.Surname} {group.First().Driller!.Name} {group.First().Driller!.Patronymic}", SlipsCount = group.Count(), SlipsTimeInMinutes = group .Sum(y => (y.Operation.DateEnd - y.Operation.DateStart).TotalMinutes), diff --git a/AsbCloudWebApi.IntegrationTests/Clients/ISlipsTimeClient.cs b/AsbCloudWebApi.IntegrationTests/Clients/ISlipsTimeClient.cs new file mode 100644 index 00000000..a4cfd0ac --- /dev/null +++ b/AsbCloudWebApi.IntegrationTests/Clients/ISlipsTimeClient.cs @@ -0,0 +1,13 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Requests; +using Refit; + +namespace AsbCloudWebApi.IntegrationTests.Clients; + +public interface ISlipsTimeClient +{ + private const string BaseRoute = "/api/slipsStat"; + + [Get(BaseRoute)] + Task>> GetAll([Query] OperationStatRequest request); +} \ No newline at end of file diff --git a/AsbCloudWebApi.IntegrationTests/Controllers/SlipsStatControllerTest.cs b/AsbCloudWebApi.IntegrationTests/Controllers/SlipsStatControllerTest.cs new file mode 100644 index 00000000..4c48fdc3 --- /dev/null +++ b/AsbCloudWebApi.IntegrationTests/Controllers/SlipsStatControllerTest.cs @@ -0,0 +1,94 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Requests; +using AsbCloudDb.Model; +using AsbCloudWebApi.IntegrationTests.Clients; +using Xunit; + +namespace AsbCloudWebApi.IntegrationTests.Controllers; + +public class SlipsStatControllerTest : BaseIntegrationTest +{ + private static readonly Schedule schedule = new() + { + Id = 1, + IdDriller = Data.Defaults.Drillers[0].Id, + IdWell = Data.Defaults.Wells[0].Id, + ShiftStart = new TimeOnly(8, 0, 0), + ShiftEnd = new TimeOnly(20, 0, 0), + DrillStart = new DateTimeOffset(new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc)), + DrillEnd = new DateTimeOffset(new DateTime(2024, 2, 1, 0, 0, 0, DateTimeKind.Utc)) + }; + + private static readonly DetectedOperation detectedOperation = new() + { + Id = 1, + IdTelemetry = Data.Defaults.Telemetries[0].Id, + IdCategory = WellOperationCategory.IdSlipsTime, + DateStart = new DateTimeOffset(new DateTime(2024, 1, 23, 15, 0, 0, 0, DateTimeKind.Utc)), + DateEnd = new DateTimeOffset(new DateTime(2024, 1, 23, 15, 2, 0, 0, DateTimeKind.Utc)), + ExtraData = new Dictionary + { + { "test", 5 } + } + }; + + private static readonly WellOperation factWellOperation = new() + { + Id = 1, + IdWell = Data.Defaults.Wells[0].Id, + IdWellSectionType = 1, + IdCategory = WellOperationCategory.IdRotor, + IdType = WellOperation.IdOperationTypeFact, + DepthStart = 0, + DepthEnd = 100, + LastUpdateDate = DateTimeOffset.UtcNow, + DateStart = new DateTimeOffset(new DateTime(2024, 1, 15, 15, 0, 0, DateTimeKind.Utc)), + DurationHours = 1 + }; + + private readonly ISlipsTimeClient slipsTimeClient; + + public SlipsStatControllerTest(WebAppFactoryFixture factory) + : base(factory) + { + slipsTimeClient = factory.GetAuthorizedHttpClient(); + } + + [Fact] + public async Task GetAll_returns_success() + { + //arrange + dbContext.Schedule.Add(schedule); + dbContext.DetectedOperations.Add(detectedOperation); + dbContext.WellOperations.Add(factWellOperation); + dbContext.SaveChanges(); + + var request = new OperationStatRequest + { + DateStartUTC = schedule.DrillStart.DateTime, + DateEndUTC = schedule.DrillEnd.DateTime, + DurationMinutesMin = 0, + DurationMinutesMax = 5 + }; + + var dtoExpected = new SlipsStatDto + { + DrillerName = $"{Data.Defaults.Drillers[0].Surname} {Data.Defaults.Drillers[0].Name} {Data.Defaults.Drillers[0].Patronymic}", + WellCount = 1, + SectionCaption = "Пилотный ствол", + SlipsCount = 1, + SlipsTimeInMinutes = (detectedOperation.DateEnd - detectedOperation.DateStart).TotalMinutes, + SectionDepth = factWellOperation.DepthEnd - factWellOperation.DepthStart, + }; + + //act + var response = await slipsTimeClient.GetAll(request); + + //assert + Assert.NotNull(response.Content); + Assert.Single(response.Content); + + var dtoActual = response.Content.First(); + MatchHelper.Match(dtoExpected, dtoActual); + } +} \ No newline at end of file diff --git a/AsbCloudWebApi.IntegrationTests/Data/Defaults.cs b/AsbCloudWebApi.IntegrationTests/Data/Defaults.cs index 3458bbd1..b4c6cafa 100644 --- a/AsbCloudWebApi.IntegrationTests/Data/Defaults.cs +++ b/AsbCloudWebApi.IntegrationTests/Data/Defaults.cs @@ -4,7 +4,17 @@ namespace AsbCloudWebApi.IntegrationTests.Data { [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2211:Поля, не являющиеся константами, не должны быть видимыми", Justification = "<Ожидание>")] public static class Defaults - { + { + public static Driller[] Drillers = new Driller[] + { + new() + { + Id = 1, + Name = "test", + Surname = "test" + } + }; + public static Deposit[] Deposits = new Deposit[] { new() { @@ -32,6 +42,19 @@ namespace AsbCloudWebApi.IntegrationTests.Data } }; + public static Telemetry[] Telemetries = new Telemetry[] + { + new() + { + Id = 1, + RemoteUid = "555-555-555", + TimeZone = new SimpleTimezone + { + Hours = 1 + } + } + }; + public static Well[] Wells = new Well[] { new() { @@ -42,7 +65,7 @@ namespace AsbCloudWebApi.IntegrationTests.Data Caption = "Well1", Latitude = 10, Longitude = 20, - IdTelemetry = null, + IdTelemetry = Telemetries[0].Id, Timezone = new SimpleTimezone{ Hours = 1 } diff --git a/AsbCloudWebApi.IntegrationTests/TestFakers/EntitiesFaker.cs b/AsbCloudWebApi.IntegrationTests/TestFakers/EntitiesFaker.cs index 2cbe800e..8b39356b 100644 --- a/AsbCloudWebApi.IntegrationTests/TestFakers/EntitiesFaker.cs +++ b/AsbCloudWebApi.IntegrationTests/TestFakers/EntitiesFaker.cs @@ -3,6 +3,7 @@ using Bogus; namespace AsbCloudWebApi.IntegrationTests.TestFakers; +//TODO: выпилить public static class EntitiesFaker { public static Faker Deposit { get; } = new Faker() diff --git a/AsbCloudWebApi.IntegrationTests/WebAppFactoryFixture.cs b/AsbCloudWebApi.IntegrationTests/WebAppFactoryFixture.cs index 672ebe55..0bb7e26b 100644 --- a/AsbCloudWebApi.IntegrationTests/WebAppFactoryFixture.cs +++ b/AsbCloudWebApi.IntegrationTests/WebAppFactoryFixture.cs @@ -60,7 +60,9 @@ public class WebAppFactoryFixture : WebApplicationFactory, dbContext.AddRange(Data.Defaults.Clusters); dbContext.AddRange(Data.Defaults.Wells); dbContext.AddRange(Data.Defaults.RelationsCompanyWell); - await dbContext.SaveChangesAsync(); + dbContext.AddRange(Data.Defaults.Telemetries); + dbContext.AddRange(Data.Defaults.Drillers); + await dbContext.SaveChangesAsync(); } public new async Task DisposeAsync()