using NSubstitute; using TeleWave.Application.Common.Interfaces; using TeleWave.Application.Notifications; using TeleWave.Application.Tests.Support; using TeleWave.Domain.Broadcast; using TeleWave.Domain.Library; using TeleWave.Domain.Media; using TeleWave.Domain.Notifications; using Xunit; namespace TeleWave.Application.Tests.Notifications; /// /// Рассылка по ленте. Проверяется то, из-за чего бот становится спамом: оповещение шлётся /// на смену шоу, а не на каждую серию, и только тем, кто подписан на этот канал. /// public class TelegramNotifierTests { private static readonly DateTimeOffset Now = new(2026, 8, 1, 20, 0, 0, TimeSpan.Zero); private static TelegramSettings Settings() { var settings = TelegramSettings.CreateDefault(); settings.Update( new TelegramSettingsUpdate( true, TelegramTransport.Polling, "123:ABC", null, TelegramProxyKind.None, null, null, null, null ) ); return settings; } private static ScheduleEntry Program( Guid channelId, Guid showId, DateTimeOffset start, int minutes ) => ScheduleEntry.FromSlot( channelId, Guid.NewGuid(), ScheduleEntryKind.Program, start, start.AddMinutes(minutes), new ScheduleEntryOrigin(showId, 0, null, null, null) ); [Fact] public async Task Notifies_OnlySubscribersOfTheChannel_AndOnlyOnShowChange() { var fixture = new TestDb(); var channel = Channel.Create("Мультреалити", "mult", Now); var other = Channel.Create("Второй", "second", Now); var show = Show.Create("Футурама", ShowKind.Series); var another = Show.Create("Симпсоны", ShowKind.Series); var subscriber = TelegramSubscriber.Create(100, Guid.NewGuid(), "viewer", Now); subscriber.Toggle(channel.Id, TelegramNotificationKind.ShowStart); var stranger = TelegramSubscriber.Create(200, Guid.NewGuid(), "other", Now); stranger.Toggle(other.Id, TelegramNotificationKind.ShowStart); await using (var seed = fixture.New()) { seed.Channels.AddRange(channel, other); seed.Shows.AddRange(show, another); seed.TelegramSubscribers.AddRange(subscriber, stranger); // Предыдущая программа того же шоу: следующая серия сменой не считается. seed.ScheduleEntries.Add(Program(channel.Id, show.Id, Now.AddMinutes(-30), 25)); seed.ScheduleEntries.Add(Program(channel.Id, show.Id, Now.AddMinutes(-2), 25)); await seed.SaveChangesAsync(CancellationToken.None); } var api = Substitute.For(); var settings = Settings(); settings.MarkNotifiedUntil(Now.AddMinutes(-5)); await using var db = fixture.New(); var sent = await new TelegramNotifier(db, api).RunAsync( settings, Now, CancellationToken.None ); Assert.Equal(0, sent); await api.DidNotReceive() .SendMessageAsync( Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any>?>(), Arg.Any() ); } [Fact] public async Task Notifies_WhenShowChanges() { var fixture = new TestDb(); var channel = Channel.Create("Мультреалити", "mult", Now); var previous = Show.Create("Симпсоны", ShowKind.Series); var started = Show.Create("Футурама", ShowKind.Series); var subscriber = TelegramSubscriber.Create(100, Guid.NewGuid(), "viewer", Now); subscriber.Toggle(channel.Id, TelegramNotificationKind.ShowStart); await using (var seed = fixture.New()) { seed.Channels.Add(channel); seed.Shows.AddRange(previous, started); seed.TelegramSubscribers.Add(subscriber); seed.ScheduleEntries.Add(Program(channel.Id, previous.Id, Now.AddMinutes(-30), 25)); seed.ScheduleEntries.Add(Program(channel.Id, started.Id, Now.AddMinutes(-1), 25)); await seed.SaveChangesAsync(CancellationToken.None); } var api = Substitute.For(); var settings = Settings(); settings.MarkNotifiedUntil(Now.AddMinutes(-5)); await using var db = fixture.New(); var sent = await new TelegramNotifier(db, api).RunAsync( settings, Now, CancellationToken.None ); Assert.Equal(1, sent); await api.Received(1) .SendMessageAsync( Arg.Any(), 100, Arg.Is(s => s.Contains("Футурама")), null, Arg.Any() ); // Граница разобранного двигается всегда — иначе следующий тик оповестил бы о том же. Assert.Equal(Now, settings.NotifiedUntil); } }