1
1
using NUnit . Framework ;
2
+ using System ;
3
+ using System . Threading ;
2
4
3
5
namespace RateLimiter . Tests ;
4
6
5
7
[ TestFixture ]
6
8
public class RateLimiterTest
7
9
{
8
- [ Test ]
9
- public void Example ( )
10
- {
11
- Assert . That ( true , Is . True ) ;
12
- }
13
- }
10
+ [ Test ]
11
+ public void Example ( )
12
+ {
13
+ Assert . That ( true , Is . True ) ;
14
+ }
15
+
16
+ [ Test ]
17
+ public void TimeIntervalBetweenRequestsRule_ShouldAllowFirstRequest ( )
18
+ {
19
+ // Arrange
20
+ var requestTracker = new RequestTracker ( ) ;
21
+ var rule = new TimeIntervalBetweenRequestsRule ( requestTracker , TimeSpan . FromSeconds ( 1 ) ) ;
22
+
23
+ // Act
24
+ bool isAllowed = rule . IsRequestAllowed ( "token1" , "resource1" ) ;
25
+
26
+ // Assert
27
+ Assert . That ( isAllowed , Is . True ) ;
28
+ }
29
+
30
+ [ Test ]
31
+ public void TimeIntervalBetweenRequestsRule_ShouldBlockRequestsWithinInterval ( )
32
+ {
33
+ // Arrange
34
+ var requestTracker = new RequestTracker ( ) ;
35
+ var rule = new TimeIntervalBetweenRequestsRule ( requestTracker , TimeSpan . FromSeconds ( 1 ) ) ;
36
+
37
+ // Act
38
+ requestTracker . RecordRequest ( "token1" , "resource1" ) ;
39
+ bool isAllowed = rule . IsRequestAllowed ( "token1" , "resource1" ) ;
40
+
41
+ // Assert
42
+ Assert . That ( isAllowed , Is . False ) ;
43
+ }
44
+
45
+ [ Test ]
46
+ public void TimeIntervalBetweenRequestsRule_ShouldAllowRequestsAfterInterval ( )
47
+ {
48
+ // Arrange
49
+ var requestTracker = new RequestTracker ( ) ;
50
+ var rule = new TimeIntervalBetweenRequestsRule ( requestTracker , TimeSpan . FromMilliseconds ( 50 ) ) ;
51
+
52
+ // Act
53
+ requestTracker . RecordRequest ( "token1" , "resource1" ) ;
54
+ Thread . Sleep ( 100 ) ; // Wait longer than the interval
55
+ bool isAllowed = rule . IsRequestAllowed ( "token1" , "resource1" ) ;
56
+
57
+ // Assert
58
+ Assert . That ( isAllowed , Is . True ) ;
59
+ }
60
+
61
+ [ Test ]
62
+ public void RequestsPerTimespanRule_ShouldAllowRequestsWithinLimit ( )
63
+ {
64
+ // Arrange
65
+ var requestTracker = new RequestTracker ( ) ;
66
+ var rule = new RequestsPerTimespanRule ( requestTracker , 3 , TimeSpan . FromSeconds ( 10 ) ) ;
67
+
68
+ // Act
69
+ requestTracker . RecordRequest ( "token1" , "resource1" ) ;
70
+ requestTracker . RecordRequest ( "token1" , "resource1" ) ;
71
+ bool isAllowed = rule . IsRequestAllowed ( "token1" , "resource1" ) ;
72
+
73
+ // Assert
74
+ Assert . That ( isAllowed , Is . True ) ;
75
+ }
76
+
77
+ [ Test ]
78
+ public void RequestsPerTimespanRule_ShouldBlockRequestsOverLimit ( )
79
+ {
80
+ // Arrange
81
+ var requestTracker = new RequestTracker ( ) ;
82
+ var rule = new RequestsPerTimespanRule ( requestTracker , 2 , TimeSpan . FromSeconds ( 10 ) ) ;
83
+
84
+ // Act
85
+ requestTracker . RecordRequest ( "token1" , "resource1" ) ;
86
+ requestTracker . RecordRequest ( "token1" , "resource1" ) ;
87
+ bool isAllowed = rule . IsRequestAllowed ( "token1" , "resource1" ) ;
88
+
89
+ // Assert
90
+ Assert . That ( isAllowed , Is . False ) ;
91
+ }
92
+
93
+ [ Test ]
94
+ public void CompositeRule_And_ShouldRequireAllRulesToPass ( )
95
+ {
96
+ // Arrange
97
+ var requestTracker = new RequestTracker ( ) ;
98
+ var rule1 = new RequestsPerTimespanRule ( requestTracker , 3 , TimeSpan . FromSeconds ( 10 ) ) ;
99
+ var rule2 = new TimeIntervalBetweenRequestsRule ( requestTracker , TimeSpan . FromMilliseconds ( 50 ) ) ;
100
+
101
+ var compositeRule = new CompositeRule ( CompositeRule . LogicalOperator . And ) ;
102
+ compositeRule . AddRule ( rule1 ) ;
103
+ compositeRule . AddRule ( rule2 ) ;
104
+
105
+ // Act - First request should pass both rules
106
+ bool firstRequest = compositeRule . IsRequestAllowed ( "token1" , "resource1" ) ;
107
+
108
+ // Record the request
109
+ requestTracker . RecordRequest ( "token1" , "resource1" ) ;
110
+
111
+ // Second request should fail the interval rule but pass the count rule
112
+ bool secondRequest = compositeRule . IsRequestAllowed ( "token1" , "resource1" ) ;
113
+
114
+ // Wait for the interval to pass
115
+ Thread . Sleep ( 100 ) ;
116
+
117
+ // Third request should now pass both rules again
118
+ bool thirdRequest = compositeRule . IsRequestAllowed ( "token1" , "resource1" ) ;
119
+
120
+ // Assert
121
+ Assert . That ( firstRequest , Is . True ) ;
122
+ Assert . That ( secondRequest , Is . False ) ;
123
+ Assert . That ( thirdRequest , Is . True ) ;
124
+ }
125
+
126
+ [ Test ]
127
+ public void CompositeRule_Or_ShouldRequireAnyRuleToPass ( )
128
+ {
129
+ // Arrange
130
+ var requestTracker = new RequestTracker ( ) ;
131
+ var rule1 = new RequestsPerTimespanRule ( requestTracker , 1 , TimeSpan . FromSeconds ( 10 ) ) ;
132
+ var rule2 = new TimeIntervalBetweenRequestsRule ( requestTracker , TimeSpan . FromMilliseconds ( 50 ) ) ;
133
+
134
+ var compositeRule = new CompositeRule ( CompositeRule . LogicalOperator . Or ) ;
135
+ compositeRule . AddRule ( rule1 ) ;
136
+ compositeRule . AddRule ( rule2 ) ;
137
+
138
+ // Act - First request should pass both rules
139
+ bool firstRequest = compositeRule . IsRequestAllowed ( "token1" , "resource1" ) ;
140
+
141
+ // Record the request
142
+ requestTracker . RecordRequest ( "token1" , "resource1" ) ;
143
+
144
+ // Second request should fail the count rule but pass the interval rule after waiting
145
+ Thread . Sleep ( 100 ) ;
146
+ bool secondRequest = compositeRule . IsRequestAllowed ( "token1" , "resource1" ) ;
147
+
148
+ // Record the second request
149
+ requestTracker . RecordRequest ( "token1" , "resource1" ) ;
150
+
151
+ // Third request should fail both rules (over count limit and within interval)
152
+ bool thirdRequest = compositeRule . IsRequestAllowed ( "token1" , "resource1" ) ;
153
+
154
+ // Assert
155
+ Assert . That ( firstRequest , Is . True ) ;
156
+ Assert . That ( secondRequest , Is . True ) ;
157
+ Assert . That ( thirdRequest , Is . False ) ;
158
+ }
159
+
160
+ [ Test ]
161
+ public void RegionalRule_ShouldApplyDifferentRulesBasedOnRegion ( )
162
+ {
163
+ // Arrange
164
+ var requestTracker = new RequestTracker ( ) ;
165
+
166
+ // Strict rule for US region - only 1 request allowed
167
+ var usRule = new RequestsPerTimespanRule ( requestTracker , 1 , TimeSpan . FromSeconds ( 10 ) ) ;
168
+
169
+ // Lenient rule for EU region - 3 requests allowed
170
+ var euRule = new RequestsPerTimespanRule ( requestTracker , 3 , TimeSpan . FromSeconds ( 10 ) ) ;
171
+
172
+ // Function to resolve region from token
173
+ Func < string , Region > regionResolver = token =>
174
+ {
175
+ if ( token . StartsWith ( "us-" ) )
176
+ return Region . US ;
177
+ else if ( token . StartsWith ( "eu-" ) )
178
+ return Region . EU ;
179
+ else
180
+ return Region . Other ;
181
+ } ;
182
+
183
+ var regionalRule = new RegionalRule ( regionResolver ) ;
184
+ regionalRule . SetRuleForRegion ( Region . US , usRule ) ;
185
+ regionalRule . SetRuleForRegion ( Region . EU , euRule ) ;
186
+
187
+ // Act & Assert
188
+
189
+ // US token - first request allowed
190
+ Assert . That ( regionalRule . IsRequestAllowed ( "us-token" , "resource1" ) , Is . True ) ;
191
+ requestTracker . RecordRequest ( "us-token" , "resource1" ) ;
192
+
193
+ // US token - second request blocked (over limit)
194
+ Assert . That ( regionalRule . IsRequestAllowed ( "us-token" , "resource1" ) , Is . False ) ;
195
+
196
+ // EU token - first request allowed
197
+ Assert . That ( regionalRule . IsRequestAllowed ( "eu-token" , "resource1" ) , Is . True ) ;
198
+ requestTracker . RecordRequest ( "eu-token" , "resource1" ) ;
199
+
200
+ // EU token - second request allowed
201
+ Assert . That ( regionalRule . IsRequestAllowed ( "eu-token" , "resource1" ) , Is . True ) ;
202
+ requestTracker . RecordRequest ( "eu-token" , "resource1" ) ;
203
+
204
+ // EU token - third request allowed
205
+ Assert . That ( regionalRule . IsRequestAllowed ( "eu-token" , "resource1" ) , Is . True ) ;
206
+ requestTracker . RecordRequest ( "eu-token" , "resource1" ) ;
207
+
208
+ // EU token - fourth request blocked (over limit)
209
+ Assert . That ( regionalRule . IsRequestAllowed ( "eu-token" , "resource1" ) , Is . False ) ;
210
+
211
+ // Other region - no rule set, so allowed
212
+ Assert . That ( regionalRule . IsRequestAllowed ( "other-token" , "resource1" ) , Is . True ) ;
213
+ }
214
+
215
+ [ Test ]
216
+ public void RateLimiter_ShouldApplyRulesForResources ( )
217
+ {
218
+ // Arrange
219
+ var rateLimiter = new RateLimiter ( ) ;
220
+ var requestTracker = rateLimiter . GetRequestTracker ( ) ;
221
+
222
+ // Create rules
223
+ var rule1 = new RequestsPerTimespanRule ( requestTracker , 2 , TimeSpan . FromSeconds ( 10 ) ) ;
224
+ var rule2 = new TimeIntervalBetweenRequestsRule ( requestTracker , TimeSpan . FromMilliseconds ( 50 ) ) ;
225
+
226
+ // Set rules for different resources
227
+ rateLimiter . SetRuleForResource ( "resource1" , rule1 ) ;
228
+ rateLimiter . SetRuleForResource ( "resource2" , rule2 ) ;
229
+
230
+ // Act & Assert
231
+
232
+ // Resource 1 - first request allowed
233
+ Assert . That ( rateLimiter . IsRequestAllowed ( "token1" , "resource1" ) , Is . True ) ;
234
+ requestTracker . RecordRequest ( "token1" , "resource1" ) ;
235
+
236
+ // Resource 1 - second request allowed
237
+ Assert . That ( rateLimiter . IsRequestAllowed ( "token1" , "resource1" ) , Is . True ) ;
238
+ requestTracker . RecordRequest ( "token1" , "resource1" ) ;
239
+
240
+ // Resource 1 - third request blocked (over limit)
241
+ Assert . That ( rateLimiter . IsRequestAllowed ( "token1" , "resource1" ) , Is . False ) ;
242
+
243
+ // Resource 2 - first request allowed
244
+ Assert . That ( rateLimiter . IsRequestAllowed ( "token1" , "resource2" ) , Is . True ) ;
245
+ requestTracker . RecordRequest ( "token1" , "resource2" ) ;
246
+
247
+ // Resource 2 - second request blocked (within interval)
248
+ Assert . That ( rateLimiter . IsRequestAllowed ( "token1" , "resource2" ) , Is . False ) ;
249
+
250
+ // Wait for interval to pass
251
+ Thread . Sleep ( 100 ) ;
252
+
253
+ // Resource 2 - third request allowed (after interval)
254
+ Assert . That ( rateLimiter . IsRequestAllowed ( "token1" , "resource2" ) , Is . True ) ;
255
+
256
+ // Resource 3 - no rule set, so allowed
257
+ Assert . That ( rateLimiter . IsRequestAllowed ( "token1" , "resource3" ) , Is . True ) ;
258
+ }
259
+ }
0 commit comments