Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c42621f74 | ||
| 1b98282202 | |||
|
|
b6731b220c | ||
| 3507d455e8 |
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "flyer-crawler",
|
"name": "flyer-crawler",
|
||||||
"version": "0.9.31",
|
"version": "0.9.33",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "flyer-crawler",
|
"name": "flyer-crawler",
|
||||||
"version": "0.9.31",
|
"version": "0.9.33",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bull-board/api": "^6.14.2",
|
"@bull-board/api": "^6.14.2",
|
||||||
"@bull-board/express": "^6.14.2",
|
"@bull-board/express": "^6.14.2",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "flyer-crawler",
|
"name": "flyer-crawler",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.9.31",
|
"version": "0.9.33",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "concurrently \"npm:start:dev\" \"vite\"",
|
"dev": "concurrently \"npm:start:dev\" \"vite\"",
|
||||||
|
|||||||
@@ -113,8 +113,8 @@ describe('Deals Routes (/api/users/deals)', () => {
|
|||||||
.set('X-Test-Rate-Limit-Enable', 'true');
|
.set('X-Test-Rate-Limit-Enable', 'true');
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
expect(parseInt(response.headers['x-ratelimit-limit'])).toBe(100);
|
expect(parseInt(response.headers['ratelimit-limit'])).toBe(100);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -319,8 +319,8 @@ describe('Flyer Routes (/api/flyers)', () => {
|
|||||||
.set('X-Test-Rate-Limit-Enable', 'true');
|
.set('X-Test-Rate-Limit-Enable', 'true');
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
expect(parseInt(response.headers['x-ratelimit-limit'])).toBe(100);
|
expect(parseInt(response.headers['ratelimit-limit'])).toBe(100);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should apply batchLimiter to POST /items/batch-fetch', async () => {
|
it('should apply batchLimiter to POST /items/batch-fetch', async () => {
|
||||||
@@ -331,8 +331,8 @@ describe('Flyer Routes (/api/flyers)', () => {
|
|||||||
.send({ flyerIds: [1] });
|
.send({ flyerIds: [1] });
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
expect(parseInt(response.headers['x-ratelimit-limit'])).toBe(50);
|
expect(parseInt(response.headers['ratelimit-limit'])).toBe(50);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should apply batchLimiter to POST /items/batch-count', async () => {
|
it('should apply batchLimiter to POST /items/batch-count', async () => {
|
||||||
@@ -343,8 +343,8 @@ describe('Flyer Routes (/api/flyers)', () => {
|
|||||||
.send({ flyerIds: [1] });
|
.send({ flyerIds: [1] });
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
expect(parseInt(response.headers['x-ratelimit-limit'])).toBe(50);
|
expect(parseInt(response.headers['ratelimit-limit'])).toBe(50);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should apply trackingLimiter to POST /items/:itemId/track', async () => {
|
it('should apply trackingLimiter to POST /items/:itemId/track', async () => {
|
||||||
@@ -357,8 +357,8 @@ describe('Flyer Routes (/api/flyers)', () => {
|
|||||||
.send({ type: 'view' });
|
.send({ type: 'view' });
|
||||||
|
|
||||||
expect(response.status).toBe(202);
|
expect(response.status).toBe(202);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
expect(parseInt(response.headers['x-ratelimit-limit'])).toBe(200);
|
expect(parseInt(response.headers['ratelimit-limit'])).toBe(200);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -345,8 +345,8 @@ describe('Gamification Routes (/api/achievements)', () => {
|
|||||||
.set('X-Test-Rate-Limit-Enable', 'true');
|
.set('X-Test-Rate-Limit-Enable', 'true');
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
expect(parseInt(response.headers['x-ratelimit-limit'])).toBe(100);
|
expect(parseInt(response.headers['ratelimit-limit'])).toBe(100);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should apply userReadLimiter to GET /me', async () => {
|
it('should apply userReadLimiter to GET /me', async () => {
|
||||||
@@ -360,8 +360,8 @@ describe('Gamification Routes (/api/achievements)', () => {
|
|||||||
.set('X-Test-Rate-Limit-Enable', 'true');
|
.set('X-Test-Rate-Limit-Enable', 'true');
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
expect(parseInt(response.headers['x-ratelimit-limit'])).toBe(100);
|
expect(parseInt(response.headers['ratelimit-limit'])).toBe(100);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should apply adminTriggerLimiter to POST /award', async () => {
|
it('should apply adminTriggerLimiter to POST /award', async () => {
|
||||||
@@ -378,8 +378,8 @@ describe('Gamification Routes (/api/achievements)', () => {
|
|||||||
.send({ userId: 'some-user', achievementName: 'some-achievement' });
|
.send({ userId: 'some-user', achievementName: 'some-achievement' });
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
expect(parseInt(response.headers['x-ratelimit-limit'])).toBe(30);
|
expect(parseInt(response.headers['ratelimit-limit'])).toBe(30);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ describe('Personalization Routes (/api/personalization)', () => {
|
|||||||
.set('X-Test-Rate-Limit-Enable', 'true');
|
.set('X-Test-Rate-Limit-Enable', 'true');
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -159,8 +159,8 @@ describe('Price Routes (/api/price-history)', () => {
|
|||||||
.send({ masterItemIds: [1, 2] });
|
.send({ masterItemIds: [1, 2] });
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
expect(parseInt(response.headers['x-ratelimit-limit'])).toBe(50);
|
expect(parseInt(response.headers['ratelimit-limit'])).toBe(50);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -211,13 +211,14 @@ describe('Reaction Routes (/api/reactions)', () => {
|
|||||||
|
|
||||||
describe('Rate Limiting', () => {
|
describe('Rate Limiting', () => {
|
||||||
it('should apply publicReadLimiter to GET /', async () => {
|
it('should apply publicReadLimiter to GET /', async () => {
|
||||||
|
const app = createTestApp({ router: reactionsRouter, basePath: '/api/reactions' });
|
||||||
vi.mocked(reactionRepo.getReactions).mockResolvedValue([]);
|
vi.mocked(reactionRepo.getReactions).mockResolvedValue([]);
|
||||||
const response = await supertest(app)
|
const response = await supertest(app)
|
||||||
.get('/api/reactions')
|
.get('/api/reactions')
|
||||||
.set('X-Test-Rate-Limit-Enable', 'true');
|
.set('X-Test-Rate-Limit-Enable', 'true');
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should apply userUpdateLimiter to POST /toggle', async () => {
|
it('should apply userUpdateLimiter to POST /toggle', async () => {
|
||||||
@@ -235,8 +236,8 @@ describe('Reaction Routes (/api/reactions)', () => {
|
|||||||
.send({ entity_type: 'recipe', entity_id: '1', reaction_type: 'like' });
|
.send({ entity_type: 'recipe', entity_id: '1', reaction_type: 'like' });
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
expect(parseInt(response.headers['x-ratelimit-limit'])).toBe(150);
|
expect(parseInt(response.headers['ratelimit-limit'])).toBe(150);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -350,7 +350,7 @@ describe('Recipe Routes (/api/recipes)', () => {
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(blockedResponse.status).toBe(429);
|
expect(blockedResponse.status).toBe(429);
|
||||||
expect(blockedResponse.text).toContain('Too many recipe suggestion requests');
|
expect(blockedResponse.text).toContain('Too many AI generation requests');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should NOT block requests when the opt-in header is not sent', async () => {
|
it('should NOT block requests when the opt-in header is not sent', async () => {
|
||||||
@@ -375,8 +375,8 @@ describe('Recipe Routes (/api/recipes)', () => {
|
|||||||
.set('X-Test-Rate-Limit-Enable', 'true');
|
.set('X-Test-Rate-Limit-Enable', 'true');
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
expect(parseInt(response.headers['x-ratelimit-limit'])).toBe(100);
|
expect(parseInt(response.headers['ratelimit-limit'])).toBe(100);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ describe('Stats Routes (/api/stats)', () => {
|
|||||||
.set('X-Test-Rate-Limit-Enable', 'true');
|
.set('X-Test-Rate-Limit-Enable', 'true');
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -171,10 +171,10 @@ describe('System Routes (/api/system)', () => {
|
|||||||
.send({ address });
|
.send({ address });
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-remaining');
|
expect(response.headers).toHaveProperty('ratelimit-remaining');
|
||||||
expect(parseInt(response.headers['x-ratelimit-limit'])).toBe(limit);
|
expect(parseInt(response.headers['ratelimit-limit'])).toBe(limit);
|
||||||
expect(parseInt(response.headers['x-ratelimit-remaining'])).toBeLessThan(limit);
|
expect(parseInt(response.headers['ratelimit-remaining'])).toBeLessThan(limit);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1237,6 +1237,19 @@ describe('User Routes (/api/users)', () => {
|
|||||||
}); // End of Recipe Routes
|
}); // End of Recipe Routes
|
||||||
|
|
||||||
describe('Rate Limiting', () => {
|
describe('Rate Limiting', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Advance time to ensure rate limits are reset between tests
|
||||||
|
vi.advanceTimersByTime(60 * 60 * 1000 + 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
it('should apply userUpdateLimiter to PUT /profile', async () => {
|
it('should apply userUpdateLimiter to PUT /profile', async () => {
|
||||||
vi.mocked(db.userRepo.updateUserProfile).mockResolvedValue(mockUserProfile);
|
vi.mocked(db.userRepo.updateUserProfile).mockResolvedValue(mockUserProfile);
|
||||||
|
|
||||||
@@ -1246,8 +1259,8 @@ describe('User Routes (/api/users)', () => {
|
|||||||
.send({ full_name: 'Rate Limit Test' });
|
.send({ full_name: 'Rate Limit Test' });
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
expect(parseInt(response.headers['x-ratelimit-limit'])).toBe(100);
|
expect(parseInt(response.headers['ratelimit-limit'])).toBe(100);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should apply userSensitiveUpdateLimiter to PUT /profile/password and block after limit', async () => {
|
it('should apply userSensitiveUpdateLimiter to PUT /profile/password and block after limit', async () => {
|
||||||
@@ -1283,8 +1296,8 @@ describe('User Routes (/api/users)', () => {
|
|||||||
.attach('avatar', Buffer.from('dummy-image-content'), dummyImagePath);
|
.attach('avatar', Buffer.from('dummy-image-content'), dummyImagePath);
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers).toHaveProperty('x-ratelimit-limit');
|
expect(response.headers).toHaveProperty('ratelimit-limit');
|
||||||
expect(parseInt(response.headers['x-ratelimit-limit'])).toBe(20);
|
expect(parseInt(response.headers['ratelimit-limit'])).toBe(20);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user