refactor: generic withManaged and connection tools in core

This commit is contained in:
admin committed 2026-06-16 20:28:31 +05:00
1 parent a1869b60bf
commit 6a2706130a
7 files changed
+657 -135

No files matched your search

+124
View File
@@ -0,0 +1,124 @@
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'
import { describe, expect, it, vi } from 'vitest'
import { type Backend, BackendDisposedError } from '../../../src/backend.js'
import type { ManagedConnection, Manager } from '../../../src/manager.js'
import { withManaged } from '../../../src/mcp/managed.js'
type TestConfig = { name: string; type: string }
type TestBackend = Backend & { ping: () => Promise<string> }
type TestManaged = ManagedConnection<TestConfig, TestBackend>
const config: TestConfig = { name: 'c', type: 'postgres' }
const managed = (backend: Partial<TestBackend> = {}): TestManaged => ({
backend: { dispose: vi.fn(async () => {}), ping: vi.fn(async () => 'ok'), ...backend },
config,
source: 'store'
})
type FakeManager = Manager<TestConfig, TestBackend> & {
get: ReturnType<typeof vi.fn>
invalidate: ReturnType<typeof vi.fn>
disposeAll: ReturnType<typeof vi.fn>
}
const fakeManager = (): FakeManager => ({
get: vi.fn(),
invalidate: vi.fn(async () => {}),
disposeAll: vi.fn(async () => {})
})
const formatError = (cfg: TestConfig, error: unknown): string =>
'[' + cfg.type + '] ' + (error instanceof Error ? error.message : String(error))
const text = (result: CallToolResult): string => {
const first = result.content[0]
return first && first.type === 'text' ? first.text : ''
}
describe('withManaged', () => {
it('returns ok with the fn result on success', async () => {
const manager = fakeManager()
manager.get.mockResolvedValue(managed())
const fn = vi.fn(async () => ({ value: 42 }))
const result = await withManaged(manager, 'c', fn, { formatError })
expect(result.isError).toBeFalsy()
expect(JSON.parse(text(result))).toEqual({ value: 42 })
expect(manager.get).toHaveBeenCalledTimes(1)
})
it('fails when manager.get throws, without invoking fn', async () => {
const manager = fakeManager()
manager.get.mockRejectedValue(new Error('no such connection'))
const fn = vi.fn()
const result = await withManaged(manager, 'c', fn, { formatError })
expect(result.isError).toBe(true)
expect(text(result)).toBe('no such connection')
expect(fn).not.toHaveBeenCalled()
})
it('retries once when the backend was disposed under the caller', async () => {
const manager = fakeManager()
manager.get.mockResolvedValue(managed())
const fn = vi
.fn()
.mockRejectedValueOnce(new BackendDisposedError())
.mockResolvedValueOnce({ value: 7 })
const result = await withManaged(manager, 'c', fn, { formatError })
expect(result.isError).toBeFalsy()
expect(JSON.parse(text(result))).toEqual({ value: 7 })
expect(manager.get).toHaveBeenCalledTimes(2)
expect(fn).toHaveBeenCalledTimes(2)
})
it('gives up with a reconfiguring message after a second stale error', async () => {
const manager = fakeManager()
manager.get.mockResolvedValue(managed())
const fn = vi.fn().mockRejectedValue(new BackendDisposedError())
const result = await withManaged(manager, 'c', fn, { formatError })
expect(result.isError).toBe(true)
expect(text(result)).toContain('being reconfigured')
expect(manager.get).toHaveBeenCalledTimes(2)
expect(fn).toHaveBeenCalledTimes(2)
})
it('formats non-stale errors via formatError, no retry', async () => {
const manager = fakeManager()
manager.get.mockResolvedValue(managed())
const fn = vi.fn().mockRejectedValue(new Error('boom'))
const result = await withManaged(manager, 'c', fn, { formatError })
expect(result.isError).toBe(true)
expect(text(result)).toBe('[postgres] boom')
expect(manager.get).toHaveBeenCalledTimes(1)
expect(fn).toHaveBeenCalledTimes(1)
})
it('honors a custom isStaleError predicate', async () => {
const manager = fakeManager()
manager.get.mockResolvedValue(managed())
const fn = vi
.fn()
.mockRejectedValueOnce(Object.assign(new Error('stale'), { code: 'STALE' }))
.mockResolvedValueOnce({ value: 1 })
const result = await withManaged(manager, 'c', fn, {
formatError,
isStaleError: (error) =>
error instanceof Error && (error as { code?: string }).code === 'STALE'
})
expect(result.isError).toBeFalsy()
expect(JSON.parse(text(result))).toEqual({ value: 1 })
expect(fn).toHaveBeenCalledTimes(2)
})
})