< Summary

Information
Class: Services.EmailRoutingServices.ImapFetchWindow
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/EmailRoutingServices/ImapFetchWindow.cs
Line coverage
70%
Covered lines: 14
Uncovered lines: 6
Coverable lines: 20
Total lines: 69
Line coverage: 70%
Branch coverage
50%
Covered branches: 8
Total branches: 16
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
ResolveSinceUtc(...)60%1010100%
ResolveScanLimit(...)100%22100%
IsCandidateMessage(...)0%2040%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/EmailRoutingServices/ImapFetchWindow.cs

#LineLine coverage
 1using MailKit;
 2
 3namespace Services.EmailRoutingServices;
 4
 5/// <summary>
 6/// Окно выборки писем для IMAP-polling.
 7/// </summary>
 8public static class ImapFetchWindow
 9{
 110  private static readonly TimeZoneInfo RussianTimeZone =
 111      TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time");
 12
 13  public const int DefaultFetchWindowDays = 3;
 14
 15  /// <summary>
 16  /// Cap на количество заголовков НЕизвестных (ещё не обработанных, отсутствующих в БД) писем,
 17  /// запрашиваемых у IMAP-сервера за один проход (ImapEmailInboxClient.LoadUnprocessedSummariesAsync).
 18  /// Не путать со "последние N писем" — известные письма в этот лимит не попадают вовсе.
 19  /// При backlog больше этого значения он дренируется несколькими батчами Hangfire-джобы.
 20  /// </summary>
 21  public const int DefaultFetchScanLimit = 200;
 22
 23  public static DateTime ResolveSinceUtc(ImapSettings settings, DateTime? utcNow = null)
 24  {
 225    if (settings.FetchAfterUtc.HasValue)
 26    {
 127      var value = settings.FetchAfterUtc.Value;
 128      return value.Kind == DateTimeKind.Utc
 129          ? value
 130          : DateTime.SpecifyKind(value, DateTimeKind.Utc);
 31    }
 32
 133    var now = utcNow ?? DateTime.UtcNow;
 34
 35    // Начало текущих суток по MSK — чтобы «сегодня» и вечерние письма не выпадали из окна.
 136    var nowMsk = TimeZoneInfo.ConvertTimeFromUtc(now, RussianTimeZone);
 137    var startOfTodayMskUtc = TimeZoneInfo.ConvertTimeToUtc(nowMsk.Date, RussianTimeZone);
 38
 139    var windowDays = settings.FetchWindowDays > 0 ? settings.FetchWindowDays : DefaultFetchWindowDays;
 140    var rollingWindowUtc = now.AddDays(-windowDays);
 41
 142    return rollingWindowUtc < startOfTodayMskUtc ? rollingWindowUtc : startOfTodayMskUtc;
 43  }
 44
 45  public static int ResolveScanLimit(ImapSettings settings) =>
 346      settings.FetchScanLimit > 0 ? settings.FetchScanLimit : DefaultFetchScanLimit;
 47
 48  /// <summary>
 49  /// Используется только диагностикой (ImapConnectionTester) — отбор кандидатов реального
 50  /// pipeline теперь строится на сверке с БД (ImapEmailInboxClient.LoadUnprocessedSummariesAsync),
 51  /// а не на Seen-флаге/дате.
 52  /// </summary>
 53  public static bool IsCandidateMessage(IMessageSummary summary, DateTime sinceUtc)
 54  {
 055    var flags = summary.Flags.GetValueOrDefault(MessageFlags.None);
 056    if (!flags.HasFlag(MessageFlags.Seen))
 57    {
 058      return true;
 59    }
 60
 061    if (summary.InternalDate.HasValue)
 62    {
 063      return summary.InternalDate.Value.UtcDateTime >= sinceUtc;
 64    }
 65
 66    // Без InternalDate не отбрасываем — лучше лишний раз проверить Message-Id в БД.
 067    return true;
 68  }
 69}