Перевести FakeTelegramGateway и FakeDiscoveryGateway на NSubstitute
ci / build-test (push) Canceled after 0s
ci / build-test (push) Canceled after 0s
Хелперы Support/TestTelegramGateway (журналы SetMonitor/Backfill/ReadSource, сценарии Status/Catalog/сбоев по диалогам) и Support/TestDiscoveryGateway (поиск/инфо/чтения/Join + NotFoundException на неожиданных методах). Потребители (7 файлов) перетипизированы на .Gateway, фейк-классы удалены, тесты 1340 зелёные.
This commit is contained in:
@@ -37,8 +37,8 @@ public sealed class DiscoveryWorkerSchedulerTests
|
|||||||
DiscoveryWorkerScheduler Scheduler,
|
DiscoveryWorkerScheduler Scheduler,
|
||||||
FakeDiscoveryStore StoreA,
|
FakeDiscoveryStore StoreA,
|
||||||
FakeDiscoveryStore StoreB,
|
FakeDiscoveryStore StoreB,
|
||||||
FakeDiscoveryGateway GatewayA,
|
TestDiscoveryGateway GatewayA,
|
||||||
FakeDiscoveryGateway GatewayB,
|
TestDiscoveryGateway GatewayB,
|
||||||
TenantContext TenantContext,
|
TenantContext TenantContext,
|
||||||
ListLogger Logs);
|
ListLogger Logs);
|
||||||
|
|
||||||
@@ -96,8 +96,8 @@ public sealed class DiscoveryWorkerSchedulerTests
|
|||||||
var storeB = new FakeDiscoveryStore();
|
var storeB = new FakeDiscoveryStore();
|
||||||
var settingsA = new FakeSettingsStore();
|
var settingsA = new FakeSettingsStore();
|
||||||
var settingsB = new FakeSettingsStore();
|
var settingsB = new FakeSettingsStore();
|
||||||
var gatewayA = new FakeDiscoveryGateway();
|
var gatewayA = new TestDiscoveryGateway();
|
||||||
var gatewayB = new FakeDiscoveryGateway();
|
var gatewayB = new TestDiscoveryGateway();
|
||||||
PreloadDiscoveryEvalOff(settingsA);
|
PreloadDiscoveryEvalOff(settingsA);
|
||||||
PreloadDiscoveryEvalOff(settingsB);
|
PreloadDiscoveryEvalOff(settingsB);
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ public sealed class DiscoveryWorkerSchedulerTests
|
|||||||
// (эталон PipelineWorkerSchedulerTests/ConnectionStringProvider.ForTenant).
|
// (эталон PipelineWorkerSchedulerTests/ConnectionStringProvider.ForTenant).
|
||||||
services.AddScoped<IDiscoveryStore>(provider => TenantOf(provider) == TenantA ? storeA : storeB);
|
services.AddScoped<IDiscoveryStore>(provider => TenantOf(provider) == TenantA ? storeA : storeB);
|
||||||
services.AddScoped<ISettingsStore>(provider => TenantOf(provider) == TenantA ? settingsA : settingsB);
|
services.AddScoped<ISettingsStore>(provider => TenantOf(provider) == TenantA ? settingsA : settingsB);
|
||||||
services.AddScoped<ITelegramGateway>(provider => TenantOf(provider) == TenantA ? gatewayA : gatewayB);
|
services.AddScoped<ITelegramGateway>(provider => TenantOf(provider) == TenantA ? gatewayA.Gateway : gatewayB.Gateway);
|
||||||
// Реальные сервисы модуля Discovery — как AddDiscoveryModule в Program.cs: цикл резолвит их в tenant-scope.
|
// Реальные сервисы модуля Discovery — как AddDiscoveryModule в Program.cs: цикл резолвит их в tenant-scope.
|
||||||
services.AddDiscoveryModule();
|
services.AddDiscoveryModule();
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public sealed class TgStatusServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetAsync_GatewayUnavailable_ReturnsIdleFormWithKvAccountCountAndKeys()
|
public async Task GetAsync_GatewayUnavailable_ReturnsIdleFormWithKvAccountCountAndKeys()
|
||||||
{
|
{
|
||||||
(TgStatusService service, FakeTelegramStore store, FakeSettingsStore settings, FakeTelegramGateway gateway, _, TestGlobalSettingsStore globalSettings) = Create();
|
(TgStatusService service, FakeTelegramStore store, FakeSettingsStore settings, TestTelegramGateway gateway, _, TestGlobalSettingsStore globalSettings) = Create();
|
||||||
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: true));
|
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: true));
|
||||||
store.Seed(Dialog("d_2", "Группа", "group", Monitor: true));
|
store.Seed(Dialog("d_2", "Группа", "group", Monitor: true));
|
||||||
store.Seed(Dialog("d_off", "Выключен", "channel", Monitor: false));
|
store.Seed(Dialog("d_off", "Выключен", "channel", Monitor: false));
|
||||||
@@ -68,7 +68,7 @@ public sealed class TgStatusServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetAsync_ReadyGateway_ComposesLiveFieldsWithKvAccountMonitoredAndKeys()
|
public async Task GetAsync_ReadyGateway_ComposesLiveFieldsWithKvAccountMonitoredAndKeys()
|
||||||
{
|
{
|
||||||
(TgStatusService service, FakeTelegramStore store, FakeSettingsStore settings, FakeTelegramGateway gateway, _, TestGlobalSettingsStore globalSettings) = Create();
|
(TgStatusService service, FakeTelegramStore store, FakeSettingsStore settings, TestTelegramGateway gateway, _, TestGlobalSettingsStore globalSettings) = Create();
|
||||||
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: true));
|
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: true));
|
||||||
settings.Preload(SettingsKeys.TgAccount, "\"@realuser\"");
|
settings.Preload(SettingsKeys.TgAccount, "\"@realuser\"");
|
||||||
PreloadKeys(globalSettings, "123456", "abcdefghijklmnop");
|
PreloadKeys(globalSettings, "123456", "abcdefghijklmnop");
|
||||||
@@ -91,7 +91,7 @@ public sealed class TgStatusServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetAsync_QrPhase_ReturnsQrUrlFromGateway()
|
public async Task GetAsync_QrPhase_ReturnsQrUrlFromGateway()
|
||||||
{
|
{
|
||||||
(TgStatusService service, _, _, FakeTelegramGateway gateway, _, _) = Create();
|
(TgStatusService service, _, _, TestTelegramGateway gateway, _, _) = Create();
|
||||||
gateway.Status = new TelegramAccountStatusDto(
|
gateway.Status = new TelegramAccountStatusDto(
|
||||||
Phase: "qr", Connected: false, Listener: false, Account: string.Empty, Error: null, QrUrl: "https://t.me/qr/test");
|
Phase: "qr", Connected: false, Listener: false, Account: string.Empty, Error: null, QrUrl: "https://t.me/qr/test");
|
||||||
|
|
||||||
@@ -105,16 +105,16 @@ public sealed class TgStatusServiceTests
|
|||||||
// ─── Хелперы ────────────────────────────────────────────────────────────
|
// ─── Хелперы ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
// Собирает сервис на фейках (FakeTelegramGateway/Store/SettingsStore/GlobalSettingsStore + FakeSecretCipher).
|
// Собирает сервис на фейках (FakeTelegramGateway/Store/SettingsStore/GlobalSettingsStore + FakeSecretCipher).
|
||||||
private static (TgStatusService Service, FakeTelegramStore Store, FakeSettingsStore Settings, FakeTelegramGateway Gateway, FakeSecretCipher Cipher, TestGlobalSettingsStore GlobalSettings) Create()
|
private static (TgStatusService Service, FakeTelegramStore Store, FakeSettingsStore Settings, TestTelegramGateway Gateway, FakeSecretCipher Cipher, TestGlobalSettingsStore GlobalSettings) Create()
|
||||||
{
|
{
|
||||||
var store = new FakeTelegramStore();
|
var store = new FakeTelegramStore();
|
||||||
var settings = new FakeSettingsStore();
|
var settings = new FakeSettingsStore();
|
||||||
var gateway = new FakeTelegramGateway();
|
var gateway = new TestTelegramGateway();
|
||||||
var cipher = new FakeSecretCipher();
|
var cipher = new FakeSecretCipher();
|
||||||
var globalSettings = new TestGlobalSettingsStore();
|
var globalSettings = new TestGlobalSettingsStore();
|
||||||
var keys = new TelegramKeysService(globalSettings.Store, cipher);
|
var keys = new TelegramKeysService(globalSettings.Store, cipher);
|
||||||
var dialogs = new DialogsService(store, settings, gateway, NullLogger<DialogsService>.Instance);
|
var dialogs = new DialogsService(store, settings, gateway.Gateway, NullLogger<DialogsService>.Instance);
|
||||||
var service = new TgStatusService(gateway, dialogs, settings, keys);
|
var service = new TgStatusService(gateway.Gateway, dialogs, settings, keys);
|
||||||
return (service, store, settings, gateway, cipher, globalSettings);
|
return (service, store, settings, gateway, cipher, globalSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ public sealed class DialogsServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task SetMonitor_EnableFirstTimeNotBackfilled_SetsFlagCallsGatewayAndNeedsBackfill()
|
public async Task SetMonitor_EnableFirstTimeNotBackfilled_SetsFlagCallsGatewayAndNeedsBackfill()
|
||||||
{
|
{
|
||||||
(DialogsService service, FakeTelegramStore store, _, FakeTelegramGateway gateway) = Create();
|
(DialogsService service, FakeTelegramStore store, _, TestTelegramGateway gateway) = Create();
|
||||||
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: false, Backfilled: false));
|
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: false, Backfilled: false));
|
||||||
|
|
||||||
TelegramMonitorToggleDto result = await service.SetMonitorAsync("d_1", enabled: true, CancellationToken.None);
|
TelegramMonitorToggleDto result = await service.SetMonitorAsync("d_1", enabled: true, CancellationToken.None);
|
||||||
@@ -127,7 +127,7 @@ public sealed class DialogsServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task SetMonitor_EnableAlreadyBackfilled_NoBackfillNeeded()
|
public async Task SetMonitor_EnableAlreadyBackfilled_NoBackfillNeeded()
|
||||||
{
|
{
|
||||||
(DialogsService service, FakeTelegramStore store, _, FakeTelegramGateway gateway) = Create();
|
(DialogsService service, FakeTelegramStore store, _, TestTelegramGateway gateway) = Create();
|
||||||
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: false, Backfilled: true));
|
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: false, Backfilled: true));
|
||||||
|
|
||||||
TelegramMonitorToggleDto result = await service.SetMonitorAsync("d_1", enabled: true, CancellationToken.None);
|
TelegramMonitorToggleDto result = await service.SetMonitorAsync("d_1", enabled: true, CancellationToken.None);
|
||||||
@@ -143,7 +143,7 @@ public sealed class DialogsServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task SetMonitor_Disable_TurnsOffAndNotifiesMirror()
|
public async Task SetMonitor_Disable_TurnsOffAndNotifiesMirror()
|
||||||
{
|
{
|
||||||
(DialogsService service, FakeTelegramStore store, _, FakeTelegramGateway gateway) = Create();
|
(DialogsService service, FakeTelegramStore store, _, TestTelegramGateway gateway) = Create();
|
||||||
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: true, Backfilled: true));
|
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: true, Backfilled: true));
|
||||||
|
|
||||||
TelegramMonitorToggleDto result = await service.SetMonitorAsync("d_1", enabled: false, CancellationToken.None);
|
TelegramMonitorToggleDto result = await service.SetMonitorAsync("d_1", enabled: false, CancellationToken.None);
|
||||||
@@ -160,7 +160,7 @@ public sealed class DialogsServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task SetMonitor_UnknownDialog_NoOpWithoutGatewayCall()
|
public async Task SetMonitor_UnknownDialog_NoOpWithoutGatewayCall()
|
||||||
{
|
{
|
||||||
(DialogsService service, FakeTelegramStore store, _, FakeTelegramGateway gateway) = Create();
|
(DialogsService service, FakeTelegramStore store, _, TestTelegramGateway gateway) = Create();
|
||||||
|
|
||||||
TelegramMonitorToggleDto result = await service.SetMonitorAsync("missing", enabled: true, CancellationToken.None);
|
TelegramMonitorToggleDto result = await service.SetMonitorAsync("missing", enabled: true, CancellationToken.None);
|
||||||
|
|
||||||
@@ -178,7 +178,7 @@ public sealed class DialogsServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task SetMonitorAll_Enable_ReturnsCountAndNotBackfilledIdsAndNotifiesMirror()
|
public async Task SetMonitorAll_Enable_ReturnsCountAndNotBackfilledIdsAndNotifiesMirror()
|
||||||
{
|
{
|
||||||
(DialogsService service, FakeTelegramStore store, _, FakeTelegramGateway gateway) = Create();
|
(DialogsService service, FakeTelegramStore store, _, TestTelegramGateway gateway) = Create();
|
||||||
store.Seed(Dialog("d_a", "А", "channel", Monitor: false, Backfilled: true));
|
store.Seed(Dialog("d_a", "А", "channel", Monitor: false, Backfilled: true));
|
||||||
store.Seed(Dialog("d_b", "Б", "channel", Monitor: false, Backfilled: false));
|
store.Seed(Dialog("d_b", "Б", "channel", Monitor: false, Backfilled: false));
|
||||||
store.Seed(Dialog("d_c", "В", "group", Monitor: false, Backfilled: false));
|
store.Seed(Dialog("d_c", "В", "group", Monitor: false, Backfilled: false));
|
||||||
@@ -197,7 +197,7 @@ public sealed class DialogsServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task SetMonitorAll_Disable_TurnsAllOffAndEmptyBackfillList()
|
public async Task SetMonitorAll_Disable_TurnsAllOffAndEmptyBackfillList()
|
||||||
{
|
{
|
||||||
(DialogsService service, FakeTelegramStore store, _, FakeTelegramGateway gateway) = Create();
|
(DialogsService service, FakeTelegramStore store, _, TestTelegramGateway gateway) = Create();
|
||||||
store.Seed(Dialog("d_a", "А", "channel", Monitor: true, Backfilled: false));
|
store.Seed(Dialog("d_a", "А", "channel", Monitor: true, Backfilled: false));
|
||||||
store.Seed(Dialog("d_b", "Б", "channel", Monitor: true, Backfilled: true));
|
store.Seed(Dialog("d_b", "Б", "channel", Monitor: true, Backfilled: true));
|
||||||
|
|
||||||
@@ -230,7 +230,7 @@ public sealed class DialogsServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task ReadRecent_OneDialogFails_ContinuesWithOthersAndMarksOnlySuccessful()
|
public async Task ReadRecent_OneDialogFails_ContinuesWithOthersAndMarksOnlySuccessful()
|
||||||
{
|
{
|
||||||
(DialogsService service, FakeTelegramStore store, _, FakeTelegramGateway gateway) = Create();
|
(DialogsService service, FakeTelegramStore store, _, TestTelegramGateway gateway) = Create();
|
||||||
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: true, Backfilled: false));
|
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: true, Backfilled: false));
|
||||||
store.Seed(Dialog("d_2", "Группа", "group", Monitor: true, Backfilled: false));
|
store.Seed(Dialog("d_2", "Группа", "group", Monitor: true, Backfilled: false));
|
||||||
store.Seed(Dialog("d_3", "Форум", "forum", Monitor: true, Backfilled: false));
|
store.Seed(Dialog("d_3", "Форум", "forum", Monitor: true, Backfilled: false));
|
||||||
@@ -251,7 +251,7 @@ public sealed class DialogsServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task BackfillOne_UnknownOrAlreadyBackfilledWithoutForce_ReturnsZeroWithoutRpc()
|
public async Task BackfillOne_UnknownOrAlreadyBackfilledWithoutForce_ReturnsZeroWithoutRpc()
|
||||||
{
|
{
|
||||||
(DialogsService service, FakeTelegramStore store, _, FakeTelegramGateway gateway) = Create();
|
(DialogsService service, FakeTelegramStore store, _, TestTelegramGateway gateway) = Create();
|
||||||
store.Seed(Dialog("d_known", "Канал", "channel", Monitor: true, Backfilled: true));
|
store.Seed(Dialog("d_known", "Канал", "channel", Monitor: true, Backfilled: true));
|
||||||
store.Seed(Dialog("d_fresh", "Группа", "group", Monitor: false, Backfilled: false));
|
store.Seed(Dialog("d_fresh", "Группа", "group", Monitor: false, Backfilled: false));
|
||||||
|
|
||||||
@@ -272,7 +272,7 @@ public sealed class DialogsServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task BackfillOne_ForceTrue_BackfillsAlreadyBackfilledDialogAndMarksAgain()
|
public async Task BackfillOne_ForceTrue_BackfillsAlreadyBackfilledDialogAndMarksAgain()
|
||||||
{
|
{
|
||||||
(DialogsService service, FakeTelegramStore store, _, FakeTelegramGateway gateway) = Create();
|
(DialogsService service, FakeTelegramStore store, _, TestTelegramGateway gateway) = Create();
|
||||||
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: true, Backfilled: true));
|
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: true, Backfilled: true));
|
||||||
|
|
||||||
int processed = await service.BackfillOneAsync("d_1", force: true, CancellationToken.None);
|
int processed = await service.BackfillOneAsync("d_1", force: true, CancellationToken.None);
|
||||||
@@ -290,7 +290,7 @@ public sealed class DialogsServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task ReadRecent_OnlyMonitoredDialogs_BackfillsEachMonitoredWithForce()
|
public async Task ReadRecent_OnlyMonitoredDialogs_BackfillsEachMonitoredWithForce()
|
||||||
{
|
{
|
||||||
(DialogsService service, FakeTelegramStore store, _, FakeTelegramGateway gateway) = Create();
|
(DialogsService service, FakeTelegramStore store, _, TestTelegramGateway gateway) = Create();
|
||||||
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: true, Backfilled: true));
|
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: true, Backfilled: true));
|
||||||
store.Seed(Dialog("d_2", "Группа", "group", Monitor: true, Backfilled: false));
|
store.Seed(Dialog("d_2", "Группа", "group", Monitor: true, Backfilled: false));
|
||||||
store.Seed(Dialog("d_off", "Выключен", "channel", Monitor: false, Backfilled: false));
|
store.Seed(Dialog("d_off", "Выключен", "channel", Monitor: false, Backfilled: false));
|
||||||
@@ -309,7 +309,7 @@ public sealed class DialogsServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task ReadRecent_NoMonitoredDialogs_ReturnsZeroWithoutGatewayCalls()
|
public async Task ReadRecent_NoMonitoredDialogs_ReturnsZeroWithoutGatewayCalls()
|
||||||
{
|
{
|
||||||
(DialogsService service, FakeTelegramStore store, _, FakeTelegramGateway gateway) = Create();
|
(DialogsService service, FakeTelegramStore store, _, TestTelegramGateway gateway) = Create();
|
||||||
store.Seed(Dialog("d_off", "Выключен", "channel", Monitor: false, Backfilled: false));
|
store.Seed(Dialog("d_off", "Выключен", "channel", Monitor: false, Backfilled: false));
|
||||||
|
|
||||||
int count = await service.ReadRecentAsync(CancellationToken.None);
|
int count = await service.ReadRecentAsync(CancellationToken.None);
|
||||||
@@ -426,7 +426,7 @@ public sealed class DialogsServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task AddDiscoveredMonitored_NewDialog_CreatesMonitoredRowAndNotifiesMirror()
|
public async Task AddDiscoveredMonitored_NewDialog_CreatesMonitoredRowAndNotifiesMirror()
|
||||||
{
|
{
|
||||||
(DialogsService service, FakeTelegramStore store, _, FakeTelegramGateway gateway) = Create();
|
(DialogsService service, FakeTelegramStore store, _, TestTelegramGateway gateway) = Create();
|
||||||
|
|
||||||
await service.AddDiscoveredMonitoredAsync("d_100", "Канал", "channel_handle", "channel", "#a33", CancellationToken.None);
|
await service.AddDiscoveredMonitoredAsync("d_100", "Канал", "channel_handle", "channel", "#a33", CancellationToken.None);
|
||||||
|
|
||||||
@@ -447,7 +447,7 @@ public sealed class DialogsServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task AddDiscoveredMonitored_ExistingDialog_UpdatesMetadataAndForcesMonitorOn()
|
public async Task AddDiscoveredMonitored_ExistingDialog_UpdatesMetadataAndForcesMonitorOn()
|
||||||
{
|
{
|
||||||
(DialogsService service, FakeTelegramStore store, _, FakeTelegramGateway gateway) = Create();
|
(DialogsService service, FakeTelegramStore store, _, TestTelegramGateway gateway) = Create();
|
||||||
store.Seed(Dialog("d_100", "Старое имя", "group", Monitor: false, Backfilled: true));
|
store.Seed(Dialog("d_100", "Старое имя", "group", Monitor: false, Backfilled: true));
|
||||||
|
|
||||||
await service.AddDiscoveredMonitoredAsync("d_100", "Новое имя", "new_handle", "channel", "#c21", CancellationToken.None);
|
await service.AddDiscoveredMonitoredAsync("d_100", "Новое имя", "new_handle", "channel", "#c21", CancellationToken.None);
|
||||||
@@ -485,7 +485,7 @@ public sealed class DialogsServiceTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task AddDiscoveredMonitored_GatewayMirrorFails_RowStillWritten()
|
public async Task AddDiscoveredMonitored_GatewayMirrorFails_RowStillWritten()
|
||||||
{
|
{
|
||||||
(DialogsService service, FakeTelegramStore store, _, FakeTelegramGateway gateway) = Create();
|
(DialogsService service, FakeTelegramStore store, _, TestTelegramGateway gateway) = Create();
|
||||||
gateway.SetMonitorFailures.Add("d_100");
|
gateway.SetMonitorFailures.Add("d_100");
|
||||||
|
|
||||||
await service.AddDiscoveredMonitoredAsync("d_100", "Канал", string.Empty, "channel", "#666", CancellationToken.None);
|
await service.AddDiscoveredMonitoredAsync("d_100", "Канал", string.Empty, "channel", "#666", CancellationToken.None);
|
||||||
@@ -497,12 +497,12 @@ public sealed class DialogsServiceTests
|
|||||||
// ─── Хелперы ────────────────────────────────────────────────────────────
|
// ─── Хелперы ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
// Собирает сервис на фейках (FakeTelegramStore — семантика адаптера; настройки/гейт чистые).
|
// Собирает сервис на фейках (FakeTelegramStore — семантика адаптера; настройки/гейт чистые).
|
||||||
private static (DialogsService Service, FakeTelegramStore Store, FakeSettingsStore Settings, FakeTelegramGateway Gateway) Create()
|
private static (DialogsService Service, FakeTelegramStore Store, FakeSettingsStore Settings, TestTelegramGateway Gateway) Create()
|
||||||
{
|
{
|
||||||
var store = new FakeTelegramStore();
|
var store = new FakeTelegramStore();
|
||||||
var settings = new FakeSettingsStore();
|
var settings = new FakeSettingsStore();
|
||||||
var gateway = new FakeTelegramGateway();
|
var gateway = new TestTelegramGateway();
|
||||||
return (new DialogsService(store, settings, gateway, NullLogger<DialogsService>.Instance), store, settings, gateway);
|
return (new DialogsService(store, settings, gateway.Gateway, NullLogger<DialogsService>.Instance), store, settings, gateway);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Строка каталога минимально (id/имя/kind + флаги/хвост каталога).
|
// Строка каталога минимально (id/имя/kind + флаги/хвост каталога).
|
||||||
|
|||||||
@@ -420,7 +420,7 @@ public sealed class DiscoveryWorkerServiceTests
|
|||||||
private sealed record Fixture(
|
private sealed record Fixture(
|
||||||
FakeDiscoveryStore Store,
|
FakeDiscoveryStore Store,
|
||||||
FakeSettingsStore Settings,
|
FakeSettingsStore Settings,
|
||||||
FakeDiscoveryGateway Gateway,
|
TestDiscoveryGateway Gateway,
|
||||||
IDiscoveryPacer Pacer,
|
IDiscoveryPacer Pacer,
|
||||||
DiscoveryWorkerService Worker,
|
DiscoveryWorkerService Worker,
|
||||||
DiscoveryTasksService Tasks);
|
DiscoveryTasksService Tasks);
|
||||||
@@ -441,9 +441,9 @@ public sealed class DiscoveryWorkerServiceTests
|
|||||||
var tasks = new DiscoveryTasksService(store, planGuard, settings);
|
var tasks = new DiscoveryTasksService(store, planGuard, settings);
|
||||||
var banGuard = new DiscoveryBanGuard(store, settings);
|
var banGuard = new DiscoveryBanGuard(store, settings);
|
||||||
var pacer = Substitute.For<IDiscoveryPacer>();
|
var pacer = Substitute.For<IDiscoveryPacer>();
|
||||||
var gateway = new FakeDiscoveryGateway();
|
var gateway = new TestDiscoveryGateway();
|
||||||
var worker = new DiscoveryWorkerService(
|
var worker = new DiscoveryWorkerService(
|
||||||
store, tasks, candidates, log, new DiscoveryEvaluator(settings, ml, ai), banGuard, pacer, gateway);
|
store, tasks, candidates, log, new DiscoveryEvaluator(settings, ml, ai), banGuard, pacer, gateway.Gateway);
|
||||||
return new Fixture(store, settings, gateway, pacer, worker, tasks);
|
return new Fixture(store, settings, gateway, pacer, worker, tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,192 +0,0 @@
|
|||||||
using Deal.Contracts.Integrations.Abstractions;
|
|
||||||
using Deal.Contracts.Integrations.Models;
|
|
||||||
|
|
||||||
namespace Deal.Tests.Unit.Contracts;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Фейковый <see cref="ITelegramGateway"/> для тестов Discovery-воркера
|
|
||||||
/// </summary>
|
|
||||||
public sealed class FakeDiscoveryGateway : ITelegramGateway
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Результат глобального поиска
|
|
||||||
/// </summary>
|
|
||||||
public IReadOnlyList<TelegramDialogEntryDto> SearchResults { get; set; } = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Исключение SearchAsync
|
|
||||||
/// </summary>
|
|
||||||
public Exception? SearchError { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Инфо об источниках по dialogId; отсутствующий — fallback как Local
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<string, TelegramChannelInfoDto> InfoByDialog { get; } = new(StringComparer.Ordinal);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Выборки сообщений по dialogId; отсутствующий — ok=false "no_history"
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<string, TelegramEvalReadDto> ReadsByDialog { get; } = new(StringComparer.Ordinal);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Исключение JoinAsync
|
|
||||||
/// </summary>
|
|
||||||
public Exception? JoinError { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Сколько сообщений «разобрал» Backfill
|
|
||||||
/// </summary>
|
|
||||||
public int BackfillResult { get; set; } = 3;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Поисковые запросы в порядке вызовов.
|
|
||||||
/// </summary>
|
|
||||||
public List<string> SearchedKeywords { get; } = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Запросы Info
|
|
||||||
/// </summary>
|
|
||||||
public List<string> InfoRequests { get; } = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Запросы ReadForEval
|
|
||||||
/// </summary>
|
|
||||||
public List<(string DialogId, int Limit)> ReadRequests { get; } = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Вызовы Join (username без «@») в порядке вызовов.
|
|
||||||
/// </summary>
|
|
||||||
public List<string> JoinedUsernames { get; } = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Вызовы SetMonitor
|
|
||||||
/// </summary>
|
|
||||||
public List<(string DialogId, bool Enabled)> SetMonitorCalls { get; } = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Вызовы Backfill
|
|
||||||
/// </summary>
|
|
||||||
public List<(string DialogId, bool Force)> BackfillCalls { get; } = [];
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<TelegramAccountStatusDto> StatusAsync(CancellationToken ct)
|
|
||||||
=> Task.FromResult(new TelegramAccountStatusDto("ready", Connected: true, Listener: true, "@test", Error: null, QrUrl: null));
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<TelegramAuthResultDto> StartPhoneAsync(
|
|
||||||
string phone,
|
|
||||||
int apiId,
|
|
||||||
string apiHash,
|
|
||||||
CancellationToken ct)
|
|
||||||
=> throw new NotSupportedException();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<TelegramAuthResultDto> StartQrAsync(
|
|
||||||
int apiId,
|
|
||||||
string apiHash,
|
|
||||||
CancellationToken ct)
|
|
||||||
=> throw new NotSupportedException();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<string> SendCodeAsync(string code, CancellationToken ct) => throw new NotSupportedException();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<string> SendPasswordAsync(string password, CancellationToken ct) => throw new NotSupportedException();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task LogoutAsync(CancellationToken ct) => throw new NotSupportedException();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<IReadOnlyList<TelegramDialogEntryDto>> RefreshDialogsAsync(CancellationToken ct)
|
|
||||||
=> throw new NotSupportedException();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task SetMonitorAsync(
|
|
||||||
string dialogId,
|
|
||||||
bool enabled,
|
|
||||||
CancellationToken ct)
|
|
||||||
{
|
|
||||||
SetMonitorCalls.Add((dialogId, enabled));
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task SetMonitorAllAsync(bool enabled, CancellationToken ct) => throw new NotSupportedException();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<int> BackfillAsync(
|
|
||||||
string dialogId,
|
|
||||||
bool force,
|
|
||||||
CancellationToken ct)
|
|
||||||
{
|
|
||||||
BackfillCalls.Add((dialogId, force));
|
|
||||||
return Task.FromResult(BackfillResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<IReadOnlyList<TelegramRecentMessageDto>> ReadRecentAsync(
|
|
||||||
string dialogId,
|
|
||||||
int limit,
|
|
||||||
CancellationToken ct)
|
|
||||||
=> throw new NotSupportedException();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<TelegramSourceContentDto> ReadSourceAsync(
|
|
||||||
string dialogId,
|
|
||||||
long msgId,
|
|
||||||
CancellationToken ct)
|
|
||||||
=> throw new NotSupportedException();
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<IReadOnlyList<TelegramDialogEntryDto>> SearchAsync(
|
|
||||||
string query,
|
|
||||||
int limit,
|
|
||||||
CancellationToken ct)
|
|
||||||
{
|
|
||||||
SearchedKeywords.Add(query);
|
|
||||||
if (SearchError is not null)
|
|
||||||
{
|
|
||||||
throw SearchError;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.FromResult(SearchResults);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<TelegramChannelInfoDto> InfoAsync(string dialogId, CancellationToken ct)
|
|
||||||
{
|
|
||||||
InfoRequests.Add(dialogId);
|
|
||||||
return Task.FromResult(
|
|
||||||
InfoByDialog.TryGetValue(dialogId, out TelegramChannelInfoDto? info)
|
|
||||||
? info
|
|
||||||
: new TelegramChannelInfoDto(dialogId, string.Empty, string.Empty, string.Empty, "#666", Participants: null, IsForum: false));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<TelegramEvalReadDto> ReadForEvalAsync(
|
|
||||||
string dialogId,
|
|
||||||
int limit,
|
|
||||||
CancellationToken ct)
|
|
||||||
{
|
|
||||||
ReadRequests.Add((dialogId, limit));
|
|
||||||
return Task.FromResult(
|
|
||||||
ReadsByDialog.TryGetValue(dialogId, out TelegramEvalReadDto? read)
|
|
||||||
? read
|
|
||||||
: new TelegramEvalReadDto(Ok: false, "no_history", []));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task JoinAsync(string username, CancellationToken ct)
|
|
||||||
{
|
|
||||||
JoinedUsernames.Add(username);
|
|
||||||
if (JoinError is not null)
|
|
||||||
{
|
|
||||||
throw JoinError;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task LeaveAsync(string dialogId, CancellationToken ct) => throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
@@ -1,189 +0,0 @@
|
|||||||
using Deal.Contracts.Integrations.Abstractions;
|
|
||||||
using Deal.Contracts.Integrations.Models;
|
|
||||||
using Grpc.Core;
|
|
||||||
|
|
||||||
namespace Deal.Tests.Unit.Contracts;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// In-memory реализация <see cref="ITelegramGateway"/> для юнит-тестов модуля Telegram/ингресса.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class FakeTelegramGateway : ITelegramGateway
|
|
||||||
{
|
|
||||||
// Фаза idle-формы (по умолчанию не подключён).
|
|
||||||
private const string IdlePhase = "idle";
|
|
||||||
|
|
||||||
private const string NotConnectedDetail = "Telegram не подключён";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Вызовы SetMonitor
|
|
||||||
/// </summary>
|
|
||||||
public List<(string DialogId, bool Enabled)> SetMonitorCalls { get; } = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Вызовы SetMonitorAll
|
|
||||||
/// </summary>
|
|
||||||
public List<bool> SetMonitorAllCalls { get; } = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Вызовы Backfill
|
|
||||||
/// </summary>
|
|
||||||
public List<(string DialogId, bool Force)> BackfillCalls { get; } = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Вызовы ReadSource
|
|
||||||
/// </summary>
|
|
||||||
public List<(string DialogId, long MsgId)> ReadSourceCalls { get; } = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Результат ReadSourceAsync
|
|
||||||
/// </summary>
|
|
||||||
public TelegramSourceContentDto SourceContent { get; set; } = new(false, null, null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Сколько сообщений «разобрал» Backfill
|
|
||||||
/// </summary>
|
|
||||||
public int BackfillResult { get; set; } = 3;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Каталог, который возвращает RefreshDialogsAsync/SearchAsync
|
|
||||||
/// </summary>
|
|
||||||
public IReadOnlyList<TelegramDialogEntryDto> Catalog { get; set; } = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Статус, который возвращает StatusAsync
|
|
||||||
/// </summary>
|
|
||||||
public TelegramAccountStatusDto Status { get; set; } =
|
|
||||||
new(IdlePhase, Connected: false, Listener: false, Account: string.Empty, Error: null, QrUrl: null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Заданное исключение StatusAsync
|
|
||||||
/// </summary>
|
|
||||||
public RpcException? StatusFailure { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Диалоги, чей BackfillAsync падает
|
|
||||||
/// </summary>
|
|
||||||
public HashSet<string> BackfillFailures { get; } = new(StringComparer.Ordinal);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Диалоги, чей SetMonitorAsync падает
|
|
||||||
/// </summary>
|
|
||||||
public HashSet<string> SetMonitorFailures { get; } = new(StringComparer.Ordinal);
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<TelegramAccountStatusDto> StatusAsync(CancellationToken ct)
|
|
||||||
{
|
|
||||||
if (StatusFailure is not null)
|
|
||||||
{
|
|
||||||
throw StatusFailure;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.FromResult(Status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<TelegramAuthResultDto> StartPhoneAsync(
|
|
||||||
string phone,
|
|
||||||
int apiId,
|
|
||||||
string apiHash,
|
|
||||||
CancellationToken ct)
|
|
||||||
=> Task.FromResult(new TelegramAuthResultDto("code", null));
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<TelegramAuthResultDto> StartQrAsync(
|
|
||||||
int apiId,
|
|
||||||
string apiHash,
|
|
||||||
CancellationToken ct)
|
|
||||||
=> Task.FromResult(new TelegramAuthResultDto("qr", "https://t.me/qr/test"));
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<string> SendCodeAsync(string code, CancellationToken ct) => Task.FromResult("ready");
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<string> SendPasswordAsync(string password, CancellationToken ct) => Task.FromResult("ready");
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task LogoutAsync(CancellationToken ct) => Task.CompletedTask;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<IReadOnlyList<TelegramDialogEntryDto>> RefreshDialogsAsync(CancellationToken ct)
|
|
||||||
=> Task.FromResult(Catalog);
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task SetMonitorAsync(
|
|
||||||
string dialogId,
|
|
||||||
bool enabled,
|
|
||||||
CancellationToken ct)
|
|
||||||
{
|
|
||||||
if (SetMonitorFailures.Contains(dialogId))
|
|
||||||
{
|
|
||||||
throw new RpcException(new Status(StatusCode.Unavailable, NotConnectedDetail));
|
|
||||||
}
|
|
||||||
|
|
||||||
SetMonitorCalls.Add((dialogId, enabled));
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task SetMonitorAllAsync(bool enabled, CancellationToken ct)
|
|
||||||
{
|
|
||||||
SetMonitorAllCalls.Add(enabled);
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<int> BackfillAsync(
|
|
||||||
string dialogId,
|
|
||||||
bool force,
|
|
||||||
CancellationToken ct)
|
|
||||||
{
|
|
||||||
BackfillCalls.Add((dialogId, force));
|
|
||||||
if (BackfillFailures.Contains(dialogId))
|
|
||||||
{
|
|
||||||
throw new RpcException(new Status(StatusCode.Unavailable, NotConnectedDetail));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.FromResult(BackfillResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<IReadOnlyList<TelegramRecentMessageDto>> ReadRecentAsync(
|
|
||||||
string dialogId,
|
|
||||||
int limit,
|
|
||||||
CancellationToken ct)
|
|
||||||
=> Task.FromResult<IReadOnlyList<TelegramRecentMessageDto>>([]);
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<TelegramSourceContentDto> ReadSourceAsync(
|
|
||||||
string dialogId,
|
|
||||||
long msgId,
|
|
||||||
CancellationToken ct)
|
|
||||||
{
|
|
||||||
ReadSourceCalls.Add((dialogId, msgId));
|
|
||||||
return Task.FromResult(SourceContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<IReadOnlyList<TelegramDialogEntryDto>> SearchAsync(
|
|
||||||
string query,
|
|
||||||
int limit,
|
|
||||||
CancellationToken ct)
|
|
||||||
=> Task.FromResult(Catalog);
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<TelegramChannelInfoDto> InfoAsync(string dialogId, CancellationToken ct)
|
|
||||||
=> Task.FromResult(new TelegramChannelInfoDto(dialogId, string.Empty, string.Empty, string.Empty, "#666", null, false));
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task<TelegramEvalReadDto> ReadForEvalAsync(
|
|
||||||
string dialogId,
|
|
||||||
int limit,
|
|
||||||
CancellationToken ct)
|
|
||||||
=> Task.FromResult(new TelegramEvalReadDto(false, "no_history", []));
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task JoinAsync(string username, CancellationToken ct) => Task.CompletedTask;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task LeaveAsync(string dialogId, CancellationToken ct) => Task.CompletedTask;
|
|
||||||
}
|
|
||||||
@@ -101,7 +101,7 @@ internal static class TelegramIngressTestHost
|
|||||||
|
|
||||||
builder.Services.AddScoped<ISettingsStore>(_ => new FakeSettingsStore());
|
builder.Services.AddScoped<ISettingsStore>(_ => new FakeSettingsStore());
|
||||||
builder.Services.AddScoped<ITelegramStore>(_ => new FakeTelegramStore());
|
builder.Services.AddScoped<ITelegramStore>(_ => new FakeTelegramStore());
|
||||||
builder.Services.AddScoped<ITelegramGateway>(_ => new FakeTelegramGateway());
|
builder.Services.AddScoped<ITelegramGateway>(_ => new TestTelegramGateway().Gateway);
|
||||||
builder.Services.AddTelegramModule();
|
builder.Services.AddTelegramModule();
|
||||||
|
|
||||||
configureServices(builder.Services);
|
configureServices(builder.Services);
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ public sealed class TelegramSourceContentProviderTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task LoadAsync_ForeignKind_ReturnsNullWithoutGatewayCall()
|
public async Task LoadAsync_ForeignKind_ReturnsNullWithoutGatewayCall()
|
||||||
{
|
{
|
||||||
var gateway = new FakeTelegramGateway();
|
var gateway = new TestTelegramGateway();
|
||||||
var provider = new TelegramSourceContentProvider(gateway);
|
var provider = new TelegramSourceContentProvider(gateway.Gateway);
|
||||||
|
|
||||||
SourceContent? content = await provider.LoadAsync(
|
SourceContent? content = await provider.LoadAsync(
|
||||||
SourceItemFactory.Source(kind: "local"), CancellationToken.None);
|
SourceItemFactory.Source(kind: "local"), CancellationToken.None);
|
||||||
@@ -26,11 +26,11 @@ public sealed class TelegramSourceContentProviderTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task LoadAsync_TelegramKindCaseInsensitive_LoadsContent()
|
public async Task LoadAsync_TelegramKindCaseInsensitive_LoadsContent()
|
||||||
{
|
{
|
||||||
var gateway = new FakeTelegramGateway
|
var gateway = new TestTelegramGateway
|
||||||
{
|
{
|
||||||
SourceContent = new TelegramSourceContentDto(true, "текст", null),
|
SourceContent = new TelegramSourceContentDto(true, "текст", null),
|
||||||
};
|
};
|
||||||
var provider = new TelegramSourceContentProvider(gateway);
|
var provider = new TelegramSourceContentProvider(gateway.Gateway);
|
||||||
|
|
||||||
SourceContent? content = await provider.LoadAsync(
|
SourceContent? content = await provider.LoadAsync(
|
||||||
SourceItemFactory.Source(kind: "Telegram", origin: "d_1", externalId: "100"), CancellationToken.None);
|
SourceItemFactory.Source(kind: "Telegram", origin: "d_1", externalId: "100"), CancellationToken.None);
|
||||||
@@ -42,8 +42,8 @@ public sealed class TelegramSourceContentProviderTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task LoadAsync_EmptyOriginRef_ReturnsNullWithoutGatewayCall()
|
public async Task LoadAsync_EmptyOriginRef_ReturnsNullWithoutGatewayCall()
|
||||||
{
|
{
|
||||||
var gateway = new FakeTelegramGateway();
|
var gateway = new TestTelegramGateway();
|
||||||
var provider = new TelegramSourceContentProvider(gateway);
|
var provider = new TelegramSourceContentProvider(gateway.Gateway);
|
||||||
|
|
||||||
SourceContent? content = await provider.LoadAsync(
|
SourceContent? content = await provider.LoadAsync(
|
||||||
SourceItemFactory.Source(kind: "telegram", origin: null, externalId: "100"), CancellationToken.None);
|
SourceItemFactory.Source(kind: "telegram", origin: null, externalId: "100"), CancellationToken.None);
|
||||||
@@ -55,8 +55,8 @@ public sealed class TelegramSourceContentProviderTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task LoadAsync_NonNumericExternalId_ReturnsNullWithoutGatewayCall()
|
public async Task LoadAsync_NonNumericExternalId_ReturnsNullWithoutGatewayCall()
|
||||||
{
|
{
|
||||||
var gateway = new FakeTelegramGateway();
|
var gateway = new TestTelegramGateway();
|
||||||
var provider = new TelegramSourceContentProvider(gateway);
|
var provider = new TelegramSourceContentProvider(gateway.Gateway);
|
||||||
|
|
||||||
SourceContent? content = await provider.LoadAsync(
|
SourceContent? content = await provider.LoadAsync(
|
||||||
SourceItemFactory.Source(kind: "telegram", origin: "d_1", externalId: "not-a-number"), CancellationToken.None);
|
SourceItemFactory.Source(kind: "telegram", origin: "d_1", externalId: "not-a-number"), CancellationToken.None);
|
||||||
@@ -68,11 +68,11 @@ public sealed class TelegramSourceContentProviderTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task LoadAsync_GatewayNotFound_ReturnsNull()
|
public async Task LoadAsync_GatewayNotFound_ReturnsNull()
|
||||||
{
|
{
|
||||||
var gateway = new FakeTelegramGateway
|
var gateway = new TestTelegramGateway
|
||||||
{
|
{
|
||||||
SourceContent = new TelegramSourceContentDto(false, null, null),
|
SourceContent = new TelegramSourceContentDto(false, null, null),
|
||||||
};
|
};
|
||||||
var provider = new TelegramSourceContentProvider(gateway);
|
var provider = new TelegramSourceContentProvider(gateway.Gateway);
|
||||||
|
|
||||||
SourceContent? content = await provider.LoadAsync(
|
SourceContent? content = await provider.LoadAsync(
|
||||||
SourceItemFactory.Source(kind: "telegram", origin: "d_1", externalId: "100"), CancellationToken.None);
|
SourceItemFactory.Source(kind: "telegram", origin: "d_1", externalId: "100"), CancellationToken.None);
|
||||||
@@ -83,11 +83,11 @@ public sealed class TelegramSourceContentProviderTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task LoadAsync_Found_ReturnsText()
|
public async Task LoadAsync_Found_ReturnsText()
|
||||||
{
|
{
|
||||||
var gateway = new FakeTelegramGateway
|
var gateway = new TestTelegramGateway
|
||||||
{
|
{
|
||||||
SourceContent = new TelegramSourceContentDto(true, "исходный текст", 1_700_000_000_000),
|
SourceContent = new TelegramSourceContentDto(true, "исходный текст", 1_700_000_000_000),
|
||||||
};
|
};
|
||||||
var provider = new TelegramSourceContentProvider(gateway);
|
var provider = new TelegramSourceContentProvider(gateway.Gateway);
|
||||||
|
|
||||||
SourceContent? content = await provider.LoadAsync(
|
SourceContent? content = await provider.LoadAsync(
|
||||||
SourceItemFactory.Source(kind: "telegram", origin: "d_42", externalId: "777"), CancellationToken.None);
|
SourceItemFactory.Source(kind: "telegram", origin: "d_42", externalId: "777"), CancellationToken.None);
|
||||||
@@ -99,8 +99,8 @@ public sealed class TelegramSourceContentProviderTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task LoadAsync_NumericExternalIdAndOriginRef_CallsGatewayWithParsedArguments()
|
public async Task LoadAsync_NumericExternalIdAndOriginRef_CallsGatewayWithParsedArguments()
|
||||||
{
|
{
|
||||||
var gateway = new FakeTelegramGateway();
|
var gateway = new TestTelegramGateway();
|
||||||
var provider = new TelegramSourceContentProvider(gateway);
|
var provider = new TelegramSourceContentProvider(gateway.Gateway);
|
||||||
|
|
||||||
await provider.LoadAsync(
|
await provider.LoadAsync(
|
||||||
SourceItemFactory.Source(kind: "telegram", origin: "d_42", externalId: "777"), CancellationToken.None);
|
SourceItemFactory.Source(kind: "telegram", origin: "d_42", externalId: "777"), CancellationToken.None);
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public sealed class TelegramSourceIngestObserverTests
|
|||||||
var dialogs = new DialogsService(
|
var dialogs = new DialogsService(
|
||||||
store,
|
store,
|
||||||
new FakeSettingsStore(),
|
new FakeSettingsStore(),
|
||||||
new FakeTelegramGateway(),
|
new TestTelegramGateway().Gateway,
|
||||||
NullLogger<DialogsService>.Instance);
|
NullLogger<DialogsService>.Instance);
|
||||||
var observer = new TelegramSourceIngestObserver(dialogs, NullLogger<TelegramSourceIngestObserver>.Instance);
|
var observer = new TelegramSourceIngestObserver(dialogs, NullLogger<TelegramSourceIngestObserver>.Instance);
|
||||||
return (observer, store);
|
return (observer, store);
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
using Deal.Contracts.Integrations.Abstractions;
|
||||||
|
using Deal.Contracts.Integrations.Models;
|
||||||
|
using NSubstitute;
|
||||||
|
|
||||||
|
namespace Deal.Tests.Unit.Support;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Подставка <see cref="ITelegramGateway"/> для тестов Discovery-воркера: сервисы получают
|
||||||
|
/// NSubstitute-подставку (<see cref="Gateway"/>), тесты управляют каталогом/сбоями и читают
|
||||||
|
/// журналы вызовов (как раньше — через фейк). Неожиданные методы бросают <see cref="NotSupportedException"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class TestDiscoveryGateway
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Подставка порта гейтвея (создаётся в конструкторе).
|
||||||
|
/// </summary>
|
||||||
|
public ITelegramGateway Gateway { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Результат SearchAsync
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<TelegramDialogEntryDto> SearchResults { get; set; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Заданное исключение SearchAsync
|
||||||
|
/// </summary>
|
||||||
|
public Exception? SearchError { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Инфо по конкретным диалогам
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, TelegramChannelInfoDto> InfoByDialog { get; } = new(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Чтения для оценки по конкретным диалогам
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, TelegramEvalReadDto> ReadsByDialog { get; } = new(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Заданное исключение JoinAsync
|
||||||
|
/// </summary>
|
||||||
|
public Exception? JoinError { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Сколько сообщений «разобрал» Backfill
|
||||||
|
/// </summary>
|
||||||
|
public int BackfillResult { get; set; } = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ключи, по которым был Search
|
||||||
|
/// </summary>
|
||||||
|
public List<string> SearchedKeywords { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Запросы Info
|
||||||
|
/// </summary>
|
||||||
|
public List<string> InfoRequests { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Запросы ReadForEval
|
||||||
|
/// </summary>
|
||||||
|
public List<(string DialogId, int Limit)> ReadRequests { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Юзернеймы, к которым был Join
|
||||||
|
/// </summary>
|
||||||
|
public List<string> JoinedUsernames { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Вызовы SetMonitor
|
||||||
|
/// </summary>
|
||||||
|
public List<(string DialogId, bool Enabled)> SetMonitorCalls { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Вызовы Backfill
|
||||||
|
/// </summary>
|
||||||
|
public List<(string DialogId, bool Force)> BackfillCalls { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Создаёт подставку с поведением по умолчанию.
|
||||||
|
/// </summary>
|
||||||
|
public TestDiscoveryGateway()
|
||||||
|
{
|
||||||
|
Gateway = Substitute.For<ITelegramGateway>();
|
||||||
|
Gateway.StatusAsync(Arg.Any<CancellationToken>())
|
||||||
|
.Returns(new TelegramAccountStatusDto("ready", Connected: true, Listener: true, "@test", Error: null, QrUrl: null));
|
||||||
|
Gateway.StartPhoneAsync(Arg.Any<string>(), Arg.Any<int>(), Arg.Any<string>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns<TelegramAuthResultDto>(_ => throw new NotSupportedException());
|
||||||
|
Gateway.StartQrAsync(Arg.Any<int>(), Arg.Any<string>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns<TelegramAuthResultDto>(_ => throw new NotSupportedException());
|
||||||
|
Gateway.SendCodeAsync(Arg.Any<string>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns<string>(_ => throw new NotSupportedException());
|
||||||
|
Gateway.SendPasswordAsync(Arg.Any<string>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns<string>(_ => throw new NotSupportedException());
|
||||||
|
Gateway.When(g => g.LogoutAsync(Arg.Any<CancellationToken>()))
|
||||||
|
.Do(_ => throw new NotSupportedException());
|
||||||
|
Gateway.RefreshDialogsAsync(Arg.Any<CancellationToken>())
|
||||||
|
.Returns<IReadOnlyList<TelegramDialogEntryDto>>(_ => throw new NotSupportedException());
|
||||||
|
Gateway.When(g => g.SetMonitorAsync(Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<CancellationToken>()))
|
||||||
|
.Do(ci => SetMonitorCalls.Add((ci.ArgAt<string>(0), ci.ArgAt<bool>(1))));
|
||||||
|
Gateway.SetMonitorAllAsync(Arg.Any<bool>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns<Task>(_ => throw new NotSupportedException());
|
||||||
|
Gateway.When(g => g.BackfillAsync(Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<CancellationToken>()))
|
||||||
|
.Do(ci => BackfillCalls.Add((ci.ArgAt<string>(0), ci.ArgAt<bool>(1))));
|
||||||
|
Gateway.BackfillAsync(Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns(BackfillResult);
|
||||||
|
Gateway.ReadRecentAsync(Arg.Any<string>(), Arg.Any<int>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns<IReadOnlyList<TelegramRecentMessageDto>>(_ => throw new NotSupportedException());
|
||||||
|
Gateway.ReadSourceAsync(Arg.Any<string>(), Arg.Any<long>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns<TelegramSourceContentDto>(_ => throw new NotSupportedException());
|
||||||
|
Gateway.When(g => g.SearchAsync(Arg.Any<string>(), Arg.Any<int>(), Arg.Any<CancellationToken>()))
|
||||||
|
.Do(ci => SearchedKeywords.Add(ci.ArgAt<string>(0)));
|
||||||
|
Gateway.SearchAsync(Arg.Any<string>(), Arg.Any<int>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns(ci =>
|
||||||
|
{
|
||||||
|
if (SearchError is not null)
|
||||||
|
{
|
||||||
|
throw SearchError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SearchResults;
|
||||||
|
});
|
||||||
|
Gateway.When(g => g.InfoAsync(Arg.Any<string>(), Arg.Any<CancellationToken>()))
|
||||||
|
.Do(ci => InfoRequests.Add(ci.ArgAt<string>(0)));
|
||||||
|
Gateway.InfoAsync(Arg.Any<string>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns(ci => InfoByDialog.TryGetValue(ci.ArgAt<string>(0), out TelegramChannelInfoDto? info)
|
||||||
|
? info
|
||||||
|
: new TelegramChannelInfoDto(ci.ArgAt<string>(0), string.Empty, string.Empty, string.Empty, "#666", Participants: null, IsForum: false));
|
||||||
|
Gateway.When(g => g.ReadForEvalAsync(Arg.Any<string>(), Arg.Any<int>(), Arg.Any<CancellationToken>()))
|
||||||
|
.Do(ci => ReadRequests.Add((ci.ArgAt<string>(0), ci.ArgAt<int>(1))));
|
||||||
|
Gateway.ReadForEvalAsync(Arg.Any<string>(), Arg.Any<int>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns(ci => ReadsByDialog.TryGetValue(ci.ArgAt<string>(0), out TelegramEvalReadDto? read)
|
||||||
|
? read
|
||||||
|
: new TelegramEvalReadDto(Ok: false, "no_history", []));
|
||||||
|
Gateway.When(g => g.JoinAsync(Arg.Any<string>(), Arg.Any<CancellationToken>()))
|
||||||
|
.Do(ci =>
|
||||||
|
{
|
||||||
|
JoinedUsernames.Add(ci.ArgAt<string>(0));
|
||||||
|
if (JoinError is not null)
|
||||||
|
{
|
||||||
|
throw JoinError;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Gateway.LeaveAsync(Arg.Any<string>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns<Task>(_ => throw new NotSupportedException());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
using Deal.Contracts.Integrations.Abstractions;
|
||||||
|
using Deal.Contracts.Integrations.Models;
|
||||||
|
using Grpc.Core;
|
||||||
|
using NSubstitute;
|
||||||
|
|
||||||
|
namespace Deal.Tests.Unit.Support;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Подставка <see cref="ITelegramGateway"/> для юнит-тестов модуля Telegram/ингресса: сервисы получают
|
||||||
|
/// NSubstitute-подставку (<see cref="Gateway"/>), тесты управляют поведением свойствами и читают
|
||||||
|
/// журналы вызовов (как раньше — через фейк).
|
||||||
|
/// </summary>
|
||||||
|
public sealed class TestTelegramGateway
|
||||||
|
{
|
||||||
|
// Фаза idle-формы (по умолчанию не подключён).
|
||||||
|
private const string IdlePhase = "idle";
|
||||||
|
|
||||||
|
private const string NotConnectedDetail = "Telegram не подключён";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Подставка порта гейтвея (создаётся в конструкторе).
|
||||||
|
/// </summary>
|
||||||
|
public ITelegramGateway Gateway { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Вызовы SetMonitor
|
||||||
|
/// </summary>
|
||||||
|
public List<(string DialogId, bool Enabled)> SetMonitorCalls { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Вызовы SetMonitorAll
|
||||||
|
/// </summary>
|
||||||
|
public List<bool> SetMonitorAllCalls { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Вызовы Backfill
|
||||||
|
/// </summary>
|
||||||
|
public List<(string DialogId, bool Force)> BackfillCalls { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Вызовы ReadSource
|
||||||
|
/// </summary>
|
||||||
|
public List<(string DialogId, long MsgId)> ReadSourceCalls { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Результат ReadSourceAsync
|
||||||
|
/// </summary>
|
||||||
|
public TelegramSourceContentDto SourceContent { get; set; } = new(false, null, null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Сколько сообщений «разобрал» Backfill
|
||||||
|
/// </summary>
|
||||||
|
public int BackfillResult { get; set; } = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Каталог, который возвращает RefreshDialogsAsync/SearchAsync
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<TelegramDialogEntryDto> Catalog { get; set; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Статус, который возвращает StatusAsync
|
||||||
|
/// </summary>
|
||||||
|
public TelegramAccountStatusDto Status { get; set; } =
|
||||||
|
new(IdlePhase, Connected: false, Listener: false, Account: string.Empty, Error: null, QrUrl: null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Заданное исключение StatusAsync
|
||||||
|
/// </summary>
|
||||||
|
public RpcException? StatusFailure { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Диалоги, чей BackfillAsync падает
|
||||||
|
/// </summary>
|
||||||
|
public HashSet<string> BackfillFailures { get; } = new(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Диалоги, чей SetMonitorAsync падает
|
||||||
|
/// </summary>
|
||||||
|
public HashSet<string> SetMonitorFailures { get; } = new(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Создаёт подставку с поведением по умолчанию.
|
||||||
|
/// </summary>
|
||||||
|
public TestTelegramGateway()
|
||||||
|
{
|
||||||
|
Gateway = Substitute.For<ITelegramGateway>();
|
||||||
|
Gateway.StatusAsync(Arg.Any<CancellationToken>())
|
||||||
|
.Returns(ci =>
|
||||||
|
{
|
||||||
|
if (StatusFailure is not null)
|
||||||
|
{
|
||||||
|
throw StatusFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Status;
|
||||||
|
});
|
||||||
|
Gateway.StartPhoneAsync(Arg.Any<string>(), Arg.Any<int>(), Arg.Any<string>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns(new TelegramAuthResultDto("code", null));
|
||||||
|
Gateway.StartQrAsync(Arg.Any<int>(), Arg.Any<string>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns(new TelegramAuthResultDto("qr", "https://t.me/qr/test"));
|
||||||
|
Gateway.SendCodeAsync(Arg.Any<string>(), Arg.Any<CancellationToken>()).Returns("ready");
|
||||||
|
Gateway.SendPasswordAsync(Arg.Any<string>(), Arg.Any<CancellationToken>()).Returns("ready");
|
||||||
|
Gateway.RefreshDialogsAsync(Arg.Any<CancellationToken>()).Returns(ci => Catalog);
|
||||||
|
Gateway.When(g => g.SetMonitorAsync(Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<CancellationToken>()))
|
||||||
|
.Do(ci =>
|
||||||
|
{
|
||||||
|
if (SetMonitorFailures.Contains(ci.ArgAt<string>(0)))
|
||||||
|
{
|
||||||
|
throw new RpcException(new Status(StatusCode.Unavailable, NotConnectedDetail));
|
||||||
|
}
|
||||||
|
|
||||||
|
SetMonitorCalls.Add((ci.ArgAt<string>(0), ci.ArgAt<bool>(1)));
|
||||||
|
});
|
||||||
|
Gateway.When(g => g.SetMonitorAllAsync(Arg.Any<bool>(), Arg.Any<CancellationToken>()))
|
||||||
|
.Do(ci => SetMonitorAllCalls.Add(ci.ArgAt<bool>(0)));
|
||||||
|
Gateway.When(g => g.BackfillAsync(Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<CancellationToken>()))
|
||||||
|
.Do(ci => BackfillCalls.Add((ci.ArgAt<string>(0), ci.ArgAt<bool>(1))));
|
||||||
|
Gateway.BackfillAsync(Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns(ci =>
|
||||||
|
{
|
||||||
|
if (BackfillFailures.Contains(ci.ArgAt<string>(0)))
|
||||||
|
{
|
||||||
|
throw new RpcException(new Status(StatusCode.Unavailable, NotConnectedDetail));
|
||||||
|
}
|
||||||
|
|
||||||
|
return BackfillResult;
|
||||||
|
});
|
||||||
|
Gateway.ReadRecentAsync(Arg.Any<string>(), Arg.Any<int>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns<IReadOnlyList<TelegramRecentMessageDto>>([]);
|
||||||
|
Gateway.When(g => g.ReadSourceAsync(Arg.Any<string>(), Arg.Any<long>(), Arg.Any<CancellationToken>()))
|
||||||
|
.Do(ci => ReadSourceCalls.Add((ci.ArgAt<string>(0), ci.ArgAt<long>(1))));
|
||||||
|
Gateway.ReadSourceAsync(Arg.Any<string>(), Arg.Any<long>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns(ci => SourceContent);
|
||||||
|
Gateway.SearchAsync(Arg.Any<string>(), Arg.Any<int>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns(ci => Catalog);
|
||||||
|
Gateway.InfoAsync(Arg.Any<string>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns(ci => new TelegramChannelInfoDto(ci.ArgAt<string>(0), string.Empty, string.Empty, string.Empty, "#666", null, false));
|
||||||
|
Gateway.ReadForEvalAsync(Arg.Any<string>(), Arg.Any<int>(), Arg.Any<CancellationToken>())
|
||||||
|
.Returns(new TelegramEvalReadDto(false, "no_history", []));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user