@@ -6,18 +6,26 @@ import { RateLimit } from './rate-limit';
66import { parseConfigRules } from './config' ;
77import Redis from 'ioredis' ;
88import { after } from 'node:test' ;
9+ import { StatsD } from 'hot-shots' ;
910
1011describe ( 'rate-limit' , ( ) => {
12+ let mockIncrement : jest . Mock ;
13+ let statsd : StatsD ;
1114 let redis : Redis . Redis ;
1215 let rateLimit : RateLimit ;
1316
1417 beforeAll ( ( ) => {
1518 redis = new Redis ( 'localhost' ) ;
19+ mockIncrement = jest . fn ( ) ;
20+ statsd = {
21+ increment : mockIncrement ,
22+ } as unknown as StatsD ;
1623 } ) ;
1724
1825 beforeEach ( async ( ) => {
1926 await redis . flushall ( ) ;
20- rateLimit = new RateLimit ( { } , redis ) ;
27+ rateLimit = new RateLimit ( { } , redis , statsd ) ;
28+ mockIncrement . mockReset ( ) ;
2129 } ) ;
2230
2331 after ( async ( ) => {
@@ -28,7 +36,8 @@ describe('rate-limit', () => {
2836 it ( 'should block on ' + blockOn , async ( ) => {
2937 rateLimit = new RateLimit (
3038 parseConfigRules ( [ 'testBlock:ip:1:1s:1s' ] ) ,
31- redis
39+ redis ,
40+ statsd
3241 ) ;
3342
3443 const check1 = await rateLimit . check ( 'testBlock' , { ip : '127.0.0.1' } ) ;
@@ -37,13 +46,20 @@ describe('rate-limit', () => {
3746 expect ( check1 ) . toBeNull ( ) ;
3847 expect ( check2 ?. reason ) . toEqual ( 'too-many-attempts' ) ;
3948 expect ( check2 ?. retryAfter ) . toEqual ( 1000 ) ;
49+
50+ expect ( mockIncrement ) . toBeCalledTimes ( 1 ) ;
51+ expect ( mockIncrement ) . toBeCalledWith ( 'rate_limit.block' , [
52+ 'on:ip' ,
53+ 'action:testBlock' ,
54+ ] ) ;
4055 } ) ;
4156 }
4257
4358 it ( `should not block after window clears` , async ( ) => {
4459 rateLimit = new RateLimit (
4560 parseConfigRules ( [ 'testWindowCleared:ip:1:1s:1s' ] ) ,
46- redis
61+ redis ,
62+ statsd
4763 ) ;
4864
4965 const check1 = await rateLimit . check ( 'testWindowCleared' , {
@@ -61,7 +77,8 @@ describe('rate-limit', () => {
6177 it ( 'can block multiple rules on a single action' , async ( ) => {
6278 rateLimit = new RateLimit (
6379 parseConfigRules ( [ 'testMulti:ip:2:10s:2s' , 'testMulti:email:2:10s:3s' ] ) ,
64- redis
80+ redis ,
81+ statsd
6582 ) ;
6683 const check1 = await rateLimit . check ( 'testMulti' , {
6784 ip : '127.0.0.1' ,
@@ -107,7 +124,8 @@ describe('rate-limit', () => {
107124 'testDouble:email:2:10s:2s' ,
108125 'testDouble:email:3:10s:4s' ,
109126 ] ) ,
110- redis
127+ redis ,
128+ statsd
111129 ) ;
112130 const check1 = await rateLimit . check ( 'testDouble' , {
113131@@ -134,7 +152,11 @@ describe('rate-limit', () => {
134152 /**
135153 * Note that once the ban kicks in the window disappears. So despite
136154 */
137- rateLimit = new RateLimit ( parseConfigRules ( [ 'test:ip:1:20s:1s' ] ) , redis ) ;
155+ rateLimit = new RateLimit (
156+ parseConfigRules ( [ 'test:ip:1:20s:1s' ] ) ,
157+ redis ,
158+ statsd
159+ ) ;
138160
139161 const check1 = await rateLimit . check ( 'test' , { ip : '127.0.0.1' } ) ;
140162 const check2 = await rateLimit . check ( 'test' , { ip : '127.0.0.1' } ) ;
@@ -153,7 +175,8 @@ describe('rate-limit', () => {
153175 it ( 'can unblock' , async ( ) => {
154176 rateLimit = new RateLimit (
155177 parseConfigRules ( [ 'testBlock:ip:1:1s:1s' ] ) ,
156- redis
178+ redis ,
179+ statsd
157180 ) ;
158181
159182 const check1 = await rateLimit . check ( 'testBlock' , { ip : '127.0.0.1' } ) ;
@@ -165,5 +188,15 @@ describe('rate-limit', () => {
165188 expect ( check2 ?. reason ) . toEqual ( 'too-many-attempts' ) ;
166189 expect ( check2 ?. retryAfter ) . toEqual ( 1000 ) ;
167190 expect ( check3 ) . toBeNull ( ) ;
191+
192+ expect ( statsd . increment ) . toBeCalledTimes ( 2 ) ;
193+ expect ( statsd . increment ) . toBeCalledWith ( 'rate_limit.block' , [
194+ 'on:ip' ,
195+ 'action:testBlock' ,
196+ ] ) ;
197+ expect ( statsd . increment ) . toBeCalledWith ( 'rate_limit.unblock' , [
198+ 'on:ip' ,
199+ 'action:testBlock' ,
200+ ] ) ;
168201 } ) ;
169202} ) ;
0 commit comments