110 lines
6.4 KiB
JavaScript
110 lines
6.4 KiB
JavaScript
const { test } = require('node:test')
|
|
const assert = require('node:assert/strict')
|
|
const fs = require('node:fs/promises')
|
|
const path = require('node:path')
|
|
const os = require('node:os')
|
|
const vm = require('node:vm')
|
|
const ts = require('typescript')
|
|
const { execFile } = require('node:child_process')
|
|
const { promisify } = require('node:util')
|
|
const exec = promisify(execFile)
|
|
const root = path.resolve(__dirname, '..')
|
|
|
|
async function loadHelpers() {
|
|
const exports = {}
|
|
const code = ts.transpileModule(await fs.readFile(path.join(root, 'src/main/gitModuleImport.ts'), 'utf8'), { compilerOptions: { module: ts.ModuleKind.CommonJS } }).outputText
|
|
vm.runInNewContext(code, { exports, require, URL })
|
|
return exports
|
|
}
|
|
|
|
test('repository addresses strip credentials and reject local/helper protocols', async () => {
|
|
const api = await loadHelpers()
|
|
assert.equal(api.repositoryAddress('https://user:secret@example.com/team/module.git?token=secret'), 'https://example.com/team/module.git')
|
|
assert.equal(api.repositoryAddress('git@example.com:team/module.git'), 'ssh://git@example.com/team/module.git')
|
|
for (const url of ['file:///tmp/repo', 'ext::command', '/tmp/repo']) assert.throws(() => api.repositoryAddress(url))
|
|
assert.equal(api.remoteBranches('a'.repeat(40) + '\trefs/heads/main\ninvalid')[0].name, 'main')
|
|
})
|
|
|
|
test('local module discovery ignores malformed folders and rejects symlink escapes', async () => {
|
|
const api = await loadHelpers()
|
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'lms-git-list-'))
|
|
try {
|
|
const workspace = path.join(dir, 'workspace')
|
|
const folder = '[lms-nav2-custom]demo_v1.0.0_ws'
|
|
await fs.mkdir(path.join(workspace, folder), { recursive: true })
|
|
await fs.writeFile(path.join(workspace, folder, '.autonomy-studio-module.json'), JSON.stringify({ sourceType: 'CUSTOM', name: 'Demo', category: 'Control', packageName: 'demo', launchFile: 'demo.launch.py' }))
|
|
assert.equal((await api.listLocalGitModules(workspace))[0].name, 'Demo')
|
|
await fs.symlink(dir, path.join(workspace, '[lms-nav2-custom]escape_v1.0.0_ws'))
|
|
await assert.rejects(api.localGitModulePath(workspace, '[lms-nav2-custom]escape_v1.0.0_ws'), /outside/)
|
|
await assert.rejects(api.localGitModulePath(workspace, '../escape'), /Invalid/)
|
|
assert.equal((await api.listLocalGitModules(path.join(dir, 'missing'))).length, 0)
|
|
} finally { await fs.rm(dir, { recursive: true, force: true }) }
|
|
})
|
|
|
|
test('Git clone imports only the selected commit, refuses overwrite and cleans failed staging', async () => {
|
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'lms-git-import-'))
|
|
const repo = path.join(dir, 'source')
|
|
const workspace = path.join(dir, 'remote')
|
|
const folderName = '[lms-nav2-custom]demo_v1.0.0_ws'
|
|
const git = (...args) => exec('git', args, { cwd: repo })
|
|
try {
|
|
await fs.mkdir(path.join(repo, 'src/demo/launch'), { recursive: true })
|
|
await fs.writeFile(path.join(repo, 'src/demo/package.xml'), '<package><name>demo</name></package>')
|
|
await fs.writeFile(path.join(repo, 'src/demo/launch/demo.launch.py'), '# launch')
|
|
for (const file of ['build.sh', 'run.sh']) await fs.writeFile(path.join(repo, file), '#!/bin/bash\nexit 0\n')
|
|
await git('init', '-b', 'main')
|
|
await git('add', '.')
|
|
await git('-c', 'user.name=Test', '-c', 'user.email=test@example.com', 'commit', '-m', 'initial')
|
|
const commit = (await git('rev-parse', 'HEAD')).stdout.trim()
|
|
await fs.writeFile(path.join(repo, 'uncommitted.txt'), 'must not transfer')
|
|
let dirty = false
|
|
let pushed = true
|
|
let requestedCommit = commit
|
|
const index = await fs.readFile(path.join(root, 'src/main/index.ts'), 'utf8')
|
|
const fn = index.slice(index.indexOf('async function importRemoteGitModule('), index.indexOf('async function transferLocalWorkspaceModuleToRemote('))
|
|
const quote = value => "'" + value.replaceAll("'", "'\\''") + "'"
|
|
const context = {
|
|
URL, Buffer,
|
|
normalizeRemoteTarget: target => target,
|
|
validateCustomModuleScaffold: input => input,
|
|
inspectLocalGitModule: async () => ({ repository: 'https://example.com/module.git', dirty, pushed, branches: [{ name: 'main', commit: requestedCommit }] }),
|
|
remoteRepositoryUrl: () => 'file://' + repo,
|
|
withGitCredential: value => value,
|
|
customModuleConfigContent: input => JSON.stringify(input),
|
|
encodeBase64: value => Buffer.from(value).toString('base64'),
|
|
quoteShellSingle: quote,
|
|
expandRemotePathVar: () => [],
|
|
runSshOutput: async (_target, script) => (await exec('bash', ['-c', script], { timeout: 15000 })).stdout,
|
|
}
|
|
vm.runInNewContext(ts.transpileModule(fn, { compilerOptions: { target: ts.ScriptTarget.ES2022 } }).outputText, context)
|
|
const input = { sourceFolder: folderName, folderName, branch: 'main', commit, name: 'Remote demo', category: 'Control', packageName: 'demo', launchFile: 'demo.launch.py', dockerImage: 'vehicle:arm64' }
|
|
const run = () => context.importRemoteGitModule({ workspaceRoot: workspace }, repo, input)
|
|
dirty = true
|
|
await assert.rejects(run(), /커밋/)
|
|
dirty = false
|
|
pushed = false
|
|
await assert.rejects(run(), /푸시/)
|
|
pushed = true
|
|
assert.equal((await run()).ok, true)
|
|
const target = path.join(workspace, folderName)
|
|
assert.equal((await exec('git', ['rev-parse', 'HEAD'], { cwd: target })).stdout.trim(), commit)
|
|
await assert.rejects(fs.stat(path.join(target, 'uncommitted.txt')), { code: 'ENOENT' })
|
|
assert.equal(JSON.parse(await fs.readFile(path.join(target, '.autonomy-studio-module.json'), 'utf8')).dockerImage, 'vehicle:arm64')
|
|
await fs.writeFile(path.join(target, 'keep.txt'), 'keep')
|
|
await assert.rejects(run(), /복제에 실패/)
|
|
assert.equal(await fs.readFile(path.join(target, 'keep.txt'), 'utf8'), 'keep')
|
|
await fs.rm(target, { recursive: true })
|
|
requestedCommit = 'a'.repeat(40)
|
|
input.commit = requestedCommit
|
|
await assert.rejects(run(), /복제에 실패/)
|
|
assert.equal((await fs.readdir(workspace)).length, 0)
|
|
await fs.mkdir(path.join(repo, 'install'))
|
|
await fs.writeFile(path.join(repo, 'install/binary'), 'not portable')
|
|
await git('add', 'install')
|
|
await git('-c', 'user.name=Test', '-c', 'user.email=test@example.com', 'commit', '-m', 'bad artifacts')
|
|
input.commit = requestedCommit = (await git('rev-parse', 'HEAD')).stdout.trim()
|
|
await assert.rejects(run(), /복제에 실패/)
|
|
assert.equal((await fs.readdir(workspace)).length, 0)
|
|
} finally { await fs.rm(dir, { recursive: true, force: true }) }
|
|
})
|