LibreChat/api/cache/distributedLock.spec.js
Atef Bellaaj d481b6b695
🏬 feat: Sync Langfuse Prompts into the Catalog (#17095)
* feat(prompts): sync Langfuse prompts into the catalog

* refactor(prompts): compose the Langfuse prompt sync runner outside the admin handler

Add createLangfusePromptSyncRunner, which binds the lock, the prompt-group
methods, the ACL grant and the Mongo error predicates once and returns a
runner that takes the per-request source, connection-bound listPrompts,
lock lease and page cap. The admin handler receives that runner as one
dependency instead of forwarding its parts, and its config method
signatures return a LangfuseAdminConfig interface in place of Mongoose
document types.

In api, api/cache/distributedLock.js creates the one DistributedLock for
the process and api/server/services/LangfusePromptSync.js composes the
runner once with it; the admin route imports the composed runner.

* fix(prompts): report incomplete Langfuse syncs and gate the sync action

- Refresh the prompt-group and effective-permission queries after a failed
  sync too, because earlier imports can already be committed.
- Return `complete: false` when the page limit stops a sync before the last
  upstream page, and show a warning instead of the success message.
- Show the Sync prompts action only to users with `manage:prompts`, the
  capability the sync route requires. Resolve the Langfuse startup-config
  capabilities in one batched lookup.
- Document that an interrupted import can leave a group without its initial
  PUBLIC grant, and that a page in progress can outlast an expired lease.

---------

Co-authored-by: Rebase Probe <probe@example.com>
2026-10-24 13:48:52 +02:00

18 lines
699 B
JavaScript

const mockLock = { acquire: jest.fn(), extend: jest.fn(), release: jest.fn() };
const mockCreateDistributedLock = jest.fn(() => mockLock);
const sentinelRedisClient = {};
jest.mock('@librechat/api', () => ({
createDistributedLock: mockCreateDistributedLock,
ioredisClient: sentinelRedisClient,
}));
describe('distributedLock', () => {
it('builds the process-wide lock once with the shared Redis client', () => {
const { distributedLock } = require('./distributedLock');
expect(mockCreateDistributedLock).toHaveBeenCalledTimes(1);
expect(mockCreateDistributedLock).toHaveBeenCalledWith({ redisClient: sentinelRedisClient });
expect(distributedLock).toBe(mockLock);
});
});