Add more tests
This commit is contained in:
647
ChatBot.Tests/Services/Telegram/TelegramMessageSenderTests.cs
Normal file
647
ChatBot.Tests/Services/Telegram/TelegramMessageSenderTests.cs
Normal file
@@ -0,0 +1,647 @@
|
||||
using ChatBot.Services.Telegram.Interfaces;
|
||||
using ChatBot.Services.Telegram.Services;
|
||||
using ChatBot.Tests.TestUtilities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Exceptions;
|
||||
using Telegram.Bot.Types;
|
||||
|
||||
namespace ChatBot.Tests.Services.Telegram;
|
||||
|
||||
public class TelegramMessageSenderTests : UnitTestBase
|
||||
{
|
||||
private readonly Mock<ILogger<TelegramMessageSender>> _loggerMock;
|
||||
private readonly Mock<ITelegramBotClient> _botClientMock;
|
||||
private readonly Mock<ITelegramMessageSenderWrapper> _messageSenderWrapperMock;
|
||||
private readonly TelegramMessageSender _messageSender;
|
||||
|
||||
public TelegramMessageSenderTests()
|
||||
{
|
||||
_loggerMock = new Mock<ILogger<TelegramMessageSender>>();
|
||||
_botClientMock = new Mock<ITelegramBotClient>();
|
||||
_messageSenderWrapperMock = new Mock<ITelegramMessageSenderWrapper>();
|
||||
_messageSender = new TelegramMessageSender(
|
||||
_loggerMock.Object,
|
||||
_messageSenderWrapperMock.Object
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ShouldCreateInstance()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.NotNull(_messageSender);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendMessageWithRetry_ShouldSendMessageSuccessfully()
|
||||
{
|
||||
// Arrange
|
||||
var chatId = 12345L;
|
||||
var text = "Test message";
|
||||
var replyToMessageId = 67890;
|
||||
var cancellationToken = CancellationToken.None;
|
||||
|
||||
_messageSenderWrapperMock
|
||||
.Setup(x =>
|
||||
x.SendMessageAsync(
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<int>(),
|
||||
It.IsAny<CancellationToken>()
|
||||
)
|
||||
)
|
||||
.ReturnsAsync(new Message());
|
||||
|
||||
// Act
|
||||
await _messageSender.SendMessageWithRetry(
|
||||
_botClientMock.Object,
|
||||
chatId,
|
||||
text,
|
||||
replyToMessageId,
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
// Assert
|
||||
_messageSenderWrapperMock.Verify(
|
||||
x =>
|
||||
x.SendMessageAsync(
|
||||
It.Is<long>(id => id == chatId),
|
||||
It.Is<string>(t => t == text),
|
||||
It.Is<int>(r => r == replyToMessageId),
|
||||
It.Is<CancellationToken>(ct => ct == cancellationToken)
|
||||
),
|
||||
Times.Once
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendMessageWithRetry_WithCustomMaxRetries_ShouldUseCustomValue()
|
||||
{
|
||||
// Arrange
|
||||
var chatId = 12345L;
|
||||
var text = "Test message";
|
||||
var replyToMessageId = 67890;
|
||||
var maxRetries = 5;
|
||||
var cancellationToken = CancellationToken.None;
|
||||
|
||||
_messageSenderWrapperMock
|
||||
.Setup(x =>
|
||||
x.SendMessageAsync(
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<int>(),
|
||||
It.IsAny<CancellationToken>()
|
||||
)
|
||||
)
|
||||
.ReturnsAsync(new Message());
|
||||
|
||||
// Act
|
||||
await _messageSender.SendMessageWithRetry(
|
||||
_botClientMock.Object,
|
||||
chatId,
|
||||
text,
|
||||
replyToMessageId,
|
||||
cancellationToken,
|
||||
maxRetries
|
||||
);
|
||||
|
||||
// Assert
|
||||
_messageSenderWrapperMock.Verify(
|
||||
x =>
|
||||
x.SendMessageAsync(
|
||||
It.Is<long>(id => id == chatId),
|
||||
It.Is<string>(t => t == text),
|
||||
It.Is<int>(r => r == replyToMessageId),
|
||||
It.Is<CancellationToken>(ct => ct == cancellationToken)
|
||||
),
|
||||
Times.Once
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendMessageWithRetry_WithRateLimitError_ShouldRetryAndSucceed()
|
||||
{
|
||||
// Arrange
|
||||
var chatId = 12345L;
|
||||
var text = "Test message";
|
||||
var replyToMessageId = 67890;
|
||||
var cancellationToken = CancellationToken.None;
|
||||
|
||||
var rateLimitException = new ApiRequestException("Rate limit exceeded", 429);
|
||||
|
||||
_messageSenderWrapperMock
|
||||
.SetupSequence(x =>
|
||||
x.SendMessageAsync(
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<int>(),
|
||||
It.IsAny<CancellationToken>()
|
||||
)
|
||||
)
|
||||
.ThrowsAsync(rateLimitException)
|
||||
.ReturnsAsync(new Message());
|
||||
|
||||
// Act
|
||||
await _messageSender.SendMessageWithRetry(
|
||||
_botClientMock.Object,
|
||||
chatId,
|
||||
text,
|
||||
replyToMessageId,
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
// Assert
|
||||
_messageSenderWrapperMock.Verify(
|
||||
x =>
|
||||
x.SendMessageAsync(
|
||||
It.Is<long>(id => id == chatId),
|
||||
It.Is<string>(t => t == text),
|
||||
It.Is<int>(r => r == replyToMessageId),
|
||||
It.Is<CancellationToken>(ct => ct == cancellationToken)
|
||||
),
|
||||
Times.Exactly(2)
|
||||
);
|
||||
|
||||
_loggerMock.Verify(
|
||||
x =>
|
||||
x.Log(
|
||||
LogLevel.Warning,
|
||||
It.IsAny<EventId>(),
|
||||
It.Is<It.IsAnyType>(
|
||||
(v, t) => v.ToString()!.Contains("Rate limit exceeded (429)")
|
||||
),
|
||||
It.IsAny<Exception>(),
|
||||
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
|
||||
),
|
||||
Times.Once
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendMessageWithRetry_WithRateLimitErrorMaxRetries_ShouldThrowException()
|
||||
{
|
||||
// Arrange
|
||||
var chatId = 12345L;
|
||||
var text = "Test message";
|
||||
var replyToMessageId = 67890;
|
||||
var maxRetries = 2;
|
||||
var cancellationToken = CancellationToken.None;
|
||||
|
||||
var rateLimitException = new ApiRequestException("Rate limit exceeded", 429);
|
||||
|
||||
_messageSenderWrapperMock
|
||||
.Setup(x =>
|
||||
x.SendMessageAsync(
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<int>(),
|
||||
It.IsAny<CancellationToken>()
|
||||
)
|
||||
)
|
||||
.ThrowsAsync(rateLimitException);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||
_messageSender.SendMessageWithRetry(
|
||||
_botClientMock.Object,
|
||||
chatId,
|
||||
text,
|
||||
replyToMessageId,
|
||||
cancellationToken,
|
||||
maxRetries
|
||||
)
|
||||
);
|
||||
|
||||
Assert.Contains(
|
||||
"Failed to send message after 2 attempts due to rate limiting",
|
||||
exception.Message
|
||||
);
|
||||
|
||||
_messageSenderWrapperMock.Verify(
|
||||
x =>
|
||||
x.SendMessageAsync(
|
||||
It.Is<long>(id => id == chatId),
|
||||
It.Is<string>(t => t == text),
|
||||
It.Is<int>(r => r == replyToMessageId),
|
||||
It.Is<CancellationToken>(ct => ct == cancellationToken)
|
||||
),
|
||||
Times.Exactly(maxRetries)
|
||||
);
|
||||
|
||||
_loggerMock.Verify(
|
||||
x =>
|
||||
x.Log(
|
||||
LogLevel.Error,
|
||||
It.IsAny<EventId>(),
|
||||
It.Is<It.IsAnyType>(
|
||||
(v, t) =>
|
||||
v.ToString()!
|
||||
.Contains(
|
||||
"Failed to send message after 2 attempts due to rate limiting"
|
||||
)
|
||||
),
|
||||
It.IsAny<Exception>(),
|
||||
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
|
||||
),
|
||||
Times.Once
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendMessageWithRetry_WithGenericException_ShouldThrowInvalidOperationException()
|
||||
{
|
||||
// Arrange
|
||||
var chatId = 12345L;
|
||||
var text = "Test message";
|
||||
var replyToMessageId = 67890;
|
||||
var cancellationToken = CancellationToken.None;
|
||||
var originalException = new HttpRequestException("Network error");
|
||||
|
||||
_messageSenderWrapperMock
|
||||
.Setup(x =>
|
||||
x.SendMessageAsync(
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<int>(),
|
||||
It.IsAny<CancellationToken>()
|
||||
)
|
||||
)
|
||||
.ThrowsAsync(originalException);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||
_messageSender.SendMessageWithRetry(
|
||||
_botClientMock.Object,
|
||||
chatId,
|
||||
text,
|
||||
replyToMessageId,
|
||||
cancellationToken
|
||||
)
|
||||
);
|
||||
|
||||
Assert.Contains("Failed to send message to chat 12345 after 1 attempts", exception.Message);
|
||||
Assert.Equal(originalException, exception.InnerException);
|
||||
|
||||
_loggerMock.Verify(
|
||||
x =>
|
||||
x.Log(
|
||||
LogLevel.Error,
|
||||
It.IsAny<EventId>(),
|
||||
It.Is<It.IsAnyType>(
|
||||
(v, t) =>
|
||||
v.ToString()!
|
||||
.Contains(
|
||||
"Unexpected error sending message to chat 12345 on attempt 1"
|
||||
)
|
||||
),
|
||||
originalException,
|
||||
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
|
||||
),
|
||||
Times.Once
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendMessageWithRetry_WithApiRequestExceptionNon429_ShouldThrowInvalidOperationException()
|
||||
{
|
||||
// Arrange
|
||||
var chatId = 12345L;
|
||||
var text = "Test message";
|
||||
var replyToMessageId = 67890;
|
||||
var cancellationToken = CancellationToken.None;
|
||||
var apiException = new ApiRequestException("Bad Request", 400);
|
||||
|
||||
_messageSenderWrapperMock
|
||||
.Setup(x =>
|
||||
x.SendMessageAsync(
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<int>(),
|
||||
It.IsAny<CancellationToken>()
|
||||
)
|
||||
)
|
||||
.ThrowsAsync(apiException);
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||
_messageSender.SendMessageWithRetry(
|
||||
_botClientMock.Object,
|
||||
chatId,
|
||||
text,
|
||||
replyToMessageId,
|
||||
cancellationToken
|
||||
)
|
||||
);
|
||||
|
||||
Assert.Contains("Failed to send message to chat 12345 after 1 attempts", exception.Message);
|
||||
Assert.Equal(apiException, exception.InnerException);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendMessageWithRetry_WithCancelledToken_ShouldHandleCancellation()
|
||||
{
|
||||
// Arrange
|
||||
var chatId = 12345L;
|
||||
var text = "Test message";
|
||||
var replyToMessageId = 67890;
|
||||
using var cancellationTokenSource = new CancellationTokenSource();
|
||||
await cancellationTokenSource.CancelAsync();
|
||||
var cancelledToken = cancellationTokenSource.Token;
|
||||
|
||||
_messageSenderWrapperMock
|
||||
.Setup(x =>
|
||||
x.SendMessageAsync(
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<int>(),
|
||||
It.IsAny<CancellationToken>()
|
||||
)
|
||||
)
|
||||
.ReturnsAsync(new Message());
|
||||
|
||||
// Act
|
||||
await _messageSender.SendMessageWithRetry(
|
||||
_botClientMock.Object,
|
||||
chatId,
|
||||
text,
|
||||
replyToMessageId,
|
||||
cancelledToken
|
||||
);
|
||||
|
||||
// Assert
|
||||
_messageSenderWrapperMock.Verify(
|
||||
x =>
|
||||
x.SendMessageAsync(
|
||||
It.Is<long>(id => id == chatId),
|
||||
It.Is<string>(t => t == text),
|
||||
It.Is<int>(r => r == replyToMessageId),
|
||||
It.Is<CancellationToken>(ct => ct == cancelledToken)
|
||||
),
|
||||
Times.Once
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendMessageWithRetry_WithEmptyText_ShouldSendEmptyMessage()
|
||||
{
|
||||
// Arrange
|
||||
var chatId = 12345L;
|
||||
var text = "";
|
||||
var replyToMessageId = 0;
|
||||
var cancellationToken = CancellationToken.None;
|
||||
|
||||
_messageSenderWrapperMock
|
||||
.Setup(x =>
|
||||
x.SendMessageAsync(
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<int>(),
|
||||
It.IsAny<CancellationToken>()
|
||||
)
|
||||
)
|
||||
.ReturnsAsync(new Message());
|
||||
|
||||
// Act
|
||||
await _messageSender.SendMessageWithRetry(
|
||||
_botClientMock.Object,
|
||||
chatId,
|
||||
text,
|
||||
replyToMessageId,
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
// Assert
|
||||
_messageSenderWrapperMock.Verify(
|
||||
x =>
|
||||
x.SendMessageAsync(
|
||||
It.Is<long>(id => id == chatId),
|
||||
It.Is<string>(t => t == text),
|
||||
It.Is<int>(r => r == replyToMessageId),
|
||||
It.Is<CancellationToken>(ct => ct == cancellationToken)
|
||||
),
|
||||
Times.Once
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendMessageWithRetry_WithLongText_ShouldSendLongMessage()
|
||||
{
|
||||
// Arrange
|
||||
var chatId = 12345L;
|
||||
var text = new string('A', 4096); // Very long message
|
||||
var replyToMessageId = 67890;
|
||||
var cancellationToken = CancellationToken.None;
|
||||
|
||||
_messageSenderWrapperMock
|
||||
.Setup(x =>
|
||||
x.SendMessageAsync(
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<int>(),
|
||||
It.IsAny<CancellationToken>()
|
||||
)
|
||||
)
|
||||
.ReturnsAsync(new Message());
|
||||
|
||||
// Act
|
||||
await _messageSender.SendMessageWithRetry(
|
||||
_botClientMock.Object,
|
||||
chatId,
|
||||
text,
|
||||
replyToMessageId,
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
// Assert
|
||||
_messageSenderWrapperMock.Verify(
|
||||
x =>
|
||||
x.SendMessageAsync(
|
||||
It.Is<long>(id => id == chatId),
|
||||
It.Is<string>(t => t == text),
|
||||
It.Is<int>(r => r == replyToMessageId),
|
||||
It.Is<CancellationToken>(ct => ct == cancellationToken)
|
||||
),
|
||||
Times.Once
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendMessageWithRetry_WithSpecialCharacters_ShouldSendMessageWithSpecialChars()
|
||||
{
|
||||
// Arrange
|
||||
var chatId = 12345L;
|
||||
var text = "Test message with special chars: !@#$%^&*()_+-=[]{}|;':\",./<>?`~";
|
||||
var replyToMessageId = 67890;
|
||||
var cancellationToken = CancellationToken.None;
|
||||
|
||||
_messageSenderWrapperMock
|
||||
.Setup(x =>
|
||||
x.SendMessageAsync(
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<int>(),
|
||||
It.IsAny<CancellationToken>()
|
||||
)
|
||||
)
|
||||
.ReturnsAsync(new Message());
|
||||
|
||||
// Act
|
||||
await _messageSender.SendMessageWithRetry(
|
||||
_botClientMock.Object,
|
||||
chatId,
|
||||
text,
|
||||
replyToMessageId,
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
// Assert
|
||||
_messageSenderWrapperMock.Verify(
|
||||
x =>
|
||||
x.SendMessageAsync(
|
||||
It.Is<long>(id => id == chatId),
|
||||
It.Is<string>(t => t == text),
|
||||
It.Is<int>(r => r == replyToMessageId),
|
||||
It.Is<CancellationToken>(ct => ct == cancellationToken)
|
||||
),
|
||||
Times.Once
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendMessageWithRetry_WithUnicodeText_ShouldSendUnicodeMessage()
|
||||
{
|
||||
// Arrange
|
||||
var chatId = 12345L;
|
||||
var text = "Test message with unicode: 🚀 Hello 世界 🌍 Привет";
|
||||
var replyToMessageId = 67890;
|
||||
var cancellationToken = CancellationToken.None;
|
||||
|
||||
_messageSenderWrapperMock
|
||||
.Setup(x =>
|
||||
x.SendMessageAsync(
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<int>(),
|
||||
It.IsAny<CancellationToken>()
|
||||
)
|
||||
)
|
||||
.ReturnsAsync(new Message());
|
||||
|
||||
// Act
|
||||
await _messageSender.SendMessageWithRetry(
|
||||
_botClientMock.Object,
|
||||
chatId,
|
||||
text,
|
||||
replyToMessageId,
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
// Assert
|
||||
_messageSenderWrapperMock.Verify(
|
||||
x =>
|
||||
x.SendMessageAsync(
|
||||
It.Is<long>(id => id == chatId),
|
||||
It.Is<string>(t => t == text),
|
||||
It.Is<int>(r => r == replyToMessageId),
|
||||
It.Is<CancellationToken>(ct => ct == cancellationToken)
|
||||
),
|
||||
Times.Once
|
||||
);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1)]
|
||||
[InlineData(2)]
|
||||
[InlineData(3)]
|
||||
[InlineData(5)]
|
||||
[InlineData(10)]
|
||||
public async Task SendMessageWithRetry_WithDifferentMaxRetries_ShouldRespectMaxRetries(
|
||||
int maxRetries
|
||||
)
|
||||
{
|
||||
// Arrange
|
||||
var chatId = 12345L;
|
||||
var text = "Test message";
|
||||
var replyToMessageId = 67890;
|
||||
var cancellationToken = CancellationToken.None;
|
||||
|
||||
_messageSenderWrapperMock
|
||||
.Setup(x =>
|
||||
x.SendMessageAsync(
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<int>(),
|
||||
It.IsAny<CancellationToken>()
|
||||
)
|
||||
)
|
||||
.ReturnsAsync(new Message());
|
||||
|
||||
// Act
|
||||
await _messageSender.SendMessageWithRetry(
|
||||
_botClientMock.Object,
|
||||
chatId,
|
||||
text,
|
||||
replyToMessageId,
|
||||
cancellationToken,
|
||||
maxRetries
|
||||
);
|
||||
|
||||
// Assert
|
||||
_messageSenderWrapperMock.Verify(
|
||||
x =>
|
||||
x.SendMessageAsync(
|
||||
It.Is<long>(id => id == chatId),
|
||||
It.Is<string>(t => t == text),
|
||||
It.Is<int>(r => r == replyToMessageId),
|
||||
It.Is<CancellationToken>(ct => ct == cancellationToken)
|
||||
),
|
||||
Times.Once
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendMessageWithRetry_WithNegativeMaxRetries_ShouldUseDefaultValue()
|
||||
{
|
||||
// Arrange
|
||||
var chatId = 12345L;
|
||||
var text = "Test message";
|
||||
var replyToMessageId = 67890;
|
||||
var maxRetries = -1; // Invalid value
|
||||
var cancellationToken = CancellationToken.None;
|
||||
|
||||
_messageSenderWrapperMock
|
||||
.Setup(x =>
|
||||
x.SendMessageAsync(
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<int>(),
|
||||
It.IsAny<CancellationToken>()
|
||||
)
|
||||
)
|
||||
.ReturnsAsync(new Message());
|
||||
|
||||
// Act
|
||||
await _messageSender.SendMessageWithRetry(
|
||||
_botClientMock.Object,
|
||||
chatId,
|
||||
text,
|
||||
replyToMessageId,
|
||||
cancellationToken,
|
||||
maxRetries
|
||||
);
|
||||
|
||||
// Assert
|
||||
_messageSenderWrapperMock.Verify(
|
||||
x =>
|
||||
x.SendMessageAsync(
|
||||
It.Is<long>(id => id == chatId),
|
||||
It.Is<string>(t => t == text),
|
||||
It.Is<int>(r => r == replyToMessageId),
|
||||
It.Is<CancellationToken>(ct => ct == cancellationToken)
|
||||
),
|
||||
Times.Once
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user