Skip to content

Commit

Permalink
Add IProvider interface (#16)
Browse files Browse the repository at this point in the history
* Add IProvider and a MockProvider

* Add MockProviderTest
  • Loading branch information
ValMati authored Nov 26, 2023
1 parent 24d4746 commit a5a66b6
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 9 deletions.
4 changes: 4 additions & 0 deletions src/ValMati.StockBot/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using Serilog;
using Serilog.Events;
using Telegram.Bot;
using ValMati.StockBot.Providers;
using ValMati.StockBot.Providers.Abstractions;
using ValMati.StockBot.Services;
using ValMati.StockBot.Services.Abstractions;

Expand Down Expand Up @@ -51,6 +53,8 @@ private static async Task Main(string[] args)

services.AddScoped<IMessageHandler, MessageHandler>();

services.AddScoped<IProvider, MockProvider>();

// Build service provider
IServiceProvider serviceProvider = services.BuildServiceProvider();

Expand Down
8 changes: 8 additions & 0 deletions src/ValMati.StockBot/Providers/Abstractions/IProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using ValMati.StockBot.Providers.Model;

namespace ValMati.StockBot.Providers.Abstractions;

internal interface IProvider
{
Task<Data> GetDataAsync(string symbol);
}
24 changes: 24 additions & 0 deletions src/ValMati.StockBot/Providers/MockProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using ValMati.StockBot.Providers.Abstractions;
using ValMati.StockBot.Providers.Model;

namespace ValMati.StockBot.Providers;

internal class MockProvider : IProvider
{
public Task<Data> GetDataAsync(string symbol)
{
Data result = new Data
{
Currency = "EUR",
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(-1)),
Open = 75.5m,
Close = 78.6m,
Maximum = 100.0m,
Minimum = 65.4m,
Volume = 7896.45m,
AdditionalInfo = "This is mock information."
};

return Task.FromResult(result);
}
}
20 changes: 20 additions & 0 deletions src/ValMati.StockBot/Providers/Model/Data.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace ValMati.StockBot.Providers.Model;

internal sealed record Data
{
public string Currency { get; init; } = "EUR";

public DateOnly Date { get; init; }

public Decimal Open { get; init; }

public Decimal Close { get; init; }

public Decimal Maximum { get; init; }

public Decimal Minimum { get; init; }

public Decimal Volume { get; init; }

public string AdditionalInfo { get; init; } = null!;
}
23 changes: 21 additions & 2 deletions src/ValMati.StockBot/Services/MessageHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@
using Telegram.Bot;
using Telegram.Bot.Exceptions;
using Telegram.Bot.Types;
using ValMati.StockBot.Providers.Abstractions;
using ValMati.StockBot.Providers.Model;
using ValMati.StockBot.Services.Abstractions;

namespace ValMati.StockBot.Services;

internal class MessageHandler : IMessageHandler
{
private readonly ILogger<MessageHandler> logger;
private readonly IProvider provider;

public MessageHandler(ILogger<MessageHandler> logger)
public MessageHandler(
ILogger<MessageHandler> logger,
IProvider provider)
{
this.logger = logger;
this.provider = provider;
}

public async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
Expand All @@ -33,10 +39,14 @@ public async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update,

logger.LogInformation("Received a '{messageText}' message in chat {chatId}.", messageText, chatId);

Data data = await provider.GetDataAsync(messageText);

string text = GetText(messageText, data);

// Echo received message text
_ = await botClient.SendTextMessageAsync(
chatId: chatId,
text: "You said:\n" + messageText,
text: text,
cancellationToken: cancellationToken);
}

Expand All @@ -53,4 +63,13 @@ ApiRequestException apiRequestException

return Task.CompletedTask;
}

private string GetText(string messageText, Data data) =>
$"Symbol: {messageText}\n" +
$"Date: {data.Date}\n" +
$"Open: {data.Open}\n" +
$"Close: {data.Close}\n" +
$"Max: {data.Maximum}\n" +
$"Min: {data.Minimum}\n" +
$"Vol: {data.Volume}";
}
20 changes: 20 additions & 0 deletions tests/ValMati.StockBot.Test/Providers/MockProviderTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using FluentAssertions;
using ValMati.StockBot.Providers;

namespace ValMati.StockBot.Test.Providers;

public class MockProviderTest
{
[Fact]
public async Task GetDataAsync_Ok()
{
// Arrange
MockProvider sut = new();

// Act
var actual = await sut.GetDataAsync(Guid.NewGuid().ToString());

// Assert
actual.Should().NotBeNull();
}
}
37 changes: 31 additions & 6 deletions tests/ValMati.StockBot.Test/Services/MessageHandlerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using Telegram.Bot.Exceptions;
using Telegram.Bot.Requests;
using Telegram.Bot.Types;
using ValMati.StockBot.Providers.Abstractions;
using ValMati.StockBot.Providers.Model;
using ValMati.StockBot.Services;

namespace ValMati.StockBot.Tests.Services;
Expand All @@ -19,7 +21,8 @@ public async Task HandleUpdateAsync_NotAMessage()
Update update = new Update { Message = null };

ILogger<MessageHandler> logger = Substitute.For<ILogger<MessageHandler>>();
MessageHandler sut = new MessageHandler(logger);
IProvider provider = Substitute.For<IProvider>();
MessageHandler sut = new MessageHandler(logger, provider);

// Act
await sut.HandleUpdateAsync(botClient, update, CancellationToken.None);
Expand All @@ -28,6 +31,10 @@ public async Task HandleUpdateAsync_NotAMessage()
await botClient
.DidNotReceiveWithAnyArgs()
.SendTextMessageAsync(chatId: default!, text: default!, cancellationToken: default);

await provider
.DidNotReceiveWithAnyArgs()
.GetDataAsync(symbol: default!);
}

[Fact]
Expand All @@ -39,7 +46,8 @@ public async Task HandleUpdateAsync_MessageIsText()
Update update = new Update { Message = new Message { Text = null } };

ILogger<MessageHandler> logger = Substitute.For<ILogger<MessageHandler>>();
MessageHandler sut = new MessageHandler(logger);
IProvider provider = Substitute.For<IProvider>();
MessageHandler sut = new MessageHandler(logger, provider);

// Act
await sut.HandleUpdateAsync(botClient, update, CancellationToken.None);
Expand All @@ -48,6 +56,10 @@ public async Task HandleUpdateAsync_MessageIsText()
await botClient
.DidNotReceiveWithAnyArgs()
.SendTextMessageAsync(chatId: default!, text: default!, cancellationToken: default);

await provider
.DidNotReceiveWithAnyArgs()
.GetDataAsync(symbol: default!);
}

[Fact]
Expand All @@ -74,7 +86,11 @@ public async Task HandleUpdateAsync_OK()
};

ILogger<MessageHandler> logger = Substitute.For<ILogger<MessageHandler>>();
MessageHandler sut = new MessageHandler(logger);

IProvider provider = Substitute.For<IProvider>();
provider.GetDataAsync(textMessage).Returns(new Data());

MessageHandler sut = new MessageHandler(logger, provider);

// Act
await sut.HandleUpdateAsync(botClient, update, CancellationToken.None);
Expand All @@ -85,10 +101,14 @@ await botClient
.MakeRequestAsync(
Arg.Is<SendMessageRequest>(r => r.ChatId == chatId && r.Text.Contains(textMessage)),
cancellationToken);

await provider
.Received(1)
.GetDataAsync(textMessage);
}

[Fact]
public void HandlePollingErrorAsync_LogError()
public async Task HandlePollingErrorAsync_LogError()
{
// Arrange
ITelegramBotClient botClient = Substitute.For<ITelegramBotClient>();
Expand All @@ -97,10 +117,11 @@ public void HandlePollingErrorAsync_LogError()
ApiRequestException exception = new ApiRequestException(exceptionMessage);

ILogger<MessageHandler> logger = Substitute.For<ILogger<MessageHandler>>();
MessageHandler sut = new MessageHandler(logger);
IProvider provider = Substitute.For<IProvider>();
MessageHandler sut = new MessageHandler(logger, provider);

// Act
sut.HandlePollingErrorAsync(botClient, exception, CancellationToken.None);
await sut.HandlePollingErrorAsync(botClient, exception, CancellationToken.None);

// Assert
logger
Expand All @@ -111,5 +132,9 @@ public void HandlePollingErrorAsync_LogError()
Arg.Any<object>(),
exception,
Arg.Any<Func<object, Exception?, string>>());

await provider
.DidNotReceiveWithAnyArgs()
.GetDataAsync(symbol: default!);
}
}
3 changes: 2 additions & 1 deletion tests/ValMati.StockBot.Test/ValMati.StockBot.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NSubstitute" Version="5.1.0" />
<PackageReference Include="NSubstitute.Analyzers.CSharp" Version="1.0.16">
Expand All @@ -17,7 +18,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PackageReference Include="coverlet.collector" Version="6.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down

0 comments on commit a5a66b6

Please sign in to comment.