DSH-Arch-Setup/plugins/dsh-touched-git/test/editor-file-bridge.test.js
Шурупов Илья Викторович cf9443753e Add one-command Arch DSH setup
Package the portable DSH profiles, local plugins, desktop integration, Firefox wrapper, and ownership-aware installer lifecycle. Bundle checksum-pinned OpenVSCode, editor extensions, translation assets, and cloudflared so unreliable upstream artifact downloads cannot break a clean installation.
2026-09-12 19:13:36 +03:00

246 lines
11 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import net from 'node:net'
import { mkdtemp, mkdir, writeFile, rm, stat, rename, symlink } from 'node:fs/promises'
import { join } from 'node:path'
import { openFileInExistingWorkspace } from '../lib/editor-file-bridge.js'
import bridgeProtocol from '../editor-file-bridge/extension.js'
const { startWorkspaceBridge, workspaceSocketPrefix, MAX_MESSAGE_BYTES } = bridgeProtocol
async function fixture(t) {
const directory = await mkdtemp('/tmp/db-')
const workspaceDirectory = join(directory, 'workspace')
const bridgeDirectory = join(directory, 's')
const filePath = join(workspaceDirectory, 'space # + ü.txt')
await mkdir(workspaceDirectory)
await mkdir(bridgeDirectory, { mode: 0o700 })
await writeFile(filePath, 'disk content')
t.after(() => rm(directory, { recursive: true, force: true }))
const shown = []
const dirtyDocument = { isDirty: true, content: 'unsaved content' }
const vscode = {
Uri: { file: filePath => ({ fsPath: filePath }) },
workspace: {
workspaceFolders: [{ uri: { fsPath: workspaceDirectory } }],
openTextDocument: async uri => {
dirtyDocument.uri = uri
return dirtyDocument
},
},
window: { showTextDocument: async (document, options) => { shown.push({ document, options }) } },
}
return { directory, workspaceDirectory, bridgeDirectory, filePath, shown, dirtyDocument, vscode }
}
async function startBridge(t, setup) {
const bridge = await startWorkspaceBridge(setup)
t.after(() => bridge.dispose())
return bridge
}
async function rawServer(t, setup, suffix, onRequest) {
const socketPath = join(setup.bridgeDirectory, `${workspaceSocketPrefix(setup.workspaceDirectory)}${suffix}.sock`)
const connections = new Set()
const server = net.createServer(socket => {
connections.add(socket)
socket.once('close', () => connections.delete(socket))
socket.on('error', () => {})
let buffer = ''
socket.on('data', chunk => {
buffer += chunk
if (buffer.includes('\n')) onRequest(socket, JSON.parse(buffer.split('\n')[0]))
})
})
await new Promise((resolve, reject) => {
server.once('error', reject)
server.listen(socketPath, resolve)
})
const close = () => new Promise(resolve => {
server.close(resolve)
for (const socket of connections) socket.end()
})
t.after(close)
return { socketPath, close }
}
function rawRequest(socketPath, message) {
return new Promise((resolve, reject) => {
const socket = net.createConnection(socketPath)
let buffer = ''
socket.once('error', reject)
socket.once('connect', () => socket.write(message))
socket.on('data', chunk => { buffer += chunk })
socket.once('end', () => resolve(JSON.parse(buffer)))
})
}
test('opens an existing dirty document without changing its content or workspace', async t => {
const setup = await fixture(t)
const bridge = await startBridge(t, setup)
assert.equal((await stat(bridge.socketPath)).mode & 0o777, 0o600)
assert.deepEqual(await openFileInExistingWorkspace(setup), { opened: true })
assert.deepEqual(await openFileInExistingWorkspace(setup), { opened: true })
assert.equal(setup.shown.length, 2)
assert.equal(setup.shown[0].document, setup.dirtyDocument)
assert.deepEqual(setup.shown[0].options, { preview: false, preserveFocus: false })
assert.equal(setup.dirtyDocument.isDirty, true)
assert.equal(setup.dirtyDocument.content, 'unsaved content')
assert.equal(setup.dirtyDocument.uri.fsPath, setup.filePath)
})
test('permits a parent-authorized file outside the opened workspace subfolder', async t => {
const setup = await fixture(t)
setup.filePath = join(setup.directory, 'outside.txt')
await writeFile(setup.filePath, 'outside')
await startBridge(t, setup)
assert.deepEqual(await openFileInExistingWorkspace(setup), { opened: true })
})
test('returns unopened for absent bridge directory or unrelated workspaces', async t => {
const setup = await fixture(t)
assert.deepEqual(await openFileInExistingWorkspace(setup), { opened: false })
assert.deepEqual(await openFileInExistingWorkspace({ ...setup, bridgeDirectory: join(setup.directory, 'missing') }), { opened: false })
await startBridge(t, setup)
const otherWorkspace = join(setup.directory, 'other')
await mkdir(otherWorkspace)
assert.deepEqual(await openFileInExistingWorkspace({ ...setup, workspaceDirectory: otherWorkspace }), { opened: false })
})
test('canonicalizes workspace aliases and removes its socket on repeated disposal', async t => {
const setup = await fixture(t)
const bridge = await startBridge(t, setup)
const alias = join(setup.directory, 'alias')
await symlink(setup.workspaceDirectory, alias)
assert.deepEqual(await openFileInExistingWorkspace({ ...setup, workspaceDirectory: alias }), { opened: true })
await bridge.dispose()
await bridge.dispose()
await assert.rejects(stat(bridge.socketPath), { code: 'ENOENT' })
assert.deepEqual(await openFileInExistingWorkspace(setup), { opened: false })
})
test('skips stale sockets and selects a deterministic live instance', async t => {
const setup = await fixture(t)
const stale = await rawServer(t, setup, '000', () => {})
const stalePath = join(setup.bridgeDirectory, `${workspaceSocketPrefix(setup.workspaceDirectory)}001.sock`)
await rename(stale.socketPath, stalePath)
await stale.close()
const requests = []
await rawServer(t, setup, '100', (socket, request) => {
requests.push('first')
socket.end(`${JSON.stringify({ requestId: request.requestId, ok: true })}\n`)
})
await rawServer(t, setup, '200', (socket, request) => {
requests.push('second')
socket.end(`${JSON.stringify({ requestId: request.requestId, ok: true })}\n`)
})
assert.deepEqual(await openFileInExistingWorkspace(setup), { opened: true })
assert.deepEqual(requests, ['first'])
})
test('returns unopened when only stale sockets remain', async t => {
const setup = await fixture(t)
const stale = await rawServer(t, setup, '000', () => {})
await rename(stale.socketPath, join(setup.bridgeDirectory, `${workspaceSocketPrefix(setup.workspaceDirectory)}001.sock`))
await stale.close()
assert.deepEqual(await openFileInExistingWorkspace(setup), { opened: false })
})
test('live document failures throw and never fall through to another instance', async t => {
const setup = await fixture(t)
let secondCalled = false
await rawServer(t, setup, '100', (socket, request) => socket.end(`${JSON.stringify({ requestId: request.requestId, ok: false, error: 'Cannot open document' })}\n`))
await rawServer(t, setup, '200', () => { secondCalled = true })
await assert.rejects(openFileInExistingWorkspace(setup), /Cannot open document/)
assert.equal(secondCalled, false)
})
test('rejects invalid acknowledgements and disconnected live editors', async t => {
for (const response of ['invalid\n', '{"requestId":"wrong","ok":true}\n', '']) {
await t.test(JSON.stringify(response), async t => {
const setup = await fixture(t)
await rawServer(t, setup, '100', socket => socket.end(response))
await assert.rejects(openFileInExistingWorkspace(setup), /acknowledg|disconnected/)
})
}
})
test('cancellation after a live connection throws rather than allowing fallback', async t => {
const setup = await fixture(t)
const controller = new AbortController()
await rawServer(t, setup, '100', () => controller.abort(new Error('Cancelled by test')))
await assert.rejects(openFileInExistingWorkspace({ ...setup, signal: controller.signal }), /Cancelled by test/)
})
test('rejects workspace mismatch, changed workspace, relative paths and non-files', async t => {
const setup = await fixture(t)
const bridge = await startBridge(t, setup)
const request = { kind: 'open-file', workspaceDirectory: setup.workspaceDirectory, filePath: setup.filePath, requestId: 'test' }
for (const changes of [{ workspaceDirectory: setup.directory }, { filePath: 'relative.txt' }, { filePath: setup.workspaceDirectory }, { filePath: join(setup.directory, 'missing') }]) {
const response = await rawRequest(bridge.socketPath, `${JSON.stringify({ ...request, ...changes })}\n`)
assert.equal(response.ok, false)
}
setup.vscode.workspace.workspaceFolders = []
await assert.rejects(openFileInExistingWorkspace(setup), /workspace has changed/)
assert.equal(setup.shown.length, 0)
})
test('bounds incoming messages and rejects malformed JSON', async t => {
const setup = await fixture(t)
const bridge = await startBridge(t, setup)
assert.equal((await rawRequest(bridge.socketPath, 'not-json\n')).ok, false)
assert.equal((await rawRequest(bridge.socketPath, `${'x'.repeat(MAX_MESSAGE_BYTES + 1)}\n`)).ok, false)
assert.equal(setup.shown.length, 0)
})
test('rejects insecure bridge directory and overly long socket paths', async t => {
const setup = await fixture(t)
const insecureDirectory = join(setup.directory, 'insecure')
await mkdir(insecureDirectory, { mode: 0o755 })
await assert.rejects(startWorkspaceBridge({ ...setup, bridgeDirectory: insecureDirectory }), /mode 0700/)
const longDirectory = join(setup.directory, 'x'.repeat(80))
await mkdir(longDirectory, { mode: 0o700 })
await assert.rejects(startWorkspaceBridge({ ...setup, bridgeDirectory: longDirectory }), /too long/)
})
test('propagates public VS Code document opening failures', async t => {
const setup = await fixture(t)
setup.vscode.workspace.openTextDocument = async () => { throw new Error('Document provider rejected file') }
await startBridge(t, setup)
await assert.rejects(openFileInExistingWorkspace(setup), /Document provider rejected file/)
assert.equal(setup.shown.length, 0)
})
test('bounds acknowledgements from live clients', async t => {
const setup = await fixture(t)
await rawServer(t, setup, '100', socket => socket.end('x'.repeat(MAX_MESSAGE_BYTES + 1)))
await assert.rejects(openFileInExistingWorkspace(setup), /acknowledgement is too large/)
})
test('accepts fragmented UTF-8 messages and processes only one request per connection', async t => {
const setup = await fixture(t)
const bridge = await startBridge(t, setup)
const request = Buffer.from(`${JSON.stringify({ kind: 'open-file', workspaceDirectory: setup.workspaceDirectory, filePath: setup.filePath, requestId: 'fragmented' })}\n`)
const response = await new Promise((resolve, reject) => {
const socket = net.createConnection(bridge.socketPath)
let buffered = ''
socket.once('error', reject)
socket.once('connect', () => {
const unicodeOffset = request.indexOf(Buffer.from('ü'))
socket.write(request.subarray(0, unicodeOffset + 1), () => {
socket.write(Buffer.concat([request.subarray(unicodeOffset + 1), request]))
})
})
socket.on('data', chunk => { buffered += chunk })
socket.once('end', () => resolve(JSON.parse(buffered)))
})
assert.deepEqual(response, { requestId: 'fragmented', ok: true })
assert.equal(setup.shown.length, 1)
})
test('does not create a bridge for an empty workspace or missing configuration', async t => {
const setup = await fixture(t)
assert.equal(await startWorkspaceBridge({ ...setup, bridgeDirectory: undefined }), null)
setup.vscode.workspace.workspaceFolders = []
assert.equal(await startWorkspaceBridge(setup), null)
})