Some checks are pending
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
Docker Web / Build + push Web (push) Waiting to run
Docker Web / Deploy Web on stage (push) Blocked by required conditions
Backend: - Organization.OwnerTelegramChatId (long?) — миграция Phase9a. - TelegramOptions / TelegramBotClient (Telegram Bot API sendMessage). Disabled-mode когда токен пустой (Dev/CI). HTML parse_mode. - OwnerDailySummaryJob.RunAsync — пробегает по org с привязанным chatId, рендерит сводку (выручка вчера, чеков, средний чек, топ-3 по выручке, low-stock 5 строк) и шлёт. Best-effort на каждой org. RenderSummaryAsync — publicный для тестов. - HangfireJobsConfigurator: cron "0 6 * * *" UTC = 09:00 МСК. - TelegramBindingController: GET /status (botEnabled, username, chatId, deepLink), PUT /bind (тестовое сообщение → проверка chatId → save), DELETE (unbind). Конфиг: - Telegram:BotToken — env Telegram__BotToken. - Telegram:BotUsername — для deep-link. UI: - OrganizationSettings.TelegramSection: показывает статус (bot enabled? bound?), deep-link к боту, пошаговая инструкция (start → userinfobot → ввести chat_id → проверка). Toast на привязку/отвязку через meta.successMessage. Тесты: - TelegramOwnerSummaryTests: рендер содержит org_name, метрики, HTML. ✓ 1/1. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
51 lines
2.6 KiB
C#
51 lines
2.6 KiB
C#
using System.Net.Http.Json;
|
||
using FluentAssertions;
|
||
using foodmarket.IntegrationTests.Support;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
using Xunit;
|
||
|
||
namespace foodmarket.IntegrationTests;
|
||
|
||
/// <summary>Тест рендеринга ежедневной сводки OwnerDailySummaryJob.
|
||
/// Сравниваем text-output с ожидаемым: заголовок org-name, цифры,
|
||
/// топ-3 + low-stock. Не отправляем реальное сообщение — тестируем
|
||
/// чистую функцию RenderSummaryAsync (она publicна именно ради тестов).</summary>
|
||
[Collection(ApiCollection.Name)]
|
||
public class TelegramOwnerSummaryTests
|
||
{
|
||
private readonly ApiFactory _factory;
|
||
public TelegramOwnerSummaryTests(ApiFactory factory) => _factory = factory;
|
||
|
||
[Fact]
|
||
public async Task Render_summary_contains_org_name_and_metrics()
|
||
{
|
||
var actor = new ApiActor(_factory.CreateClient());
|
||
var email = $"tg-{Guid.NewGuid():N}@example.kz";
|
||
(await actor.SignupAsync(email, "Passw0rd!", "Telegram Test Org")).EnsureSuccessStatusCode();
|
||
var token = await actor.TokenAsync(email, "Passw0rd!");
|
||
actor.UseToken(token);
|
||
var meDoc = await actor.GetJsonAsync("/api/me");
|
||
var orgId = Guid.Parse(meDoc.GetProperty("orgId").GetString()!);
|
||
|
||
// Засеем демо-данные чтобы было что показать (50 товаров + 30 продаж за
|
||
// последние 30 дней + пара low-stock).
|
||
(await actor.Http.PostAsync("/api/admin/seed-demo", null)).EnsureSuccessStatusCode();
|
||
|
||
// Вытащим джоб напрямую из DI и сгенерируем текст.
|
||
using var scope = _factory.Services.CreateScope();
|
||
var job = scope.ServiceProvider.GetRequiredService<foodmarket.Api.Background.OwnerDailySummaryJob>();
|
||
var text = await job.RenderSummaryAsync(orgId, "Telegram Test Org", CancellationToken.None);
|
||
|
||
// Базовые поля
|
||
text.Should().Contain("Food Market");
|
||
text.Should().Contain("Telegram Test Org");
|
||
text.Should().Contain("Выручка");
|
||
text.Should().Contain("Чеков");
|
||
// HTML-форматирование parse_mode=HTML
|
||
text.Should().Contain("<b>");
|
||
// На свежей орге за «вчера» может ничего не быть (demo сеется на даты
|
||
// сегодня-30..сегодня). В разном таймзоне может попасть, может нет —
|
||
// главное чтобы render не падал и базовая структура была.
|
||
}
|
||
}
|