const assert = require('node:assert/strict') const { test } = require('node:test') const { readFileSync, mkdtempSync, writeFileSync, rmSync } = require('node:fs') const { tmpdir } = require('node:os') const { join } = require('node:path') const { spawn } = require('node:child_process') const vm = require('node:vm') const ts = require('typescript') const transpile = (source) => ts.transpileModule(source, { compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS } }).outputText const helpers = { exports: {} } vm.runInNewContext(transpile(readFileSync(join(__dirname, '../src/main/remoteShell.ts'), 'utf8')), helpers) const { remoteShellCommand, remoteShellEnvironment } = helpers.exports const source = readFileSync(join(__dirname, '../src/main/index.ts'), 'utf8') const start = source.indexOf('async function runCommandOutput(') const end = source.indexOf('\nasync function runCommand(', start) const context = { spawn, process, setTimeout, clearTimeout, createModuleProgressParser: () => () => {} } vm.runInNewContext(transpile(source.slice(start, end)), context) const run = context.runCommandOutput test('SSH data output excludes locale warnings even when command succeeds', async () => { const result = await run('/bin/bash', ['--noprofile', '--norc', '-c', 'printf %s "/tmp/module with spaces"'], undefined, { LC_ALL: 'missing_TEST.UTF-8' }, 5000, 'stdout') assert.equal(result, '/tmp/module with spaces') }) test('failed commands retain stderr diagnostics and stdout', async () => { await assert.rejects(run('/bin/sh', ['-c', 'printf context; printf "permission denied" >&2; exit 7'], undefined, undefined, 5000, 'stdout'), error => error.message.includes('context') && error.message.includes('permission denied')) }) test('non-SSH combined output stays compatible and explicit log merging works', async () => { const command = 'printf output; printf diagnostic >&2' const combined = await run('/bin/sh', ['-c', command]) assert.match(combined, /output/) assert.match(combined, /diagnostic/) const logs = await run('/bin/sh', ['-c', `{ ${command}; } 2>&1`], undefined, undefined, 5000, 'stdout') assert.match(logs, /diagnostic/) }) test('SSH environment neutralizes locale forwarding without losing other settings', () => { const original = { LANG: 'ko_KR.UTF-8', LC_ALL: 'en_US.UTF-8', LC_CTYPE: 'bad', LANGUAGE: 'ko:en', PATH: '/bin', TERM: 'xterm-256color' } const env = remoteShellEnvironment(original) assert.equal(env.LANG, 'C') assert.equal(env.LC_ALL, 'C') assert.equal(env.LC_CTYPE, undefined) assert.equal(env.LANGUAGE, undefined) assert.equal(env.PATH, '/bin') assert.equal(env.TERM, 'xterm-256color') assert.equal(original.LC_CTYPE, 'bad') }) test('remote wrapper preserves multiline commands, Unicode, quotes and exit status', async () => { const command = `cat <<'END' /tmp/한글 module's folder END` const result = await run('/bin/sh', ['-c', remoteShellCommand(command)], undefined, remoteShellEnvironment(process.env), 5000, 'stdout') assert.equal(result, "/tmp/한글 module's folder\n") await assert.rejects(run('/bin/sh', ['-c', remoteShellCommand('echo remote-failure >&2; exit 9')], undefined, undefined, 5000, 'stdout'), /remote-failure/) }) test('locale selection prefers C UTF-8, supports other UTF-8 locales and falls back to C', async () => { const dir = mkdtempSync(join(tmpdir(), 'lms-locale-test-')) try { for (const [locales, expected] of [['en_GB.utf8 C.utf8', 'C.utf8'], ['C en_GB.utf8', 'en_GB.utf8'], ['C POSIX', 'C']]) { writeFileSync(join(dir, 'locale'), `#!/bin/sh\nprintf '%s\\n' ${locales}\n`, { mode: 0o755 }) const result = await run('/bin/sh', ['-c', remoteShellCommand('printf "%s|%s" "$LANG" "$LC_ALL"')], undefined, { PATH: `${dir}:${process.env.PATH}`, LC_ALL: 'C' }, 5000, 'stdout') assert.equal(result, `${expected}|${expected}`) } } finally { rmSync(dir, { recursive: true, force: true }) } }) test('queued SSH, stats probes and remote PTY use normalization; container logs keep stderr', () => { const ssh = source.slice(source.indexOf('async function runSshOutput('), source.indexOf('async function readRemoteDockerStatsOutput(')) assert.equal((ssh.match(/remoteShellCommand\(command\)/g) || []).length, 2) assert.equal((ssh.match(/'stdout'/g) || []).length, 2) const terminal = source.slice(source.indexOf("ipcMain.handle('remote-workspace:terminal:start'"), source.indexOf("ipcMain.handle('terminal-resize'")) assert.match(terminal, /remoteShellCommand\(mode === 'container'/) assert.match(terminal, /remoteShellEnvironment\(terminalEnvironment/) assert.match(source, /target \? remoteShellCommand\(init\)/) assert.match(source, /docker logs --timestamps --tail 300.*2>&1/) })