fix(dal): исправлены методы получения nextRun в сервисе EsppScheduleTransformService

This commit is contained in:
Mikhail Kuznetsov
2025-11-26 15:19:31 +10:00
parent 2823823784
commit ff4691b6a7
6 changed files with 817 additions and 139 deletions

View File

@@ -0,0 +1,614 @@
using Microsoft.Extensions.Logging;
using Moq;
using PARR.DAL.Contracts;
using PARR.DAL.DomainModels;
using PARR.DAL.Models;
using PARR.DAL.Services.Interfaces;
using PARR.DAL.TransformServices;
namespace PARR.DAL.Tests.TransformServices
{
public class EsppScheduleTransformServiceTests
{
private Mock<IEsppSchTypeConfigService> esppSchTypeConfigServiceMock;
private Mock<INextRunModifierService> nextRunModifierServiceMock;
private ILogger<EsppScheduleTransformService> logger;
private EsppScheduleTransformService service;
public EsppScheduleTransformServiceTests()
{
esppSchTypeConfigServiceMock = new Mock<IEsppSchTypeConfigService>();
nextRunModifierServiceMock = new Mock<INextRunModifierService>();
var loggerFactory = new LoggerFactory();
logger = loggerFactory.CreateLogger<EsppScheduleTransformService>();
service = new EsppScheduleTransformService(
logger,
esppSchTypeConfigServiceMock.Object,
nextRunModifierServiceMock.Object
);
}
[Fact]
public async Task GetNextDate_Monthly2_ReturnsSameDateForAllReferenceDates()
{
// Arrange
var pastDate = new DateTimeOffset(2024, 10, 10, 10, 0, 0, TimeSpan.Zero); // Прошлое время
var futureNonMatchingDate = new DateTimeOffset(2025, 12, 08, 10, 0, 0, TimeSpan.Zero); // Будущее, не среда и до ожидаемой даты
var futureMatchingDate = new DateTimeOffset(2025, 12, 10, 10, 0, 0, TimeSpan.Zero); // Вторая среда и совпадает с ожидаемой датой
var expectedDate = futureMatchingDate;
var scheduleDto = new EsppScheduleDto
{
TypeSchedule = new EsppSchTypeSchedule
{
Id = (int)EsppSchTypeScheduleEnum.Monthly2,
Name = "Ежемесячно-2",
Description = "Расписание по дням недели в месяце"
},
Values = new List<EsppScheduleValDto>
{
new EsppScheduleValDto
{
Order = 1,
Type = new EsppSchType
{
Id = 1,
Name = "Порядок",
Description = "Порядковый номер дня недели"
},
Value = new EsppSchTypeValue
{
Id = Guid.NewGuid(),
Value = "Второй",
EsppExportValue = "Второй (экспорт)",
TypeId = 1,
DateCreated = DateTimeOffset.UtcNow,
Order = 2
}
},
new EsppScheduleValDto
{
Order = 2,
Type = new EsppSchType
{
Id = 2,
Name = "День недели",
Description = "День недели (например, понедельник)"
},
Value = new EsppSchTypeValue
{
Id = Guid.NewGuid(),
Value = "Среда",
EsppExportValue = "Среда",
TypeId = 2,
DateCreated = DateTimeOffset.UtcNow,
Order = 3
}
}
}
};
esppSchTypeConfigServiceMock
.Setup(x => x.GetEsppScheduleDtoAsync(It.IsAny<Guid>()))
.ReturnsAsync(scheduleDto);
//Act
var resultFromPast = await service.GetNextDateAsync(Guid.NewGuid(), pastDate);
var resultFromFutureNonMatching = await service.GetNextDateAsync(Guid.NewGuid(), futureNonMatchingDate);
var resultFromFutureMatching = await service.GetNextDateAsync(Guid.NewGuid(), futureMatchingDate);
//Assert
Assert.Equal(expectedDate, resultFromPast);
Assert.Equal(expectedDate, resultFromFutureNonMatching);
Assert.Equal(expectedDate, resultFromFutureMatching);
}
[Fact]
public async Task GetNextDate_Monthly_ReturnsSameDateForAllReferenceDates()
{
// Arrange
var pastDate = new DateTimeOffset(2024, 10, 10, 10, 0, 0, TimeSpan.Zero); // Прошлое время
var futureNonMatchingDate = new DateTimeOffset(2025, 12, 08, 10, 0, 0, TimeSpan.Zero); // Будущее, не 15-е
var futureMatchingDate = new DateTimeOffset(2025, 12, 15, 10, 0, 0, TimeSpan.Zero); // 15-е декабря — совпадает с ожидаемой датой
var expectedDate = futureMatchingDate;
var scheduleDto = new EsppScheduleDto
{
TypeSchedule = new EsppSchTypeSchedule
{
Id = (int)EsppSchTypeScheduleEnum.Monthly,
Name = "Ежемесячно",
Description = "Расписание по числу месяца"
},
Values = new List<EsppScheduleValDto>
{
new EsppScheduleValDto
{
Order = 1,
Type = new EsppSchType
{
Id = 1,
Name = "День месяца",
Description = "Число месяца"
},
Value = new EsppSchTypeValue
{
Id = Guid.NewGuid(),
Value = "15",
EsppExportValue = "15-е число",
TypeId = 1,
DateCreated = DateTimeOffset.UtcNow,
Order = 1
}
}
}
};
esppSchTypeConfigServiceMock
.Setup(x => x.GetEsppScheduleDtoAsync(It.IsAny<Guid>()))
.ReturnsAsync(scheduleDto);
var resultFromPast = await service.GetNextDateAsync(Guid.NewGuid(), pastDate);
var resultFromFutureNonMatching = await service.GetNextDateAsync(Guid.NewGuid(), futureNonMatchingDate);
var resultFromFutureMatching = await service.GetNextDateAsync(Guid.NewGuid(), futureMatchingDate);
Assert.Equal(expectedDate, resultFromPast);
Assert.Equal(expectedDate, resultFromFutureNonMatching);
Assert.Equal(expectedDate, resultFromFutureMatching);
}
[Fact]
public async Task GetNextDate_Annually_ReturnsSameDateForAllReferenceDates()
{
// Arrange
var pastDate = new DateTimeOffset(2024, 10, 10, 10, 0, 0, TimeSpan.Zero); // Прошлое время
var futureNonMatchingDate = new DateTimeOffset(2025, 11, 20, 10, 0, 0, TimeSpan.Zero); // Будущее, не январь, не 1-е
var futureMatchingDate = new DateTimeOffset(2026, 1, 1, 10, 0, 0, TimeSpan.Zero); // 1 января 2026 — совпадает с ожидаемой датой
var expectedDate = futureMatchingDate; // 1 января 2026
var scheduleDto = new EsppScheduleDto
{
TypeSchedule = new EsppSchTypeSchedule
{
Id = (int)EsppSchTypeScheduleEnum.Annually,
Name = "Ежегодно",
Description = "Расписание по месяцу и дню года"
},
Values = new List<EsppScheduleValDto>
{
new EsppScheduleValDto
{
Order = 1,
Type = new EsppSchType
{
Id = 1,
Name = "Месяц",
Description = "Месяц года"
},
Value = new EsppSchTypeValue
{
Id = Guid.NewGuid(),
Value = "Январь",
EsppExportValue = "Январь",
TypeId = 1,
DateCreated = DateTimeOffset.UtcNow,
Order = 1
}
},
new EsppScheduleValDto
{
Order = 2,
Type = new EsppSchType
{
Id = 2,
Name = "День месяца",
Description = "Число месяца"
},
Value = new EsppSchTypeValue
{
Id = Guid.NewGuid(),
Value = "1",
EsppExportValue = "1-е число",
TypeId = 2,
DateCreated = DateTimeOffset.UtcNow,
Order = 1
}
}
}
};
esppSchTypeConfigServiceMock
.Setup(x => x.GetEsppScheduleDtoAsync(It.IsAny<Guid>()))
.ReturnsAsync(scheduleDto);
// Act
var resultFromPast = await service.GetNextDateAsync(Guid.NewGuid(), pastDate);
var resultFromFutureNonMatching = await service.GetNextDateAsync(Guid.NewGuid(), futureNonMatchingDate);
var resultFromFutureMatching = await service.GetNextDateAsync(Guid.NewGuid(), futureMatchingDate);
// Assert
Assert.Equal(expectedDate, resultFromPast);
Assert.Equal(expectedDate, resultFromFutureNonMatching);
Assert.Equal(expectedDate, resultFromFutureMatching);
}
[Fact]
public async Task GetNextDate_Annually2_ReturnsSameDateForAllReferenceDates()
{
// Arrange
var pastDate = new DateTimeOffset(2024, 10, 10, 10, 0, 0, TimeSpan.Zero); // Прошлое время
var futureNonMatchingDate = new DateTimeOffset(2026, 01, 02, 10, 0, 0, TimeSpan.Zero); // Будущее, не понедельник
var futureMatchingDate = new DateTimeOffset(2026, 1, 5, 10, 0, 0, TimeSpan.Zero); // Первый понедельник января 2026 — совпадает с ожидаемой датой
var expectedDate = futureMatchingDate;
var scheduleDto = new EsppScheduleDto
{
TypeSchedule = new EsppSchTypeSchedule
{
Id = (int)EsppSchTypeScheduleEnum.Annually2,
Name = "Ежегодно-2",
Description = "Расписание по порядку дня недели в месяце (например, первый понедельник января)"
},
Values = new List<EsppScheduleValDto>
{
new EsppScheduleValDto
{
Order = 1,
Type = new EsppSchType
{
Id = 1,
Name = "Порядок",
Description = "Порядковый номер дня недели (например, первый)"
},
Value = new EsppSchTypeValue
{
Id = Guid.NewGuid(),
Value = "Первый",
EsppExportValue = "Первый",
TypeId = 1,
DateCreated = DateTimeOffset.UtcNow,
Order = 1
}
},
new EsppScheduleValDto
{
Order = 2,
Type = new EsppSchType
{
Id = 2,
Name = "День недели",
Description = "День недели (например, понедельник)"
},
Value = new EsppSchTypeValue
{
Id = Guid.NewGuid(),
Value = "Понедельник",
EsppExportValue = "Понедельник",
TypeId = 2,
DateCreated = DateTimeOffset.UtcNow,
Order = 2
}
},
new EsppScheduleValDto
{
Order = 3,
Type = new EsppSchType
{
Id = 3,
Name = "Месяц",
Description = "Месяц года (например, Января)"
},
Value = new EsppSchTypeValue
{
Id = Guid.NewGuid(),
Value = "Января",
EsppExportValue = "Января",
TypeId = 3,
DateCreated = DateTimeOffset.UtcNow,
Order = 3
}
}
}
};
esppSchTypeConfigServiceMock
.Setup(x => x.GetEsppScheduleDtoAsync(It.IsAny<Guid>()))
.ReturnsAsync(scheduleDto);
// Act
var resultFromPast = await service.GetNextDateAsync(Guid.NewGuid(), pastDate);
var resultFromFutureNonMatching = await service.GetNextDateAsync(Guid.NewGuid(), futureNonMatchingDate);
var resultFromFutureMatching = await service.GetNextDateAsync(Guid.NewGuid(), futureMatchingDate);
// Assert
Assert.Equal(expectedDate, resultFromPast);
Assert.Equal(expectedDate, resultFromFutureNonMatching);
Assert.Equal(expectedDate, resultFromFutureMatching);
}
[Fact]
public async Task GetNextDate_Weekly_ReturnsSameDateForAllReferenceDates()
{
// Arrange
var pastDate = new DateTimeOffset(2025, 11, 19, 10, 0, 0, TimeSpan.Zero); // Прошлое время
var futureNonMatchingDate = new DateTimeOffset(2025, 11, 27, 10, 0, 0, TimeSpan.Zero); // Будущее, четверг — не понедельник
var futureMatchingDate = new DateTimeOffset(2025, 12, 01, 10, 0, 0, TimeSpan.Zero); // четверг — совпадает с ожидаемой датой
var expectedDate = futureMatchingDate;
var scheduleDto = new EsppScheduleDto
{
TypeSchedule = new EsppSchTypeSchedule
{
Id = (int)EsppSchTypeScheduleEnum.Weekly,
Name = "Еженедельно",
Description = "Расписание по дню недели"
},
Values = new List<EsppScheduleValDto>
{
new EsppScheduleValDto
{
Order = 1,
Type = new EsppSchType
{
Id = 1,
Name = "День недели",
Description = "День недели"
},
Value = new EsppSchTypeValue
{
Id = Guid.NewGuid(),
Value = "Понедельник",
EsppExportValue = "Понедельник",
TypeId = 1,
DateCreated = DateTimeOffset.UtcNow,
Order = 1
}
}
}
};
esppSchTypeConfigServiceMock
.Setup(x => x.GetEsppScheduleDtoAsync(It.IsAny<Guid>()))
.ReturnsAsync(scheduleDto);
// Act
var resultFromPast = await service.GetNextDateAsync(Guid.NewGuid(), pastDate);
var resultFromFutureNonMatching = await service.GetNextDateAsync(Guid.NewGuid(), futureNonMatchingDate);
var resultFromFutureMatching = await service.GetNextDateAsync(Guid.NewGuid(), futureMatchingDate);
// Assert
Assert.Equal(expectedDate, resultFromPast);
Assert.Equal(expectedDate, resultFromFutureNonMatching);
Assert.Equal(expectedDate, resultFromFutureMatching);
}
[Fact]
public async Task GetNextDate_Regularly_EveryDay()
{
// Arrange
var pastDate = new DateTimeOffset(2025, 11, 20, 10, 0, 0, TimeSpan.Zero); // Прошлое время
var futureNonMatchingDate = new DateTimeOffset(2025, 11, 27, 10, 0, 0, TimeSpan.Zero); // Будущее время
var matchingDate = new DateTimeOffset(2025, 11, 26, 10, 0, 0, TimeSpan.Zero); // Совпадает время
var scheduleDto = new EsppScheduleDto
{
TypeSchedule = new EsppSchTypeSchedule
{
Id = (int)EsppSchTypeScheduleEnum.Regularly,
Name = "Регулярно",
Description = "Регулярное расписание"
},
Values = new List<EsppScheduleValDto>
{
new EsppScheduleValDto
{
Order = 1,
Type = new EsppSchType
{
Id = 1,
Name = "Интервал",
Description = "Интервал повторения (например, Ежедневно)"
},
Value = new EsppSchTypeValue
{
Id = Guid.NewGuid(),
Value = "Ежедневно",
EsppExportValue = "Ежедневно",
TypeId = 1,
DateCreated = DateTimeOffset.UtcNow,
Order = 1
}
}
}
};
esppSchTypeConfigServiceMock
.Setup(x => x.GetEsppScheduleDtoAsync(It.IsAny<Guid>()))
.ReturnsAsync(scheduleDto);
// Act
var resultFromPast = await service.GetNextDateAsync(Guid.NewGuid(), pastDate);
var resultFromFutureNonMatching = await service.GetNextDateAsync(Guid.NewGuid(), futureNonMatchingDate);
var resultFromFutureMatching = await service.GetNextDateAsync(Guid.NewGuid(), matchingDate);
// Assert
Assert.Equal(matchingDate.AddDays(1), resultFromPast);
Assert.Equal(futureNonMatchingDate.AddDays(1), resultFromFutureNonMatching);
Assert.Equal(matchingDate.AddDays(1), resultFromFutureMatching);
}
/*
/// <summary>
/// дата в будущем, но не по расписанию
/// </summary>
/// <returns></returns>
[Fact]
public async Task GetNextDate_Weekly_FutureDateNotMatchingSchedule_ReturnsNextScheduledDate()
{
// Arrange
var futureDate = new DateTimeOffset(2025, 12, 04, 10, 0, 0, TimeSpan.Zero); // Четверг
var scheduleDto = new EsppScheduleDto
{
TypeSchedule = new EsppSchTypeSchedule
{
Id = (int)EsppSchTypeScheduleEnum.Weekly,
Name = "Еженедельно",
Description = "Расписание по дню недели"
},
Values = new List<EsppScheduleValDto>
{
new EsppScheduleValDto
{
Order = 1,
Type = new EsppSchType
{
Id = 1,
Name = "День недели",
Description = "День недели"
},
Value = new EsppSchTypeValue
{
Id = Guid.NewGuid(),
Value = "Понедельник",
EsppExportValue = "Понедельник",
TypeId = 1,
DateCreated = DateTimeOffset.UtcNow,
Order = 1
}
}
}
};
esppSchTypeConfigServiceMock
.Setup(x => x.GetEsppScheduleDtoAsync(It.IsAny<Guid>()))
.ReturnsAsync(scheduleDto);
// Act
var result = await service.GetNextDateAsync(Guid.NewGuid(), futureDate);
// Assert
Assert.Equal(DayOfWeek.Monday, result.DayOfWeek);
Assert.True(result >= DateTimeOffset.UtcNow, "Дата должна быть в будущем");
}
/// <summary>
/// несуществующая дата (31 февраля)
/// </summary>
/// <returns></returns>
[Fact]
public async Task GetNextDate_Monthly_InvalidDay_FallsBackToLastDay()
{
// Arrange
var pastDate = new DateTimeOffset(2025, 1, 10, 10, 0, 0, TimeSpan.Zero); // Прошлое
var scheduleDto = new EsppScheduleDto
{
TypeSchedule = new EsppSchTypeSchedule
{
Id = (int)EsppSchTypeScheduleEnum.Monthly,
Name = "Ежемесячно",
Description = "Расписание по числу месяца"
},
Values = new List<EsppScheduleValDto>
{
new EsppScheduleValDto
{
Order = 1,
Type = new EsppSchType
{
Id = 1,
Name = "День месяца",
Description = "Число месяца"
},
Value = new EsppSchTypeValue
{
Id = Guid.NewGuid(),
Value = "31", // <-- 31 февраля не существует
EsppExportValue = "31-е число",
TypeId = 1,
DateCreated = DateTimeOffset.UtcNow,
Order = 1
}
}
}
};
esppSchTypeConfigServiceMock
.Setup(x => x.GetEsppScheduleDtoAsync(It.IsAny<Guid>()))
.ReturnsAsync(scheduleDto);
// Act
var result = await service.GetNextDateAsync(Guid.NewGuid(), pastDate);
// Assert
// Результат должен быть 28 февраля (или 29 в високосный год)
Assert.Equal(28, result.Day); // или 29, если високосный
Assert.Equal(2, result.Month);
}
/// <summary>
/// корректная рекурсия (без зацикливания)
/// </summary>
/// <returns></returns>
[Fact]
public async Task GetNextDate_Regularly_RecursionDoesNotLoop()
{
// Arrange
var pastDate = new DateTimeOffset(1925, 11, 20, 10, 0, 0, TimeSpan.Zero); // Давно прошло
var scheduleDto = new EsppScheduleDto
{
TypeSchedule = new EsppSchTypeSchedule
{
Id = (int)EsppSchTypeScheduleEnum.Regularly,
Name = "Регулярно",
Description = "Регулярное расписание"
},
Values = new List<EsppScheduleValDto>
{
new EsppScheduleValDto
{
Order = 1,
Type = new EsppSchType
{
Id = 1,
Name = "Интервал",
Description = "Интервал повторения (например, Ежедневно)"
},
Value = new EsppSchTypeValue
{
Id = Guid.NewGuid(),
Value = "Каждые 2 часа", // <-- Интервал 2 часа
EsppExportValue = "Каждые 2 часа",
TypeId = 1,
DateCreated = DateTimeOffset.UtcNow,
Order = 1
}
}
}
};
esppSchTypeConfigServiceMock
.Setup(x => x.GetEsppScheduleDtoAsync(It.IsAny<Guid>()))
.ReturnsAsync(scheduleDto);
// Act
var result = await service.GetNextDateAsync(Guid.NewGuid(), pastDate);
// Assert
// Результат должен быть >= DateTimeOffset.UtcNow
Assert.True(result >= DateTimeOffset.UtcNow, "Дата должна быть в будущем");
// Должно быть кратно интервалу
var diff = (result - pastDate).TotalHours;
Assert.True(diff % 2 == 0 || diff % 2 == 2, "Разница должна быть кратна 2 часам");
}*/
}
}