mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-11-12 09:45:47 +00:00
* 📏 fix: Measure Usage Percent Against the Allowance Each Period Granted Store the credits granted at the start of each balance period on the record and measure the usage percent against it, so changing refillAmount or startBalance no longer moves a user's percent until their next grant. Legacy records fall back to the configured amount. * 🕰️ fix: Ignore a Stored Allotment a Later Refill Has Replaced Stamp each stored allotment with the time of its grant and measure against it only while no refill is more recent, so a refill by a replica that predates the field falls back to the configured amount instead of reporting usage against the prior period's allowance. * 🔒 fix: Write the Allotment Grant Atomically and Return Its Period Start Insert lastRefill with the credit, allotment and grant time when a balance record is created, so a racing creator never moves another's period start past its grant. Return lastRefill even without auto-refill so the client can place a stored allotment in its period, and fall back when it cannot.
97 lines
2.7 KiB
JavaScript
97 lines
2.7 KiB
JavaScript
jest.mock('~/models', () => ({
|
|
findBalanceByUser: jest.fn(),
|
|
}));
|
|
|
|
const { findBalanceByUser } = require('~/models');
|
|
const balanceController = require('./Balance');
|
|
|
|
describe('balanceController', () => {
|
|
const createResponse = () => ({
|
|
locals: {},
|
|
status: jest.fn().mockReturnThis(),
|
|
json: jest.fn().mockReturnThis(),
|
|
sendStatus: jest.fn().mockReturnThis(),
|
|
});
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
it('returns no content without reading balance when balance config is disabled', async () => {
|
|
const req = {
|
|
user: { id: 'user-1' },
|
|
};
|
|
const res = createResponse();
|
|
res.locals.balanceConfigEnabled = false;
|
|
|
|
await balanceController(req, res);
|
|
|
|
expect(findBalanceByUser).not.toHaveBeenCalled();
|
|
expect(res.sendStatus).toHaveBeenCalledWith(204);
|
|
expect(res.status).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('uses balance data attached by middleware without a second read', async () => {
|
|
const req = {
|
|
user: { id: 'user-1' },
|
|
};
|
|
const res = createResponse();
|
|
res.locals.balanceConfigEnabled = true;
|
|
res.locals.balanceData = {
|
|
_id: 'balance-1',
|
|
user: 'user-1',
|
|
tokenCredits: 100,
|
|
autoRefillEnabled: false,
|
|
};
|
|
|
|
await balanceController(req, res);
|
|
|
|
expect(findBalanceByUser).not.toHaveBeenCalled();
|
|
expect(res.status).toHaveBeenCalledWith(200);
|
|
expect(res.json).toHaveBeenCalledWith({
|
|
user: 'user-1',
|
|
tokenCredits: 100,
|
|
autoRefillEnabled: false,
|
|
});
|
|
});
|
|
|
|
it('keeps the period start that validates a stored allotment without auto-refill', async () => {
|
|
const lastRefill = new Date('2026-07-01T00:00:00.000Z');
|
|
const res = createResponse();
|
|
res.locals.balanceConfigEnabled = true;
|
|
res.locals.balanceData = {
|
|
_id: 'balance-1',
|
|
tokenCredits: 100,
|
|
autoRefillEnabled: false,
|
|
refillAmount: 500,
|
|
lastRefill,
|
|
allotment: 1000,
|
|
allottedAt: lastRefill,
|
|
};
|
|
|
|
await balanceController({ user: { id: 'user-1' } }, res);
|
|
|
|
expect(res.json).toHaveBeenCalledWith({
|
|
tokenCredits: 100,
|
|
autoRefillEnabled: false,
|
|
lastRefill,
|
|
allotment: 1000,
|
|
allottedAt: lastRefill,
|
|
});
|
|
});
|
|
|
|
it('returns not found when balance is enabled and no record exists', async () => {
|
|
findBalanceByUser.mockResolvedValue(null);
|
|
const req = {
|
|
user: { id: 'user-1' },
|
|
};
|
|
const res = createResponse();
|
|
res.locals.balanceConfigEnabled = true;
|
|
|
|
await balanceController(req, res);
|
|
|
|
expect(findBalanceByUser).toHaveBeenCalledWith('user-1');
|
|
expect(res.status).toHaveBeenCalledWith(404);
|
|
expect(res.json).toHaveBeenCalledWith({ error: 'Balance not found' });
|
|
});
|
|
});
|