1/* eslint-disable camelcase */ 2var t = require('tap') 3var test = t.test 4var assert = require('assert') 5var requireInject = require('require-inject') 6const common = require('../common-tap.js') 7var cache_dir = common.pkg 8common.skipIfWindows('windows does not use correct-mkdir behavior') 9 10test('correct-mkdir: no race conditions', function (t) { 11 var mock_fs = {} 12 var did_hook = false 13 mock_fs.lstat = function (path, cb) { 14 if (path === cache_dir) { 15 // Return a non-matching owner 16 cb(null, { 17 uid: +process.getuid() + 1, 18 isDirectory: function () { 19 return true 20 } 21 }) 22 if (!did_hook) { 23 did_hook = true 24 doHook() 25 } 26 } else { 27 assert.ok(false, 'Unhandled stat path: ' + path) 28 } 29 } 30 var chown_in_progress = 0 31 var mock_chownr = function (path, uid, gid, cb) { 32 ++chown_in_progress 33 process.nextTick(function () { 34 --chown_in_progress 35 cb(null) 36 }) 37 } 38 var mocks = { 39 'graceful-fs': mock_fs, 40 'chownr': mock_chownr, 41 'infer-owner': requireInject('infer-owner', { fs: mock_fs }) 42 } 43 var correctMkdir = requireInject('../../lib/utils/correct-mkdir.js', mocks) 44 45 var calls_in_progress = 3 46 function handleCallFinish () { 47 t.equal(chown_in_progress, 0, 'should not return while chown still in progress') 48 if (!--calls_in_progress) { 49 t.end() 50 } 51 } 52 function doHook () { 53 // This is fired during the first correctMkdir call, after the stat has finished 54 // but before the chownr has finished 55 // Buggy old code will fail and return a cached value before initial call is done 56 correctMkdir(cache_dir, handleCallFinish) 57 } 58 // Initial call 59 correctMkdir(cache_dir, handleCallFinish) 60 // Immediate call again in case of race condition there 61 correctMkdir(cache_dir, handleCallFinish) 62}) 63 64test('correct-mkdir: ignore ENOENTs from chownr', function (t) { 65 var mock_fs = {} 66 mock_fs.lstat = function (path, cb) { 67 if (path === cache_dir) { 68 cb(null, { 69 isDirectory: function () { 70 return true 71 } 72 }) 73 } else { 74 assert.ok(false, 'Unhandled stat path: ' + path) 75 } 76 } 77 var mock_chownr = function (path, uid, gid, cb) { 78 cb(Object.assign(new Error(), {code: 'ENOENT'})) 79 } 80 var mocks = { 81 'graceful-fs': mock_fs, 82 'chownr': mock_chownr 83 } 84 var correctMkdir = requireInject('../../lib/utils/correct-mkdir.js', mocks) 85 86 function handleCallFinish (err) { 87 t.ifErr(err, 'chownr\'s ENOENT errors were ignored') 88 t.end() 89 } 90 correctMkdir(cache_dir, handleCallFinish) 91}) 92 93// NEED TO RUN LAST 94 95// These test checks that Windows users are protected by crashes related to 96// unexpectedly having a UID/GID other than 0 if a user happens to add these 97// variables to their environment. There are assumptions in correct-mkdir 98// that special-case Windows by checking on UID-related things. 99test('correct-mkdir: SUDO_UID and SUDO_GID non-Windows', function (t) { 100 process.env.SUDO_UID = 999 101 process.env.SUDO_GID = 999 102 process.getuid = function () { return 0 } 103 process.getgid = function () { return 0 } 104 var mock_fs = {} 105 mock_fs.lstat = function (path, cb) { 106 if (path === cache_dir) { 107 cb(null, { 108 uid: 0, 109 isDirectory: function () { 110 return true 111 } 112 }) 113 } else { 114 assert.ok(false, 'Unhandled stat path: ' + path) 115 } 116 } 117 var mock_chownr = function (path, uid, gid, cb) { 118 t.is(uid, +process.env.SUDO_UID, 'using the environment\'s UID') 119 t.is(gid, +process.env.SUDO_GID, 'using the environment\'s GID') 120 cb(null, {}) 121 } 122 var mocks = { 123 'graceful-fs': mock_fs, 124 'chownr': mock_chownr 125 } 126 var correctMkdir = requireInject('../../lib/utils/correct-mkdir.js', mocks) 127 128 function handleCallFinish () { 129 t.end() 130 } 131 correctMkdir(cache_dir, handleCallFinish) 132}) 133 134test('correct-mkdir: SUDO_UID and SUDO_GID Windows', function (t) { 135 process.env.SUDO_UID = 999 136 process.env.SUDO_GID = 999 137 delete process.getuid 138 delete process.getgid 139 var mock_fs = {} 140 mock_fs.lstat = function (path, cb) { 141 if (path === cache_dir) { 142 cb(null, { 143 uid: 0, 144 isDirectory: function () { 145 return true 146 } 147 }) 148 } else { 149 assert.ok(false, 'Unhandled stat path: ' + path) 150 } 151 } 152 var mock_chownr = function (path, uid, gid, cb) { 153 t.fail('chownr should not be called at all on Windows') 154 cb(new Error('nope')) 155 } 156 var mocks = { 157 'graceful-fs': mock_fs, 158 'chownr': mock_chownr 159 } 160 var correctMkdir = requireInject('../../lib/utils/correct-mkdir.js', mocks) 161 162 function handleCallFinish (err) { 163 t.ifErr(err, 'chownr was not called because Windows') 164 t.end() 165 } 166 correctMkdir(cache_dir, handleCallFinish) 167}) 168