Last commit july 5th

This commit is contained in:
2024-07-05 13:46:23 +02:00
parent dad0d86e8c
commit b0e4dfbb76
24982 changed files with 2621219 additions and 413 deletions

510
spa/node_modules/atomically/test/basic.js generated vendored Executable file
View File

@@ -0,0 +1,510 @@
'use strict'
process.setMaxListeners(1000000);
const _ = require('lodash')
const fs = require('fs')
const os = require('os')
const path = require('path')
const {test} = require('tap')
const requireInject = require('require-inject')
let expectClose = 0
let closeCalled = 0
let expectCloseSync = 0
let closeSyncCalled = 0
const createErr = code => Object.assign(new Error(code), { code })
let unlinked = []
const fsMock = Object.assign ( {}, fs, {
/* ASYNC */
mkdir (filename, opts, cb) {
return cb(null);
},
realpath (filename, cb) {
return cb(null, filename)
},
open (tmpfile, options, mode, cb) {
if (/noopen/.test(tmpfile)) return cb(createErr('ENOOPEN'))
expectClose++
cb(null, tmpfile)
},
write (fd) {
const cb = arguments[arguments.length - 1]
if (/nowrite/.test(fd)) return cb(createErr('ENOWRITE'))
cb()
},
fsync (fd, cb) {
if (/nofsync/.test(fd)) return cb(createErr('ENOFSYNC'))
cb()
},
close (fd, cb) {
closeCalled++
cb()
},
chown (tmpfile, uid, gid, cb) {
if (/nochown/.test(tmpfile)) return cb(createErr('ENOCHOWN'))
if (/enosys/.test(tmpfile)) return cb(createErr('ENOSYS'))
if (/einval/.test(tmpfile)) return cb(createErr('EINVAL'))
if (/eperm/.test(tmpfile)) return cb(createErr('EPERM'))
cb()
},
chmod (tmpfile, mode, cb) {
if (/nochmod/.test(tmpfile)) return cb(createErr('ENOCHMOD'))
if (/enosys/.test(tmpfile)) return cb(createErr('ENOSYS'))
if (/eperm/.test(tmpfile)) return cb(createErr('EPERM'))
if (/einval/.test(tmpfile)) return cb(createErr('EINVAL'))
cb()
},
rename (tmpfile, filename, cb) {
if (/norename/.test(tmpfile)) return cb(createErr('ENORENAME'))
cb()
},
unlink (tmpfile, cb) {
if (/nounlink/.test(tmpfile)) return cb(createErr('ENOUNLINK'))
cb()
},
stat (tmpfile, cb) {
if (/nostat/.test(tmpfile)) return cb(createErr('ENOSTAT'))
if (/statful/.test(tmpfile)) return cb(null, fs.statSync('/'));
cb()
},
/* SYNC */
mkdirSync (filename) {},
realpathSync (filename, cb) {
return filename
},
openSync (tmpfile, options) {
if (/noopen/.test(tmpfile)) throw createErr('ENOOPEN')
expectCloseSync++
return tmpfile
},
writeSync (fd) {
if (/nowrite/.test(fd)) throw createErr('ENOWRITE')
},
fsyncSync (fd) {
if (/nofsync/.test(fd)) throw createErr('ENOFSYNC')
},
closeSync (fd) {
closeSyncCalled++
},
chownSync (tmpfile, uid, gid) {
if (/nochown/.test(tmpfile)) throw createErr('ENOCHOWN')
if (/enosys/.test(tmpfile)) throw createErr('ENOSYS')
if (/einval/.test(tmpfile)) throw createErr('EINVAL')
if (/eperm/.test(tmpfile)) throw createErr('EPERM')
},
chmodSync (tmpfile, mode) {
if (/nochmod/.test(tmpfile)) throw createErr('ENOCHMOD')
if (/enosys/.test(tmpfile)) throw createErr('ENOSYS')
if (/einval/.test(tmpfile)) throw createErr('EINVAL')
if (/eperm/.test(tmpfile)) throw createErr('EPERM')
},
renameSync (tmpfile, filename) {
if (/norename/.test(tmpfile)) throw createErr('ENORENAME')
},
unlinkSync (tmpfile) {
if (/nounlink/.test(tmpfile)) throw createErr('ENOUNLINK')
unlinked.push(tmpfile)
},
statSync (tmpfile) {
if (/nostat/.test(tmpfile)) throw createErr('ENOSTAT')
if (/statful/.test(tmpfile)) return fs.statSync('/');
}
});
const makeUnstableAsyncFn = function () {
return function () {
if ( Math.random () <= .75 ) {
const code = _.shuffle ([ 'EMFILE', 'ENFILE', 'EAGAIN', 'EBUSY', 'EACCESS', 'EPERM' ])[0];
throw createErr ( code );
}
return arguments[arguments.length -1](null, arguments[0]);
};
};
const makeUnstableSyncFn = function ( fn ) {
return function () {
if ( Math.random () <= .75 ) {
const code = _.shuffle ([ 'EMFILE', 'ENFILE', 'EAGAIN', 'EBUSY', 'EACCESS', 'EPERM' ])[0];
throw createErr ( code );
}
return fn.apply(undefined, arguments)
};
};
const fsMockUnstable = Object.assign ( {}, fsMock, {
open: makeUnstableAsyncFn (),
write: makeUnstableAsyncFn (),
fsync: makeUnstableAsyncFn (),
close: makeUnstableAsyncFn (),
rename: makeUnstableAsyncFn (),
openSync: makeUnstableSyncFn ( _.identity ),
writeSync: makeUnstableSyncFn ( _.noop ),
fsyncSync: makeUnstableSyncFn ( _.noop ),
closeSync: makeUnstableSyncFn ( _.noop ),
renameSync: makeUnstableSyncFn ( _.noop )
});
const {writeFile: writeFileAtomic, writeFileSync: writeFileAtomicSync} = requireInject('../dist', { fs: fsMock });
test('async tests', t => {
t.plan(2)
expectClose = 0
closeCalled = 0
t.teardown(() => {
t.parent.equal(closeCalled, expectClose, 'async tests closed all files')
expectClose = 0
closeCalled = 0
})
t.test('non-root tests', t => {
t.plan(28)
writeFileAtomic('good', 'test', { mode: '0777' }, err => {
t.notOk(err, 'No errors occur when passing in options')
})
writeFileAtomic('good', 'test', 'utf8', err => {
t.notOk(err, 'No errors occur when passing in options as string')
})
writeFileAtomic('good', 'test', undefined, err => {
t.notOk(err, 'No errors occur when NOT passing in options')
})
writeFileAtomic('good', 'test', err => {
t.notOk(err)
})
writeFileAtomic('noopen', 'test', err => {
t.is(err && err.message, 'ENOOPEN', 'fs.open failures propagate')
})
writeFileAtomic('nowrite', 'test', err => {
t.is(err && err.message, 'ENOWRITE', 'fs.writewrite failures propagate')
})
writeFileAtomic('nowrite', Buffer.from('test', 'utf8'), err => {
t.is(err && err.message, 'ENOWRITE', 'fs.writewrite failures propagate for buffers')
})
writeFileAtomic('nochown', 'test', { chown: { uid: 100, gid: 100 } }, err => {
t.is(err && err.message, 'ENOCHOWN', 'Chown failures propagate')
})
writeFileAtomic('nochown', 'test', err => {
t.notOk(err, 'No attempt to chown when no uid/gid passed in')
})
writeFileAtomic('nochmod', 'test', { mode: parseInt('741', 8) }, err => {
t.is(err && err.message, 'ENOCHMOD', 'Chmod failures propagate')
})
writeFileAtomic('nofsyncopt', 'test', { fsync: false }, err => {
t.notOk(err, 'fsync skipped if options.fsync is false')
})
writeFileAtomic('norename', 'test', err => {
t.is(err && err.message, 'ENORENAME', 'Rename errors propagate')
})
writeFileAtomic('norename nounlink', 'test', err => {
t.is(err && err.message, 'ENORENAME', 'Failure to unlink the temp file does not clobber the original error')
})
writeFileAtomic('nofsync', 'test', err => {
t.is(err && err.message, 'ENOFSYNC', 'Fsync failures propagate')
})
writeFileAtomic('enosys', 'test', err => {
t.notOk(err, 'No errors on ENOSYS')
})
writeFileAtomic('einval', 'test', { mode: 0o741 }, err => {
t.notOk(err, 'No errors on EINVAL for non root')
})
writeFileAtomic('eperm', 'test', { mode: 0o741 }, err => {
t.notOk(err, 'No errors on EPERM for non root')
})
writeFileAtomic('einval', 'test', { chown: { uid: 100, gid: 100 } }, err => {
t.notOk(err, 'No errors on EINVAL for non root')
})
writeFileAtomic('eperm', 'test', { chown: { uid: 100, gid: 100 } }, err => {
t.notOk(err, 'No errors on EPERM for non root')
})
const optionsImmutable = {};
writeFileAtomic('statful', 'test', optionsImmutable, err => {
t.notOk(err);
t.deepEquals(optionsImmutable, {});
});
const schedule = filePath => {
t.is(filePath, 'good');
return new Promise ( resolve => {
resolve ( () => {
t.is(true,true);
});
});
};
writeFileAtomic('good','test', {schedule}, err => {
t.notOk(err);
});
const tmpCreate = filePath => `.${filePath}.custom`;
const tmpCreated = filePath => t.is ( filePath, '.good.custom' );
writeFileAtomic('good','test', {tmpCreate, tmpCreated}, err => {
t.notOk(err)
})
const longPath = path.join(os.tmpdir(),'.012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt');
const {writeFile: writeFileAtomicNative} = requireInject('../dist', { fs });
writeFileAtomicNative(longPath,'test', err => {
t.notOk(err)
})
const pathMissingFolders = path.join(os.tmpdir(),String(Math.random()),String(Math.random()),String(Math.random()),'foo.txt');
writeFileAtomicNative(pathMissingFolders,'test', err => {
t.notOk(err)
})
})
t.test('errors for root', t => {
const { getuid } = process
process.getuid = () => 0
t.teardown(() => {
process.getuid = getuid
})
const {writeFile: writeFileAtomic} = requireInject('../dist', { fs: fsMock });
t.plan(2)
writeFileAtomic('einval', 'test', { chown: { uid: 100, gid: 100 } }, err => {
t.match(err, { code: 'EINVAL' })
})
writeFileAtomic('einval', 'test', { mode: 0o741 }, err => {
t.match(err, { code: 'EINVAL' })
})
})
})
test('unstable async tests', t => {
t.plan(2);
const {writeFile: writeFileAtomic} = requireInject('../dist', { fs: fsMockUnstable });
writeFileAtomic('good', 'test', err => {
t.notOk(err, 'No errors occur when retryable errors are thrown')
})
writeFileAtomic('good', 'test', { timeout: 0 }, err => {
t.is(!!err.code, true, 'Retrying can be disabled')
})
});
test('sync tests', t => {
t.plan(2)
closeSyncCalled = 0
expectCloseSync = 0
t.teardown(() => {
t.parent.equal(closeSyncCalled, expectCloseSync, 'sync closed all files')
expectCloseSync = 0
closeSyncCalled = 0
})
const throws = function (t, shouldthrow, msg, todo) {
let err
try { todo() } catch (e) { err = e }
t.is(shouldthrow, err && err.message, msg)
}
const noexception = function (t, msg, todo) {
let err
try { todo() } catch (e) { err = e }
t.ifError(err, msg)
}
let tmpfile
t.test('non-root', t => {
t.plan(38)
noexception(t, 'No errors occur when passing in options', () => {
writeFileAtomicSync('good', 'test', { mode: '0777' })
})
noexception(t, 'No errors occur when passing in options as string', () => {
writeFileAtomicSync('good', 'test', 'utf8')
})
noexception(t, 'No errors occur when NOT passing in options', () => {
writeFileAtomicSync('good', 'test')
})
noexception(t, 'fsync never called if options.fsync is falsy', () => {
writeFileAtomicSync('good', 'test', { fsync: false })
})
noexception(t, 'tmpCreated is called on success', () => {
writeFileAtomicSync('good', 'test', {
tmpCreated (gottmpfile) {
tmpfile = gottmpfile
}
})
t.match(tmpfile, /^good\.tmp-\w+$/, 'tmpCreated called for success')
t.match(tmpfile, /^good\.tmp-\d{10}[a-f0-9]{6}$/, 'tmpCreated format')
})
tmpfile = undefined
throws(t, 'ENOOPEN', 'fs.openSync failures propagate', () => {
writeFileAtomicSync('noopen', 'test', {
tmpCreated (gottmpfile) {
tmpfile = gottmpfile
}
})
})
t.is(tmpfile, undefined, 'tmpCreated not called for open failure')
throws(t, 'ENOWRITE', 'fs.writeSync failures propagate', () => {
writeFileAtomicSync('nowrite', 'test', {
tmpCreated (gottmpfile) {
tmpfile = gottmpfile
}
})
})
t.match(tmpfile, /^nowrite\.tmp-\w+$/, 'tmpCreated called for failure after open')
throws(t, 'ENOCHOWN', 'Chown failures propagate', () => {
writeFileAtomicSync('nochown', 'test', { chown: { uid: 100, gid: 100 } })
})
noexception(t, 'No attempt to chown when false passed in', () => {
writeFileAtomicSync('nochown', 'test', { chown: false })
})
noexception(t, 'No errors occured when chown is undefined and original file owner used', () => {
writeFileAtomicSync('chowncopy', 'test', { chown: undefined })
})
throws(t, 'ENORENAME', 'Rename errors propagate', () => {
writeFileAtomicSync('norename', 'test')
})
throws(t, 'ENORENAME', 'Failure to unlink the temp file does not clobber the original error', () => {
writeFileAtomicSync('norename nounlink', 'test')
})
throws(t, 'ENOFSYNC', 'Fsync errors propagate', () => {
writeFileAtomicSync('nofsync', 'test')
})
noexception(t, 'No errors on ENOSYS', () => {
writeFileAtomicSync('enosys', 'test', { chown: { uid: 100, gid: 100 } })
})
noexception(t, 'No errors on EINVAL for non root', () => {
writeFileAtomicSync('einval', 'test', { chown: { uid: 100, gid: 100 } })
})
noexception(t, 'No errors on EPERM for non root', () => {
writeFileAtomicSync('eperm', 'test', { chown: { uid: 100, gid: 100 } })
})
throws(t, 'ENOCHMOD', 'Chmod failures propagate', () => {
writeFileAtomicSync('nochmod', 'test', { mode: 0o741 })
})
noexception(t, 'No errors on EPERM for non root', () => {
writeFileAtomicSync('eperm', 'test', { mode: 0o741 })
})
noexception(t, 'No attempt to chmod when no mode provided', () => {
writeFileAtomicSync('nochmod', 'test', { mode: false })
})
const optionsImmutable = {};
noexception(t, 'options are immutable', () => {
writeFileAtomicSync('statful', 'test', optionsImmutable)
})
t.deepEquals(optionsImmutable, {});
const tmpCreate = filePath => `.${filePath}.custom`;
const tmpCreated = filePath => t.is ( filePath, '.good.custom' );
noexception(t, 'custom temp creator', () => {
writeFileAtomicSync('good', 'test', {tmpCreate, tmpCreated})
})
const path0 = path.join(os.tmpdir(),'atomically-test-0');
const tmpPath0 = path0 + '.temp';
noexception(t, 'temp files are purged on success', () => {
const {writeFileSync: writeFileAtomicSync} = requireInject('../dist', { fs });
writeFileAtomicSync(path0, 'test', {tmpCreate: () => tmpPath0})
})
t.is(true,fs.existsSync(path0));
t.is(false,fs.existsSync(tmpPath0));
const path1 = path.join(os.tmpdir(),'atomically-test-norename-1');
const tmpPath1 = path1 + '.temp';
throws(t, 'ENORENAME', 'temp files are purged on error', () => {
const {writeFileSync: writeFileAtomicSync} = requireInject('../dist', { fs: Object.assign ( {}, fs, { renameSync: fsMock.renameSync })});
writeFileAtomicSync(path1, 'test', {tmpCreate: () => tmpPath1})
})
t.is(false,fs.existsSync(path1));
t.is(false,fs.existsSync(tmpPath1));
const path2 = path.join(os.tmpdir(),'atomically-test-norename-2');
const tmpPath2 = path2 + '.temp';
throws(t, 'ENORENAME', 'temp files can also not be purged on error', () => {
const {writeFileSync: writeFileAtomicSync} = requireInject('../dist', { fs: Object.assign ( {}, fs, { renameSync: fsMock.renameSync })});
writeFileAtomicSync(path2, 'test', {tmpCreate: () => tmpPath2,tmpPurge: false})
})
t.is(false,fs.existsSync(path2));
t.is(true,fs.existsSync(tmpPath2));
const longPath = path.join(os.tmpdir(),'.012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.txt');
noexception(t, 'temp files are truncated', () => {
const {writeFileSync: writeFileAtomicSync} = requireInject('../dist', { fs });
writeFileAtomicSync(longPath, 'test')
})
const pathMissingFolders = path.join(os.tmpdir(),String(Math.random()),String(Math.random()),String(Math.random()),'foo.txt');
noexception(t, 'parent folders are created', () => {
const {writeFileSync: writeFileAtomicSync} = requireInject('../dist', { fs });
writeFileAtomicSync(pathMissingFolders, 'test')
})
})
t.test('errors for root', t => {
const { getuid } = process
process.getuid = () => 0
t.teardown(() => {
process.getuid = getuid
})
const {writeFileSync: writeFileAtomicSync} = requireInject('../dist', { fs: fsMock });
t.plan(2)
throws(t, 'EINVAL', 'Chown error as root user', () => {
writeFileAtomicSync('einval', 'test', { chown: { uid: 100, gid: 100 } })
})
throws(t, 'EINVAL', 'Chmod error as root user', () => {
writeFileAtomicSync('einval', 'test', { mode: 0o741 })
})
})
})
test('unstable sync tests', t => {
t.plan(2);
const throws = function (t, msg, todo) {
let err
try { todo() } catch (e) { err = e }
t.is(!!err.code, true, msg)
}
const noexception = function (t, msg, todo) {
let err
try { todo() } catch (e) { err = e }
t.ifError(err, msg)
}
noexception(t, 'No errors occur when retryable errors are thrown', () => {
const {writeFileSync: writeFileAtomicSync} = requireInject('../dist', { fs: fsMockUnstable });
writeFileAtomicSync('good', 'test')
})
throws(t, 'retrying can be disabled', () => {
const {writeFileSync: writeFileAtomicSync} = requireInject('../dist', { fs: fsMockUnstable });
writeFileAtomicSync('good', 'test', { timeout: 0 })
})
});
test('promises', async t => {
let tmpfile
closeCalled = 0
expectClose = 0
t.teardown(() => {
t.parent.equal(closeCalled, expectClose, 'promises closed all files')
closeCalled = 0
expectClose = 0
})
await writeFileAtomic('good', 'test', {
tmpCreated (gottmpfile) {
tmpfile = gottmpfile
}
})
t.match(tmpfile, /^good\.tmp-\w+$/, 'tmpCreated is called for success')
await writeFileAtomic('good', 'test', {
tmpCreated (gottmpfile) {
return Promise.resolve()
}
})
tmpfile = undefined
await t.rejects(writeFileAtomic('noopen', 'test', {
tmpCreated (gottmpfile) {
tmpfile = gottmpfile
}
}))
t.is(tmpfile, undefined, 'tmpCreated is not called on open failure')
await t.rejects(writeFileAtomic('nowrite', 'test', {
tmpCreated (gottmpfile) {
tmpfile = gottmpfile
}
}))
t.match(tmpfile, /^nowrite\.tmp-\w+$/, 'tmpCreated is called if failure is after open')
})

153
spa/node_modules/atomically/test/concurrency.js generated vendored Executable file
View File

@@ -0,0 +1,153 @@
'use strict'
process.setMaxListeners(1000000);
const fs = require('fs')
const {test} = require('tap')
const requireInject = require('require-inject')
// defining mock for fs so its functions can be modified
const fsMock = Object.assign ( {}, fs, {
/* ASYNC */
mkdir (filename, opts, cb) {
return cb(null);
},
realpath (filename, cb) {
return cb(null, filename)
},
open (tmpfile, options, mode, cb) {
if (/noopen/.test(tmpfile)) return cb(new Error('ENOOPEN'))
cb(null, tmpfile)
},
write (fd) {
const cb = arguments[arguments.length - 1]
if (/nowrite/.test(fd)) return cb(new Error('ENOWRITE'))
cb()
},
fsync (fd, cb) {
if (/nofsync/.test(fd)) return cb(new Error('ENOFSYNC'))
cb()
},
close (fd, cb) {
cb()
},
chown (tmpfile, uid, gid, cb) {
if (/nochown/.test(tmpfile)) return cb(new Error('ENOCHOWN'))
cb()
},
chmod (tmpfile, mode, cb) {
if (/nochmod/.test(tmpfile)) return cb(new Error('ENOCHMOD'))
cb()
},
rename (tmpfile, filename, cb) {
if (/norename/.test(tmpfile)) return cb(new Error('ENORENAME'))
cb()
},
unlink (tmpfile, cb) {
if (/nounlink/.test(tmpfile)) return cb(new Error('ENOUNLINK'))
cb()
},
stat (tmpfile, cb) {
if (/nostat/.test(tmpfile)) return cb(new Error('ENOSTAT'))
cb()
},
/* SYNC */
mkdirSync (filename) {},
realpathSync (filename, cb) {
return filename
},
openSync (tmpfile, options) {
if (/noopen/.test(tmpfile)) throw new Error('ENOOPEN')
return tmpfile
},
writeSync (fd) {
if (/nowrite/.test(fd)) throw new Error('ENOWRITE')
},
fsyncSync (fd) {
if (/nofsync/.test(fd)) throw new Error('ENOFSYNC')
},
closeSync () {},
chownSync (tmpfile, uid, gid) {
if (/nochown/.test(tmpfile)) throw new Error('ENOCHOWN')
},
chmodSync (tmpfile, mode) {
if (/nochmod/.test(tmpfile)) throw new Error('ENOCHMOD')
},
renameSync (tmpfile, filename) {
if (/norename/.test(tmpfile)) throw new Error('ENORENAME')
},
unlinkSync (tmpfile) {
if (/nounlink/.test(tmpfile)) throw new Error('ENOUNLINK')
},
statSync (tmpfile) {
if (/nostat/.test(tmpfile)) throw new Error('ENOSTAT')
}
})
const {writeFile: writeFileAtomic} = requireInject('../dist', { fs: fsMock });
// preserve original functions
const oldRealPath = fsMock.realpath
const oldRename = fsMock.rename
test('ensure writes to the same file are serial', t => {
let fileInUse = false
const ops = 5 // count for how many concurrent write ops to request
t.plan(ops * 3 + 3)
fsMock.realpath = (...args) => {
t.false(fileInUse, 'file not in use')
fileInUse = true
oldRealPath(...args)
}
fsMock.rename = (...args) => {
t.true(fileInUse, 'file in use')
fileInUse = false
oldRename(...args)
}
const {writeFile: writeFileAtomic} = requireInject('../dist', { fs: fsMock });
for (let i = 0; i < ops; i++) {
writeFileAtomic('test', 'test', err => {
if (err) t.fail(err)
else t.pass('wrote without error')
})
}
setTimeout(() => {
writeFileAtomic('test', 'test', err => {
if (err) t.fail(err)
else t.pass('successive writes after delay')
})
}, 500)
})
test('allow write to multiple files in parallel, but same file writes are serial', t => {
const filesInUse = []
const ops = 5
let wasParallel = false
fsMock.realpath = (filename, ...args) => {
filesInUse.push(filename)
const firstOccurence = filesInUse.indexOf(filename)
t.equal(filesInUse.indexOf(filename, firstOccurence + 1), -1, 'serial writes') // check for another occurence after the first
if (filesInUse.length > 1) wasParallel = true // remember that a parallel operation took place
oldRealPath(filename, ...args)
}
fsMock.rename = (filename, ...args) => {
filesInUse.splice(filesInUse.indexOf(filename), 1)
oldRename(filename, ...args)
}
const {writeFile: writeFileAtomic} = requireInject('../dist', { fs: fsMock });
t.plan(ops * 2 * 2 + 1)
let opCount = 0
for (let i = 0; i < ops; i++) {
writeFileAtomic('test', 'test', err => {
if (err) t.fail(err, 'wrote without error')
else t.pass('wrote without error')
})
writeFileAtomic('test2', 'test', err => {
opCount++
if (opCount === ops) t.true(wasParallel, 'parallel writes')
if (err) t.fail(err, 'wrote without error')
else t.pass('wrote without error')
})
}
})

291
spa/node_modules/atomically/test/integration.js generated vendored Executable file
View File

@@ -0,0 +1,291 @@
'use strict'
process.setMaxListeners(1000000);
const fs = require('fs')
const path = require('path')
const {test} = require('tap')
const rimraf = require('rimraf')
const requireInject = require('require-inject')
const workdir = path.join(__dirname, path.basename(__filename, '.js'))
let testfiles = 0
function tmpFile () {
return path.join(workdir, 'test-' + (++testfiles))
}
function readFile (path) {
return fs.readFileSync(path).toString()
}
function didWriteFileAtomic (t, expected, filename, data, options, callback) {
if (options instanceof Function) {
callback = options
options = null
}
if (!options) options = {}
const actual = {}
const {writeFile: writeFileAtomic} = requireInject('../dist', {
fs: Object.assign({}, fs, {
chown (filename, uid, gid, cb) {
actual.uid = uid
actual.gid = gid
process.nextTick(cb)
},
stat (filename, cb) {
fs.stat(filename, (err, stats) => {
if (err) return cb(err)
cb(null, Object.assign(stats, expected || {}))
})
}
})
})
return writeFileAtomic(filename, data, options, err => {
t.isDeeply(actual, expected, 'ownership is as expected')
callback(err)
})
}
function didWriteFileAtomicSync (t, expected, filename, data, options) {
const actual = {}
const {writeFileSync} = requireInject('../dist', {
fs: Object.assign({}, fs, {
chownSync (filename, uid, gid) {
actual.uid = uid
actual.gid = gid
},
statSync (filename) {
const stats = fs.statSync(filename)
return Object.assign(stats, expected || {})
}
})
})
writeFileSync(filename, data, options)
t.isDeeply(actual, expected)
}
function currentUser () {
return {
uid: process.getuid(),
gid: process.getgid()
}
}
test('setup', t => {
rimraf.sync(workdir)
fs.mkdirSync(workdir, {recursive: true})
t.done()
})
test('writes simple file (async)', t => {
t.plan(3)
const file = tmpFile()
didWriteFileAtomic(t, {}, file, '42', err => {
t.ifError(err, 'no error')
t.is(readFile(file), '42', 'content ok')
})
})
test('writes simple file with encoding (async)', t => {
t.plan(3)
const file = tmpFile()
didWriteFileAtomic(t, {}, file, 'foo', 'utf16le', err => {
t.ifError(err, 'no error')
t.is(readFile(file), 'f\u0000o\u0000o\u0000', 'content ok')
})
})
test('writes buffers to simple file (async)', t => {
t.plan(3)
const file = tmpFile()
didWriteFileAtomic(t, {}, file, Buffer.from('42'), err => {
t.ifError(err, 'no error')
t.is(readFile(file), '42', 'content ok')
})
})
test('writes undefined to simple file (async)', t => {
t.plan(3)
const file = tmpFile()
didWriteFileAtomic(t, {}, file, undefined, err => {
t.ifError(err, 'no error')
t.is(readFile(file), '', 'content ok')
})
})
test('writes to symlinks without clobbering (async)', t => {
t.plan(5)
const file = tmpFile()
const link = tmpFile()
fs.writeFileSync(file, '42')
fs.symlinkSync(file, link)
didWriteFileAtomic(t, currentUser(), link, '43', err => {
t.ifError(err, 'no error')
t.is(readFile(file), '43', 'target content ok')
t.is(readFile(link), '43', 'link content ok')
t.ok(fs.lstatSync(link).isSymbolicLink(), 'link is link')
})
})
test('runs chown on given file (async)', t => {
const file = tmpFile()
didWriteFileAtomic(t, { uid: 42, gid: 43 }, file, '42', { chown: { uid: 42, gid: 43 } }, err => {
t.ifError(err, 'no error')
t.is(readFile(file), '42', 'content ok')
t.done()
})
})
test('writes simple file with no chown (async)', t => {
t.plan(3)
const file = tmpFile()
didWriteFileAtomic(t, {}, file, '42', { chown: false }, err => {
t.ifError(err, 'no error')
t.is(readFile(file), '42', 'content ok')
t.done()
})
})
test('runs chmod on given file (async)', t => {
t.plan(5)
const file = tmpFile()
didWriteFileAtomic(t, {}, file, '42', { mode: parseInt('741', 8) }, err => {
t.ifError(err, 'no error')
const stat = fs.statSync(file)
t.is(stat.mode, parseInt('100741', 8))
didWriteFileAtomic(t, { uid: 42, gid: 43 }, file, '23', { chown: { uid: 42, gid: 43 } }, err => {
t.ifError(err, 'no error')
})
})
})
test('run chmod AND chown (async)', t => {
t.plan(3)
const file = tmpFile()
didWriteFileAtomic(t, { uid: 42, gid: 43 }, file, '42', { mode: parseInt('741', 8), chown: { uid: 42, gid: 43 } }, err => {
t.ifError(err, 'no error')
const stat = fs.statSync(file)
t.is(stat.mode, parseInt('100741', 8))
})
})
test('does not change chmod by default (async)', t => {
t.plan(5)
const file = tmpFile()
didWriteFileAtomic(t, {}, file, '42', { mode: parseInt('741', 8) }, err => {
t.ifError(err, 'no error')
didWriteFileAtomic(t, currentUser(), file, '43', err => {
t.ifError(err, 'no error')
const stat = fs.statSync(file)
t.is(stat.mode, parseInt('100741', 8))
})
})
})
test('does not change chown by default (async)', t => {
t.plan(6)
const file = tmpFile()
didWriteFileAtomic(t, { uid: 42, gid: 43 }, file, '42', { chown: { uid: 42, gid: 43 } }, _setModeOnly)
function _setModeOnly (err) {
t.ifError(err, 'no error')
didWriteFileAtomic(t, { uid: 42, gid: 43 }, file, '43', { mode: parseInt('741', 8) }, _allDefault)
}
function _allDefault (err) {
t.ifError(err, 'no error')
didWriteFileAtomic(t, { uid: 42, gid: 43 }, file, '43', _noError)
}
function _noError (err) {
t.ifError(err, 'no error')
}
})
test('writes simple file (sync)', t => {
t.plan(2)
const file = tmpFile()
didWriteFileAtomicSync(t, {}, file, '42')
t.is(readFile(file), '42')
})
test('writes simple file with encoding (sync)', t => {
t.plan(2)
const file = tmpFile()
didWriteFileAtomicSync(t, {}, file, 'foo', 'utf16le')
t.is(readFile(file), 'f\u0000o\u0000o\u0000')
})
test('writes simple buffer file (sync)', t => {
t.plan(2)
const file = tmpFile()
didWriteFileAtomicSync(t, {}, file, Buffer.from('42'))
t.is(readFile(file), '42')
})
test('writes undefined file (sync)', t => {
t.plan(2)
const file = tmpFile()
didWriteFileAtomicSync(t, {}, file, undefined)
t.is(readFile(file), '')
})
test('writes to symlinks without clobbering (sync)', t => {
t.plan(4)
const file = tmpFile()
const link = tmpFile()
fs.writeFileSync(file, '42')
fs.symlinkSync(file, link)
didWriteFileAtomicSync(t, currentUser(), link, '43')
t.is(readFile(file), '43', 'target content ok')
t.is(readFile(link), '43', 'link content ok')
t.ok(fs.lstatSync(link).isSymbolicLink(), 'link is link')
})
test('runs chown on given file (sync)', t => {
t.plan(1)
const file = tmpFile()
didWriteFileAtomicSync(t, { uid: 42, gid: 43 }, file, '42', { chown: { uid: 42, gid: 43 } })
})
test('runs chmod on given file (sync)', t => {
t.plan(3)
const file = tmpFile()
didWriteFileAtomicSync(t, {}, file, '42', { mode: parseInt('741', 8) })
const stat = fs.statSync(file)
t.is(stat.mode, parseInt('100741', 8))
didWriteFileAtomicSync(t, { uid: 42, gid: 43 }, file, '23', { chown: { uid: 42, gid: 43 } })
})
test('runs chown and chmod (sync)', t => {
t.plan(2)
const file = tmpFile()
didWriteFileAtomicSync(t, { uid: 42, gid: 43 }, file, '42', { mode: parseInt('741', 8), chown: { uid: 42, gid: 43 } })
const stat = fs.statSync(file)
t.is(stat.mode, parseInt('100741', 8))
})
test('does not change chmod by default (sync)', t => {
t.plan(3)
const file = tmpFile()
didWriteFileAtomicSync(t, {}, file, '42', { mode: parseInt('741', 8) })
didWriteFileAtomicSync(t, currentUser(), file, '43')
const stat = fs.statSync(file)
t.is(stat.mode, parseInt('100741', 8))
})
test('does not change chown by default (sync)', t => {
t.plan(3)
const file = tmpFile()
didWriteFileAtomicSync(t, { uid: 42, gid: 43 }, file, '42', { chown: { uid: 42, gid: 43 } })
didWriteFileAtomicSync(t, { uid: 42, gid: 43 }, file, '43', { mode: parseInt('741', 8) })
didWriteFileAtomicSync(t, { uid: 42, gid: 43 }, file, '44')
})
test('cleanup', t => {
rimraf.sync(workdir)
t.done()
})