Skip to content

Commit af0afe7

Browse files
Convert Lottoland API jackpot values from millions to euros (#44)
* Initial plan * Fix jackpot value display by converting million euros to euros Co-authored-by: thomasneuberger <23504477+thomasneuberger@users.noreply.github.com> * Add comment documenting safe range for jackpot multiplication Co-authored-by: thomasneuberger <23504477+thomasneuberger@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: thomasneuberger <23504477+thomasneuberger@users.noreply.github.com>
1 parent db027ca commit af0afe7

3 files changed

Lines changed: 193 additions & 1 deletion

File tree

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
using Microsoft.Extensions.Logging;
2+
using NSubstitute;
3+
using NUnit.Framework;
4+
using System.Net;
5+
using System.Text;
6+
using TgHomeBot.Notifications.Contract;
7+
using TgHomeBot.Scheduling.Tasks;
8+
9+
namespace TgHomeBot.Scheduling.Tests.Tasks;
10+
11+
[TestFixture]
12+
public class JackpotReportTaskTests
13+
{
14+
private ILogger<JackpotReportTask> _logger = null!;
15+
private INotificationConnector _notificationConnector = null!;
16+
private IHttpClientFactory _httpClientFactory = null!;
17+
18+
[SetUp]
19+
public void SetUp()
20+
{
21+
_logger = Substitute.For<ILogger<JackpotReportTask>>();
22+
_notificationConnector = Substitute.For<INotificationConnector>();
23+
_httpClientFactory = Substitute.For<IHttpClientFactory>();
24+
}
25+
26+
[Test]
27+
public async Task ExecuteAsync_ShouldFormatJackpotCorrectly_WhenApiReturnsMillionEuros()
28+
{
29+
// Arrange
30+
var apiResponse = @"{
31+
""last"": {
32+
""date"": {
33+
""full"": ""2024-01-15""
34+
},
35+
""numbers"": [5, 12, 23, 34, 45],
36+
""euroNumbers"": [3, 7],
37+
""jackpot"": 10
38+
},
39+
""next"": {
40+
""date"": {
41+
""full"": ""2024-01-19""
42+
},
43+
""numbers"": [],
44+
""euroNumbers"": [],
45+
""jackpot"": 15
46+
}
47+
}";
48+
49+
var httpMessageHandler = new MockHttpMessageHandler(apiResponse, HttpStatusCode.OK);
50+
var httpClient = new HttpClient(httpMessageHandler);
51+
_httpClientFactory.CreateClient().Returns(httpClient);
52+
53+
var task = new JackpotReportTask(_logger, _notificationConnector, _httpClientFactory);
54+
55+
// Act
56+
await task.ExecuteAsync(CancellationToken.None);
57+
58+
// Assert
59+
await _notificationConnector.Received(1).SendAsync(
60+
Arg.Is<string>(msg =>
61+
msg.Contains("10.000.000 €") && // 10 million euros
62+
msg.Contains("15.000.000 €")), // 15 million euros
63+
NotificationType.Eurojackpot);
64+
}
65+
66+
[Test]
67+
public async Task ExecuteAsync_ShouldHandleSmallJackpots()
68+
{
69+
// Arrange - jackpot of 1 million euros (1 in the API)
70+
var apiResponse = @"{
71+
""last"": {
72+
""date"": {
73+
""full"": ""2024-01-15""
74+
},
75+
""numbers"": [5, 12, 23, 34, 45],
76+
""euroNumbers"": [3, 7],
77+
""jackpot"": 1
78+
}
79+
}";
80+
81+
var httpMessageHandler = new MockHttpMessageHandler(apiResponse, HttpStatusCode.OK);
82+
var httpClient = new HttpClient(httpMessageHandler);
83+
_httpClientFactory.CreateClient().Returns(httpClient);
84+
85+
var task = new JackpotReportTask(_logger, _notificationConnector, _httpClientFactory);
86+
87+
// Act
88+
await task.ExecuteAsync(CancellationToken.None);
89+
90+
// Assert
91+
await _notificationConnector.Received(1).SendAsync(
92+
Arg.Is<string>(msg => msg.Contains("1.000.000 €")),
93+
NotificationType.Eurojackpot);
94+
}
95+
96+
[Test]
97+
public async Task ExecuteAsync_ShouldHandleLargeJackpots()
98+
{
99+
// Arrange - jackpot of 120 million euros (120 in the API)
100+
var apiResponse = @"{
101+
""last"": {
102+
""date"": {
103+
""full"": ""2024-01-15""
104+
},
105+
""numbers"": [5, 12, 23, 34, 45],
106+
""euroNumbers"": [3, 7],
107+
""jackpot"": 120
108+
}
109+
}";
110+
111+
var httpMessageHandler = new MockHttpMessageHandler(apiResponse, HttpStatusCode.OK);
112+
var httpClient = new HttpClient(httpMessageHandler);
113+
_httpClientFactory.CreateClient().Returns(httpClient);
114+
115+
var task = new JackpotReportTask(_logger, _notificationConnector, _httpClientFactory);
116+
117+
// Act
118+
await task.ExecuteAsync(CancellationToken.None);
119+
120+
// Assert
121+
await _notificationConnector.Received(1).SendAsync(
122+
Arg.Is<string>(msg => msg.Contains("120.000.000 €")),
123+
NotificationType.Eurojackpot);
124+
}
125+
126+
[Test]
127+
public void Constructor_ShouldThrowArgumentNullException_WhenLoggerIsNull()
128+
{
129+
// Act & Assert
130+
Assert.Throws<ArgumentNullException>(() =>
131+
new JackpotReportTask(null!, _notificationConnector, _httpClientFactory));
132+
}
133+
134+
[Test]
135+
public void Constructor_ShouldThrowArgumentNullException_WhenNotificationConnectorIsNull()
136+
{
137+
// Act & Assert
138+
Assert.Throws<ArgumentNullException>(() =>
139+
new JackpotReportTask(_logger, null!, _httpClientFactory));
140+
}
141+
142+
[Test]
143+
public void Constructor_ShouldThrowArgumentNullException_WhenHttpClientFactoryIsNull()
144+
{
145+
// Act & Assert
146+
Assert.Throws<ArgumentNullException>(() =>
147+
new JackpotReportTask(_logger, _notificationConnector, null!));
148+
}
149+
150+
[Test]
151+
public async Task ExecuteAsync_ShouldNotSendNotification_WhenApiReturnsError()
152+
{
153+
// Arrange
154+
var httpMessageHandler = new MockHttpMessageHandler("", HttpStatusCode.InternalServerError);
155+
var httpClient = new HttpClient(httpMessageHandler);
156+
_httpClientFactory.CreateClient().Returns(httpClient);
157+
158+
var task = new JackpotReportTask(_logger, _notificationConnector, _httpClientFactory);
159+
160+
// Act
161+
await task.ExecuteAsync(CancellationToken.None);
162+
163+
// Assert
164+
await _notificationConnector.DidNotReceive().SendAsync(Arg.Any<string>(), Arg.Any<NotificationType>());
165+
}
166+
167+
private class MockHttpMessageHandler : HttpMessageHandler
168+
{
169+
private readonly string _response;
170+
private readonly HttpStatusCode _statusCode;
171+
172+
public MockHttpMessageHandler(string response, HttpStatusCode statusCode)
173+
{
174+
_response = response;
175+
_statusCode = statusCode;
176+
}
177+
178+
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
179+
{
180+
return Task.FromResult(new HttpResponseMessage
181+
{
182+
StatusCode = _statusCode,
183+
Content = new StringContent(_response, Encoding.UTF8, "application/json")
184+
});
185+
}
186+
}
187+
}

TgHomeBot.Scheduling.Tests/TgHomeBot.Scheduling.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<ProjectReference Include="..\TgHomeBot.Scheduling\TgHomeBot.Scheduling.csproj" />
2020
<ProjectReference Include="..\TgHomeBot.Scheduling.Contract\TgHomeBot.Scheduling.Contract.csproj" />
2121
<ProjectReference Include="..\TgHomeBot.Common.Contract\TgHomeBot.Common.Contract.csproj" />
22+
<ProjectReference Include="..\TgHomeBot.Notifications.Contract\TgHomeBot.Notifications.Contract.csproj" />
2223
</ItemGroup>
2324

2425
</Project>

TgHomeBot.Scheduling/Tasks/JackpotReportTask.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,11 @@ private static string FormatDate(DrawDate? date)
121121

122122
private static string FormatJackpotAmount(long jackpot)
123123
{
124-
return jackpot.ToString("N0", CultureInfo.GetCultureInfo("de-DE")) + " €";
124+
// API returns jackpot in million euros, convert to euros
125+
// Note: jackpot values are typically 1-500 (million euros)
126+
// Max safe value before overflow: ~9.2 billion millions (9.2e15 euros)
127+
var jackpotInEuros = jackpot * 1_000_000;
128+
return jackpotInEuros.ToString("N0", CultureInfo.GetCultureInfo("de-DE")) + " €";
125129
}
126130

127131
private class EurojackpotApiResponse

0 commit comments

Comments
 (0)