summaryrefslogtreecommitdiff
path: root/deps/npm/test/tap/correct-mkdir.js
blob: 2c93f943ad5c464055c9e7ad501308b2ce4b211b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/* eslint-disable camelcase */
var test = require('tap').test
var assert = require('assert')
var path = require('path')
var requireInject = require('require-inject')
var cache_dir = path.resolve(__dirname, 'correct-mkdir')

test('correct-mkdir: no race conditions', function (t) {
  var mock_fs = {}
  var did_hook = false
  mock_fs.stat = function (path, cb) {
    if (path === cache_dir) {
      // Return a non-matching owner
      cb(null, {
        uid: +process.uid + 1,
        isDirectory: function () {
          return true
        }
      })
      if (!did_hook) {
        did_hook = true
        doHook()
      }
    } else {
      assert.ok(false, 'Unhandled stat path: ' + path)
    }
  }
  var chown_in_progress = 0
  var mock_chownr = function (path, uid, gid, cb) {
    ++chown_in_progress
    process.nextTick(function () {
      --chown_in_progress
      cb(null)
    })
  }
  var mocks = {
    'graceful-fs': mock_fs,
    'chownr': mock_chownr
  }
  var correctMkdir = requireInject('../../lib/utils/correct-mkdir.js', mocks)

  var calls_in_progress = 3
  function handleCallFinish () {
    t.equal(chown_in_progress, 0, 'should not return while chown still in progress')
    if (!--calls_in_progress) {
      t.end()
    }
  }
  function doHook () {
    // This is fired during the first correctMkdir call, after the stat has finished
    // but before the chownr has finished
    // Buggy old code will fail and return a cached value before initial call is done
    correctMkdir(cache_dir, handleCallFinish)
  }
  // Initial call
  correctMkdir(cache_dir, handleCallFinish)
  // Immediate call again in case of race condition there
  correctMkdir(cache_dir, handleCallFinish)
})

test('correct-mkdir: ignore ENOENTs from chownr', function (t) {
  var mock_fs = {}
  mock_fs.stat = function (path, cb) {
    if (path === cache_dir) {
      cb(null, {
        isDirectory: function () {
          return true
        }
      })
    } else {
      assert.ok(false, 'Unhandled stat path: ' + path)
    }
  }
  var mock_chownr = function (path, uid, gid, cb) {
    cb(Object.assign(new Error(), {code: 'ENOENT'}))
  }
  var mocks = {
    'graceful-fs': mock_fs,
    'chownr': mock_chownr
  }
  var correctMkdir = requireInject('../../lib/utils/correct-mkdir.js', mocks)

  function handleCallFinish (err) {
    t.ifErr(err, 'chownr\'s ENOENT errors were ignored')
    t.end()
  }
  correctMkdir(cache_dir, handleCallFinish)
})

// NEED TO RUN LAST

// These test checks that Windows users are protected by crashes related to
// unexpectedly having a UID/GID other than 0 if a user happens to add these
// variables to their environment. There are assumptions in correct-mkdir
// that special-case Windows by checking on UID-related things.
test('correct-mkdir: SUDO_UID and SUDO_GID non-Windows', function (t) {
  process.env.SUDO_UID = 999
  process.env.SUDO_GID = 999
  process.getuid = function () { return 0 }
  process.getgid = function () { return 0 }
  var mock_fs = {}
  mock_fs.stat = function (path, cb) {
    if (path === cache_dir) {
      cb(null, {
        uid: 0,
        isDirectory: function () {
          return true
        }
      })
    } else {
      assert.ok(false, 'Unhandled stat path: ' + path)
    }
  }
  var mock_chownr = function (path, uid, gid, cb) {
    t.is(uid, +process.env.SUDO_UID, 'using the environment\'s UID')
    t.is(gid, +process.env.SUDO_GID, 'using the environment\'s GID')
    cb(null, {})
  }
  var mocks = {
    'graceful-fs': mock_fs,
    'chownr': mock_chownr
  }
  var correctMkdir = requireInject('../../lib/utils/correct-mkdir.js', mocks)

  function handleCallFinish () {
    t.end()
  }
  correctMkdir(cache_dir, handleCallFinish)
})

test('correct-mkdir: SUDO_UID and SUDO_GID Windows', function (t) {
  process.env.SUDO_UID = 999
  process.env.SUDO_GID = 999
  delete process.getuid
  delete process.getgid
  var mock_fs = {}
  mock_fs.stat = function (path, cb) {
    if (path === cache_dir) {
      cb(null, {
        uid: 0,
        isDirectory: function () {
          return true
        }
      })
    } else {
      assert.ok(false, 'Unhandled stat path: ' + path)
    }
  }
  var mock_chownr = function (path, uid, gid, cb) {
    t.fail('chownr should not be called at all on Windows')
    cb(new Error('nope'))
  }
  var mocks = {
    'graceful-fs': mock_fs,
    'chownr': mock_chownr
  }
  var correctMkdir = requireInject('../../lib/utils/correct-mkdir.js', mocks)

  function handleCallFinish (err) {
    t.ifErr(err, 'chownr was not called because Windows')
    t.end()
  }
  correctMkdir(cache_dir, handleCallFinish)
})