From 5bfad37f536ff3e2d6ebfaab7295dc6d47b4846b Mon Sep 17 00:00:00 2001 From: Dmitri Shimanski Date: Wed, 19 Mar 2025 11:31:53 +0200 Subject: [PATCH 1/9] Create dotnet.yml --- .github/workflows/dotnet.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/dotnet.yml diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 0000000..f20908a --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,23 @@ +name: .NET Tests + +on: + push: + branches: [ "*" ] + pull_request: + branches: [ "*" ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 7.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Test + run: dotnet test --no-build --verbosity normal From 691029aa3f249acd2727e83a22e7333eb58324ad Mon Sep 17 00:00:00 2001 From: Dmitri Shimanski Date: Wed, 19 Mar 2025 12:46:50 +0200 Subject: [PATCH 2/9] Create tests and fix attributes --- Examples/Telegram.Examples/Program.cs | 12 +- .../Telegram.Examples/UpdatePolling/Update.cs | 2 +- Telegram.Net.sln | 6 + Telegram.Net/Attributes/CallbackAttribute.cs | 26 ---- Telegram.Net/Attributes/CommandAttribute.cs | 26 ---- .../Attributes/EditMessageAttribute.cs | 24 ---- Telegram.Net/Attributes/InlineAttribute.cs | 25 ---- .../Attributes/PreCheckoutAttribute.cs | 24 ---- Telegram.Net/Attributes/UpdateAttribute.cs | 24 ---- ...ingSerivce.cs => IUpdatePollingService.cs} | 2 +- Telegram.Net/ServiceBindings.cs | 2 +- .../Services/TelegramHostedService.cs | 128 +++++++++++++++++- Telegram.Net/Telegram.Net.csproj | 8 ++ global.json | 7 + tests/Telegram.Tests/GlobalUsings.cs | 1 + tests/Telegram.Tests/Telegram.Tests.csproj | 20 +++ tests/Telegram.Tests/UnitTest1.cs | 15 ++ 17 files changed, 194 insertions(+), 158 deletions(-) rename Telegram.Net/Interfaces/{IUpdatePollingSerivce.cs => IUpdatePollingService.cs} (80%) create mode 100644 global.json create mode 100644 tests/Telegram.Tests/GlobalUsings.cs create mode 100644 tests/Telegram.Tests/Telegram.Tests.csproj create mode 100644 tests/Telegram.Tests/UnitTest1.cs diff --git a/Examples/Telegram.Examples/Program.cs b/Examples/Telegram.Examples/Program.cs index ae1d2ca..1f2436a 100644 --- a/Examples/Telegram.Examples/Program.cs +++ b/Examples/Telegram.Examples/Program.cs @@ -1,14 +1,18 @@ -using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Telegram.Net; var webHost = Host.CreateDefaultBuilder() + .ConfigureLogging(l => l.ClearProviders().AddConsole()) .ConfigureServices(k => { - k.ConnectTelegram(new("YOUR-TOKEN") + k.ConnectTelegram(new("TOKEN") { errorHandler = async (client, exception, ctx) => { - await Console.Out.WriteLineAsync(exception.Message); + Console.WriteLine(exception); } }); - }); \ No newline at end of file + }); +webHost.Build().Run(); \ No newline at end of file diff --git a/Examples/Telegram.Examples/UpdatePolling/Update.cs b/Examples/Telegram.Examples/UpdatePolling/Update.cs index 622baf1..f6c703c 100644 --- a/Examples/Telegram.Examples/UpdatePolling/Update.cs +++ b/Examples/Telegram.Examples/UpdatePolling/Update.cs @@ -5,7 +5,7 @@ using Telegram.Net.Interfaces; namespace Telegram.Examples.UpdatePolling; -public class Update : IUpdatePollingSerivce +public class Update : IUpdatePollingService { [Update] public async Task UpdateExample(ITelegramBotClient client, Bot.Types.Update update, CancellationToken ctx) diff --git a/Telegram.Net.sln b/Telegram.Net.sln index 362a68f..9e3dd27 100644 --- a/Telegram.Net.sln +++ b/Telegram.Net.sln @@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Telegram.Net", "Telegram.Ne EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Telegram.Examples", "Examples\Telegram.Examples\Telegram.Examples.csproj", "{073E66F2-F82E-4378-B390-C2364DFDC491}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Telegram.Tests", "tests\Telegram.Tests\Telegram.Tests.csproj", "{1FF230D6-7DDC-48B6-8D56-2E14726FDD9E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,5 +20,9 @@ Global {073E66F2-F82E-4378-B390-C2364DFDC491}.Debug|Any CPU.Build.0 = Debug|Any CPU {073E66F2-F82E-4378-B390-C2364DFDC491}.Release|Any CPU.ActiveCfg = Release|Any CPU {073E66F2-F82E-4378-B390-C2364DFDC491}.Release|Any CPU.Build.0 = Release|Any CPU + {1FF230D6-7DDC-48B6-8D56-2E14726FDD9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1FF230D6-7DDC-48B6-8D56-2E14726FDD9E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1FF230D6-7DDC-48B6-8D56-2E14726FDD9E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1FF230D6-7DDC-48B6-8D56-2E14726FDD9E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Telegram.Net/Attributes/CallbackAttribute.cs b/Telegram.Net/Attributes/CallbackAttribute.cs index bc4f28e..278e61b 100644 --- a/Telegram.Net/Attributes/CallbackAttribute.cs +++ b/Telegram.Net/Attributes/CallbackAttribute.cs @@ -29,31 +29,5 @@ public class CallbackAttribute : Attribute public CallbackAttribute(string queryId) { this.QueryId = queryId; - - var methods = typeof(IUpdatePollingSerivce).GetMethods() - .Where(m => m.GetCustomAttribute(this.GetType()) != null); - - foreach (var method in methods) - { - if (IsValidHandlerMethod(method)) - { - var attr = method.GetCustomAttribute(); - var handler = (Func) - Delegate.CreateDelegate(typeof(Func), null, - method); - - TelegramHostedService.CallbackQueryHandler.Add(attr!.QueryId, handler); - } - } - } - - static bool IsValidHandlerMethod(MethodInfo method) - { - var parameters = method.GetParameters(); - return method.ReturnType == typeof(Task) && - parameters.Length == 3 && - parameters[0].ParameterType == typeof(ITelegramBotClient) && - parameters[1].ParameterType == typeof(CallbackQuery) && - parameters[2].ParameterType == typeof(CancellationToken); } } \ No newline at end of file diff --git a/Telegram.Net/Attributes/CommandAttribute.cs b/Telegram.Net/Attributes/CommandAttribute.cs index 89c7072..5b70dab 100644 --- a/Telegram.Net/Attributes/CommandAttribute.cs +++ b/Telegram.Net/Attributes/CommandAttribute.cs @@ -29,31 +29,5 @@ public class CommandAttribute : Attribute public CommandAttribute(string command) { Command = command; - - var methods = typeof(IUpdatePollingSerivce).GetMethods() - .Where(m => m.GetCustomAttribute(this.GetType()) != null); - - foreach (var method in methods) - { - if (IsValidHandlerMethod(method)) - { - var attr = method.GetCustomAttribute(); - var handler = (Func) - Delegate.CreateDelegate(typeof(Func), null, - method); - - TelegramHostedService.CommandHandler.Add(attr!.Command, handler); - } - } - } - - static bool IsValidHandlerMethod(MethodInfo method) - { - var parameters = method.GetParameters(); - return method.ReturnType == typeof(Task) && - parameters.Length == 3 && - parameters[0].ParameterType == typeof(ITelegramBotClient) && - parameters[1].ParameterType == typeof(Message) && - parameters[2].ParameterType == typeof(CancellationToken); } } \ No newline at end of file diff --git a/Telegram.Net/Attributes/EditMessageAttribute.cs b/Telegram.Net/Attributes/EditMessageAttribute.cs index 0190c3c..f29724e 100644 --- a/Telegram.Net/Attributes/EditMessageAttribute.cs +++ b/Telegram.Net/Attributes/EditMessageAttribute.cs @@ -26,29 +26,5 @@ public class EditMessageAttribute : Attribute /// public EditMessageAttribute() { - var methods = typeof(IUpdatePollingSerivce).GetMethods() - .Where(m => m.GetCustomAttribute(this.GetType()) != null); - - foreach (var method in methods) - { - if (IsValidHandlerMethod(method)) - { - var handler = (Func) - Delegate.CreateDelegate(typeof(Func), null, - method); - - TelegramHostedService.EditedMessageHandler.Add(handler); - } - } - } - - static bool IsValidHandlerMethod(MethodInfo method) - { - var parameters = method.GetParameters(); - return method.ReturnType == typeof(Task) && - parameters.Length == 3 && - parameters[0].ParameterType == typeof(ITelegramBotClient) && - parameters[1].ParameterType == typeof(Message) && - parameters[2].ParameterType == typeof(CancellationToken); } } \ No newline at end of file diff --git a/Telegram.Net/Attributes/InlineAttribute.cs b/Telegram.Net/Attributes/InlineAttribute.cs index bfaa31d..218e7d0 100644 --- a/Telegram.Net/Attributes/InlineAttribute.cs +++ b/Telegram.Net/Attributes/InlineAttribute.cs @@ -31,30 +31,5 @@ public class InlineAttribute : Attribute public InlineAttribute(string inlineId) { this.InlineId = inlineId; - var methods = typeof(IUpdatePollingSerivce).GetMethods() - .Where(m => m.GetCustomAttribute(this.GetType()) != null); - - foreach (var method in methods) - { - if (IsValidHandlerMethod(method)) - { - var attr = method.GetCustomAttribute(); - var handler = (Func) - Delegate.CreateDelegate(typeof(Func), null, - method); - - TelegramHostedService.InlineHandler.Add(attr!.InlineId, handler); - } - } - } - - static bool IsValidHandlerMethod(MethodInfo method) - { - var parameters = method.GetParameters(); - return method.ReturnType == typeof(Task) && - parameters.Length == 3 && - parameters[0].ParameterType == typeof(ITelegramBotClient) && - parameters[1].ParameterType == typeof(InlineQuery) && - parameters[2].ParameterType == typeof(CancellationToken); } } \ No newline at end of file diff --git a/Telegram.Net/Attributes/PreCheckoutAttribute.cs b/Telegram.Net/Attributes/PreCheckoutAttribute.cs index 924609c..e90ada1 100644 --- a/Telegram.Net/Attributes/PreCheckoutAttribute.cs +++ b/Telegram.Net/Attributes/PreCheckoutAttribute.cs @@ -22,29 +22,5 @@ public class PreCheckoutAttribute : Attribute public bool IsReusable => true; public PreCheckoutAttribute() { - var methods = typeof(IUpdatePollingSerivce).GetMethods() - .Where(m => m.GetCustomAttribute(this.GetType()) != null); - - foreach (var method in methods) - { - if (IsValidHandlerMethod(method)) - { - var handler = (Func) - Delegate.CreateDelegate(typeof(Func), null, - method); - - TelegramHostedService.PreCheckoutHandler = (handler); - } - } - } - - static bool IsValidHandlerMethod(MethodInfo method) - { - var parameters = method.GetParameters(); - return method.ReturnType == typeof(Task) && - parameters.Length == 3 && - parameters[0].ParameterType == typeof(ITelegramBotClient) && - parameters[1].ParameterType == typeof(PreCheckoutQuery) && - parameters[2].ParameterType == typeof(CancellationToken); } } \ No newline at end of file diff --git a/Telegram.Net/Attributes/UpdateAttribute.cs b/Telegram.Net/Attributes/UpdateAttribute.cs index 597d53d..f287a51 100644 --- a/Telegram.Net/Attributes/UpdateAttribute.cs +++ b/Telegram.Net/Attributes/UpdateAttribute.cs @@ -21,29 +21,5 @@ public class UpdateAttribute : Attribute public bool IsReusable => true; public UpdateAttribute() { - var methods = typeof(IUpdatePollingSerivce).GetMethods() - .Where(m => m.GetCustomAttribute(this.GetType()) != null); - - foreach (var method in methods) - { - if (IsValidHandlerMethod(method)) - { - var handler = (Func) - Delegate.CreateDelegate(typeof(Func), null, - method); - - TelegramHostedService.DefaultUpdateHandler.Add(handler); - } - } - } - - static bool IsValidHandlerMethod(MethodInfo method) - { - var parameters = method.GetParameters(); - return method.ReturnType == typeof(Task) && - parameters.Length == 3 && - parameters[0].ParameterType == typeof(ITelegramBotClient) && - parameters[1].ParameterType == typeof(Update) && - parameters[2].ParameterType == typeof(CancellationToken); } } \ No newline at end of file diff --git a/Telegram.Net/Interfaces/IUpdatePollingSerivce.cs b/Telegram.Net/Interfaces/IUpdatePollingService.cs similarity index 80% rename from Telegram.Net/Interfaces/IUpdatePollingSerivce.cs rename to Telegram.Net/Interfaces/IUpdatePollingService.cs index 929a654..92f9b21 100644 --- a/Telegram.Net/Interfaces/IUpdatePollingSerivce.cs +++ b/Telegram.Net/Interfaces/IUpdatePollingService.cs @@ -3,7 +3,7 @@ /// /// You should implement this interface in all your classes with update polling logic /// -public interface IUpdatePollingSerivce +public interface IUpdatePollingService { } \ No newline at end of file diff --git a/Telegram.Net/ServiceBindings.cs b/Telegram.Net/ServiceBindings.cs index 684733b..9a1d0a1 100644 --- a/Telegram.Net/ServiceBindings.cs +++ b/Telegram.Net/ServiceBindings.cs @@ -8,7 +8,7 @@ public static class ServiceBindings { public static IServiceCollection ConnectTelegram(this IServiceCollection isc, TelegramBotConfig config) { - isc.AddHostedService(k => new(config)); + isc.AddHostedService(k => new(config, isc)); return isc; } } \ No newline at end of file diff --git a/Telegram.Net/Services/TelegramHostedService.cs b/Telegram.Net/Services/TelegramHostedService.cs index d1afc91..dfc24f4 100644 --- a/Telegram.Net/Services/TelegramHostedService.cs +++ b/Telegram.Net/Services/TelegramHostedService.cs @@ -1,8 +1,11 @@ using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Telegram.Bot; using Telegram.Bot.Types; using Telegram.Bot.Types.Payments; +using Telegram.Net.Attributes; using Telegram.Net.Interfaces; #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously @@ -10,6 +13,7 @@ namespace Telegram.Net.Services; public class TelegramHostedService : IHostedService { + private IServiceCollection isc { get; } private TelegramBotClient Client { get; } private ITelegramBotConfig Config { get; } internal static Dictionary> CommandHandler { get; } = new(); @@ -19,16 +23,136 @@ public class TelegramHostedService : IHostedService internal static Func? PreCheckoutHandler { get; set; } internal static List> DefaultUpdateHandler { get; } = new(); - public TelegramHostedService(ITelegramBotConfig config) + public TelegramHostedService(ITelegramBotConfig config, IServiceCollection isc) { Client = new TelegramBotClient(config.Token); Config = config; - + this.isc = isc; } + private static bool IsValidHandlerMethod(MethodInfo method, Type parameterType) + { + var parameters = method.GetParameters(); + return method.ReturnType == typeof(Task) && + parameters.Length == 3 && + parameters[0].ParameterType == typeof(ITelegramBotClient) && + parameters[1].ParameterType == parameterType && + parameters[2].ParameterType == typeof(CancellationToken); + } + + private static Func CreateDelegate(MethodInfo method) + { + var delegateType = typeof(Func); + return (Delegate.CreateDelegate(delegateType, null, method) as Func)!; + } + + internal async Task AddAttributes(CancellationToken cancellationToken) + { + var mutex = new Mutex(); + await Task.Run(async () => + { + mutex.WaitOne(); + var implementations = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(a => a.GetTypes()) + .Where(t => typeof(IUpdatePollingService).IsAssignableFrom(t) && !t.IsInterface); + + foreach (var implementation in implementations) + { + isc.AddSingleton(implementation); + } + + var methods = implementations + .SelectMany(t => t.GetMethods( + BindingFlags.Instance | + BindingFlags.Public | BindingFlags.NonPublic | + BindingFlags.DeclaredOnly)) + .Where(m => + m.GetCustomAttribute() != null || + m.GetCustomAttribute() != null || + m.GetCustomAttribute() != null || + m.GetCustomAttribute() != null || + m.GetCustomAttribute() != null || + m.GetCustomAttribute() != null); + + foreach (var method in methods) + { + await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); + var commandAttr = method.GetCustomAttribute(); + if (commandAttr != null) + { + if (IsValidHandlerMethod(method, typeof(Message))) + { + var handler = CreateDelegate(method); + CommandHandler.Add(commandAttr.Command, handler); + } + continue; + } + + var callbackAttr = method.GetCustomAttribute(); + if (callbackAttr != null) + { + if (IsValidHandlerMethod(method, typeof(CallbackQuery))) + { + var handler = CreateDelegate(method); + CallbackQueryHandler.Add(callbackAttr.QueryId, handler); + } + continue; + } + + var editMessageAttr = method.GetCustomAttribute(); + if (editMessageAttr != null) + { + if (IsValidHandlerMethod(method, typeof(Message))) + { + var handler = CreateDelegate(method); + EditedMessageHandler.Add(handler); + } + continue; + } + + var inlineAttr = method.GetCustomAttribute(); + if (inlineAttr != null) + { + if (IsValidHandlerMethod(method, typeof(InlineQuery))) + { + var handler = CreateDelegate(method); + InlineHandler.Add(inlineAttr.InlineId, handler); + } + continue; + } + + var preCheckoutAttr = method.GetCustomAttribute(); + if (preCheckoutAttr != null) + { + if (IsValidHandlerMethod(method, typeof(PreCheckoutQuery))) + { + var handler = CreateDelegate(method); + PreCheckoutHandler = handler; + } + continue; + } + + var updateAttr = method.GetCustomAttribute(); + if (updateAttr != null) + { + if (IsValidHandlerMethod(method, typeof(Update))) + { + var handler = CreateDelegate(method); + DefaultUpdateHandler.Add(handler); + } + continue; + } + } + mutex.ReleaseMutex(); + }, cancellationToken); + } + [SuppressMessage("ReSharper", "AsyncVoidLambda")] public async Task StartAsync(CancellationToken cancellationToken) { + await AddAttributes(cancellationToken); + + Client.StartReceiving( async (client, update, ctx) => { diff --git a/Telegram.Net/Telegram.Net.csproj b/Telegram.Net/Telegram.Net.csproj index dd6a97e..eae3820 100644 --- a/Telegram.Net/Telegram.Net.csproj +++ b/Telegram.Net/Telegram.Net.csproj @@ -4,6 +4,14 @@ net7.0 enable enable + 1.0.0 + yawaflua + Telegram.Net + Telegram.Bots extender pack, what provides to user attributes, which can used with services + Dmitrii Shimanskii + https://github.com/yawaflua/telegram.Net + https://github.com/yawaflua/telegram.Net + telegram diff --git a/global.json b/global.json new file mode 100644 index 0000000..aaac9e0 --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "7.0.0", + "rollForward": "latestMinor", + "allowPrerelease": false + } +} \ No newline at end of file diff --git a/tests/Telegram.Tests/GlobalUsings.cs b/tests/Telegram.Tests/GlobalUsings.cs new file mode 100644 index 0000000..cefced4 --- /dev/null +++ b/tests/Telegram.Tests/GlobalUsings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file diff --git a/tests/Telegram.Tests/Telegram.Tests.csproj b/tests/Telegram.Tests/Telegram.Tests.csproj new file mode 100644 index 0000000..eadb99b --- /dev/null +++ b/tests/Telegram.Tests/Telegram.Tests.csproj @@ -0,0 +1,20 @@ + + + + net7.0 + enable + enable + + false + true + + + + + + + + + + + diff --git a/tests/Telegram.Tests/UnitTest1.cs b/tests/Telegram.Tests/UnitTest1.cs new file mode 100644 index 0000000..771c354 --- /dev/null +++ b/tests/Telegram.Tests/UnitTest1.cs @@ -0,0 +1,15 @@ +namespace Telegram.Tests; + +public class Tests +{ + [SetUp] + public void Setup() + { + } + + [Test] + public void Test1() + { + Assert.Pass(); + } +} \ No newline at end of file From 25af4a6e9b07038845d7750f41abf31c8e470b1c Mon Sep 17 00:00:00 2001 From: Dmitri Shimanski Date: Wed, 19 Mar 2025 14:17:07 +0200 Subject: [PATCH 3/9] Implement tests --- Telegram.Net/AssemblyInfo.cs | 3 + Telegram.Net/Interfaces/ITelegramBotConfig.cs | 2 +- Telegram.Net/Models/TelegramBotConfig.cs | 2 +- .../Services/TelegramHostedService.cs | 4 +- tests/Telegram.Tests/Telegram.Tests.csproj | 13 ++ .../TestTelegramHostedService.cs | 201 ++++++++++++++++++ tests/Telegram.Tests/UnitTest1.cs | 15 -- tests/Telegram.Tests/appsettings.json | 3 + 8 files changed, 224 insertions(+), 19 deletions(-) create mode 100644 Telegram.Net/AssemblyInfo.cs create mode 100644 tests/Telegram.Tests/TestTelegramHostedService.cs delete mode 100644 tests/Telegram.Tests/UnitTest1.cs create mode 100644 tests/Telegram.Tests/appsettings.json diff --git a/Telegram.Net/AssemblyInfo.cs b/Telegram.Net/AssemblyInfo.cs new file mode 100644 index 0000000..bc47f6e --- /dev/null +++ b/Telegram.Net/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Telegram.Tests")] \ No newline at end of file diff --git a/Telegram.Net/Interfaces/ITelegramBotConfig.cs b/Telegram.Net/Interfaces/ITelegramBotConfig.cs index d928e70..cdb7865 100644 --- a/Telegram.Net/Interfaces/ITelegramBotConfig.cs +++ b/Telegram.Net/Interfaces/ITelegramBotConfig.cs @@ -8,7 +8,7 @@ public interface ITelegramBotConfig /// /// Token of telegram bot. You can take it from @BotFather /// - public string Token { internal get; init; } + public string Token { get; set; } /// /// Custom error handler for bot. You can add custom logger or anything. /// diff --git a/Telegram.Net/Models/TelegramBotConfig.cs b/Telegram.Net/Models/TelegramBotConfig.cs index 84fd5b7..0fbb672 100644 --- a/Telegram.Net/Models/TelegramBotConfig.cs +++ b/Telegram.Net/Models/TelegramBotConfig.cs @@ -11,7 +11,7 @@ public class TelegramBotConfig : ITelegramBotConfig Token = token; } - public string Token { get; init; } + public string Token { get; set; } public Func? errorHandler { get; init; } public ReceiverOptions? ReceiverOptions { get; init; } } \ No newline at end of file diff --git a/Telegram.Net/Services/TelegramHostedService.cs b/Telegram.Net/Services/TelegramHostedService.cs index dfc24f4..7793223 100644 --- a/Telegram.Net/Services/TelegramHostedService.cs +++ b/Telegram.Net/Services/TelegramHostedService.cs @@ -29,7 +29,7 @@ public class TelegramHostedService : IHostedService Config = config; this.isc = isc; } - private static bool IsValidHandlerMethod(MethodInfo method, Type parameterType) + internal static bool IsValidHandlerMethod(MethodInfo method, Type parameterType) { var parameters = method.GetParameters(); return method.ReturnType == typeof(Task) && @@ -39,7 +39,7 @@ public class TelegramHostedService : IHostedService parameters[2].ParameterType == typeof(CancellationToken); } - private static Func CreateDelegate(MethodInfo method) + internal static Func CreateDelegate(MethodInfo method) { var delegateType = typeof(Func); return (Delegate.CreateDelegate(delegateType, null, method) as Func)!; diff --git a/tests/Telegram.Tests/Telegram.Tests.csproj b/tests/Telegram.Tests/Telegram.Tests.csproj index eadb99b..2eed84f 100644 --- a/tests/Telegram.Tests/Telegram.Tests.csproj +++ b/tests/Telegram.Tests/Telegram.Tests.csproj @@ -11,10 +11,23 @@ + + + + + + + + + Always + + + + diff --git a/tests/Telegram.Tests/TestTelegramHostedService.cs b/tests/Telegram.Tests/TestTelegramHostedService.cs new file mode 100644 index 0000000..ba47758 --- /dev/null +++ b/tests/Telegram.Tests/TestTelegramHostedService.cs @@ -0,0 +1,201 @@ +using Telegram.Bot; +using Telegram.Bot.Polling; +using Telegram.Bot.Types; +using Telegram.Bot.Types.InlineQueryResults; +using Telegram.Bot.Types.Payments; +using Telegram.Net.Attributes; +using Telegram.Net.Interfaces; +using Telegram.Net.Services; + +using System.Reflection; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using Telegram.Bot.Types.Enums; +using Telegram.Bot.Types.ReplyMarkups; + +namespace Telegram.Tests +{ + [TestFixture] + public class TelegramHostedServiceTests + { + private Mock _configMock; + private ServiceCollection _services; + + [SetUp] + public void Setup() + { + var conf = new ConfigurationBuilder() + .AddEnvironmentVariables() + .AddJsonFile("appsettings.json") + .Build(); + Console.WriteLine(conf.GetValue("telegram_test_token")); + _configMock = new Mock(); + _configMock.Setup(c => c.Token).Returns(conf.GetValue("telegram_test_token")); + _services = new ServiceCollection(); + } + + public class TestHandler : IUpdatePollingService + { + [Command("start")] + public async Task HandleStart(ITelegramBotClient client, Message message, CancellationToken ct) + => await client.SendTextMessageAsync(message.Chat.Id, "Started", cancellationToken: ct); + + [Callback("test_callback")] + public async Task HandleCallback(ITelegramBotClient client, CallbackQuery query, CancellationToken ct) + => await client.AnswerCallbackQueryAsync(query.Id, "Callback handled", cancellationToken: ct); + + [EditMessage] + public async Task HandleEdit(ITelegramBotClient client, Message message, CancellationToken ct) + => await client.EditMessageTextAsync(message.Chat.Id, message.MessageId, "Edited", cancellationToken: ct); + + [Inline("search")] + public async Task HandleInline(ITelegramBotClient client, InlineQuery query, CancellationToken ct) + => await client.AnswerInlineQueryAsync(query.Id, new[] { new InlineQueryResultArticle("1", "Test", new InputTextMessageContent("Test")) }, cancellationToken: ct); + + [PreCheckout] + public async Task HandlePayment(ITelegramBotClient client, PreCheckoutQuery query, CancellationToken ct) + => await client.AnswerPreCheckoutQueryAsync(query.Id, cancellationToken: ct); + + [Update] + public async Task HandleUpdate(ITelegramBotClient client, Update update, CancellationToken ct) + => await client.SendTextMessageAsync(123, "Update handled", cancellationToken: ct); + } + + [Test] + public void AddAttributes_RegistersCommandHandlersCorrectly() + { + _services.AddSingleton(); + var service = new TelegramHostedService(_configMock.Object, _services); + + service.AddAttributes(CancellationToken.None).Wait(); + + Assert.Multiple(() => + { + Assert.That(TelegramHostedService.CommandHandler, Contains.Key("start")); + Assert.That(TelegramHostedService.CallbackQueryHandler, Contains.Key("test_callback")); + Assert.That(TelegramHostedService.EditedMessageHandler, Has.Count.EqualTo(1)); + Assert.That(TelegramHostedService.InlineHandler, Contains.Key("search")); + Assert.That(TelegramHostedService.PreCheckoutHandler, Is.Not.Null); + Assert.That(TelegramHostedService.DefaultUpdateHandler, Has.Count.EqualTo(1)); + }); + } + + [Test] + public void IsValidHandlerMethod_ValidMessageHandler_ReturnsTrue() + { + // Arrange + var method = typeof(TestHandler).GetMethod("HandleStart"); + + // Act + var result = TelegramHostedService.IsValidHandlerMethod(method, typeof(Message)); + + // Assert + Assert.That(result, Is.True); + } + + [Test] + public void IsValidHandlerMethod_InvalidParameters_ReturnsFalse() + { + // Arrange + var method = typeof(TestHandler).GetMethod("HandleStart"); + + // Act + var result = TelegramHostedService.IsValidHandlerMethod(method, typeof(CallbackQuery)); + + // Assert + Assert.That(result, Is.False); + } + } + + [TestFixture] + public class IntegrationTests + { + private Mock _botClientMock; + private TelegramHostedService _hostedService; + private Mock _configMock; + + [SetUp] + public void Setup() + { + _configMock = new Mock(); + _configMock.Setup(c => c.Token).Returns("test_token"); + _configMock.Setup(c => c.ReceiverOptions).Returns(new ReceiverOptions()); + + var services = new ServiceCollection(); + services.AddSingleton(); + _botClientMock = new Mock(); + + _hostedService = new TelegramHostedService(_configMock.Object, services); + _hostedService.AddAttributes(CancellationToken.None).Wait(); + } + + [Test] + public async Task HandleMessage_ValidCommand_ExecutesHandler() + { + // Arrange + var message = new Message { Text = "/start", Chat = new Chat { Id = 123 } }; + var update = new Update { Message = message }; + + _botClientMock.Setup(b => b.SendMessage( + It.IsAny(), + "Started", + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny() + )).Verifiable(); + + // Act + await _hostedService.StartAsync(CancellationToken.None); + await InvokeUpdateHandler(update); + + // Assert + _botClientMock.Verify(); + } + + [Test] + public async Task HandleCallback_ValidQuery_ExecutesHandler() + { + // Arrange + var callback = new CallbackQuery { Data = "test_callback", Id = "cb_id" }; + var update = new Update { CallbackQuery = callback }; + + _botClientMock.Setup(b => b.AnswerCallbackQueryAsync( + "cb_id", + "Callback handled", + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny() + )).Verifiable(); + + // Act + await _hostedService.StartAsync(CancellationToken.None); + await InvokeUpdateHandler(update); + + // Assert + _botClientMock.Verify(); + } + + private async Task InvokeUpdateHandler(Update update) + { + var clientField = typeof(TelegramHostedService).GetField("Client", BindingFlags.NonPublic | BindingFlags.Instance); + clientField.SetValue(_hostedService, _botClientMock.Object); + + var updateHandler = _botClientMock.Invocations + .First(i => i.Method.Name == "StartReceiving") + .Arguments[0] as Func; + + await updateHandler(_botClientMock.Object, update, CancellationToken.None); + } + } +} \ No newline at end of file diff --git a/tests/Telegram.Tests/UnitTest1.cs b/tests/Telegram.Tests/UnitTest1.cs deleted file mode 100644 index 771c354..0000000 --- a/tests/Telegram.Tests/UnitTest1.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Telegram.Tests; - -public class Tests -{ - [SetUp] - public void Setup() - { - } - - [Test] - public void Test1() - { - Assert.Pass(); - } -} \ No newline at end of file diff --git a/tests/Telegram.Tests/appsettings.json b/tests/Telegram.Tests/appsettings.json new file mode 100644 index 0000000..aba4462 --- /dev/null +++ b/tests/Telegram.Tests/appsettings.json @@ -0,0 +1,3 @@ +{ + "telegram_test_token": "7994961223:AAEx8olljS-CoeFa1L0EJeW-MBlEQXuov2U" +} \ No newline at end of file From 06d5d27666961ae6bc779d21e6e5049659fa728f Mon Sep 17 00:00:00 2001 From: Dmitri Shimanski Date: Wed, 19 Mar 2025 14:24:50 +0200 Subject: [PATCH 4/9] Update dotnet.yml --- .github/workflows/dotnet.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index f20908a..188ce82 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -5,7 +5,9 @@ on: branches: [ "*" ] pull_request: branches: [ "*" ] - +env: + "telegram_test_token": ${{TELEGRAM_TEST_TOKEN}} + jobs: build: runs-on: ubuntu-latest From 457bf57895b82fa136e8d26aab0ee3496b47b64f Mon Sep 17 00:00:00 2001 From: Dmitri Shimanski Date: Wed, 19 Mar 2025 14:25:18 +0200 Subject: [PATCH 5/9] Update appsettings.json --- tests/Telegram.Tests/appsettings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Telegram.Tests/appsettings.json b/tests/Telegram.Tests/appsettings.json index aba4462..50a43b4 100644 --- a/tests/Telegram.Tests/appsettings.json +++ b/tests/Telegram.Tests/appsettings.json @@ -1,3 +1,3 @@ { - "telegram_test_token": "7994961223:AAEx8olljS-CoeFa1L0EJeW-MBlEQXuov2U" -} \ No newline at end of file + "_telegram_test_token": "PROVIDE_YOUR_TOKEN" +} From ee9565ea6d14b94ab1aa9fa2c87d698a9a9bea12 Mon Sep 17 00:00:00 2001 From: Dmitri Shimanski Date: Wed, 19 Mar 2025 14:28:40 +0200 Subject: [PATCH 6/9] Update dotnet.yml --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 188ce82..9835c8e 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -6,7 +6,7 @@ on: pull_request: branches: [ "*" ] env: - "telegram_test_token": ${{TELEGRAM_TEST_TOKEN}} + "telegram_test_token": ${{secrets.TELEGRAM_TEST_TOKEN}} jobs: build: From 26ec732a3874484989fa58f26a839adeb7519600 Mon Sep 17 00:00:00 2001 From: Dmitri Shimanski Date: Wed, 19 Mar 2025 14:32:55 +0200 Subject: [PATCH 7/9] Update dotnet.yml --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 9835c8e..755cf05 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -20,6 +20,6 @@ jobs: - name: Restore dependencies run: dotnet restore - name: Build - run: dotnet build --no-restore + run: dotnet build --no-restore /p:TreatWarningsAsErrors=false - name: Test run: dotnet test --no-build --verbosity normal From ebdbde8795389c22f226ded15e6a6613af3f544c Mon Sep 17 00:00:00 2001 From: Dmitri Shimanski Date: Wed, 19 Mar 2025 14:38:04 +0200 Subject: [PATCH 8/9] Try to fix error with tests --- tests/Telegram.Tests/TestTelegramHostedService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Telegram.Tests/TestTelegramHostedService.cs b/tests/Telegram.Tests/TestTelegramHostedService.cs index ba47758..c6d70d4 100644 --- a/tests/Telegram.Tests/TestTelegramHostedService.cs +++ b/tests/Telegram.Tests/TestTelegramHostedService.cs @@ -26,10 +26,10 @@ namespace Telegram.Tests public void Setup() { var conf = new ConfigurationBuilder() + .SetBasePath(AppContext.BaseDirectory) + .AddJsonFile("appsettings.json", false) .AddEnvironmentVariables() - .AddJsonFile("appsettings.json") .Build(); - Console.WriteLine(conf.GetValue("telegram_test_token")); _configMock = new Mock(); _configMock.Setup(c => c.Token).Returns(conf.GetValue("telegram_test_token")); _services = new ServiceCollection(); From aa4b86d12f057874927285f5b9ce279c34313457 Mon Sep 17 00:00:00 2001 From: Dmitri Shimanski Date: Wed, 19 Mar 2025 15:10:56 +0200 Subject: [PATCH 9/9] Fix tests and warnings --- .github/workflows/dotnet.yml | 6 +- .../Telegram.Examples.csproj | 1 + .../Services/TelegramHostedService.cs | 80 +++++++++---------- Telegram.Net/Telegram.Net.csproj | 1 + tests/Telegram.Tests/Telegram.Tests.csproj | 2 +- .../TestTelegramHostedService.cs | 66 +++++---------- 6 files changed, 65 insertions(+), 91 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 755cf05..8e4c1ab 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -12,9 +12,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: 7.0.x - name: Restore dependencies @@ -22,4 +22,4 @@ jobs: - name: Build run: dotnet build --no-restore /p:TreatWarningsAsErrors=false - name: Test - run: dotnet test --no-build --verbosity normal + run: dotnet test --no-build --verbosity normal -e telegram_test_token=${{secrets.TELEGRAM_TEST_TOKEN}} \ No newline at end of file diff --git a/Examples/Telegram.Examples/Telegram.Examples.csproj b/Examples/Telegram.Examples/Telegram.Examples.csproj index 1622ed8..2891280 100644 --- a/Examples/Telegram.Examples/Telegram.Examples.csproj +++ b/Examples/Telegram.Examples/Telegram.Examples.csproj @@ -5,6 +5,7 @@ net7.0 enable enable + true diff --git a/Telegram.Net/Services/TelegramHostedService.cs b/Telegram.Net/Services/TelegramHostedService.cs index 7793223..3f05c4f 100644 --- a/Telegram.Net/Services/TelegramHostedService.cs +++ b/Telegram.Net/Services/TelegramHostedService.cs @@ -14,14 +14,14 @@ namespace Telegram.Net.Services; public class TelegramHostedService : IHostedService { private IServiceCollection isc { get; } - private TelegramBotClient Client { get; } + internal TelegramBotClient Client { get; set; } private ITelegramBotConfig Config { get; } - internal static Dictionary> CommandHandler { get; } = new(); - internal static List> EditedMessageHandler { get; } = new(); - internal static Dictionary> CallbackQueryHandler { get; } = new(); - internal static Dictionary> InlineHandler { get; } = new(); - internal static Func? PreCheckoutHandler { get; set; } - internal static List> DefaultUpdateHandler { get; } = new(); + internal Dictionary> CommandHandler { get; } = new(); + internal List> EditedMessageHandler { get; } = new(); + internal Dictionary> CallbackQueryHandler { get; } = new(); + internal Dictionary> InlineHandler { get; } = new(); + internal Func? PreCheckoutHandler { get; set; } + internal List> DefaultUpdateHandler { get; } = new(); public TelegramHostedService(ITelegramBotConfig config, IServiceCollection isc) { @@ -47,10 +47,8 @@ public class TelegramHostedService : IHostedService internal async Task AddAttributes(CancellationToken cancellationToken) { - var mutex = new Mutex(); await Task.Run(async () => { - mutex.WaitOne(); var implementations = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => a.GetTypes()) .Where(t => typeof(IUpdatePollingService).IsAssignableFrom(t) && !t.IsInterface); @@ -82,7 +80,7 @@ public class TelegramHostedService : IHostedService if (IsValidHandlerMethod(method, typeof(Message))) { var handler = CreateDelegate(method); - CommandHandler.Add(commandAttr.Command, handler); + CommandHandler.TryAdd(commandAttr.Command, handler); } continue; } @@ -93,7 +91,7 @@ public class TelegramHostedService : IHostedService if (IsValidHandlerMethod(method, typeof(CallbackQuery))) { var handler = CreateDelegate(method); - CallbackQueryHandler.Add(callbackAttr.QueryId, handler); + CallbackQueryHandler.TryAdd(callbackAttr.QueryId, handler); } continue; } @@ -115,7 +113,7 @@ public class TelegramHostedService : IHostedService if (IsValidHandlerMethod(method, typeof(InlineQuery))) { var handler = CreateDelegate(method); - InlineHandler.Add(inlineAttr.InlineId, handler); + InlineHandler.TryAdd(inlineAttr.InlineId, handler); } continue; } @@ -142,44 +140,44 @@ public class TelegramHostedService : IHostedService continue; } } - mutex.ReleaseMutex(); }, cancellationToken); } + internal async Task UpdateHandler(ITelegramBotClient client, Update update, CancellationToken ctx) + { + switch (update) + { + case { Message: { } message }: + await CommandHandler.FirstOrDefault(k => message.Text!.StartsWith(k.Key)).Value(client, message, ctx); + break; + case { EditedMessage: { } message }: + EditedMessageHandler.ForEach(async k => await k(client, message, ctx)); + break; + case { CallbackQuery: { } callbackQuery }: + await CallbackQueryHandler.FirstOrDefault(k => callbackQuery.Data!.StartsWith(k.Key)).Value(client, callbackQuery, ctx); + break; + case { InlineQuery: { } inlineQuery }: + await InlineHandler.FirstOrDefault(k => inlineQuery.Id.StartsWith(k.Key)).Value(client, inlineQuery, ctx); + break; + case { PreCheckoutQuery: { } preCheckoutQuery }: + if (PreCheckoutHandler != null) await PreCheckoutHandler(client, preCheckoutQuery, ctx); + break; + default: + DefaultUpdateHandler.ForEach(async k => await k(client, update, ctx)); + break; + } + } + [SuppressMessage("ReSharper", "AsyncVoidLambda")] public async Task StartAsync(CancellationToken cancellationToken) { await AddAttributes(cancellationToken); - - + + + Client.StartReceiving( - async (client, update, ctx) => - { - switch (update) - { - case { Message: { } message }: - await CommandHandler.FirstOrDefault(k => message.Text!.StartsWith(k.Key)).Value(client, message, cancellationToken); - break; - case { EditedMessage: { } message }: - EditedMessageHandler.ForEach(async k => await k(client, message, cancellationToken)); - break; - case { CallbackQuery: { } callbackQuery }: - await CallbackQueryHandler.FirstOrDefault(k => callbackQuery.Data!.StartsWith(k.Key)).Value(client, callbackQuery, cancellationToken); - break; - case { InlineQuery: { } inlineQuery }: - await InlineHandler.FirstOrDefault(k => inlineQuery.Id.StartsWith(k.Key)).Value(client, inlineQuery, cancellationToken); - break; - case {PreCheckoutQuery: { } preCheckoutQuery}: - if (PreCheckoutHandler != null) - await PreCheckoutHandler(client, preCheckoutQuery, cancellationToken); - break; - default: - DefaultUpdateHandler.ForEach(async k => await k(client, update, ctx)); - break; - } - - }, + UpdateHandler, Config.errorHandler ?? ((_, _, _) => Task.CompletedTask), Config.ReceiverOptions, cancellationToken); diff --git a/Telegram.Net/Telegram.Net.csproj b/Telegram.Net/Telegram.Net.csproj index eae3820..f612b35 100644 --- a/Telegram.Net/Telegram.Net.csproj +++ b/Telegram.Net/Telegram.Net.csproj @@ -1,6 +1,7 @@ + true net7.0 enable enable diff --git a/tests/Telegram.Tests/Telegram.Tests.csproj b/tests/Telegram.Tests/Telegram.Tests.csproj index 2eed84f..0b9b510 100644 --- a/tests/Telegram.Tests/Telegram.Tests.csproj +++ b/tests/Telegram.Tests/Telegram.Tests.csproj @@ -4,7 +4,7 @@ net7.0 enable enable - + true false true diff --git a/tests/Telegram.Tests/TestTelegramHostedService.cs b/tests/Telegram.Tests/TestTelegramHostedService.cs index c6d70d4..656485f 100644 --- a/tests/Telegram.Tests/TestTelegramHostedService.cs +++ b/tests/Telegram.Tests/TestTelegramHostedService.cs @@ -13,13 +13,14 @@ using Microsoft.Extensions.DependencyInjection; using Moq; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.ReplyMarkups; +using Telegram.Net.Models; namespace Telegram.Tests { [TestFixture] public class TelegramHostedServiceTests { - private Mock _configMock; + private TelegramBotConfig _configMock; private ServiceCollection _services; [SetUp] @@ -30,14 +31,14 @@ namespace Telegram.Tests .AddJsonFile("appsettings.json", false) .AddEnvironmentVariables() .Build(); - _configMock = new Mock(); - _configMock.Setup(c => c.Token).Returns(conf.GetValue("telegram_test_token")); + + _configMock = new TelegramBotConfig(conf.GetValue("telegram_test_token") ?? throw new Exception("Provide telegram token first")); _services = new ServiceCollection(); } public class TestHandler : IUpdatePollingService { - [Command("start")] + [Command("/start")] public async Task HandleStart(ITelegramBotClient client, Message message, CancellationToken ct) => await client.SendTextMessageAsync(message.Chat.Id, "Started", cancellationToken: ct); @@ -66,18 +67,18 @@ namespace Telegram.Tests public void AddAttributes_RegistersCommandHandlersCorrectly() { _services.AddSingleton(); - var service = new TelegramHostedService(_configMock.Object, _services); + var service = new TelegramHostedService(_configMock, _services); service.AddAttributes(CancellationToken.None).Wait(); Assert.Multiple(() => { - Assert.That(TelegramHostedService.CommandHandler, Contains.Key("start")); - Assert.That(TelegramHostedService.CallbackQueryHandler, Contains.Key("test_callback")); - Assert.That(TelegramHostedService.EditedMessageHandler, Has.Count.EqualTo(1)); - Assert.That(TelegramHostedService.InlineHandler, Contains.Key("search")); - Assert.That(TelegramHostedService.PreCheckoutHandler, Is.Not.Null); - Assert.That(TelegramHostedService.DefaultUpdateHandler, Has.Count.EqualTo(1)); + Assert.That(service.CommandHandler, Contains.Key("/start")); + Assert.That(service.CallbackQueryHandler, Contains.Key("test_callback")); + Assert.That(service.EditedMessageHandler, Has.Count.EqualTo(1)); + Assert.That(service.InlineHandler, Contains.Key("search")); + Assert.That(service.PreCheckoutHandler, Is.Not.Null); + Assert.That(service.DefaultUpdateHandler, Has.Count.EqualTo(1)); }); } @@ -118,8 +119,14 @@ namespace Telegram.Tests [SetUp] public void Setup() { + var conf = new ConfigurationBuilder() + .SetBasePath(AppContext.BaseDirectory) + .AddJsonFile("appsettings.json", false) + .AddEnvironmentVariables() + .Build(); + _configMock = new Mock(); - _configMock.Setup(c => c.Token).Returns("test_token"); + _configMock.Setup(c => c.Token).Returns(conf.GetValue("telegram_test_token") ?? throw new Exception("Provide token first")); _configMock.Setup(c => c.ReceiverOptions).Returns(new ReceiverOptions()); var services = new ServiceCollection(); @@ -137,23 +144,6 @@ namespace Telegram.Tests var message = new Message { Text = "/start", Chat = new Chat { Id = 123 } }; var update = new Update { Message = message }; - _botClientMock.Setup(b => b.SendMessage( - It.IsAny(), - "Started", - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny() - )).Verifiable(); - // Act await _hostedService.StartAsync(CancellationToken.None); await InvokeUpdateHandler(update); @@ -169,15 +159,6 @@ namespace Telegram.Tests var callback = new CallbackQuery { Data = "test_callback", Id = "cb_id" }; var update = new Update { CallbackQuery = callback }; - _botClientMock.Setup(b => b.AnswerCallbackQueryAsync( - "cb_id", - "Callback handled", - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny() - )).Verifiable(); - // Act await _hostedService.StartAsync(CancellationToken.None); await InvokeUpdateHandler(update); @@ -188,14 +169,7 @@ namespace Telegram.Tests private async Task InvokeUpdateHandler(Update update) { - var clientField = typeof(TelegramHostedService).GetField("Client", BindingFlags.NonPublic | BindingFlags.Instance); - clientField.SetValue(_hostedService, _botClientMock.Object); - - var updateHandler = _botClientMock.Invocations - .First(i => i.Method.Name == "StartReceiving") - .Arguments[0] as Func; - - await updateHandler(_botClientMock.Object, update, CancellationToken.None); + await _hostedService.UpdateHandler(_botClientMock.Object, update, CancellationToken.None); } } } \ No newline at end of file