LibreChat/api/cache/banViolation.spec.js
Danny Avila a2fc7d312a
🏗️ refactor: Extract DB layers to data-schemas for shared use (#7650)
* refactor: move model definitions and database-related methods to packages/data-schemas

* ci: update tests due to new DB structure

fix: disable mocking `librechat-data-provider`

feat: Add schema exports to data-schemas package

- Introduced a new schema module that exports various schemas including action, agent, and user schemas.
- Updated index.ts to include the new schema exports for better modularity and organization.

ci: fix appleStrategy tests

fix: Agent.spec.js

ci: refactor handleTools tests to use MongoMemoryServer for in-memory database

fix: getLogStores imports

ci: update banViolation tests to use MongoMemoryServer and improve session mocking

test: refactor samlStrategy tests to improve mock configurations and user handling

ci: fix crypto mock in handleText tests for improved accuracy

ci: refactor spendTokens tests to improve model imports and setup

ci: refactor Message model tests to use MongoMemoryServer and improve database interactions

* refactor: streamline IMessage interface and move feedback properties to types/message.ts

* refactor: use exported initializeRoles from `data-schemas`, remove api workspace version (this serves as an example of future migrations that still need to happen)

* refactor: update model imports to use destructuring from `~/db/models` for consistency and clarity

* refactor: remove unused mongoose imports from model files for cleaner code

* refactor: remove unused mongoose imports from Share, Prompt, and Transaction model files for cleaner code

* refactor: remove unused import in Transaction model for cleaner code

* ci: update deploy workflow to reference new Docker Dev Branch Images Build and add new workflow for building Docker images on dev branch

* chore: cleanup imports
2025-05-30 22:18:13 -04:00

136 lines
4.5 KiB
JavaScript

const mongoose = require('mongoose');
const { MongoMemoryServer } = require('mongodb-memory-server');
const banViolation = require('./banViolation');
// Mock deleteAllUserSessions since we're testing ban logic, not session deletion
jest.mock('~/models', () => ({
...jest.requireActual('~/models'),
deleteAllUserSessions: jest.fn().mockResolvedValue(true),
}));
describe('banViolation', () => {
let mongoServer;
let req, res, errorMessage;
beforeAll(async () => {
mongoServer = await MongoMemoryServer.create();
const mongoUri = mongoServer.getUri();
await mongoose.connect(mongoUri);
});
afterAll(async () => {
await mongoose.disconnect();
await mongoServer.stop();
});
beforeEach(() => {
req = {
ip: '127.0.0.1',
cookies: {
refreshToken: 'someToken',
},
};
res = {
clearCookie: jest.fn(),
};
errorMessage = {
type: 'someViolation',
user_id: new mongoose.Types.ObjectId().toString(), // Use valid ObjectId
prev_count: 0,
violation_count: 0,
};
process.env.BAN_VIOLATIONS = 'true';
process.env.BAN_DURATION = '7200000'; // 2 hours in ms
process.env.BAN_INTERVAL = '20';
});
afterEach(() => {
jest.clearAllMocks();
});
it('should not ban if BAN_VIOLATIONS are not enabled', async () => {
process.env.BAN_VIOLATIONS = 'false';
await banViolation(req, res, errorMessage);
expect(errorMessage.ban).toBeFalsy();
});
it('should not ban if errorMessage is not provided', async () => {
await banViolation(req, res, null);
expect(errorMessage.ban).toBeFalsy();
});
it('[1/3] should ban if violation_count crosses the interval threshold: 19 -> 39', async () => {
errorMessage.prev_count = 19;
errorMessage.violation_count = 39;
await banViolation(req, res, errorMessage);
expect(errorMessage.ban).toBeTruthy();
});
it('[2/3] should ban if violation_count crosses the interval threshold: 19 -> 20', async () => {
errorMessage.prev_count = 19;
errorMessage.violation_count = 20;
await banViolation(req, res, errorMessage);
expect(errorMessage.ban).toBeTruthy();
});
const randomValueAbove = Math.floor(20 + Math.random() * 100);
it(`[3/3] should ban if violation_count crosses the interval threshold: 19 -> ${randomValueAbove}`, async () => {
errorMessage.prev_count = 19;
errorMessage.violation_count = randomValueAbove;
await banViolation(req, res, errorMessage);
expect(errorMessage.ban).toBeTruthy();
});
it('should handle invalid BAN_INTERVAL and default to 20', async () => {
process.env.BAN_INTERVAL = 'invalid';
errorMessage.prev_count = 19;
errorMessage.violation_count = 39;
await banViolation(req, res, errorMessage);
expect(errorMessage.ban).toBeTruthy();
});
it('should ban if BAN_DURATION is invalid as default is 2 hours', async () => {
process.env.BAN_DURATION = 'invalid';
errorMessage.prev_count = 19;
errorMessage.violation_count = 39;
await banViolation(req, res, errorMessage);
expect(errorMessage.ban).toBeTruthy();
});
it('should not ban if BAN_DURATION is 0 but should clear cookies', async () => {
process.env.BAN_DURATION = '0';
errorMessage.prev_count = 19;
errorMessage.violation_count = 39;
await banViolation(req, res, errorMessage);
expect(res.clearCookie).toHaveBeenCalledWith('refreshToken');
});
it('should not ban if violation_count does not change', async () => {
errorMessage.prev_count = 0;
errorMessage.violation_count = 0;
await banViolation(req, res, errorMessage);
expect(errorMessage.ban).toBeFalsy();
});
it('[1/2] should not ban if violation_count does not cross the interval threshold: 0 -> 19', async () => {
errorMessage.prev_count = 0;
errorMessage.violation_count = 19;
await banViolation(req, res, errorMessage);
expect(errorMessage.ban).toBeFalsy();
});
const randomValueUnder = Math.floor(1 + Math.random() * 19);
it(`[2/2] should not ban if violation_count does not cross the interval threshold: 0 -> ${randomValueUnder}`, async () => {
errorMessage.prev_count = 0;
errorMessage.violation_count = randomValueUnder;
await banViolation(req, res, errorMessage);
expect(errorMessage.ban).toBeFalsy();
});
it('[EDGE CASE] should not ban if violation_count is lower', async () => {
errorMessage.prev_count = 0;
errorMessage.violation_count = -10;
await banViolation(req, res, errorMessage);
expect(errorMessage.ban).toBeFalsy();
});
});