Skip to content

Commit 26056fc

Browse files
committed
next batch of unit tests
1 parent 53cded9 commit 26056fc

File tree

13 files changed

+384
-24
lines changed

13 files changed

+384
-24
lines changed

OddsCollector.Common/Models/UpcomingEventBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public UpcomingEventBuilder SetTraceId(Guid? traceId)
7676
return this;
7777
}
7878

79-
public UpcomingEventBuilder SetOdds(IEnumerable<Odd?>? odds)
79+
public UpcomingEventBuilder SetOdds(IEnumerable<Odd>? odds)
8080
{
8181
if (odds is null)
8282
{
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
using FluentAssertions;
2+
using NUnit.Framework;
3+
using OddsCollector.Functions.Notification.CommunicationServices.Configuration;
4+
5+
namespace OddsCollector.Functions.Notification.Tests.CommunicationServices;
6+
7+
internal class EmailSenderOptionsTests
8+
{
9+
[Test]
10+
public void SetRecipientAddress_WithValidAddress_ReturnsValidInstance()
11+
{
12+
const string address = "test@example.com";
13+
14+
var options = new EmailSenderOptions();
15+
16+
options.SetRecipientAddress(address);
17+
18+
options.RecipientAddress.Should().Be(address);
19+
}
20+
21+
[TestCase("")]
22+
[TestCase(null)]
23+
public void SetRecipientAddress_WithNullOrEmptyRecipientAddress_ThrowsException(string? recipientAddress)
24+
{
25+
const string address = nameof(address);
26+
27+
var options = new EmailSenderOptions();
28+
29+
var action = () =>
30+
{
31+
options.SetRecipientAddress(recipientAddress);
32+
};
33+
34+
action.Should().Throw<ArgumentException>().WithParameterName("recipientAddress");
35+
}
36+
37+
[Test]
38+
public void SetSenderAddress_WithValidSenderAddress_ReturnsValidInstance()
39+
{
40+
const string address = "test@example.com";
41+
42+
var options = new EmailSenderOptions();
43+
44+
options.SetSenderAddress(address);
45+
46+
options.SenderAddress.Should().Be(address);
47+
}
48+
49+
[TestCase("")]
50+
[TestCase(null)]
51+
public void SetSenderAddress_WithNullOrEmptySenderAddress_ThrowsException(string? senderAddress)
52+
{
53+
const string address = nameof(address);
54+
55+
var options = new EmailSenderOptions();
56+
57+
var action = () =>
58+
{
59+
options.SetSenderAddress(senderAddress);
60+
61+
};
62+
63+
action.Should().Throw<ArgumentException>().WithParameterName("senderAddress");
64+
}
65+
66+
[Test]
67+
public void SetSubject_WithValidSubject_ReturnsValidInstance()
68+
{
69+
const string subject = nameof(subject);
70+
71+
var options = new EmailSenderOptions();
72+
73+
options.SetSubject(subject);
74+
75+
options.Subject.Should().Be(subject);
76+
}
77+
78+
[TestCase("")]
79+
[TestCase(null)]
80+
public void SetSubject_WithNullOrEmptySubject_ThrowsException(string? subject)
81+
{
82+
const string address = nameof(address);
83+
84+
var options = new EmailSenderOptions();
85+
86+
var action = () =>
87+
{
88+
options.SetSubject(subject);
89+
90+
};
91+
92+
action.Should().Throw<ArgumentException>().WithParameterName("subject");
93+
}
94+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
using System.Text.Json;
2+
using Azure;
3+
using Azure.Communication.Email;
4+
using FluentAssertions;
5+
using Google.Protobuf.WellKnownTypes;
6+
using Microsoft.Extensions.Options;
7+
using NSubstitute;
8+
using NUnit.Framework;
9+
using OddsCollector.Common.Models;
10+
using OddsCollector.Functions.Notification.CommunicationServices;
11+
using OddsCollector.Functions.Notification.CommunicationServices.Configuration;
12+
13+
namespace OddsCollector.Functions.Notification.Tests.CommunicationServices;
14+
15+
internal class EmailSenderTests
16+
{
17+
[Test]
18+
public void Constructor_WithValidDependencies_ReturnsNewInstance()
19+
{
20+
var optionsStub = Substitute.For<IOptions<EmailSenderOptions>>();
21+
optionsStub.Value.Returns(new EmailSenderOptions());
22+
var clientStub = Substitute.For<EmailClient>();
23+
24+
var sender = new EmailSender(optionsStub, clientStub);
25+
26+
sender.Should().NotBeNull();
27+
}
28+
29+
[Test]
30+
public void Constructor_WithNullOptions_ThrowsException()
31+
{
32+
var clientStub = Substitute.For<EmailClient>();
33+
34+
var action = () =>
35+
{
36+
_ = new EmailSender(null, clientStub);
37+
};
38+
39+
action.Should().Throw<ArgumentNullException>().WithParameterName("options");
40+
}
41+
42+
[Test]
43+
public void Constructor_WithNullEmailSender_ThrowsException()
44+
{
45+
var optionsStub = Substitute.For<IOptions<EmailSenderOptions>>();
46+
optionsStub.Value.Returns(new EmailSenderOptions());
47+
48+
var action = () =>
49+
{
50+
_ = new EmailSender(optionsStub, null);
51+
};
52+
53+
action.Should().Throw<ArgumentNullException>().WithParameterName("client");
54+
}
55+
56+
[Test]
57+
public async Task SendEmailAsync_WithValidOptions_SendsEmails()
58+
{
59+
var predictions = new List<EventPrediction>()
60+
{
61+
new()
62+
{
63+
AwayTeam = "away",
64+
Bookmaker = "bookmaker",
65+
CommenceTime = DateTime.UtcNow,
66+
HomeTeam = "Home",
67+
Id = "id",
68+
Strategy = "strategy",
69+
Timestamp = DateTime.UtcNow,
70+
TraceId = Guid.NewGuid(),
71+
Winner = "winner"
72+
}
73+
};
74+
75+
const string recipientAddress = "test@example.com";
76+
const string senderAddress = "test2@example.com";
77+
const string subject = "test";
78+
79+
var optionsStub = Substitute.For<IOptions<EmailSenderOptions>>();
80+
optionsStub.Value.Returns(new EmailSenderOptions() {
81+
RecipientAddress = recipientAddress,
82+
SenderAddress = senderAddress,
83+
Subject = subject
84+
});
85+
86+
var clientMock = Substitute.For<EmailClient>();
87+
88+
var sender = new EmailSender(optionsStub, clientMock);
89+
90+
await sender.SendEmailAsync(predictions).ConfigureAwait(false);
91+
92+
var received = clientMock.ReceivedCalls();
93+
94+
received.Should().NotBeNull();
95+
received.Should().HaveCount(1);
96+
97+
var firstReceived = received.First();
98+
99+
firstReceived.GetMethodInfo().Name.Should().Be("SendAsync");
100+
101+
var firstReceivedArguments = firstReceived.GetArguments();
102+
103+
firstReceivedArguments.Should().NotBeNull();
104+
firstReceivedArguments[0].Should().Be(WaitUntil.Completed);
105+
firstReceivedArguments[1].Should().Be(senderAddress);
106+
firstReceivedArguments[2].Should().Be(recipientAddress);
107+
firstReceivedArguments[3].Should().Be(subject);
108+
109+
var deserialized = JsonSerializer.Deserialize<List<EventPrediction>>((string)firstReceivedArguments[4]!);
110+
111+
deserialized![0].Id.Should().Be(predictions[0].Id);
112+
}
113+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using FluentAssertions;
2+
using NUnit.Framework;
3+
using OddsCollector.Functions.Notification.CosmosDb;
4+
5+
namespace OddsCollector.Functions.Notification.Tests.CosmosDb;
6+
7+
internal class ContainerFactoryTests
8+
{
9+
[Test]
10+
public void CreateContainer_WithValidParameters_ReturnsNewInstance()
11+
{
12+
string databaseId = nameof(databaseId);
13+
14+
var container = ContainerFactory.CreateContainer("AccountEndpoint=https://test.documents.azure.com:443/;AccountKey=test", databaseId, "containerId");
15+
16+
container.Should().NotBeNull();
17+
container.Database.Should().NotBeNull();
18+
container.Database.Id.Should().NotBeNull().And.Be(databaseId);
19+
}
20+
21+
[TestCase("")]
22+
[TestCase(null)]
23+
public void CreateContainer_WithNullOrEmptyConnectionString_ThrowsException(string? connectionString)
24+
{
25+
var action = () =>
26+
{
27+
_ = ContainerFactory.CreateContainer(connectionString, "databaseId", "containerId");
28+
};
29+
30+
action.Should().Throw<ArgumentException>().WithParameterName(nameof(connectionString));
31+
}
32+
33+
[TestCase("")]
34+
[TestCase(null)]
35+
public void CreateContainer_WithNullOrEmptyDatabaseId_ThrowsException(string? databaseId)
36+
{
37+
var action = () =>
38+
{
39+
_ = ContainerFactory.CreateContainer("AccountEndpoint=https://test.documents.azure.com:443/;AccountKey=test", databaseId, "containerId");
40+
};
41+
42+
action.Should().Throw<ArgumentException>().WithParameterName(nameof(databaseId));
43+
}
44+
45+
[TestCase("")]
46+
[TestCase(null)]
47+
public void CreateContainer_WithNullOrEmptyContainerId_ThrowsException(string? containerId)
48+
{
49+
var action = () =>
50+
{
51+
_ = ContainerFactory.CreateContainer("AccountEndpoint=https://test.documents.azure.com:443/;AccountKey=test", "databaseId", containerId);
52+
};
53+
54+
action.Should().Throw<ArgumentException>().WithParameterName(nameof(containerId));
55+
}
56+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using FluentAssertions;
2+
using Microsoft.Azure.Cosmos;
3+
using NSubstitute;
4+
using NUnit.Framework;
5+
using OddsCollector.Functions.Notification.CosmosDb;
6+
7+
namespace OddsCollector.Functions.Notification.Tests.CosmosDb;
8+
9+
internal class CosmosDbClientTests
10+
{
11+
[Test]
12+
public void Constructor_WithValidDependencies_ReturnsNewInstance()
13+
{
14+
var containerStub = Substitute.For<Container>();
15+
16+
var result = new CosmosDbClient(containerStub);
17+
18+
result.Should().NotBeNull();
19+
}
20+
21+
[Test]
22+
public void Constructor_WithNullContainer_ThrowsException()
23+
{
24+
var action = () =>
25+
{
26+
_ = new CosmosDbClient(null);
27+
};
28+
29+
action.Should().Throw<ArgumentNullException>().WithParameterName("container");
30+
}
31+
32+
// todo: test for GetEventPredictionsAsync() - hard to mock all the dependencies atm
33+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using FluentAssertions;
2+
using Microsoft.Azure.Functions.Worker;
3+
using NSubstitute;
4+
using NUnit.Framework;
5+
using OddsCollector.Common.Models;
6+
using OddsCollector.Functions.Notification.CommunicationServices;
7+
using OddsCollector.Functions.Notification.CosmosDb;
8+
9+
namespace OddsCollector.Functions.Notification.Tests;
10+
11+
internal class NotificationFunctionTests
12+
{
13+
[Test]
14+
public void Constructor_WithValidDependencies_ReturnsNewInstance()
15+
{
16+
var emailSenderStub = Substitute.For<IEmailSender>();
17+
var cosmosDbClientStub = Substitute.For<ICosmosDbClient>();
18+
19+
var function = new NotificationFunction(emailSenderStub, cosmosDbClientStub);
20+
21+
function.Should().NotBeNull();
22+
}
23+
24+
[Test]
25+
public void Constructor_WithNullEmailClient_ThrowsException()
26+
{
27+
var cosmosDbClientStub = Substitute.For<ICosmosDbClient>();
28+
29+
var action = () =>
30+
{
31+
_ = new NotificationFunction(null, cosmosDbClientStub);
32+
};
33+
34+
action.Should().Throw<ArgumentNullException>().WithParameterName("sender");
35+
}
36+
37+
[Test]
38+
public void Constructor_WithNullCosmosDbClient_ThrowsException()
39+
{
40+
var emailSenderStub = Substitute.For<IEmailSender>();
41+
42+
var action = () =>
43+
{
44+
_ = new NotificationFunction(emailSenderStub, null);
45+
};
46+
47+
action.Should().Throw<ArgumentNullException>().WithParameterName("client");
48+
}
49+
50+
[Test]
51+
public async Task Run_WithTimer_SendsEmails()
52+
{
53+
var timer = new TimerInfo();
54+
55+
IEnumerable<EventPrediction> predictons = new List<EventPrediction>();
56+
var cosmosDbClientMock = Substitute.For<ICosmosDbClient>();
57+
cosmosDbClientMock.GetEventPredictionsAsync().Returns(Task.FromResult(predictons));
58+
59+
var emailSenderMock = Substitute.For<IEmailSender>();
60+
61+
var function = new NotificationFunction(emailSenderMock, cosmosDbClientMock);
62+
63+
await function.Run(timer).ConfigureAwait(false);
64+
65+
await emailSenderMock.Received().SendEmailAsync(predictons);
66+
await cosmosDbClientMock.Received().GetEventPredictionsAsync();
67+
}
68+
}

OddsCollector.Functions.Notification.Tests/OddsCollector.Functions.Notification.Tests.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,8 @@
2525
</PackageReference>
2626
</ItemGroup>
2727

28+
<ItemGroup>
29+
<ProjectReference Include="..\OddsCollector.Functions.Notification\OddsCollector.Functions.Notification.csproj" />
30+
</ItemGroup>
31+
2832
</Project>

0 commit comments

Comments
 (0)