43 lines
2.4 KiB
JavaScript
43 lines
2.4 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import { chmod, mkdtemp, readFile, readdir, stat, writeFile } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import test from 'node:test'
|
|
import { AtomicFileEditor } from '../../control-plane/modules/file-manager/editor.mjs'
|
|
import { FileInventory } from '../../control-plane/modules/file-manager/index.mjs'
|
|
|
|
test('atomically replaces text with optimistic revision and preserves mode', async () => {
|
|
const root = await mkdtemp(join(tmpdir(), 'dsh-file-editor-'))
|
|
const path = join(root, 'file.txt')
|
|
await writeFile(path, 'before')
|
|
await chmod(path, 0o640)
|
|
const inventory = new FileInventory()
|
|
const before = await inventory.content(path)
|
|
const saved = await new AtomicFileEditor().write(path, 'after\n', before.revision)
|
|
assert.equal(await readFile(path, 'utf8'), 'after\n')
|
|
assert.equal((await stat(path)).mode & 0o777, 0o640)
|
|
assert.notEqual(saved.revision, before.revision)
|
|
await assert.rejects(new AtomicFileEditor().write(path, 'stale', before.revision), error => error.statusCode === 409)
|
|
assert.equal(await readFile(path, 'utf8'), 'after\n')
|
|
assert.equal((await readdir(root)).some(name => name.startsWith('.dsh-edit-')), false)
|
|
})
|
|
|
|
test('creates a new text file without overwriting a concurrent target', async () => {
|
|
const root = await mkdtemp(join(tmpdir(), 'dsh-file-create-'))
|
|
const path = join(root, 'new.txt')
|
|
const editor = new AtomicFileEditor()
|
|
const created = await editor.write(path, 'new', null, { create: true })
|
|
assert.equal(created.size, 3)
|
|
await assert.rejects(editor.write(path, 'replace', null, { create: true }), error => error.statusCode === 409)
|
|
assert.equal(await readFile(path, 'utf8'), 'new')
|
|
})
|
|
|
|
test('rejects binary, oversized, missing, and non-file edits', async () => {
|
|
const root = await mkdtemp(join(tmpdir(), 'dsh-file-editor-invalid-'))
|
|
const editor = new AtomicFileEditor()
|
|
await assert.rejects(editor.write(join(root, 'nul'), 'a\0b', null, { create: true }), error => error.statusCode === 415)
|
|
await assert.rejects(editor.write(join(root, 'large'), 'x'.repeat(2 * 1024 * 1024 + 1), null, { create: true }), error => error.statusCode === 413)
|
|
await assert.rejects(editor.write(join(root, 'missing'), 'x', 'sha256:missing'), error => error.statusCode === 404)
|
|
await assert.rejects(editor.write(root, 'x', 'sha256:missing'), error => error.statusCode === 415)
|
|
})
|