Skip to content

Commit

Permalink
Add validation rules (cooldown, max reqs), settings, and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
davidkensell committed Jul 30, 2024
1 parent d0a5741 commit a66d1b8
Show file tree
Hide file tree
Showing 13 changed files with 702 additions and 10 deletions.
118 changes: 118 additions & 0 deletions RateLimiter.Tests/CoolDownPeriodTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using RateLimiter;

namespace RateLimiter.Tests
{
[TestFixture]
public class CooldownPeriodTests
{
private CooldownPeriod _cooldownPeriod;
private UserSettings _userSettings;
private RuleSettings _ruleSettings;
private ValidationRequest _request;

[SetUp]
public void Setup()
{
_cooldownPeriod = new CooldownPeriod();
_userSettings = new UserSettings { Id = "user1" };
_request = new ValidationRequest { RequestTime = DateTime.UtcNow };

_ruleSettings = new RuleSettings
{
Name = "CooldownPeriod",
Enabled = true,
Options = new List<Option>()
{
new Option { Type = "Default", CooldownMsec = 1000 },
new Option { Region = "EU", CooldownMsec = 2000 },
new Option { Region = "US", CooldownMsec = 500 }
}
};
}

[Test]
public void ValidateRequest_RuleNotEnabled_ShouldPassValidation()
{
_ruleSettings.Enabled = false;
var result = _cooldownPeriod.ValidateRequest(_userSettings, _ruleSettings, _request);
Assert.IsTrue(result);
}

[Test]
public void ValidateRequest_NoHistory_ShouldPassValidation()
{
var result = _cooldownPeriod.ValidateRequest(_userSettings, _ruleSettings, _request);
Assert.IsTrue(result);
}

[Test]
public void ValidateRequest_WithinCooldown_ShouldFailValidation()
{
_cooldownPeriod.ValidateRequest(_userSettings, _ruleSettings, _request);
_request.RequestTime = _request.RequestTime.AddMilliseconds(500);
var result = _cooldownPeriod.ValidateRequest(_userSettings, _ruleSettings, _request);
Assert.IsFalse(result);
}

[Test]
public void ValidateRequest_OutsideCooldown_ShouldPassValidation()
{
_cooldownPeriod.ValidateRequest(_userSettings, _ruleSettings, _request);
_request.RequestTime = _request.RequestTime.AddMilliseconds(1500);
var result = _cooldownPeriod.ValidateRequest(_userSettings, _ruleSettings, _request);
Assert.IsTrue(result);
}

[Test]
public void ValidateRequest_RegionOverridesDefault_ShouldUseRegionCooldown()
{
_userSettings.Region = Region.EU;

var result = _cooldownPeriod.ValidateRequest(_userSettings, _ruleSettings, _request);
Assert.IsTrue(result);

_request.RequestTime = _request.RequestTime.AddMilliseconds(1500);
result = _cooldownPeriod.ValidateRequest(_userSettings, _ruleSettings, _request);
Assert.IsFalse(result);

_request.RequestTime = _request.RequestTime.AddMilliseconds(2500);
result = _cooldownPeriod.ValidateRequest(_userSettings, _ruleSettings, _request);
Assert.IsTrue(result);
}

[Test]
public void ValidateRequest_TierOverridesRegion_ShouldUseTierCooldown()
{
_userSettings.ServiceTier = ServiceTier.Pro;

_ruleSettings = new RuleSettings
{
Name = "CooldownPeriod",
Enabled = true,
Options = new List<Option>()
{
new Option { Type = "Default", CooldownMsec = 1000 },
new Option { Region = "EU", CooldownMsec = 2000 },
new Option { Tier = "Pro", CooldownMsec = 500 }
}
};

var result = _cooldownPeriod.ValidateRequest(_userSettings, _ruleSettings, _request);
Assert.IsTrue(result);

_request.RequestTime = _request.RequestTime.AddMilliseconds(400);
result = _cooldownPeriod.ValidateRequest(_userSettings, _ruleSettings, _request);
Assert.IsFalse(result);

_request.RequestTime = _request.RequestTime.AddMilliseconds(600);
result = _cooldownPeriod.ValidateRequest(_userSettings, _ruleSettings, _request);
Assert.IsTrue(result);
}
}


}
94 changes: 94 additions & 0 deletions RateLimiter.Tests/MaxRequestsRollingWindowTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using RateLimiter;

namespace RateLimiter.Tests
{
public class MaxRequestsRollingWindowTests
{
private MaxRequestsRollingWindow _maxRequestsRollingWindow;
private UserSettings _userSettings;
private RuleSettings _ruleSettings;
private ValidationRequest _request;

[SetUp]
public void Setup()
{
_maxRequestsRollingWindow = new MaxRequestsRollingWindow();
_userSettings = new UserSettings { Id = "user1" };
_request = new ValidationRequest { RequestTime = DateTime.UtcNow };

_ruleSettings = new RuleSettings
{
Name = "MaxRequestsRollingWindow",
Enabled = true,
Options = new List<Option>
{
new Option { Type = "Default", MaxRequests = 3, PeriodMsec = 1000 },
new Option { Region = "EU", MaxRequests = 5, PeriodMsec = 2000 },
new Option { Region = "US", MaxRequests = 10, PeriodMsec = 2000 }
}
};
}

[Test]
public void ValidateRequest_WithinLimit_ShouldPassValidation()
{
for (int i = 0; i < 3; i++)
{
var request = new ValidationRequest { RequestTime = _request.RequestTime.AddMilliseconds(i * 200) };
var result = _maxRequestsRollingWindow.ValidateRequest(_userSettings, _ruleSettings, request);
Assert.IsTrue(result);
}
}

[Test]
public void ValidateRequest_ExceedsLimit_ShouldFailValidation()
{
for (int i = 0; i < 3; i++)
{
var request = new ValidationRequest { RequestTime = _request.RequestTime.AddMilliseconds(i * 200) };
_maxRequestsRollingWindow.ValidateRequest(_userSettings, _ruleSettings,request);
}

_request.RequestTime = _request.RequestTime.AddMilliseconds(600);
var result = _maxRequestsRollingWindow.ValidateRequest(_userSettings, _ruleSettings, _request);
Assert.IsFalse(result);
}

[Test]
public void ValidateRequest_RegionOverridesDefault_ShouldUseRegionSettings()
{
_userSettings.Region = Region.EU;

for (int i = 0; i < 5; i++)
{
var request = new ValidationRequest { RequestTime = _request.RequestTime.AddMilliseconds(i * 300) };
var result = _maxRequestsRollingWindow.ValidateRequest(_userSettings, _ruleSettings, request);
Assert.IsTrue(result);
}

_request.RequestTime = _request.RequestTime.AddMilliseconds(1500);
var finalResult = _maxRequestsRollingWindow.ValidateRequest(_userSettings, _ruleSettings, _request);
Assert.IsFalse(finalResult);
}

[Test]
public void ValidateRequest_RemoveOldTimestamps_ShouldPassValidation()
{
var request = new ValidationRequest { RequestTime = _request.RequestTime.AddMilliseconds(-1500) };
_maxRequestsRollingWindow.ValidateRequest(_userSettings, _ruleSettings, request);

request.RequestTime = _request.RequestTime.AddMilliseconds(-1000);
_maxRequestsRollingWindow.ValidateRequest(_userSettings, _ruleSettings, request);

request.RequestTime = _request.RequestTime.AddMilliseconds(-500);
_maxRequestsRollingWindow.ValidateRequest(_userSettings, _ruleSettings, request);

var result = _maxRequestsRollingWindow.ValidateRequest(_userSettings, _ruleSettings, _request);
Assert.IsTrue(result);
}
}
}
1 change: 1 addition & 0 deletions RateLimiter.Tests/RateLimiter.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="NSubstitute" Version="5.1.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.4.2" />
</ItemGroup>
Expand Down
152 changes: 142 additions & 10 deletions RateLimiter.Tests/RateLimiterTest.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,145 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using NSubstitute;

namespace RateLimiter.Tests;

[TestFixture]
public class RateLimiterTest
namespace RateLimiter.Tests
{
[Test]
public void Example()
{
Assert.That(true, Is.True);
}
}
public class RequestValidatorTests
{
private RequestValidator _requestValidator;
private UserSettings _userSettings;
private RuleSettingsList _ruleSettings;
private ValidationRequest _request;
private IRequestValidatorRule _ruleMock;

[SetUp]
public void Setup()
{
_requestValidator = new RequestValidator();
_userSettings = new UserSettings { Id = "user1" };
_request = new ValidationRequest {RequestTime = DateTime.UtcNow };
_ruleMock = Substitute.For<IRequestValidatorRule>();
_ruleSettings = new RuleSettingsList {
RateLimiterRules = new List<RuleSettings>{
new RuleSettings
{
Name = _ruleMock.GetType().Name,
Enabled = true,
Options = new List<Option>()
}
}
};
}

[Test]
public void CheckRules_NoRulesProvided_ReturnsTrue()
{
var rules = new List<IRequestValidatorRule>();

var result = _requestValidator.CheckRules(_userSettings, _ruleSettings, rules, _request);

Assert.IsTrue(result);
}

[Test]
public void CheckRules_RuleWithNoMatchingSettings_ReturnsFalse()
{
var rule = Substitute.For<IRequestValidatorRule>();
var rules = new List<IRequestValidatorRule> { rule };

var result = _requestValidator.CheckRules(_userSettings, _ruleSettings, rules, _request);

Assert.IsFalse(result);
}

[Test]
public void CheckRules_RuleWithMatchingSettingsButNoOptions_ReturnsFalse()
{
var rule = Substitute.For<IRequestValidatorRule>();
var rules = new List<IRequestValidatorRule> { rule };
var ruleName = rule.GetType().Name;
_ruleSettings.RateLimiterRules.Add(new RuleSettings { Name = ruleName, Enabled = true, Options = null });

var result = _requestValidator.CheckRules(_userSettings, _ruleSettings, rules, _request);

Assert.IsFalse(result);
}

[Test]
public void CheckRules_RuleIsNotEnabled_SkipsRule()
{
var rule = Substitute.For<IRequestValidatorRule>();
var rules = new List<IRequestValidatorRule> { rule };
var ruleName = rule.GetType().Name;
_ruleSettings.RateLimiterRules.Clear();
_ruleSettings.RateLimiterRules.Add(new RuleSettings { Name = ruleName, Enabled = false, Options = new List<Option>() });

var result = _requestValidator.CheckRules(_userSettings, _ruleSettings, rules, _request);

Assert.IsTrue(result);
}

[Test]
public void CheckRules_AllRulesValid_ShouldReturnTrue()
{
_ruleMock.ValidateRequest(_userSettings, Arg.Any<RuleSettings>(), _request).Returns(true);

var rules = new List<IRequestValidatorRule> { _ruleMock, _ruleMock, _ruleMock };

var result = _requestValidator.CheckRules(_userSettings, _ruleSettings, rules, _request);

Assert.IsTrue(result);
}

[Test]
public void CheckRules_OneRuleInvalid_ShouldReturnFalse()
{
_ruleMock.ValidateRequest(_userSettings, Arg.Any<RuleSettings>(), _request).Returns(true, false, true);

var rules = new List<IRequestValidatorRule> { _ruleMock, _ruleMock, _ruleMock };

var result = _requestValidator.CheckRules(_userSettings, _ruleSettings, rules, _request);

Assert.IsFalse(result);
}

[Test]
public void CheckRules_NoRules_ShouldReturnTrue()
{
var rules = new List<IRequestValidatorRule>();

var result = _requestValidator.CheckRules(_userSettings, _ruleSettings, rules, _request);

Assert.IsTrue(result);
}

[Test]
public void CheckRules_FirstRuleInvalid_ShouldReturnFalse()
{
_ruleMock.ValidateRequest(_userSettings, Arg.Any<RuleSettings>(), _request).Returns(false);

var rules = new List<IRequestValidatorRule> { _ruleMock };

var result = _requestValidator.CheckRules(_userSettings, _ruleSettings, rules, _request);

Assert.IsFalse(result);
}

[Test]
public void CheckRules_MixedValidInvalidRules_ShouldReturnFalse()
{
var validRuleMock = Substitute.For<IRequestValidatorRule>();
var invalidRuleMock = Substitute.For<IRequestValidatorRule>();

validRuleMock.ValidateRequest(_userSettings, Arg.Any<RuleSettings>(), _request).Returns(true);
invalidRuleMock.ValidateRequest(_userSettings, Arg.Any<RuleSettings>(), _request).Returns(false);

var rules = new List<IRequestValidatorRule> { validRuleMock, invalidRuleMock, validRuleMock };

var result = _requestValidator.CheckRules(_userSettings, _ruleSettings, rules, _request);

Assert.IsFalse(result);
}
}
}
8 changes: 8 additions & 0 deletions RateLimiter/Contracts/IRequestValidatorRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System;

namespace RateLimiter;

public interface IRequestValidatorRule
{
public bool ValidateRequest(UserSettings user, RuleSettings settings, ValidationRequest request);
}
Loading

0 comments on commit a66d1b8

Please sign in to comment.