test: ssh tunnel e2e via sshd container
This commit is contained in:
@@ -0,0 +1,90 @@
|
|||||||
|
import { mkdtempSync, rmSync } from 'node:fs'
|
||||||
|
import { tmpdir } from 'node:os'
|
||||||
|
import { join } from 'node:path'
|
||||||
|
import { PostgreSqlContainer, type StartedPostgreSqlContainer } from '@testcontainers/postgresql'
|
||||||
|
import {
|
||||||
|
GenericContainer,
|
||||||
|
Network,
|
||||||
|
type StartedNetwork,
|
||||||
|
type StartedTestContainer,
|
||||||
|
Wait
|
||||||
|
} from 'testcontainers'
|
||||||
|
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
||||||
|
import { createRegistry } from '../../src/config/registry.js'
|
||||||
|
import type { Manager } from '../../src/db/manager.js'
|
||||||
|
import { createManager } from '../../src/db/manager.js'
|
||||||
|
|
||||||
|
describe('ssh tunnel integration', () => {
|
||||||
|
let network: StartedNetwork
|
||||||
|
let postgres: StartedPostgreSqlContainer
|
||||||
|
let sshd: StartedTestContainer
|
||||||
|
let dir: string
|
||||||
|
let manager: Manager
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
network = await new Network().start()
|
||||||
|
|
||||||
|
postgres = await new PostgreSqlContainer('postgres:17-alpine')
|
||||||
|
.withNetwork(network)
|
||||||
|
.withNetworkAliases('db')
|
||||||
|
.start()
|
||||||
|
|
||||||
|
sshd = await new GenericContainer('lscr.io/linuxserver/openssh-server:latest')
|
||||||
|
.withNetwork(network)
|
||||||
|
.withEnvironment({
|
||||||
|
PUID: '1000',
|
||||||
|
PGID: '1000',
|
||||||
|
USER_NAME: 'tunnel',
|
||||||
|
USER_PASSWORD: 'tunnelpass',
|
||||||
|
PASSWORD_ACCESS: 'true',
|
||||||
|
DOCKER_MODS: 'linuxserver/mods:openssh-server-ssh-tunnel'
|
||||||
|
})
|
||||||
|
.withExposedPorts(2222)
|
||||||
|
// The image logs 'sshd is listening on port 2222' from its FIRST
|
||||||
|
// sshd start, BEFORE the init scripts enable password auth + TCP
|
||||||
|
// forwarding and restart sshd. Connecting in that window yields
|
||||||
|
// ECONNRESET before the SSH banner. '[ls.io-init] done.' is printed
|
||||||
|
// only after the full init (and restart) completes, so wait on that.
|
||||||
|
.withWaitStrategy(Wait.forLogMessage(/\[ls\.io-init\] done\./))
|
||||||
|
.start()
|
||||||
|
|
||||||
|
dir = mkdtempSync(join(tmpdir(), 'dbmole-int-ssh-'))
|
||||||
|
const registry = createRegistry({ storePath: join(dir, 'connections.json'), env: {} })
|
||||||
|
manager = createManager(registry)
|
||||||
|
registry.add({
|
||||||
|
name: 'pg-tunneled',
|
||||||
|
type: 'postgres',
|
||||||
|
host: 'db',
|
||||||
|
port: 5432,
|
||||||
|
user: postgres.getUsername(),
|
||||||
|
password: postgres.getPassword(),
|
||||||
|
database: postgres.getDatabase(),
|
||||||
|
readonly: false,
|
||||||
|
ssh: {
|
||||||
|
host: sshd.getHost(),
|
||||||
|
port: sshd.getMappedPort(2222),
|
||||||
|
user: 'tunnel',
|
||||||
|
password: 'tunnelpass'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await manager.disposeAll()
|
||||||
|
await sshd.stop()
|
||||||
|
await postgres.stop()
|
||||||
|
await network.stop()
|
||||||
|
rmSync(dir, { recursive: true, force: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('queries postgres through the ssh tunnel', async () => {
|
||||||
|
const { driver } = await manager.get('pg-tunneled')
|
||||||
|
const result = await driver.query({ sql: 'SELECT 1 + 1', rowLimit: 10 })
|
||||||
|
expect(result.rows).toEqual([[2]])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('reports server version through the tunnel', async () => {
|
||||||
|
const { driver } = await manager.get('pg-tunneled')
|
||||||
|
expect(await driver.serverVersion()).toMatch(/^17\./)
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user