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.
This commit is contained in:
commit
cf9443753e
211 changed files with 69463 additions and 0 deletions
193
plugins/dsh-touched-git/test/editor-route.test.js
Normal file
193
plugins/dsh-touched-git/test/editor-route.test.js
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
import { createEditorOpenHandler } from '../lib/editor-route.js'
|
||||
|
||||
function trackedEntry(overrides = {}) {
|
||||
return {
|
||||
ok: true,
|
||||
id: 'entry-1',
|
||||
path: '/repo',
|
||||
repository: '/repo',
|
||||
workingDirectory: '/repo/work',
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
function makeRequest(overrides = {}) {
|
||||
return {
|
||||
method: 'POST',
|
||||
url: '/touched-git/editor/open?sessionId=s1&path=%2Frepo%2Fwork',
|
||||
headers: { host: '127.0.0.1:3080', 'x-dsh-editor-action': 'open' },
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
function makeResponse() {
|
||||
return {
|
||||
statusCode: 0,
|
||||
headers: {},
|
||||
body: null,
|
||||
setHeader(name, value) { this.headers[name] = value },
|
||||
}
|
||||
}
|
||||
|
||||
function writeJson(response, status, body) {
|
||||
response.statusCode = status
|
||||
response.body = body
|
||||
return body
|
||||
}
|
||||
|
||||
function buildHandler(overrides = {}) {
|
||||
const calls = { resolved: [], opened: [] }
|
||||
const deps = {
|
||||
store: { list: async () => [trackedEntry()] },
|
||||
resolveDirectoryRepository: async (directory) => {
|
||||
calls.resolved.push(directory)
|
||||
return { repository: '/repo', workingDirectory: '/repo/work' }
|
||||
},
|
||||
isLoopbackRequest: () => true,
|
||||
editor: {
|
||||
open: async (directory) => {
|
||||
calls.opened.push(directory)
|
||||
return { url: 'http://127.0.0.1:40000/?tkn=x&folder=/repo/work', directory }
|
||||
},
|
||||
},
|
||||
writeJson,
|
||||
...overrides,
|
||||
}
|
||||
return { handler: createEditorOpenHandler(deps), calls }
|
||||
}
|
||||
|
||||
test('a non-POST request is rejected and never opens an editor', async () => {
|
||||
const { handler, calls } = buildHandler()
|
||||
const response = makeResponse()
|
||||
|
||||
const body = await handler(makeRequest({ method: 'GET' }), response)
|
||||
|
||||
assert.equal(response.statusCode, 405)
|
||||
assert.equal(body.error.code, 'method-not-allowed')
|
||||
assert.equal(calls.opened.length, 0)
|
||||
})
|
||||
|
||||
test('a cross-site request without a loopback origin is forbidden', async () => {
|
||||
const { handler, calls } = buildHandler({ isLoopbackRequest: () => false })
|
||||
const response = makeResponse()
|
||||
|
||||
const body = await handler(makeRequest(), response)
|
||||
|
||||
assert.equal(response.statusCode, 403)
|
||||
assert.equal(body.error.code, 'forbidden')
|
||||
assert.equal(calls.opened.length, 0)
|
||||
})
|
||||
|
||||
test('a request missing the custom editor-action header is forbidden', async () => {
|
||||
const { handler, calls } = buildHandler()
|
||||
const response = makeResponse()
|
||||
const request = makeRequest({ headers: { host: '127.0.0.1:3080' } })
|
||||
|
||||
const body = await handler(request, response)
|
||||
|
||||
assert.equal(response.statusCode, 403)
|
||||
assert.equal(body.error.code, 'forbidden')
|
||||
assert.equal(calls.opened.length, 0)
|
||||
})
|
||||
|
||||
test('a request missing the session or directory is a bad request', async () => {
|
||||
const { handler, calls } = buildHandler()
|
||||
const response = makeResponse()
|
||||
const request = makeRequest({ url: '/touched-git/editor/open?sessionId=s1' })
|
||||
|
||||
const body = await handler(request, response)
|
||||
|
||||
assert.equal(response.statusCode, 400)
|
||||
assert.equal(body.error.code, 'invalid-request')
|
||||
assert.equal(calls.opened.length, 0)
|
||||
})
|
||||
|
||||
test('a directory the session does not track is refused', async () => {
|
||||
const { handler, calls } = buildHandler({ store: { list: async () => [] } })
|
||||
const response = makeResponse()
|
||||
|
||||
const body = await handler(makeRequest(), response)
|
||||
|
||||
assert.equal(response.statusCode, 403)
|
||||
assert.equal(body.error.code, 'not-tracked')
|
||||
assert.equal(calls.opened.length, 0)
|
||||
})
|
||||
|
||||
test('an invalid tracked entry is never matched or opened', async () => {
|
||||
const invalid = trackedEntry({ ok: false, error: { message: 'Directory is unavailable.' } })
|
||||
const { handler, calls } = buildHandler({ store: { list: async () => [invalid] } })
|
||||
const response = makeResponse()
|
||||
|
||||
const body = await handler(makeRequest(), response)
|
||||
|
||||
assert.equal(response.statusCode, 403)
|
||||
assert.equal(body.error.code, 'not-tracked')
|
||||
assert.equal(calls.opened.length, 0)
|
||||
})
|
||||
|
||||
test('a relocated repository is refused before opening', async () => {
|
||||
const { handler, calls } = buildHandler({
|
||||
resolveDirectoryRepository: async () => ({ repository: '/moved', workingDirectory: '/moved/work' }),
|
||||
})
|
||||
const response = makeResponse()
|
||||
|
||||
const body = await handler(makeRequest(), response)
|
||||
|
||||
assert.equal(response.statusCode, 409)
|
||||
assert.equal(body.error.code, 'directory-changed')
|
||||
assert.equal(calls.opened.length, 0)
|
||||
})
|
||||
|
||||
test('a directory deleted since tracking fails resolution and never opens', async () => {
|
||||
const { handler, calls } = buildHandler({
|
||||
resolveDirectoryRepository: async () => { throw new Error('ENOENT: no such file or directory') },
|
||||
})
|
||||
const response = makeResponse()
|
||||
|
||||
const body = await handler(makeRequest(), response)
|
||||
|
||||
assert.equal(response.statusCode, 503)
|
||||
assert.equal(body.error.code, 'editor-unavailable')
|
||||
assert.equal(body.error.message, 'ENOENT: no such file or directory')
|
||||
assert.equal(calls.opened.length, 0)
|
||||
})
|
||||
|
||||
test('a tracked directory opens its resolved working directory only', async () => {
|
||||
const { handler, calls } = buildHandler()
|
||||
const response = makeResponse()
|
||||
|
||||
const body = await handler(makeRequest(), response)
|
||||
|
||||
assert.equal(response.statusCode, 200)
|
||||
assert.equal(body.ok, true)
|
||||
assert.equal(body.value.directory, '/repo/work')
|
||||
assert.deepEqual(calls.resolved, ['/repo/work'])
|
||||
assert.deepEqual(calls.opened, ['/repo/work'])
|
||||
assert.equal(response.headers['cache-control'], 'no-store')
|
||||
})
|
||||
|
||||
test('an entry matched by its id still opens the resolved working directory', async () => {
|
||||
const { handler, calls } = buildHandler()
|
||||
const response = makeResponse()
|
||||
const request = makeRequest({ url: '/touched-git/editor/open?sessionId=s1&path=entry-1' })
|
||||
|
||||
const body = await handler(request, response)
|
||||
|
||||
assert.equal(response.statusCode, 200)
|
||||
assert.deepEqual(calls.opened, ['/repo/work'])
|
||||
})
|
||||
|
||||
test('an editor failure is reported as an unavailable editor', async () => {
|
||||
const { handler } = buildHandler({
|
||||
editor: { open: async () => { throw new Error('runtime is not installed') } },
|
||||
})
|
||||
const response = makeResponse()
|
||||
|
||||
const body = await handler(makeRequest(), response)
|
||||
|
||||
assert.equal(response.statusCode, 503)
|
||||
assert.equal(body.error.code, 'editor-unavailable')
|
||||
assert.equal(body.error.message, 'runtime is not installed')
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue