first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import type { Tool } from '@langchain/core/tools';
|
||||
|
||||
import type { SessionStore } from './SessionStore';
|
||||
|
||||
export class InMemorySessionStore implements SessionStore {
|
||||
private sessions = new Set<string>();
|
||||
|
||||
private tools: Record<string, Tool[]> = {};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
async register(sessionId: string): Promise<void> {
|
||||
this.sessions.add(sessionId);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
async validate(sessionId: string): Promise<boolean> {
|
||||
return this.sessions.has(sessionId);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
async unregister(sessionId: string): Promise<void> {
|
||||
this.sessions.delete(sessionId);
|
||||
delete this.tools[sessionId];
|
||||
}
|
||||
|
||||
getTools(sessionId: string): Tool[] | undefined {
|
||||
return this.tools[sessionId];
|
||||
}
|
||||
|
||||
setTools(sessionId: string, tools: Tool[]): void {
|
||||
this.tools[sessionId] = tools;
|
||||
}
|
||||
|
||||
clearTools(sessionId: string): void {
|
||||
delete this.tools[sessionId];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { Tool } from '@langchain/core/tools';
|
||||
|
||||
import type { SessionStore } from './SessionStore';
|
||||
|
||||
export interface RedisPublisher {
|
||||
set(key: string, value: string, ttl: number): Promise<void>;
|
||||
get(key: string): Promise<string | null>;
|
||||
clear(key: string): Promise<void>;
|
||||
}
|
||||
|
||||
export class RedisSessionStore implements SessionStore {
|
||||
private tools: Record<string, Tool[]> = {};
|
||||
|
||||
constructor(
|
||||
private publisher: RedisPublisher,
|
||||
private getSessionKey: (sessionId: string) => string,
|
||||
private ttl: number,
|
||||
) {}
|
||||
|
||||
async register(sessionId: string): Promise<void> {
|
||||
await this.publisher.set(this.getSessionKey(sessionId), '1', this.ttl);
|
||||
}
|
||||
|
||||
async validate(sessionId: string): Promise<boolean> {
|
||||
const result = await this.publisher.get(this.getSessionKey(sessionId));
|
||||
return result !== null;
|
||||
}
|
||||
|
||||
async unregister(sessionId: string): Promise<void> {
|
||||
await this.publisher.clear(this.getSessionKey(sessionId));
|
||||
delete this.tools[sessionId];
|
||||
}
|
||||
|
||||
getTools(sessionId: string): Tool[] | undefined {
|
||||
return this.tools[sessionId];
|
||||
}
|
||||
|
||||
setTools(sessionId: string, tools: Tool[]): void {
|
||||
this.tools[sessionId] = tools;
|
||||
}
|
||||
|
||||
clearTools(sessionId: string): void {
|
||||
delete this.tools[sessionId];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import type { Tool } from '@langchain/core/tools';
|
||||
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||
|
||||
import type { SessionStore } from './SessionStore';
|
||||
import type { McpTransport } from '../transport/Transport';
|
||||
|
||||
export interface SessionInfo {
|
||||
sessionId: string;
|
||||
server: Server;
|
||||
transport: McpTransport;
|
||||
}
|
||||
|
||||
export class SessionManager {
|
||||
private sessions: Record<string, SessionInfo> = {};
|
||||
|
||||
constructor(private store: SessionStore) {}
|
||||
|
||||
async registerSession(
|
||||
sessionId: string,
|
||||
server: Server,
|
||||
transport: McpTransport,
|
||||
tools?: Tool[],
|
||||
): Promise<void> {
|
||||
if (!sessionId) return;
|
||||
await this.store.register(sessionId);
|
||||
this.sessions[sessionId] = { sessionId, server, transport };
|
||||
if (tools) {
|
||||
this.store.setTools(sessionId, tools);
|
||||
}
|
||||
}
|
||||
|
||||
async destroySession(sessionId: string): Promise<void> {
|
||||
await this.store.unregister(sessionId);
|
||||
delete this.sessions[sessionId];
|
||||
}
|
||||
|
||||
getSession(sessionId: string): SessionInfo | undefined {
|
||||
return this.sessions[sessionId];
|
||||
}
|
||||
|
||||
getTransport(sessionId: string): McpTransport | undefined {
|
||||
return this.sessions[sessionId]?.transport;
|
||||
}
|
||||
|
||||
getServer(sessionId: string): Server | undefined {
|
||||
return this.sessions[sessionId]?.server;
|
||||
}
|
||||
|
||||
async isSessionValid(sessionId: string): Promise<boolean> {
|
||||
return await this.store.validate(sessionId);
|
||||
}
|
||||
|
||||
getTools(sessionId: string): Tool[] | undefined {
|
||||
return this.store.getTools(sessionId);
|
||||
}
|
||||
|
||||
setTools(sessionId: string, tools: Tool[]): void {
|
||||
this.store.setTools(sessionId, tools);
|
||||
}
|
||||
|
||||
setStore(store: SessionStore): void {
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
getStore(): SessionStore {
|
||||
return this.store;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { Tool } from '@langchain/core/tools';
|
||||
|
||||
export interface SessionStore {
|
||||
register(sessionId: string): Promise<void>;
|
||||
validate(sessionId: string): Promise<boolean>;
|
||||
unregister(sessionId: string): Promise<void>;
|
||||
getTools(sessionId: string): Tool[] | undefined;
|
||||
setTools(sessionId: string, tools: Tool[]): void;
|
||||
clearTools(sessionId: string): void;
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
import { createMockTool } from '../../__tests__/helpers';
|
||||
import { InMemorySessionStore } from '../InMemorySessionStore';
|
||||
|
||||
describe('InMemorySessionStore', () => {
|
||||
let store: InMemorySessionStore;
|
||||
|
||||
beforeEach(() => {
|
||||
store = new InMemorySessionStore();
|
||||
});
|
||||
|
||||
describe('session lifecycle', () => {
|
||||
it('should register and validate a session', async () => {
|
||||
await store.register('session-1');
|
||||
expect(await store.validate('session-1')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for unregistered session', async () => {
|
||||
expect(await store.validate('non-existent')).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle registering same session twice (idempotent)', async () => {
|
||||
await store.register('session-1');
|
||||
await store.register('session-1');
|
||||
expect(await store.validate('session-1')).toBe(true);
|
||||
});
|
||||
|
||||
it('should unregister session and invalidate it', async () => {
|
||||
await store.register('session-1');
|
||||
await store.unregister('session-1');
|
||||
expect(await store.validate('session-1')).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle unregistering non-existent session gracefully', async () => {
|
||||
await expect(store.unregister('non-existent')).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
it('should handle multiple sessions independently', async () => {
|
||||
await store.register('session-1');
|
||||
await store.register('session-2');
|
||||
await store.register('session-3');
|
||||
|
||||
expect(await store.validate('session-1')).toBe(true);
|
||||
expect(await store.validate('session-2')).toBe(true);
|
||||
expect(await store.validate('session-3')).toBe(true);
|
||||
|
||||
await store.unregister('session-2');
|
||||
|
||||
expect(await store.validate('session-1')).toBe(true);
|
||||
expect(await store.validate('session-2')).toBe(false);
|
||||
expect(await store.validate('session-3')).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle empty string as session id', async () => {
|
||||
await store.register('');
|
||||
expect(await store.validate('')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('tools management', () => {
|
||||
const mockTools = [createMockTool('tool-1'), createMockTool('tool-2')];
|
||||
|
||||
it('should set and get tools for a session', () => {
|
||||
store.setTools('session-1', mockTools);
|
||||
expect(store.getTools('session-1')).toEqual(mockTools);
|
||||
});
|
||||
|
||||
it('should return undefined for session without tools', () => {
|
||||
expect(store.getTools('session-without-tools')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should clear tools for a session', () => {
|
||||
store.setTools('session-1', mockTools);
|
||||
store.clearTools('session-1');
|
||||
expect(store.getTools('session-1')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should clear tools when session is unregistered', async () => {
|
||||
await store.register('session-1');
|
||||
store.setTools('session-1', mockTools);
|
||||
await store.unregister('session-1');
|
||||
expect(store.getTools('session-1')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle clearing tools for non-existent session', () => {
|
||||
expect(() => store.clearTools('non-existent')).not.toThrow();
|
||||
});
|
||||
|
||||
it('should isolate tools between sessions', () => {
|
||||
const tools1 = [createMockTool('tool-a')];
|
||||
const tools2 = [createMockTool('tool-b')];
|
||||
store.setTools('session-1', tools1);
|
||||
store.setTools('session-2', tools2);
|
||||
expect(store.getTools('session-1')).toEqual(tools1);
|
||||
expect(store.getTools('session-2')).toEqual(tools2);
|
||||
});
|
||||
|
||||
it('should overwrite tools when set again', () => {
|
||||
const tools1 = [createMockTool('tool-a')];
|
||||
const tools2 = [createMockTool('tool-b'), createMockTool('tool-c')];
|
||||
store.setTools('session-1', tools1);
|
||||
store.setTools('session-1', tools2);
|
||||
expect(store.getTools('session-1')).toEqual(tools2);
|
||||
});
|
||||
|
||||
it('should handle setting empty tools array', () => {
|
||||
store.setTools('session-1', []);
|
||||
expect(store.getTools('session-1')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should not affect tools when clearing non-existent session', () => {
|
||||
const tools = [createMockTool('tool-a')];
|
||||
store.setTools('session-1', tools);
|
||||
store.clearTools('session-2');
|
||||
expect(store.getTools('session-1')).toEqual(tools);
|
||||
});
|
||||
});
|
||||
|
||||
describe('combined session and tools operations', () => {
|
||||
it('should allow setting tools before registering session', () => {
|
||||
const tools = [createMockTool('tool-1')];
|
||||
store.setTools('session-1', tools);
|
||||
expect(store.getTools('session-1')).toEqual(tools);
|
||||
});
|
||||
|
||||
it('should not delete tools when registering session with existing tools', async () => {
|
||||
const tools = [createMockTool('tool-1')];
|
||||
store.setTools('session-1', tools);
|
||||
await store.register('session-1');
|
||||
expect(store.getTools('session-1')).toEqual(tools);
|
||||
});
|
||||
});
|
||||
});
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
import type { Tool } from '@langchain/core/tools';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
|
||||
import { RedisSessionStore, type RedisPublisher } from '../RedisSessionStore';
|
||||
|
||||
describe('RedisSessionStore', () => {
|
||||
let store: RedisSessionStore;
|
||||
let mockPublisher: jest.Mocked<RedisPublisher>;
|
||||
const getSessionKey = (sessionId: string) => `mcp-session:${sessionId}`;
|
||||
const ttl = 3600;
|
||||
|
||||
beforeEach(() => {
|
||||
mockPublisher = {
|
||||
set: jest.fn().mockResolvedValue(undefined),
|
||||
get: jest.fn().mockResolvedValue(null),
|
||||
clear: jest.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
store = new RedisSessionStore(mockPublisher, getSessionKey, ttl);
|
||||
});
|
||||
|
||||
describe('register', () => {
|
||||
it('should store session with TTL in Redis', async () => {
|
||||
await store.register('session-123');
|
||||
|
||||
expect(mockPublisher.set).toHaveBeenCalledWith('mcp-session:session-123', '1', ttl);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validate', () => {
|
||||
it('should return true when session exists in Redis', async () => {
|
||||
mockPublisher.get.mockResolvedValue('1');
|
||||
|
||||
const result = await store.validate('session-123');
|
||||
|
||||
expect(mockPublisher.get).toHaveBeenCalledWith('mcp-session:session-123');
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when session does not exist', async () => {
|
||||
mockPublisher.get.mockResolvedValue(null);
|
||||
|
||||
const result = await store.validate('non-existent');
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unregister', () => {
|
||||
it('should clear session from Redis and remove tools', async () => {
|
||||
const mockTool = mock<Tool>();
|
||||
store.setTools('session-123', [mockTool]);
|
||||
|
||||
await store.unregister('session-123');
|
||||
|
||||
expect(mockPublisher.clear).toHaveBeenCalledWith('mcp-session:session-123');
|
||||
expect(store.getTools('session-123')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('tool management', () => {
|
||||
it('should store and retrieve tools', () => {
|
||||
const mockTool1 = mock<Tool>();
|
||||
const mockTool2 = mock<Tool>();
|
||||
|
||||
store.setTools('session-1', [mockTool1]);
|
||||
store.setTools('session-2', [mockTool2]);
|
||||
|
||||
expect(store.getTools('session-1')).toEqual([mockTool1]);
|
||||
expect(store.getTools('session-2')).toEqual([mockTool2]);
|
||||
});
|
||||
|
||||
it('should return undefined for unknown session', () => {
|
||||
expect(store.getTools('unknown')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should clear tools for a session', () => {
|
||||
const mockTool = mock<Tool>();
|
||||
store.setTools('session-123', [mockTool]);
|
||||
|
||||
store.clearTools('session-123');
|
||||
|
||||
expect(store.getTools('session-123')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('custom key function', () => {
|
||||
it('should use custom key function for Redis operations', async () => {
|
||||
const customKeyFn = (sessionId: string) => `custom:prefix:${sessionId}`;
|
||||
const customStore = new RedisSessionStore(mockPublisher, customKeyFn, ttl);
|
||||
|
||||
await customStore.register('test-id');
|
||||
|
||||
expect(mockPublisher.set).toHaveBeenCalledWith('custom:prefix:test-id', '1', ttl);
|
||||
});
|
||||
});
|
||||
|
||||
describe('custom TTL', () => {
|
||||
it('should use custom TTL for register', async () => {
|
||||
const customTtl = 86400;
|
||||
const customStore = new RedisSessionStore(mockPublisher, getSessionKey, customTtl);
|
||||
|
||||
await customStore.register('test-id');
|
||||
|
||||
expect(mockPublisher.set).toHaveBeenCalledWith(expect.any(String), '1', customTtl);
|
||||
});
|
||||
});
|
||||
});
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
import { createMockServer, createMockTransport, createMockTool } from '../../__tests__/helpers';
|
||||
import { SessionManager } from '../SessionManager';
|
||||
import type { SessionStore } from '../SessionStore';
|
||||
|
||||
describe('SessionManager', () => {
|
||||
let manager: SessionManager;
|
||||
let mockStore: jest.Mocked<SessionStore>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockStore = {
|
||||
register: jest.fn().mockResolvedValue(undefined),
|
||||
validate: jest.fn().mockResolvedValue(true),
|
||||
unregister: jest.fn().mockResolvedValue(undefined),
|
||||
getTools: jest.fn(),
|
||||
setTools: jest.fn(),
|
||||
clearTools: jest.fn(),
|
||||
};
|
||||
manager = new SessionManager(mockStore);
|
||||
});
|
||||
|
||||
describe('registerSession', () => {
|
||||
it('should register session with server and transport', async () => {
|
||||
const server = createMockServer();
|
||||
const transport = createMockTransport('session-1');
|
||||
|
||||
await manager.registerSession('session-1', server, transport);
|
||||
|
||||
expect(mockStore.register).toHaveBeenCalledWith('session-1');
|
||||
expect(manager.getSession('session-1')).toEqual({
|
||||
sessionId: 'session-1',
|
||||
server,
|
||||
transport,
|
||||
});
|
||||
});
|
||||
|
||||
it('should store tools when provided', async () => {
|
||||
const tools = [createMockTool('tool-1')];
|
||||
await manager.registerSession(
|
||||
'session-1',
|
||||
createMockServer(),
|
||||
createMockTransport('session-1'),
|
||||
tools,
|
||||
);
|
||||
|
||||
expect(mockStore.setTools).toHaveBeenCalledWith('session-1', tools);
|
||||
});
|
||||
|
||||
it('should not call setTools when no tools provided', async () => {
|
||||
await manager.registerSession(
|
||||
'session-1',
|
||||
createMockServer(),
|
||||
createMockTransport('session-1'),
|
||||
);
|
||||
|
||||
expect(mockStore.setTools).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not register if sessionId is empty', async () => {
|
||||
await manager.registerSession('', createMockServer(), createMockTransport(''));
|
||||
|
||||
expect(mockStore.register).not.toHaveBeenCalled();
|
||||
expect(manager.getSession('')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should overwrite existing session when registering same sessionId', async () => {
|
||||
const server1 = createMockServer();
|
||||
const transport1 = createMockTransport('session-1');
|
||||
const server2 = createMockServer();
|
||||
const transport2 = createMockTransport('session-1');
|
||||
|
||||
await manager.registerSession('session-1', server1, transport1);
|
||||
await manager.registerSession('session-1', server2, transport2);
|
||||
|
||||
const session = manager.getSession('session-1');
|
||||
expect(session?.server).toBe(server2);
|
||||
expect(session?.transport).toBe(transport2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('destroySession', () => {
|
||||
it('should remove session and delegate to store', async () => {
|
||||
await manager.registerSession(
|
||||
'session-1',
|
||||
createMockServer(),
|
||||
createMockTransport('session-1'),
|
||||
);
|
||||
await manager.destroySession('session-1');
|
||||
|
||||
expect(mockStore.unregister).toHaveBeenCalledWith('session-1');
|
||||
expect(manager.getSession('session-1')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle destroying non-existent session', async () => {
|
||||
await expect(manager.destroySession('non-existent')).resolves.not.toThrow();
|
||||
expect(mockStore.unregister).toHaveBeenCalledWith('non-existent');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSession', () => {
|
||||
it('should return session info for registered session', async () => {
|
||||
const server = createMockServer();
|
||||
const transport = createMockTransport('session-1');
|
||||
await manager.registerSession('session-1', server, transport);
|
||||
|
||||
const session = manager.getSession('session-1');
|
||||
|
||||
expect(session).toEqual({
|
||||
sessionId: 'session-1',
|
||||
server,
|
||||
transport,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return undefined for unregistered session', () => {
|
||||
expect(manager.getSession('non-existent')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTransport', () => {
|
||||
it('should return transport for registered session', async () => {
|
||||
const transport = createMockTransport('session-1');
|
||||
await manager.registerSession('session-1', createMockServer(), transport);
|
||||
|
||||
expect(manager.getTransport('session-1')).toBe(transport);
|
||||
});
|
||||
|
||||
it('should return undefined for unregistered session', () => {
|
||||
expect(manager.getTransport('non-existent')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getServer', () => {
|
||||
it('should return server for registered session', async () => {
|
||||
const server = createMockServer();
|
||||
await manager.registerSession('session-1', server, createMockTransport('session-1'));
|
||||
|
||||
expect(manager.getServer('session-1')).toBe(server);
|
||||
});
|
||||
|
||||
it('should return undefined for unregistered session', () => {
|
||||
expect(manager.getServer('non-existent')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isSessionValid', () => {
|
||||
it('should delegate to store.validate and return true', async () => {
|
||||
mockStore.validate.mockResolvedValue(true);
|
||||
expect(await manager.isSessionValid('session-1')).toBe(true);
|
||||
expect(mockStore.validate).toHaveBeenCalledWith('session-1');
|
||||
});
|
||||
|
||||
it('should delegate to store.validate and return false', async () => {
|
||||
mockStore.validate.mockResolvedValue(false);
|
||||
expect(await manager.isSessionValid('session-1')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('tools management', () => {
|
||||
it('should delegate getTools to store', () => {
|
||||
const tools = [createMockTool('tool-1')];
|
||||
mockStore.getTools.mockReturnValue(tools);
|
||||
|
||||
expect(manager.getTools('session-1')).toBe(tools);
|
||||
expect(mockStore.getTools).toHaveBeenCalledWith('session-1');
|
||||
});
|
||||
|
||||
it('should delegate setTools to store', () => {
|
||||
const tools = [createMockTool('tool-1')];
|
||||
manager.setTools('session-1', tools);
|
||||
|
||||
expect(mockStore.setTools).toHaveBeenCalledWith('session-1', tools);
|
||||
});
|
||||
});
|
||||
|
||||
describe('store management', () => {
|
||||
it('should allow swapping session store', () => {
|
||||
const newStore = { ...mockStore } as jest.Mocked<SessionStore>;
|
||||
manager.setStore(newStore);
|
||||
expect(manager.getStore()).toBe(newStore);
|
||||
});
|
||||
|
||||
it('should return current store', () => {
|
||||
expect(manager.getStore()).toBe(mockStore);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
export type { SessionStore } from './SessionStore';
|
||||
export * from './InMemorySessionStore';
|
||||
export * from './RedisSessionStore';
|
||||
export * from './SessionManager';
|
||||
Reference in New Issue
Block a user