From 5cfecd6886836205a759c450a835975f50ca0572 Mon Sep 17 00:00:00 2001 From: Ayrat Hudaygulov Date: Fri, 14 Jun 2024 18:49:05 +0100 Subject: [PATCH] more tests (#17) --- .../{Tests.fs => BaseTests.fs} | 10 ++-- src/VahterBanBot.Tests/ContainerTestBase.fs | 27 ++++++++++- src/VahterBanBot.Tests/PingTests.fs | 46 +++++++++++++++++++ src/VahterBanBot.Tests/TgMessageUtils.fs | 30 ++++++++++++ .../VahterBanBot.Tests.fsproj | 4 +- src/VahterBanBot/Bot.fs | 8 +++- 6 files changed, 116 insertions(+), 9 deletions(-) rename src/VahterBanBot.Tests/{Tests.fs => BaseTests.fs} (80%) create mode 100644 src/VahterBanBot.Tests/PingTests.fs create mode 100644 src/VahterBanBot.Tests/TgMessageUtils.fs diff --git a/src/VahterBanBot.Tests/Tests.fs b/src/VahterBanBot.Tests/BaseTests.fs similarity index 80% rename from src/VahterBanBot.Tests/Tests.fs rename to src/VahterBanBot.Tests/BaseTests.fs index bca8d56..7a01316 100644 --- a/src/VahterBanBot.Tests/Tests.fs +++ b/src/VahterBanBot.Tests/BaseTests.fs @@ -1,4 +1,4 @@ -module Tests +module BaseTests open System open System.Net.Http @@ -8,10 +8,10 @@ open VahterBanBot.Tests.ContainerTestBase open Xunit open Xunit.Extensions.AssemblyFixture -type Tests(containers: VahterTestContainers) = +type BaseTests(fixture: VahterTestContainers) = [] let ``Random path returns OK`` () = task { - let! resp = containers.Http.GetAsync("/" + Guid.NewGuid().ToString()) + let! resp = fixture.Http.GetAsync("/" + Guid.NewGuid().ToString()) let! body = resp.Content.ReadAsStringAsync() Assert.Equal(System.Net.HttpStatusCode.OK, resp.StatusCode) Assert.Equal("OK", body) @@ -21,14 +21,14 @@ type Tests(containers: VahterTestContainers) = let ``Not possible to interact with the bot without authorization`` () = task { let http = new HttpClient() let content = new StringContent("""{"update_id":123}""", Encoding.UTF8, "application/json") - let uri = containers.Uri.ToString() + "bot" + let uri = fixture.Uri.ToString() + "bot" let! resp = http.PostAsync(uri, content) Assert.Equal(System.Net.HttpStatusCode.Unauthorized, resp.StatusCode) } [] let ``Should be possible to interact with the bot`` () = task { - let! resp = Update(Id = 123) |> containers.SendMessage + let! resp = Update(Id = 123) |> fixture.SendMessage let! body = resp.Content.ReadAsStringAsync() Assert.Equal(System.Net.HttpStatusCode.OK, resp.StatusCode) Assert.Equal("null", body) diff --git a/src/VahterBanBot.Tests/ContainerTestBase.fs b/src/VahterBanBot.Tests/ContainerTestBase.fs index 632833e..ff00d65 100644 --- a/src/VahterBanBot.Tests/ContainerTestBase.fs +++ b/src/VahterBanBot.Tests/ContainerTestBase.fs @@ -8,18 +8,23 @@ open DotNet.Testcontainers.Builders open DotNet.Testcontainers.Configurations open DotNet.Testcontainers.Containers open Newtonsoft.Json +open Npgsql open Telegram.Bot.Types open Testcontainers.PostgreSql +open VahterBanBot.Tests.TgMessageUtils open Xunit +open Dapper type VahterTestContainers() = let solutionDir = CommonDirectoryPath.GetSolutionDirectory() let dbAlias = "vahter-db" + let internalConnectionString = $"Server={dbAlias};Database=vahter_bot_ban;Port=5432;User Id=vahter_bot_ban_service;Password=vahter_bot_ban_service;Include Error Detail=true;Minimum Pool Size=1;Maximum Pool Size=20;Max Auto Prepare=100;Auto Prepare Min Usages=1;Trust Server Certificate=true;" let pgImage = "postgres:15.6" // same as in Azure // will be filled in IAsyncLifetime.InitializeAsync let mutable uri: Uri = null let mutable httpClient: HttpClient = null + let mutable publicConnectionString: string = null // base image for the app, we'll build exactly how we build it in Azure let image = @@ -76,7 +81,7 @@ type VahterTestContainers() = .WithEnvironment("SHOULD_DELETE_CHANNEL_MESSAGES", "true") .WithEnvironment("IGNORE_SIDE_EFFECTS", "false") .WithEnvironment("USE_POLLING", "false") - .WithEnvironment("DATABASE_URL", $"Server={dbAlias};Database=vahter_bot_ban;Port=5432;User Id=vahter_bot_ban_service;Password=vahter_bot_ban_service;Include Error Detail=true;Minimum Pool Size=1;Maximum Pool Size=20;Max Auto Prepare=100;Auto Prepare Min Usages=1;Trust Server Certificate=true;") + .WithEnvironment("DATABASE_URL", internalConnectionString) .DependsOn(flywayContainer) .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(80)) .Build() @@ -90,6 +95,7 @@ type VahterTestContainers() = // wait for both to finish do! imageTask do! dbTask + publicConnectionString <- $"Server=127.0.0.1;Database=vahter_bot_ban;Port={dbContainer.GetMappedPublicPort(5432)};User Id=vahter_bot_ban_service;Password=vahter_bot_ban_service;Include Error Detail=true;Minimum Pool Size=1;Maximum Pool Size=20;Max Auto Prepare=100;Auto Prepare Min Usages=1;Trust Server Certificate=true;" // initialize DB with the schema, database and a DB user let script = File.ReadAllText(CommonDirectoryPath.GetSolutionDirectory().DirectoryPath + "/init.sql") @@ -135,3 +141,22 @@ type VahterTestContainers() = let! resp = httpClient.PostAsync("/bot", content) return resp } + + member _.AdminUsers = [ + Tg.user(id = 34, username = "vahter_1") + Tg.user(id = 69, username = "vahter_2") + ] + + member _.LogChat = Tg.chat(id = -123, username = "logs") + member _.ChatsToMonitor = [ + Tg.chat(id = -666, username = "pro.hell") + Tg.chat(id = -42, username = "dotnetru") + ] + + member _.MessageExist(msg: Message) = task { + use conn = new NpgsqlConnection(publicConnectionString) + //language=postgresql + let sql = "SELECT COUNT(*) FROM message WHERE chat_id = @chatId AND message_id = @messageId" + let! count = conn.QuerySingleAsync(sql, {| chatId = msg.Chat.Id; messageId = msg.MessageId |}) + return count = 1 + } diff --git a/src/VahterBanBot.Tests/PingTests.fs b/src/VahterBanBot.Tests/PingTests.fs new file mode 100644 index 0000000..cb5abd8 --- /dev/null +++ b/src/VahterBanBot.Tests/PingTests.fs @@ -0,0 +1,46 @@ +module PingTests + +open System.Net +open VahterBanBot.Tests.ContainerTestBase +open VahterBanBot.Tests.TgMessageUtils +open Xunit +open Xunit.Extensions.AssemblyFixture + +type PingTests(fixture: VahterTestContainers) = + [] + let ``Message got recorded`` () = task { + // chat from the allowed list + let msg = Tg.quickMsg(chat = fixture.ChatsToMonitor[0]) + + // assert that the message is not in the db + let! msgExist = fixture.MessageExist msg.Message + Assert.False msgExist + + // send the message to the bot + let! resp = fixture.SendMessage msg + Assert.Equal(HttpStatusCode.OK, resp.StatusCode) + + // assert that the message is in the db + let! msgExist = fixture.MessageExist msg.Message + Assert.True msgExist + } + + [] + let ``Message not recorded if chat is not on the list`` () = task { + // some random chat + let msg = Tg.quickMsg(chat = Tg.chat()) + + // assert that the message is not in the db + let! msgExist = fixture.MessageExist msg.Message + Assert.False msgExist + + // send the message to the bot + let! resp = fixture.SendMessage msg + Assert.Equal(HttpStatusCode.OK, resp.StatusCode) + + // assert that the message is still not in the db + let! msgExist = fixture.MessageExist msg.Message + Assert.False msgExist + } + + interface IAssemblyFixture diff --git a/src/VahterBanBot.Tests/TgMessageUtils.fs b/src/VahterBanBot.Tests/TgMessageUtils.fs new file mode 100644 index 0000000..6159591 --- /dev/null +++ b/src/VahterBanBot.Tests/TgMessageUtils.fs @@ -0,0 +1,30 @@ +module VahterBanBot.Tests.TgMessageUtils + +open System +open Telegram.Bot.Types + +type Tg() = + static let rnd = Random.Shared + static member user (?id: int64, ?username: string, ?firstName: string) = + User( + Id = (id |> Option.defaultValue (rnd.NextInt64())), + Username = (username |> Option.defaultValue null), + FirstName = (firstName |> Option.defaultWith (fun () -> Guid.NewGuid().ToString())) + ) + static member chat (?id: int64, ?username: string) = + Chat( + Id = (id |> Option.defaultValue (rnd.NextInt64())), + Username = (username |> Option.defaultValue null) + ) + static member quickMsg (?text: string, ?chat: Chat, ?from: User, ?date: DateTime) = + Update( + Id = rnd.Next(), + Message = + Message( + MessageId = rnd.Next(), + Text = (text |> Option.defaultValue (Guid.NewGuid().ToString())), + Chat = (chat |> Option.defaultValue (Tg.chat())), + From = (from |> Option.defaultValue (Tg.user())), + Date = (date |> Option.defaultValue DateTime.UtcNow) + ) + ) diff --git a/src/VahterBanBot.Tests/VahterBanBot.Tests.fsproj b/src/VahterBanBot.Tests/VahterBanBot.Tests.fsproj index b119d95..1d20868 100644 --- a/src/VahterBanBot.Tests/VahterBanBot.Tests.fsproj +++ b/src/VahterBanBot.Tests/VahterBanBot.Tests.fsproj @@ -9,8 +9,10 @@ + - + + diff --git a/src/VahterBanBot/Bot.fs b/src/VahterBanBot/Bot.fs index a0c26b5..8b67ce5 100644 --- a/src/VahterBanBot/Bot.fs +++ b/src/VahterBanBot/Bot.fs @@ -407,13 +407,17 @@ let onUpdate (botConfig: BotConfiguration) (logger: ILogger) (message: Message) = task { - use banOnReplyActivity = botActivity.StartActivity("onUpdate") - // early return if if we can't process it + // early return if we can't process it if isNull message || isNull message.From then logger.LogWarning "Received update without message" else + + // early return if we don't monitor this chat + if not (botConfig.ChatsToMonitor.ContainsValue message.Chat.Id) then + () + else %banOnReplyActivity .SetTag("chatId", message.Chat.Id)