-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: Added unit and integration test
- Loading branch information
Adil
committed
Sep 21, 2024
1 parent
6ef94f0
commit 69bce2c
Showing
4 changed files
with
192 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import express from 'express' | ||
import { type Redis } from 'ioredis' | ||
import { type MedusaContainer } from 'medusa-core-utils' | ||
import request from 'supertest' | ||
|
||
import RateLimitService from '../../services/rate-limit' | ||
import defaultRateLimit from '../middlewares/default-rate-limit' | ||
|
||
const mockRedisClient = { | ||
incr: jest.fn(), | ||
expire: jest.fn(), | ||
get: jest.fn(), | ||
del: jest.fn(), | ||
ttl: jest.fn(), | ||
} as unknown as jest.Mocked<Redis> | ||
|
||
const mockContainer = { | ||
resolve: jest | ||
.fn() | ||
.mockReturnValue( | ||
new RateLimitService( | ||
{ redisClient: mockRedisClient }, | ||
{ limit: 5, window: 60 }, | ||
), | ||
), | ||
} | ||
|
||
const app = express() | ||
app.use((req, res, next) => { | ||
req.scope = mockContainer as unknown as jest.Mocked<MedusaContainer> | ||
next() | ||
}) | ||
app.use(defaultRateLimit) | ||
app.get('/test', (req, res) => { | ||
res.status(200).send('OK') | ||
}) | ||
|
||
describe('Rate Limit Middleware', () => { | ||
beforeEach(() => { | ||
jest.clearAllMocks() | ||
}) | ||
|
||
it('should allow requests under the limit', async () => { | ||
mockRedisClient.incr.mockResolvedValueOnce(1) | ||
mockRedisClient.expire.mockResolvedValueOnce(1) | ||
|
||
const response = await request(app).get('/test') | ||
|
||
expect(response.status).toBe(200) | ||
expect(response.text).toBe('OK') | ||
}) | ||
|
||
it('should block requests over the limit', async () => { | ||
mockRedisClient.incr.mockResolvedValueOnce(6) | ||
mockRedisClient.ttl.mockResolvedValueOnce(60) | ||
|
||
const response = await request(app).get('/test') | ||
|
||
expect(response.status).toBe(429) | ||
expect(response.body).toEqual({ | ||
error: 'Too many requests, please try again later.', | ||
}) | ||
}) | ||
|
||
it('should return the correct headers', async () => { | ||
mockRedisClient.incr.mockResolvedValueOnce(1) | ||
mockRedisClient.expire.mockResolvedValueOnce(60) | ||
mockRedisClient.get.mockResolvedValueOnce('1') | ||
|
||
const response = await request(app).get('/test') | ||
|
||
expect(response.headers['x-ratelimit-limit']).toBe('5') | ||
expect(response.headers['x-ratelimit-remaining']).toBe('4') | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import { type PluginOptions } from '../../types/options' | ||
import { type Redis } from 'ioredis' | ||
|
||
import RateLimitService from '../rate-limit' | ||
|
||
const mockRedisClient = { | ||
incr: jest.fn(), | ||
expire: jest.fn(), | ||
get: jest.fn(), | ||
del: jest.fn(), | ||
ttl: jest.fn(), | ||
} as unknown as jest.Mocked<Redis> | ||
|
||
const defaultOptions: PluginOptions = { | ||
limit: 5, | ||
window: 60, | ||
} | ||
|
||
describe('RateLimitService', () => { | ||
let rateLimitService: RateLimitService | ||
|
||
beforeEach(() => { | ||
rateLimitService = new RateLimitService( | ||
{ redisClient: mockRedisClient }, | ||
defaultOptions, | ||
) | ||
}) | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks() | ||
}) | ||
|
||
it('should increment the key and set expiry if it is the first request', async () => { | ||
mockRedisClient.incr.mockResolvedValueOnce(1) | ||
mockRedisClient.expire.mockResolvedValueOnce(1) | ||
|
||
const result = await rateLimitService.limit('test-key') | ||
|
||
expect(mockRedisClient.incr).toHaveBeenCalledWith('test-key') | ||
expect(mockRedisClient.expire).toHaveBeenCalledWith('test-key', 60) | ||
expect(result).toBe(true) | ||
}) | ||
|
||
it('should return false if the limit is exceeded', async () => { | ||
mockRedisClient.incr.mockResolvedValueOnce(6) | ||
|
||
const result = await rateLimitService.limit('test-key') | ||
|
||
expect(mockRedisClient.incr).toHaveBeenCalledWith('test-key') | ||
expect(result).toBe(false) | ||
}) | ||
|
||
it('should return the number of remaining attempts', async () => { | ||
mockRedisClient.get.mockResolvedValueOnce('3') | ||
|
||
const remainingAttempts = | ||
await rateLimitService.getRemainingAttempts('test-key') | ||
|
||
expect(mockRedisClient.get).toHaveBeenCalledWith('test-key') | ||
expect(remainingAttempts).toBe(2) | ||
}) | ||
|
||
it('should reset the limit for the given key', async () => { | ||
await rateLimitService.resetLimit('test-key') | ||
|
||
expect(mockRedisClient.del).toHaveBeenCalledWith('test-key') | ||
}) | ||
|
||
it('should return the time to live for the given key', async () => { | ||
mockRedisClient.ttl.mockResolvedValueOnce(30) | ||
|
||
const ttl = await rateLimitService.ttl('test-key') | ||
|
||
expect(mockRedisClient.ttl).toHaveBeenCalledWith('test-key') | ||
expect(ttl).toBe(30) | ||
}) | ||
|
||
it('should return the default options', () => { | ||
const options = rateLimitService.getOptions() | ||
|
||
expect(options).toEqual(defaultOptions) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"extends": "./tsconfig.json", | ||
"include": ["src"], | ||
"exclude": ["dist", "node_modules"] | ||
} |