Files
lms-fe/scripts/test-remote-shell.cjs
chyoo d44180db67 feat: add Korean-English localization and light theme
Localize studio, curriculum, visualization and loading interfaces. Add persistent language and theme settings, refine module loading states, and normalize remote shell locales.
2026-09-29 13:27:11 +09:00

86 lines
4.7 KiB
JavaScript

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/)
})