Reminders: убрать Category из общей части DTO, добавить ReceiverEmail/ReceiverName, ограничить чтение испытательного срока последними партициями
- ReminderDto: базовый класс сузился до ReceiverEmail/ReceiverName/Url; RequestTypeId/RequestTypeName/RequestId/Status/RequestUniqueCode остались только в ApprovalOverdueReminderDto — ProbationEndingReminderDto они не нужны. Дискриминатор полиморфной сериализации ($type) больше не дублируется отдельным enum-полем Category. - ApprovalOverdueReminderDto: старый Email сохранён рядом с унаследованным ReceiverEmail (то же значение) — контракт можно переключать постепенно. - ApprovalRow/RowSelect: добавлен Title согласующего (TansuSiteApprover/Title) — заполняет ReceiverName для просроченных шагов, когда он есть в SharePoint. - EmployeeDirectorySnapshot.NameByEmail: обратный поиск ФИО руководителя по email — заполняет ReceiverName для напоминаний об испытательном сроке без лишнего похода в api.tnsu.kz (данные уже загружены для EmailOf). - GetProbationEndingAsync: испытательный срок не длиннее 6 месяцев, поэтому читаем только текущую и последнюю архивную партицию (не весь архив) и фильтруем строки по HireDate не старше 6 месяцев — экономим лишние запросы к SharePoint без потери актуальных заявок.
This commit is contained in:
+63
-2
@@ -1,4 +1,5 @@
|
||||
using System.Text.Json;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
@@ -142,7 +143,6 @@ public sealed class ReminderServiceTests : IClassFixture<PostgresDatabaseFixture
|
||||
reminder.Email.ShouldBe("approver@tnsu.kz");
|
||||
reminder.HoursOverdue.ShouldBe(24);
|
||||
reminder.RequestId.ShouldBe(1);
|
||||
reminder.Category.ShouldBe(ReminderCategory.ApprovalOverdue);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Срок ещё не вышел — напоминания нет")]
|
||||
@@ -356,7 +356,6 @@ public sealed class ReminderServiceTests : IClassFixture<PostgresDatabaseFixture
|
||||
|
||||
result.IsSuccess.ShouldBeTrue();
|
||||
var reminder = result.Value.ShouldHaveSingleItem().ShouldBeOfType<ProbationEndingReminderDto>();
|
||||
reminder.Category.ShouldBe(ReminderCategory.ProbationEnding);
|
||||
reminder.ManagerEmail.ShouldBe("manager@tnsu.kz");
|
||||
reminder.CandidateEmail.ShouldBe("candidate@tnsu.kz");
|
||||
reminder.CandidateName.ShouldBe("Иванов Иван Иванович");
|
||||
@@ -471,4 +470,66 @@ public sealed class ReminderServiceTests : IClassFixture<PostgresDatabaseFixture
|
||||
|
||||
result.Value.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
// Испытательный срок не бывает длиннее 6 месяцев (см. ProbationMonthsByOption
|
||||
// в ReminderService), поэтому заявка, способная дать напоминание сегодня,
|
||||
// физически не может лежать раньше предпоследней партиции. Три партиции:
|
||||
// самая старая (№1) содержит строку-ловушку — если сервис её прочитает, тест
|
||||
// это увидит по лишнему напоминанию; предпоследняя (№2) и текущая (№3)
|
||||
// читаются как обычно.
|
||||
[Fact(DisplayName = "Читаются только текущая и последняя архивная партиция — более старые игнорируются")]
|
||||
public async Task OnlyLastTwoPartitions_AreRead()
|
||||
{
|
||||
var (listUrl1, pageUrl) = await CreateHireTypeAsync("Probation partitions");
|
||||
var db = _services.GetRequiredService<ApprovalRouteDbContext>();
|
||||
var partitionRegistry = _services.GetRequiredService<ListPartitionRegistry>();
|
||||
var type = await db.RequestTypes.FirstAsync(x => x.PageUrl == pageUrl);
|
||||
|
||||
// Партиция №1 — уедет в архив первой, её строки читаться не должны.
|
||||
await partitionRegistry.ArchiveCurrentAsync(type, CancellationToken.None);
|
||||
var listUrl2 = listUrl1 + "-2";
|
||||
type.ListId = listUrl2;
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
// Партиция №2 — последняя архивная, должна читаться наравне с текущей.
|
||||
await partitionRegistry.ArchiveCurrentAsync(type, CancellationToken.None);
|
||||
var listUrl3 = listUrl1 + "-3";
|
||||
type.ListId = listUrl3;
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
const string fio = "Партиция Тест Тестович";
|
||||
var trapRow = HireDataRow(1, "Закрыта", fio, "2026-06-15T00:00:00Z", "2 месяца");
|
||||
var realRow = HireDataRow(2, "Закрыта", fio, "2026-06-15T00:00:00Z", "2 месяца");
|
||||
|
||||
var sharePoint = new Mock<ISharePointClient>();
|
||||
sharePoint.Setup(x => x.GetListTitleAsync(SiteUrl, listUrl1, It.IsAny<CancellationToken>())).ReturnsAsync("list1 — данные");
|
||||
sharePoint.Setup(x => x.GetListTitleAsync(SiteUrl, listUrl2, It.IsAny<CancellationToken>())).ReturnsAsync("list2 — данные");
|
||||
sharePoint.Setup(x => x.GetListTitleAsync(SiteUrl, listUrl3, It.IsAny<CancellationToken>())).ReturnsAsync("list3 — данные");
|
||||
sharePoint
|
||||
.Setup(x => x.GetItemsByListTitleAsync(SiteUrl, "list1 — данные", It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync([Json(trapRow)]);
|
||||
sharePoint
|
||||
.Setup(x => x.GetItemsByListTitleAsync(SiteUrl, "list2 — данные", It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync([Json(realRow)]);
|
||||
sharePoint
|
||||
.Setup(x => x.GetItemsByListTitleAsync(SiteUrl, "list3 — данные", It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync([]);
|
||||
|
||||
var portalClient = BuildPortalClientMock(
|
||||
fio, dismissalDate: null, candidateEmail: "partition@tnsu.kz", managerEmail: "manager@tnsu.kz");
|
||||
|
||||
var service = new ReminderService(
|
||||
db, sharePoint.Object, partitionRegistry, portalClient.Object,
|
||||
Options.Create(new RemindersOptions { RecruitingHirePageUrl = pageUrl }),
|
||||
NullLogger<ReminderService>.Instance);
|
||||
|
||||
var result = await service.GetProbationEndingAsync(Now, CancellationToken.None);
|
||||
|
||||
result.IsSuccess.ShouldBeTrue();
|
||||
result.Value.ShouldHaveSingleItem().ShouldBeOfType<ProbationEndingReminderDto>()
|
||||
.CandidateEmail.ShouldBe("partition@tnsu.kz");
|
||||
sharePoint.Verify(
|
||||
x => x.GetItemsByListTitleAsync(SiteUrl, "list1 — данные", It.IsAny<string>(), It.IsAny<CancellationToken>()),
|
||||
Times.Never);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user