| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- '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()
- })
|