Skip to content

Commit

Permalink
Decompose AdjustedConsensusPredictionStrategy (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
romankr authored Nov 24, 2024
1 parent b5cf911 commit 10c4d41
Show file tree
Hide file tree
Showing 35 changed files with 553 additions and 338 deletions.

This file was deleted.

14 changes: 6 additions & 8 deletions OddsCollector.Functions.Tests/Infrastructure/Data/SampleEvent.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using OddsCollector.Functions.Models;
using OddsCollector.Functions.OddsApi.WebApi;
using OddsCollector.Functions.Strategies;

namespace OddsCollector.Functions.Tests.Infrastructure.Data;

Expand All @@ -9,7 +8,6 @@ internal static class SampleEvent
public const string Id = "4acd8f2675ca847ba33eea3664f6c0bb";
public const string AwayTeam = "Liverpool";
public const string HomeTeam = "Manchester City";
public const string Strategy = nameof(AdjustedConsensusStrategy);
public const string Winner = HomeTeam;

public const double AwayOdd1 = 4.08;
Expand All @@ -36,18 +34,18 @@ internal static class SampleEvent
public static readonly Guid TraceId = new("447b57dd-84bc-4e79-95d0-695f7493bf41");
public static readonly DateTime Timestamp = new(2023, 11, 25, 15, 30, 0);

public static readonly IEnumerable<Odd> Odds = new List<Odd>
{
public static readonly IEnumerable<Odd> Odds =
[
new OddBuilder().SetSampleData1().Instance,
new OddBuilder().SetSampleData2().Instance,
new OddBuilder().SetSampleData3().Instance
};
];

public static readonly Outcome AwayOutcome1 = new() { Name = AwayTeam, Price = AwayOdd1 };

public static readonly Outcome HomeOutcome1 = new() { Name = HomeTeam, Price = HomeOdd1 };

public static readonly Outcome DrawOutcome1 = new() { Name = Constants.Draw, Price = DrawOdd1 };
public static readonly Outcome DrawOutcome1 = new() { Name = OutcomeTypes.Draw, Price = DrawOdd1 };

public static readonly ICollection<Outcome> Outcomes1 =
[
Expand All @@ -60,14 +58,14 @@ internal static class SampleEvent
[
new Outcome { Name = AwayTeam, Price = AwayOdd2 },
new Outcome { Name = HomeTeam, Price = HomeOdd2 },
new Outcome { Name = Constants.Draw, Price = DrawOdd2 }
new Outcome { Name = OutcomeTypes.Draw, Price = DrawOdd2 }
];

private static readonly ICollection<Outcome> Outcomes3 =
[
new Outcome { Name = AwayTeam, Price = AwayOdd3 },
new Outcome { Name = HomeTeam, Price = HomeOdd3 },
new Outcome { Name = Constants.Draw, Price = DrawOdd3 }
new Outcome { Name = OutcomeTypes.Draw, Price = DrawOdd3 }
];

public static readonly ICollection<Bookmakers> Bookmakers =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using OddsCollector.Functions.Models;
using OddsCollector.Functions.Processors;
using OddsCollector.Functions.Tests.Infrastructure.CancellationToken;
using OddsCollector.Functions.Tests.Infrastructure.Data;
using OddsCollector.Functions.Tests.Infrastructure.ServiceBus;

namespace OddsCollector.Functions.Tests.Tests.Functions;
Expand All @@ -20,7 +19,7 @@ public async Task Run_WithServiceBusMessage_ReturnsEventPredictionAndLogsCount()
// Arrange
var loggerMock = new FakeLogger<OddsCollector.Functions.Functions.PredictionFunction>();

var expectedPrediction = new EventPredictionBuilder().SetSampleData().Instance;
var expectedPrediction = new EventPrediction();

var processorStub = Substitute.For<IPredictionProcessor>();
processorStub.DeserializeAndCompleteMessageAsync(Arg.Any<ServiceBusReceivedMessage>(),
Expand Down Expand Up @@ -53,7 +52,7 @@ public async Task Run_WithRequestedCancellation_ReturnsEmptyPredictionListAndLog
var processorStub = Substitute.For<IPredictionProcessor>();
processorStub.DeserializeAndCompleteMessageAsync(Arg.Any<ServiceBusReceivedMessage>(),
Arg.Any<ServiceBusMessageActions>(), Arg.Any<CancellationToken>())
.Returns(new EventPredictionBuilder().SetSampleData().Instance);
.Returns(new EventPrediction());

var function = new OddsCollector.Functions.Functions.PredictionFunction(loggerMock, processorStub);

Expand Down Expand Up @@ -85,7 +84,7 @@ public async Task Run_WithException_ReturnsEmptyPredictionListAndLogsException()
const string expectedMessageId = "123";

var message = ServiceBusReceivedMessageFactory.CreateFromObject(
new UpcomingEventBuilder().SetSampleData().Instance, expectedMessageId);
new UpcomingEvent(), expectedMessageId);

var processorStub = Substitute.For<IPredictionProcessor>();
processorStub.DeserializeAndCompleteMessageAsync(Arg.Any<ServiceBusReceivedMessage>(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
namespace OddsCollector.Functions.Tests.Tests.Models;

internal class EventPredictionBuilder
{
[TestCase("", TestName = "SetId_WithEmptyString_ThrowsException")]
[TestCase(null, TestName = "SetId_WithNullString_ThrowsException")]
public void SetId_WithNullOrEmptyString_ThrowsException(string? id)
{
var builder = new OddsCollector.Functions.Models.EventPredictionBuilder();

var action = () => builder.SetId(id);

action.Should().Throw<ArgumentException>().WithParameterName(nameof(id));
}

[TestCase("", TestName = "SetAwayTeam_WithEmptyString_ThrowsException")]
[TestCase(null, TestName = "SetAwayTeam_WithNullString_ThrowsException")]
public void SetAwayTeam_WithNullOrEmptyString_ThrowsException(string? awayTeam)
{
var builder = new OddsCollector.Functions.Models.EventPredictionBuilder();

var action = () => builder.SetAwayTeam(awayTeam);

action.Should().Throw<ArgumentException>().WithParameterName(nameof(awayTeam));
}

[TestCase("", TestName = "SetHomeTeam_WithEmptyString_ThrowsException")]
[TestCase(null, TestName = "SetHomeTeam_WithNullString_ThrowsException")]
public void SetHomeTeam_WithNullOrEmptyString_ThrowsException(string? homeTeam)
{
var builder = new OddsCollector.Functions.Models.EventPredictionBuilder();

var action = () => builder.SetHomeTeam(homeTeam);

action.Should().Throw<ArgumentException>().WithParameterName(nameof(homeTeam));
}

[TestCase("", TestName = "SetWinner_WithEmptyString_ThrowsException")]
[TestCase(null, TestName = "SetWinner_WithNullString_ThrowsException")]
public void SetWinner_WithNullOrEmptyString_ThrowsException(string? winner)
{
var builder = new OddsCollector.Functions.Models.EventPredictionBuilder();

var action = () => builder.SetWinner(winner);

action.Should().Throw<ArgumentException>().WithParameterName(nameof(winner));
}

[Test]
public void SetCommenceTime_WithNullDateTime_ThrowsException()
{
var builder = new OddsCollector.Functions.Models.EventPredictionBuilder();

var action = () => builder.SetCommenceTime(null);

action.Should().Throw<ArgumentException>().WithParameterName("commenceTime");
}

[Test]
public void SetTimestamp_WithNullDateTime_ThrowsException()
{
var builder = new OddsCollector.Functions.Models.EventPredictionBuilder();

var action = () => builder.SetTimestamp(null);

action.Should().Throw<ArgumentException>().WithParameterName("timestamp");
}

[Test]
public void SetTraceId_WithNullGuid_ThrowsException()
{
var builder = new OddsCollector.Functions.Models.EventPredictionBuilder();

var action = () => builder.SetTraceId(null);

action.Should().Throw<ArgumentException>().WithParameterName("traceId");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ [new Anonymous2Builder().SetSampleData().SetHomeTeam(null).Instance],
[
SampleEvent.AwayOutcome1,
SampleEvent.HomeOutcome1,
new Outcome { Name = Constants.Draw, Price = null }
new Outcome { Name = OutcomeTypes.Draw, Price = null }
]
}
]
Expand Down Expand Up @@ -458,7 +458,7 @@ public void ToEventResults_WithCompletedEvents_ReturnsConvertedEvents()
.BeEquivalentTo(new EventResultBuilder().SetSampleData().SetWinner(SampleEvent.AwayTeam).Instance);

eventResults.ElementAt(2).Should().NotBeNull().And
.BeEquivalentTo(new EventResultBuilder().SetSampleData().SetWinner(Constants.Draw).Instance);
.BeEquivalentTo(new EventResultBuilder().SetSampleData().SetWinner(OutcomeTypes.Draw).Instance);
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Microsoft.Extensions.DependencyInjection;
using OddsCollector.Functions.Predictions;
using OddsCollector.Functions.Predictions.Configuration;

namespace OddsCollector.Functions.Tests.Tests.Predictions.Configuration;

internal class ServiceCollectionExtensions
{
[Test]
public void AddPredictionStrategy_AddsPredictionStrategy()
{
var services = new ServiceCollection();

services.AddPredictionStrategy();

var descriptor =
services.FirstOrDefault(
x => x.ServiceType == typeof(IPredictionStrategy)
&& x.ImplementationType == typeof(OddsCollector.Functions.Predictions.PredictionStrategy)
&& x.Lifetime == ServiceLifetime.Singleton);

descriptor.Should().NotBeNull();
}

[Test]
public void AddPredictionStrategy_AddsWinnerFinder()
{
var services = new ServiceCollection();

services.AddPredictionStrategy();

var strategyDescriptor =
services.FirstOrDefault(
x => x.ServiceType == typeof(IWinnerFinder)
&& x.ImplementationType == typeof(OddsCollector.Functions.Predictions.WinnerFinder)
&& x.Lifetime == ServiceLifetime.Singleton);

strategyDescriptor.Should().NotBeNull();
}

[Test]
public void AddPredictionStrategy_AddsScoreCalculator()
{
var services = new ServiceCollection();

services.AddPredictionStrategy();

var strategyDescriptor =
services.FirstOrDefault(
x => x.ServiceType == typeof(IScoreCalculator)
&& x.ImplementationType == typeof(OddsCollector.Functions.Predictions.ScoreCalculator)
&& x.Lifetime == ServiceLifetime.Singleton);

strategyDescriptor.Should().NotBeNull();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace OddsCollector.Functions.Tests.Tests.Predictions;

internal class OutcomeScoreBuilder
{
[TestCase("", TestName = "SetScore_WithEmptyString_ThrowsException")]
[TestCase(null, TestName = "SetScore_WithNullString_ThrowsException")]
public void SetScore_WithNullOrEmptyString_ThrowsException(string? outcome)
{
var builder = new OddsCollector.Functions.Predictions.OutcomeScoreBuilder();

var action = () => builder.SetOutcome(outcome);

action.Should().Throw<ArgumentException>().WithParameterName(nameof(outcome));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using FluentAssertions.Execution;
using OddsCollector.Functions.Models;
using OddsCollector.Functions.Predictions;

namespace OddsCollector.Functions.Tests.Tests.Predictions;

internal class PredictionStrategy
{
[Test]
public void GetPrediction_WithUpcomingEvent_ReturnsPrediction()
{
// Arrange
var expectedDateTime = DateTime.UtcNow;
var expectedAwayTeam = "Liverpool";
var expectedHomeTeam = "Manchester City";
var expectedCommenceTime = new DateTime(2023, 11, 25, 12, 30, 0);
var expectedId = Guid.NewGuid().ToString();
var expectedTraceId = Guid.NewGuid();

var upcomingEvent = new UpcomingEvent()
{
AwayTeam = expectedAwayTeam,
CommenceTime = expectedCommenceTime,
HomeTeam = expectedHomeTeam,
Id = expectedId,
TraceId = expectedTraceId
};

var finderStub = Substitute.For<IWinnerFinder>();
finderStub.GetWinner(Arg.Any<ICollection<Odd>>()).Returns(expectedHomeTeam);

var strategy = new OddsCollector.Functions.Predictions.PredictionStrategy(finderStub);

// Act
var prediction = strategy.GetPrediction(upcomingEvent, expectedDateTime);

// Assert
prediction.Should().NotBeNull();

using (var scope = new AssertionScope())
{
prediction.AwayTeam.Should().NotBeNullOrEmpty().And.Be(expectedAwayTeam);
prediction.HomeTeam.Should().NotBeNullOrEmpty().And.Be(expectedHomeTeam);
prediction.CommenceTime.Should().Be(expectedCommenceTime);
prediction.Id.Should().NotBeNullOrEmpty().And.Be(expectedId);
prediction.TraceId.Should().Be(expectedTraceId);
prediction.Winner.Should().Be(expectedHomeTeam);
prediction.Timestamp.Should().BeCloseTo(DateTime.UtcNow, new TimeSpan(0, 0, 5));
}
}

[Test]
public void GetPrediction_WithNullUpcomingEvent_ThrowsException()
{
var finderStub = Substitute.For<IWinnerFinder>();

var strategy = new OddsCollector.Functions.Predictions.PredictionStrategy(finderStub);

var action = () => strategy.GetPrediction(null, DateTime.Now);

action.Should().ThrowExactly<ArgumentNullException>().WithParameterName("upcomingEvent");
}

[Test]
public void GetPrediction_WithNullTimestamp_ThrowsException()
{
var finderStub = Substitute.For<IWinnerFinder>();

var strategy = new OddsCollector.Functions.Predictions.PredictionStrategy(finderStub);

var action = () => strategy.GetPrediction(new UpcomingEvent(), null);

action.Should().ThrowExactly<ArgumentNullException>().WithParameterName("timestamp");
}
}
Loading

0 comments on commit 10c4d41

Please sign in to comment.