using Deal.Grpc.Telegram;
using Deal.Telegram.Core;
using Deal.Telegram.Dialogs;
using Grpc.Core;
using Deal.Telegram.Tests.Support;
namespace Deal.Telegram.Tests.Grpc;
// Фейк исходящего канала в ядро (ICoreIngressClient) для unit-тестов служб каталога: записывает
// PushMessage/SyncDialogs без сети; SyncDialogs возвращает настраиваемый monitored-набор (роль ядра,
// Ruling 7). Проводка реального gRPC-канала проверяется отдельно — CoreIngressClientTests против
// in-proc фейк-сервера ингресса (FakeIngressServer).
internal sealed class FakeIngress : ICoreIngressClient
{
///
/// Отправленные PushMessage (tenant + запрос), в порядке вызовов.
///
public List<(string TenantId, PushMessageRequest Message)> Pushes { get; } = [];
///
/// Синхронизации каталога (tenant + entries), в порядке вызовов.
///
public List<(string TenantId, IReadOnlyList Entries)> Syncs { get; } = [];
///
/// Monitored-набор, который фейк возвращает ответом SyncDialogs (как ядро).
///
public List MonitoredIdsToReturn { get; set; } = [];
///
/// Ошибка PushMessageAsync (null — успех).
///
public Exception? PushError { get; set; }
///
/// Ошибка SyncDialogsAsync (null — успех).
///
public Exception? SyncError { get; set; }
///
/// Текст последнего PushMessage (для быстрых проверок).
///
public string? LastPushText => Pushes.Count == 0 ? null : Pushes[^1].Message.Text;
///
/// Id последнего отправленного диалога.
///
public string? LastPushDialogId => Pushes.Count == 0 ? null : Pushes[^1].Message.DialogId;
///
public Task PushMessageAsync(
string tenantId,
PushMessageRequest message,
CancellationToken cancellationToken)
{
if (PushError is not null)
{
throw PushError;
}
Pushes.Add((tenantId, message));
return Task.FromResult(new PushMessageReply { Accepted = true, Duplicate = false });
}
///
public Task> SyncDialogsAsync(
string tenantId,
IReadOnlyList entries,
CancellationToken cancellationToken)
{
if (SyncError is not null)
{
throw SyncError;
}
Syncs.Add((tenantId, entries.ToArray()));
return Task.FromResult>(MonitoredIdsToReturn.ToArray());
}
}
// Фейк анти-бан-пейсера: паузы не ждёт, а записывает запрошенные диапазоны (сек) — проверка
// «паузы 1.5–3 с/сообщение, 3–6 с/диалог» без реальных задержек (fake clock плана Task 10).
internal sealed class RecordingPacer : IBackfillPacer
{
///
/// Запрошенные диапазоны пауз (min/max, сек), в порядке вызовов.
///
public List<(double MinSeconds, double MaxSeconds)> Waits { get; } = [];
///
public Task WaitAsync(
double minSeconds,
double maxSeconds,
CancellationToken cancellationToken)
{
Waits.Add((minSeconds, maxSeconds));
return Task.CompletedTask;
}
}