import assert from 'node:assert/strict'; import net from 'node:net'; import test from 'node:test'; import { assertGatePortAvailable, buildContainerPgConnectionString, buildPgConnectionString, makeIsolatedDatabaseName, sanitizeIsolatedGatePortalEnv, } from './local-stack.mjs'; test('isolated database names are bounded and identifier-safe', () => { const name = makeIsolatedDatabaseName('Gate 2026-07-26 / ABC', 'memind_gate'); assert.equal(name, 'memind_gate_gate_2026_07_26_abc'); assert.match(name, /^[a-z][a-z0-9_]+$/); assert.ok(name.length <= 63); }); test('PostgreSQL Unix socket DSN is preserved while database is isolated', () => { const dsn = buildPgConnectionString( 'postgresql://gate:secret@localhost/base?host=%2Ftmp&port=5433', 'mindspace_gate_abc', ); const parsed = new URL(dsn); assert.equal(parsed.pathname, '/mindspace_gate_abc'); assert.equal(parsed.searchParams.get('host'), '/tmp'); assert.equal(parsed.searchParams.get('port'), '5433'); }); test('container PostgreSQL DSN keeps credentials and host while isolating database', () => { const dsn = buildContainerPgConnectionString( 'postgresql://john:secret@host.docker.internal:5433/mindspace_userdata_dev', 'mindspace_gate_abc', ); const parsed = new URL(dsn); assert.equal(parsed.hostname, 'host.docker.internal'); assert.equal(parsed.port, '5433'); assert.equal(parsed.username, 'john'); assert.equal(parsed.password, 'secret'); assert.equal(parsed.pathname, '/mindspace_gate_abc'); }); test('isolated database names reject empty or unsafe prefixes', () => { assert.throws(() => makeIsolatedDatabaseName('abc', '../bad'), /Unsafe/); }); test('sanitizeIsolatedGatePortalEnv drops split-service MCP routing', () => { const sanitized = sanitizeIsolatedGatePortalEnv({ MINDSPACE_SERVER_ADAPTER: 'remote', MINDSPACE_REMOTE_BASE_URL: 'http://127.0.0.1:8082', MINDSPACE_REMOTE_AUTH_TOKEN: 'local-dev-secret', MINDSPACE_MCP_BASE_URL: 'http://127.0.0.1:8082', MINDSPACE_MCP_TOKEN_SECRET: 'local-dev-secret', MINDSPACE_AGENT_API_BASE_URL: 'http://127.0.0.1:8081/api', }, { port: 19087, runtimeProfile: 'local' }); assert.equal(sanitized.MINDSPACE_SERVER_ADAPTER, 'local'); assert.equal(sanitized.MEMIND_RUNTIME_PROFILE, 'local'); assert.equal(sanitized.H5_PORTAL_BASE_URL, 'http://127.0.0.1:19087'); assert.equal(sanitized.MINDSPACE_AGENT_API_BASE_URL, 'http://127.0.0.1:19087/api'); assert.equal(sanitized.MINDSPACE_REMOTE_BASE_URL, undefined); assert.equal(sanitized.MINDSPACE_MCP_BASE_URL, undefined); assert.equal(sanitized.MINDSPACE_MCP_TOKEN_SECRET, undefined); }); test('assertGatePortAvailable rejects occupied loopback ports', async () => { const server = net.createServer(); await new Promise((resolve, reject) => { server.once('error', reject); server.listen(0, '127.0.0.1', resolve); }); const address = server.address(); assert.ok(address && typeof address === 'object'); await assert.rejects( () => assertGatePortAvailable(address.port), /already in use/, ); await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); await assertGatePortAvailable(address.port); });