ci / build-test (push) Canceled after 0s
SaaS-мониторинг Telegram: ядро (модули Cards/Kanban/Pipeline/Tenants/Settings/ Discovery, Api, Infrastructure), сервисы telegram/ai/ml/storage, фронт Vue, контракты и grpc-hosting, деплой-конфиги (dev/prod/observability/CI-раннер), Gitea Actions CI, документация (ТЗ, техдок, api-map, код-стайл, планы, бэклог). Текущее состояние: все этапы роадмапа 0–12 закрыты, сборка 5 sln 0/0, тесты 1340/130/52/38/9 зелёные.
142 lines
6.4 KiB
C#
142 lines
6.4 KiB
C#
using Deal.Api.Telegram;
|
|
using Deal.Contracts.Integrations.Models;
|
|
using Deal.Modules.Settings.Application.Models;
|
|
using Deal.Modules.Telegram.Application;
|
|
using Deal.Modules.Telegram.Application.Models;
|
|
using Deal.Tests.Unit.Contracts;
|
|
using Deal.Tests.Unit.Modules.Settings;
|
|
using Deal.Tests.Unit.Support;
|
|
using Grpc.Core;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
|
|
namespace Deal.Tests.Unit.Api;
|
|
|
|
/// <summary>
|
|
/// Тесты сборки статуса вкладки Telegram — GET /api/tg/status.
|
|
/// </summary>
|
|
public sealed class TgStatusServiceTests
|
|
{
|
|
/// <summary>
|
|
/// Гейт по умолчанию
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task GetAsync_GatewayIdleAndEmptyTenant_ReturnsIdleForm()
|
|
{
|
|
(TgStatusService service, _, _, _, _, _) = Create();
|
|
|
|
TgStatusDto status = await service.GetAsync(CancellationToken.None);
|
|
|
|
Assert.Equal("idle", status.Phase);
|
|
Assert.False(status.Connected);
|
|
Assert.False(status.Listener);
|
|
Assert.Equal(string.Empty, status.Account);
|
|
Assert.Equal(0, status.Monitored);
|
|
Assert.False(status.KeysSet);
|
|
Assert.Null(status.Error);
|
|
Assert.Null(status.QrUrl);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Сервис недоступен
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task GetAsync_GatewayUnavailable_ReturnsIdleFormWithKvAccountCountAndKeys()
|
|
{
|
|
(TgStatusService service, FakeTelegramStore store, FakeSettingsStore settings, FakeTelegramGateway gateway, _, FakeGlobalSettingsStore globalSettings) = Create();
|
|
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: true));
|
|
store.Seed(Dialog("d_2", "Группа", "group", Monitor: true));
|
|
store.Seed(Dialog("d_off", "Выключен", "channel", Monitor: false));
|
|
settings.Preload(SettingsKeys.TgAccount, "\"@user\"");
|
|
PreloadKeys(globalSettings, "123456", "abcdefghijklmnop");
|
|
gateway.StatusFailure = new RpcException(new Status(StatusCode.Unavailable, "Telegram не подключён"));
|
|
|
|
TgStatusDto status = await service.GetAsync(CancellationToken.None);
|
|
|
|
Assert.Equal("idle", status.Phase);
|
|
Assert.False(status.Connected);
|
|
Assert.False(status.Listener);
|
|
Assert.Equal("@user", status.Account);
|
|
Assert.Equal(2, status.Monitored);
|
|
Assert.True(status.KeysSet);
|
|
Assert.Null(status.Error);
|
|
Assert.Null(status.QrUrl);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Готовый аккаунт
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task GetAsync_ReadyGateway_ComposesLiveFieldsWithKvAccountMonitoredAndKeys()
|
|
{
|
|
(TgStatusService service, FakeTelegramStore store, FakeSettingsStore settings, FakeTelegramGateway gateway, _, FakeGlobalSettingsStore globalSettings) = Create();
|
|
store.Seed(Dialog("d_1", "Канал", "channel", Monitor: true));
|
|
settings.Preload(SettingsKeys.TgAccount, "\"@realuser\"");
|
|
PreloadKeys(globalSettings, "123456", "abcdefghijklmnop");
|
|
gateway.Status = new TelegramAccountStatusDto(
|
|
Phase: "ready", Connected: true, Listener: true, Account: "gateway-account", Error: null, QrUrl: null);
|
|
|
|
TgStatusDto status = await service.GetAsync(CancellationToken.None);
|
|
|
|
Assert.Equal("ready", status.Phase);
|
|
Assert.True(status.Connected);
|
|
Assert.True(status.Listener);
|
|
Assert.Equal("@realuser", status.Account); // KV tgAccount перекрывает справочное поле гейта (Ruling 8)
|
|
Assert.Equal(1, status.Monitored);
|
|
Assert.True(status.KeysSet);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Фаза qr несёт qrUrl из гейта
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task GetAsync_QrPhase_ReturnsQrUrlFromGateway()
|
|
{
|
|
(TgStatusService service, _, _, FakeTelegramGateway gateway, _, _) = Create();
|
|
gateway.Status = new TelegramAccountStatusDto(
|
|
Phase: "qr", Connected: false, Listener: false, Account: string.Empty, Error: null, QrUrl: "https://t.me/qr/test");
|
|
|
|
TgStatusDto status = await service.GetAsync(CancellationToken.None);
|
|
|
|
Assert.Equal("qr", status.Phase);
|
|
Assert.Equal("https://t.me/qr/test", status.QrUrl);
|
|
Assert.False(status.Connected);
|
|
}
|
|
|
|
// ─── Хелперы ────────────────────────────────────────────────────────────
|
|
|
|
// Собирает сервис на фейках (FakeTelegramGateway/Store/SettingsStore/GlobalSettingsStore + FakeSecretCipher).
|
|
private static (TgStatusService Service, FakeTelegramStore Store, FakeSettingsStore Settings, FakeTelegramGateway Gateway, FakeSecretCipher Cipher, FakeGlobalSettingsStore GlobalSettings) Create()
|
|
{
|
|
var store = new FakeTelegramStore();
|
|
var settings = new FakeSettingsStore();
|
|
var gateway = new FakeTelegramGateway();
|
|
var cipher = new FakeSecretCipher();
|
|
var globalSettings = new FakeGlobalSettingsStore();
|
|
var keys = new TelegramKeysService(globalSettings, cipher);
|
|
var dialogs = new DialogsService(store, settings, gateway, NullLogger<DialogsService>.Instance);
|
|
var service = new TgStatusService(gateway, dialogs, settings, keys);
|
|
return (service, store, settings, gateway, cipher, globalSettings);
|
|
}
|
|
|
|
// Кладёт глобальную настройку telegramKeys (apiHash шифруется — enc: через шифр сценария).
|
|
// settings: Глобальное KV-хранилище сценария.
|
|
// apiId: apiId приложения (5..9 цифр).
|
|
// apiHash: Открытый api_hash (ядро шифрует при сохранении).
|
|
private static void PreloadKeys(
|
|
FakeGlobalSettingsStore settings,
|
|
string apiId,
|
|
string apiHash)
|
|
{
|
|
string encrypted = new FakeSecretCipher().Encrypt(apiHash);
|
|
settings.Preload(GlobalSettingsKeys.TelegramKeys, $"{{\"apiId\":\"{apiId}\",\"apiHash\":\"{encrypted}\"}}");
|
|
}
|
|
|
|
// Строка каталога минимально (id/имя/kind + флаг мониторинга).
|
|
private static FakeTelegramDialogRow Dialog(
|
|
string id,
|
|
string name,
|
|
string kind,
|
|
bool Monitor)
|
|
=> new(id, name, Handle: string.Empty, kind, Hue: "#666", Monitor, LastText: string.Empty, LastAt: null, Backfilled: false, UpdatedAt: DateTimeOffset.UtcNow);
|
|
}
|