fix: injectable tunnel log label, honest truncate budget comment

This commit is contained in:
admin committed 2026-06-16 20:38:10 +05:00
1 parent 6a2706130a
commit 703df24b13
4 files changed
+42 -6

No files matched your search

+4 -1
View File
@@ -32,7 +32,10 @@ export const sliceUtf8 = (s: string, maxBytes: number): string => {
} }
// Serialize `value` to JSON and, if it exceeds `maxBytes` (UTF-8), return a // Serialize `value` to JSON and, if it exceeds `maxBytes` (UTF-8), return a
// truncated marker object whose own serialization stays within `maxBytes`. // truncated marker object. A fixed 256-byte margin is reserved for the marker
// metadata, so the marker's own serialization stays within `maxBytes` for any
// budget at or above a few hundred bytes (esmole uses 100 KB). For
// pathologically tiny budgets the marker may exceed `maxBytes`.
export const truncateJsonBudget = ( export const truncateJsonBudget = (
value: unknown, value: unknown,
maxBytes: number maxBytes: number
+4 -3
View File
@@ -50,7 +50,8 @@ export const buildSshAuth = (
export const openTunnel = async ( export const openTunnel = async (
ssh: SshConfig, ssh: SshConfig,
dstHost: string, dstHost: string,
dstPort: number dstPort: number,
logLabel: string = 'ssh-tunnel'
): Promise<Tunnel> => { ): Promise<Tunnel> => {
const client = new Client() const client = new Client()
@@ -67,7 +68,7 @@ export const openTunnel = async (
// After 'ready' resolved, later ssh errors must not crash the process. // After 'ready' resolved, later ssh errors must not crash the process.
client.on('error', (error) => { client.on('error', (error) => {
console.error('ssh-tunnel: ssh tunnel error: ' + error.message) console.error(logLabel + ': ssh tunnel error: ' + error.message)
}) })
let closed = false let closed = false
@@ -140,7 +141,7 @@ export const openTunnel = async (
// without a persistent one, a later listener error is swallowed and the // without a persistent one, a later listener error is swallowed and the
// tunnel silently serves a dead socket. Mark it closed instead. // tunnel silently serves a dead socket. Mark it closed instead.
server.on('error', (error) => { server.on('error', (error) => {
console.error('ssh-tunnel: tunnel listener error: ' + error.message) console.error(logLabel + ': tunnel listener error: ' + error.message)
markClosed() markClosed()
}) })
@@ -248,11 +248,34 @@ describe('openTunnel', () => {
created.push(server) created.push(server)
return server return server
}) })
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const tunnel = await openTunnel(sshConfig({ password: 'pw' }), 'db-host', 5432) const tunnel = await openTunnel(sshConfig({ password: 'pw' }), 'db-host', 5432)
// A listener error after listen() resolved must not be swallowed: it // A listener error after listen() resolved must not be swallowed: it
// marks the tunnel closed so the manager can rebuild it. // marks the tunnel closed so the manager can rebuild it.
created[0].emit('error', new Error('EADDRNOTAVAIL')) created[0].emit('error', new Error('EADDRNOTAVAIL'))
expect(tunnel.isClosed()).toBe(true) expect(tunnel.isClosed()).toBe(true)
// Default log label brands the diagnostic with 'ssh-tunnel:'.
expect(errSpy).toHaveBeenCalledWith('ssh-tunnel: tunnel listener error: EADDRNOTAVAIL')
errSpy.mockRestore()
spy.mockRestore()
await tunnel.close()
})
it('uses a custom logLabel prefix on diagnostics', async () => {
const created: net.Server[] = []
const realCreateServer = net.createServer.bind(net)
const spy = vi.spyOn(net, 'createServer').mockImplementation((handler) => {
const server = realCreateServer(handler as (socket: net.Socket) => void)
created.push(server)
return server
})
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const tunnel = await openTunnel(sshConfig({ password: 'pw' }), 'db-host', 5432, 'dbmole')
created[0].emit('error', new Error('EADDRNOTAVAIL'))
expect(tunnel.isClosed()).toBe(true)
// The injected label replaces the default prefix token.
expect(errSpy).toHaveBeenCalledWith('dbmole: tunnel listener error: EADDRNOTAVAIL')
errSpy.mockRestore()
spy.mockRestore() spy.mockRestore()
await tunnel.close() await tunnel.close()
}) })
+11 -2
View File
@@ -1,4 +1,9 @@
import { createManager as coreCreate, type ManagedConnection, type Manager } from '@dbmole/core' import {
createManager as coreCreate,
type ManagedConnection,
type Manager,
openTunnel
} from '@dbmole/core'
import type { DbRegistry } from '../config/registry.js' import type { DbRegistry } from '../config/registry.js'
import type { ConnectionConfig } from '../schema.js' import type { ConnectionConfig } from '../schema.js'
import { createBackend, resolvePort } from '../sqlBackend.js' import { createBackend, resolvePort } from '../sqlBackend.js'
@@ -8,4 +13,8 @@ export type DbManager = Manager<ConnectionConfig, Driver>
export type DbManaged = ManagedConnection<ConnectionConfig, Driver> export type DbManaged = ManagedConnection<ConnectionConfig, Driver>
export const createManager = (registry: DbRegistry): DbManager => export const createManager = (registry: DbRegistry): DbManager =>
coreCreate<ConnectionConfig, Driver>(registry, { createBackend, resolvePort }) coreCreate<ConnectionConfig, Driver>(registry, {
createBackend,
resolvePort,
createTunnel: (ssh, host, port) => openTunnel(ssh, host, port, 'dbmole')
})