feat(gateway): expand sessions surface with operator metadata and paging hardening

This commit is contained in:
William Valentin
2026-02-17 16:14:06 -08:00
parent 47187aa878
commit 9c9ab92e9d
7 changed files with 309 additions and 25 deletions
+46 -2
View File
@@ -465,6 +465,7 @@ describe('session handlers', () => {
const mockSessionManager = {
listSessions: vi.fn(() => ['ws:test']),
getSession: vi.fn(() => mockSession),
getSessionConfig: vi.fn((_frontend: string, _userId: string, _key: string) => undefined),
transferSession: vi.fn(),
closeSession: vi.fn(),
};
@@ -480,15 +481,49 @@ describe('session handlers', () => {
mockSession.getHistory.mockReturnValue(mockHistory);
});
it('sessions.list returns session list with message counts', async () => {
it('sessions.list returns session list with message counts and metadata', async () => {
const req: GatewayRequest = { id: 1, method: 'sessions.list' };
const result = await handlers['sessions.list'](req) as GatewayResponse;
expect(result.id).toBe(1);
const r = result.result as { sessions: Array<{ id: string; messageCount: number }> };
const r = result.result as { sessions: Array<{ id: string; messageCount: number; frontend: string; userId: string }>; total: number };
expect(r.sessions).toHaveLength(1);
expect(r.sessions[0].id).toBe('ws:test');
expect(r.sessions[0].frontend).toBe('ws');
expect(r.sessions[0].userId).toBe('test');
expect(r.sessions[0].messageCount).toBe(2);
expect(r.total).toBe(1);
});
it('sessions.list supports persisted inclusion, frontend filter, and paging', async () => {
mockSessionManager.listSessions.mockReturnValue(['ws:a', 'ws:b', 'telegram:c']);
const req: GatewayRequest = {
id: 10,
method: 'sessions.list',
params: { includePersisted: true, frontend: 'ws', limit: 1, offset: 1 },
};
const result = await handlers['sessions.list'](req) as GatewayResponse;
const payload = result.result as { sessions: Array<{ id: string }>; total: number };
expect(mockSessionManager.listSessions).toHaveBeenCalledWith({ includePersisted: true, frontend: 'ws' });
expect(payload.total).toBe(3);
expect(payload.sessions).toHaveLength(1);
expect(payload.sessions[0].id).toBe('ws:b');
});
it('sessions.list rejects invalid pagination and filters', async () => {
const badLimit = await handlers['sessions.list']({
id: 11,
method: 'sessions.list',
params: { limit: -1 },
}) as GatewayError;
expect(badLimit.error.code).toBe(ErrorCode.InvalidRequest);
const badFrontend = await handlers['sessions.list']({
id: 12,
method: 'sessions.list',
params: { frontend: '' },
}) as GatewayError;
expect(badFrontend.error.code).toBe(ErrorCode.InvalidRequest);
});
it('sessions.history returns messages with pagination', async () => {
@@ -507,6 +542,15 @@ describe('session handlers', () => {
expect(result.error.code).toBe(ErrorCode.InvalidRequest);
});
it('sessions.history rejects invalid pagination values', async () => {
const badOffset = await handlers['sessions.history']({
id: 13,
method: 'sessions.history',
params: { sessionId: 'ws:test', offset: -1 },
}) as GatewayError;
expect(badOffset.error.code).toBe(ErrorCode.InvalidRequest);
});
it('sessions.create creates a new session', async () => {
const req: GatewayRequest = { id: 4, method: 'sessions.create', params: { sessionId: 'ws:new' } };
const result = await handlers['sessions.create'](req) as GatewayResponse;