Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 204 additions & 6 deletions RateLimiter.Tests/RateLimiterTest.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,211 @@
using NUnit.Framework;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using NUnit.Framework;
using RateLimiter.Domain.Models;
using System;
using System.Threading.Tasks;

namespace RateLimiter.Tests;

[TestFixture]
public class RateLimiterTest
{
[Test]
public void Example()
{
Assert.That(true, Is.True);
}
[Test]
public async Task RequestsTimespanPerLastRequestRuleFailTest()
{
var configurations = new Domain.Models.Configurations
{
RequestsPerTimespan = 5,
RequestTimespan = 5,
TimespanSinceLastCall = 5
};

var id = Guid.NewGuid();
var request = new Domain.Models.RateLimiterRequest
{
Country = Domain.Enumerations.Contries.US,
Id = id,
RequestDate = DateTime.UtcNow
};

var updateDate = DateTime.UtcNow.AddSeconds(-3);
var stats = new Domain.Models.RateLimiterStats
{
Id = id,
LastRequestDateTime = updateDate,
NumberOfRequestsInTimespan = 0
};

var result = new Domain.Models.RulesResult
{
updatedRateLimiterStats = stats
};

var rule = new Domain.Rules.RequestsTimespanPerLastRequestRule();
var results = await rule.ExecuteRule(request, configurations, stats);

Assert.That(results.Message == "Timespan has not expired.");
Assert.That(results.Status == false);
Assert.That(results.updatedRateLimiterStats.LastRequestDateTime == updateDate);
Assert.That(results.updatedRateLimiterStats.NumberOfRequestsInTimespan == 0);
}

[Test]
public async Task RequestsTimespanPerLastRequestRuleOverTimespanPassTest()
{
var configurations = new Domain.Models.Configurations
{
RequestsPerTimespan = 5,
RequestTimespan = 5,
TimespanSinceLastCall = 5
};

var id = Guid.NewGuid();
var request = new Domain.Models.RateLimiterRequest
{
Country = Domain.Enumerations.Contries.US,
Id = id,
RequestDate = DateTime.UtcNow
};

var updateDate = DateTime.UtcNow.AddSeconds(-6);
var stats = new Domain.Models.RateLimiterStats
{
Id = id,
LastRequestDateTime = updateDate,
NumberOfRequestsInTimespan = 0
};

var result = new Domain.Models.RulesResult
{
updatedRateLimiterStats = stats
};

var rule = new Domain.Rules.RequestsTimespanPerLastRequestRule();
var results = await rule.ExecuteRule(request, configurations, stats);

Assert.That(results.Message == "");
Assert.That(results.Status == true);
Assert.That(results.updatedRateLimiterStats.LastRequestDateTime == request.RequestDate);
Assert.That(results.updatedRateLimiterStats.NumberOfRequestsInTimespan == 0);
}

[Test]
public async Task RequestsPerTimespanRulePassTest()
{
var configurations = new Domain.Models.Configurations
{
RequestsPerTimespan = 5,
RequestTimespan = 5,
TimespanSinceLastCall = 5
};

var id = Guid.NewGuid();
var request = new Domain.Models.RateLimiterRequest
{
Country = Domain.Enumerations.Contries.US,
Id = id,
RequestDate = DateTime.UtcNow
};

var updateDate = DateTime.UtcNow.AddSeconds(-3);
var stats = new Domain.Models.RateLimiterStats
{
Id = id,
LastRequestDateTime = updateDate,
NumberOfRequestsInTimespan = 3
};

var result = new Domain.Models.RulesResult
{
updatedRateLimiterStats = stats
};

var rule = new Domain.Rules.RequestsPerTimespanRule();
var results = await rule.ExecuteRule(request, configurations, stats);

Assert.That(results.Message == "");
Assert.That(results.Status == true);
Assert.That(results.updatedRateLimiterStats.LastRequestDateTime == updateDate);
Assert.That(results.updatedRateLimiterStats.NumberOfRequestsInTimespan == 4);
}

[Test]
public async Task RequestsPerTimespanRuleResetTest()
{
var configurations = new Domain.Models.Configurations
{
RequestsPerTimespan = 5,
RequestTimespan = 5,
TimespanSinceLastCall = 5
};

var id = Guid.NewGuid();
var request = new Domain.Models.RateLimiterRequest
{
Country = Domain.Enumerations.Contries.US,
Id = id,
RequestDate = DateTime.UtcNow
};

var updateDate = DateTime.UtcNow.AddSeconds(-6);
var stats = new Domain.Models.RateLimiterStats
{
Id = id,
LastRequestDateTime = updateDate,
NumberOfRequestsInTimespan = 3
};

var result = new Domain.Models.RulesResult
{
updatedRateLimiterStats = stats
};

var rule = new Domain.Rules.RequestsPerTimespanRule();
var results = await rule.ExecuteRule(request, configurations, stats);

Assert.That(results.Message == "");
Assert.That(results.Status == true);
Assert.That(results.updatedRateLimiterStats.LastRequestDateTime == request.RequestDate);
Assert.That(results.updatedRateLimiterStats.NumberOfRequestsInTimespan == 1);
}

[Test]
public async Task RequestsPerTimespanRuleFailTest()
{
var configurations = new Domain.Models.Configurations
{
RequestsPerTimespan = 5,
RequestTimespan = 5,
TimespanSinceLastCall = 5
};

var id = Guid.NewGuid();
var request = new Domain.Models.RateLimiterRequest
{
Country = Domain.Enumerations.Contries.US,
Id = id,
RequestDate = DateTime.UtcNow
};

var updateDate = DateTime.UtcNow.AddSeconds(-3);
var stats = new Domain.Models.RateLimiterStats
{
Id = id,
LastRequestDateTime = updateDate,
NumberOfRequestsInTimespan = 6
};

var result = new Domain.Models.RulesResult
{
updatedRateLimiterStats = stats
};

var rule = new Domain.Rules.RequestsPerTimespanRule();
var results = await rule.ExecuteRule(request, configurations, stats);

Assert.That(results.Message == "To many requests for timespan.");
Assert.That(results.Status == false);
Assert.That(results.updatedRateLimiterStats.LastRequestDateTime == updateDate);
Assert.That(results.updatedRateLimiterStats.NumberOfRequestsInTimespan == 6);
}
}
14 changes: 10 additions & 4 deletions RateLimiter.sln
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.15
# Visual Studio Version 17
VisualStudioVersion = 17.10.35122.118
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RateLimiter", "RateLimiter\RateLimiter.csproj", "{36F4BDC6-D3DA-403A-8DB7-0C79F94B938F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RateLimiter", "RateLimiter\RateLimiter.csproj", "{36F4BDC6-D3DA-403A-8DB7-0C79F94B938F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RateLimiter.Tests", "RateLimiter.Tests\RateLimiter.Tests.csproj", "{C4F9249B-010E-46BE-94B8-DD20D82F1E60}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RateLimiter.Tests", "RateLimiter.Tests\RateLimiter.Tests.csproj", "{C4F9249B-010E-46BE-94B8-DD20D82F1E60}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9B206889-9841-4B5E-B79B-D5B2610CCCFF}"
ProjectSection(SolutionItems) = preProject
README.md = README.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RateLimiterAPI", "RateLimiterAPI\RateLimiterAPI.csproj", "{89B67487-006D-4805-B26A-52ABD0C081A0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -26,6 +28,10 @@ Global
{C4F9249B-010E-46BE-94B8-DD20D82F1E60}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C4F9249B-010E-46BE-94B8-DD20D82F1E60}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C4F9249B-010E-46BE-94B8-DD20D82F1E60}.Release|Any CPU.Build.0 = Release|Any CPU
{89B67487-006D-4805-B26A-52ABD0C081A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{89B67487-006D-4805-B26A-52ABD0C081A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{89B67487-006D-4805-B26A-52ABD0C081A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{89B67487-006D-4805-B26A-52ABD0C081A0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
8 changes: 8 additions & 0 deletions RateLimiter/Domain/Enumerations/Contries.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace RateLimiter.Domain.Enumerations
{
public enum Contries
{
US,
EU
}
}
11 changes: 11 additions & 0 deletions RateLimiter/Domain/Interfaces/IRequestReqository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using RateLimiter.Domain.Models;
using System;

namespace RateLimiter.Domain.Interfaces
{
public interface IRequestReqository
{
RateLimiterStats GetRateLimiter(Guid id);
void SaveRateLimiter(RateLimiterStats rateLimiterStats);
}
}
10 changes: 10 additions & 0 deletions RateLimiter/Domain/Interfaces/IRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using RateLimiter.Domain.Models;
using System.Threading.Tasks;

namespace RateLimiter.Domain.Interfaces
{
public interface IRule
{
Task<Models.RulesResult> ExecuteRule(RateLimiterRequest request, Configurations configurations, RateLimiterStats rateLimiterStats);
}
}
9 changes: 9 additions & 0 deletions RateLimiter/Domain/Interfaces/IRuleRunner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Threading.Tasks;

namespace RateLimiter.Domain.Interfaces
{
public interface IRuleRunner
{
Task<Models.RulesResult> RunRules(Models.RateLimiterRequest request, Models.Configurations configurations);
}
}
9 changes: 9 additions & 0 deletions RateLimiter/Domain/Models/Configurations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace RateLimiter.Domain.Models
{
public class Configurations
{
public int RequestsPerTimespan { get; set; }
public int RequestTimespan { get; set; }
public int TimespanSinceLastCall { get; set; }
}
}
11 changes: 11 additions & 0 deletions RateLimiter/Domain/Models/RateLimiterRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace RateLimiter.Domain.Models
{
public class RateLimiterRequest
{
public Guid Id { get; set; }
public Enumerations.Contries Country {get; set;}
public DateTime RequestDate { get; set;}
}
}
12 changes: 12 additions & 0 deletions RateLimiter/Domain/Models/RateLimiterStats.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

using System;

namespace RateLimiter.Domain.Models
{
public class RateLimiterStats
{
public Guid Id { get; set; }
public int NumberOfRequestsInTimespan { get; set; }
public DateTime LastRequestDateTime { get; set; }
}
}
10 changes: 10 additions & 0 deletions RateLimiter/Domain/Models/RulesResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace RateLimiter.Domain.Models
{
public class RulesResult
{
public string Message { get; set; }
public bool Status { get; set; }
public int RetryAfter { get; set; }
public RateLimiterStats updatedRateLimiterStats { get; set; }
}
}
51 changes: 51 additions & 0 deletions RateLimiter/Domain/RuleRunner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using RateLimiter.Domain.Models;
using RateLimiter.Domain.Rules;
using System.Threading.Tasks;

namespace RateLimiter.Domain
{
public class RuleRunner : Interfaces.IRuleRunner
{
private readonly Interfaces.IRequestReqository _requestRepository;
public RuleRunner(Interfaces.IRequestReqository requestReqository)
{
_requestRepository = requestReqository;
}
public async Task<Models.RulesResult> RunRules(Models.RateLimiterRequest request, Models.Configurations configurations)
{
RequestsPerTimespanRule requestsPerTimespanRule = new RequestsPerTimespanRule();
RequestsTimespanPerLastRequestRule requestsTimespanPerLastRequestRule = new RequestsTimespanPerLastRequestRule();

RateLimiterStats rateLimiterStats = _requestRepository.GetRateLimiter(request.Id);
if (rateLimiterStats == null) {
rateLimiterStats = new RateLimiterStats
{
LastRequestDateTime = new System.DateTime()
};
}

if (request.Country == Enumerations.Contries.US)
{
RulesResult requestsPerTimespanRuleResults = await requestsPerTimespanRule.ExecuteRule(request, configurations, rateLimiterStats);
_requestRepository.SaveRateLimiter(requestsPerTimespanRuleResults.updatedRateLimiterStats);
return requestsPerTimespanRuleResults;
}
else if(request.Country == Enumerations.Contries.EU)
{
RulesResult requestsTimespanPerLastRequestRuleResult = await requestsTimespanPerLastRequestRule.ExecuteRule(request, configurations, rateLimiterStats);
_requestRepository.SaveRateLimiter(requestsTimespanPerLastRequestRuleResult.updatedRateLimiterStats);
return requestsTimespanPerLastRequestRuleResult;
}
else
{
Models.RulesResult result1 = await requestsPerTimespanRule.ExecuteRule(request, configurations, rateLimiterStats);
Models.RulesResult result2 = await requestsTimespanPerLastRequestRule.ExecuteRule(request, configurations, result1.updatedRateLimiterStats);
_requestRepository.SaveRateLimiter(result2.updatedRateLimiterStats);

return new Models.RulesResult { Message = $"{result1.Message}, {result2.Message}", Status = (result1.Status && result2.Status ) };
}


}
}
}
Loading