summaryrefslogtreecommitdiff
path: root/deps/node/deps/npm/test/need-npm5-update/belongs-in-pacote/git-races.js
blob: 60136125d409485e18681b2557f27ed79a7ff24f (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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/* eslint-disable camelcase */
var execFile = require('child_process').execFile
var path = require('path')
var zlib = require('zlib')

var asyncMap = require('slide').asyncMap
var deepEqual = require('deep-equal')
var fs = require('graceful-fs')
var mkdirp = require('mkdirp')
var once = require('once')
var requireInject = require('require-inject')
var rimraf = require('rimraf')
var tar = require('tar')
var test = require('tap').test
var tmpdir = require('osenv').tmpdir
var which = require('which')

var wd = path.resolve(tmpdir(), 'git-races')
var fixtures = path.resolve(__dirname, '../fixtures')
var testcase = 'github-com-BryanDonovan-npm-git-test'
var testcase_git = path.resolve(wd, testcase + '.git')
var testcase_path = path.resolve(wd, testcase)
var testcase_tgz = path.resolve(fixtures, testcase + '.git.tar.gz')

var testtarballs = []
var testrepos = {}
var testurls = {}

/*
This test is specifically for #7202, where the bug was if you tried installing multiple git urls that
pointed at the same repo but had different comittishes, you'd sometimes get the wrong version.
The test cases, provided by @BryanDonovan, have a dependency tree like this:

  top
    bar#4.0.0
      buzz#3.0.0
      foo#3.0.0
    buzz#3.0.0
    foo#4.0.0
      buzz#2.0.0

But what would happen is that buzz#2.0.0 would end up installed under bar#4.0.0.

bar#4.0.0 shouldn't have gotten its own copy if buzz, and if it did, it shouldn've been buzz#3.0.0
*/

;['bar', 'foo', 'buzz'].forEach(function (name) {
  var mockurl = 'ssh://git@github.com/BryanDonovan/dummy-npm-' + name + '.git'
  var realrepo = path.resolve(wd, 'github-com-BryanDonovan-dummy-npm-' + name + '.git')
  var tgz = path.resolve(fixtures, 'github-com-BryanDonovan-dummy-npm-' + name + '.git.tar.gz')

  testrepos[mockurl] = realrepo
  testtarballs.push(tgz)
})

function cleanup () {
  process.chdir(tmpdir())
  rimraf.sync(wd)
}

var npm = requireInject.installGlobally('../../lib/npm.js', {
  'child_process': {
    'execFile': function (cmd, args, options, cb) {
      // on win 32, the following prefix is added in utils/git.js
      // $ git -c core.longpaths=true clone
      var i = process.platform === 'win32' ? 2 : 0
      // If it's a clone we swap any requests for any of the urls we're mocking
      // with the path to the bare repo
      if (args[i] === 'clone') {
        var m2 = args.length - 2
        var m1 = args.length - 1
        if (testrepos[args[m2]]) {
          testurls[args[m1]] = args[m2]
          args[m2] = testrepos[args[m2]]
        }
        execFile(cmd, args, options, cb)
      // here, we intercept npm validating the remote origin url on one of the
      // clones we've done previously and return the original url that was requested
      } else if (args[i] === 'config' && args[i + 1] === '--get' && args[i + 2] === 'remote.origin.url') {
        process.nextTick(function () {
          cb(null, testurls[options.cwd], '')
        })
      } else {
        execFile(cmd, args, options, cb)
      }
    }
  }
})

function extract (tarball, target, cb) {
  cb = once(cb)
  fs.createReadStream(tarball).on('error', function (er) { cb(er) })
    .pipe(zlib.createGunzip()).on('error', function (er) { cb(er) })
    .pipe(tar.Extract({path: target})).on('error', function (er) { cb(er) })
    .on('end', function () {
      cb()
    })
}

// Copied from lib/utils/git, because we need to use
// it before calling npm.load and lib/utils/git uses npm.js
// which doesn't allow that. =( =(

function prefixGitArgs () {
  return process.platform === 'win32' ? ['-c', 'core.longpaths=true'] : []
}

var gitcmd

function execGit (args, options, cb) {
  var fullArgs = prefixGitArgs().concat(args || [])
  return execFile(gitcmd, fullArgs, options, cb)
}

function gitWhichAndExec (args, options, cb) {
  if (gitcmd) return execGit(args, options, cb)

  which('git', function (err, pathtogit) {
    if (err) {
      err.code = 'ENOGIT'
      return cb(err)
    }
    gitcmd = pathtogit

    execGit(args, options, cb)
  })
}

function andClone (gitdir, repodir, cb) {
  return function (er) {
    if (er) return cb(er)
    gitWhichAndExec(['clone', gitdir, repodir], {}, cb)
  }
}

function setup (cb) {
  cleanup()
  mkdirp.sync(wd)

  extract(testcase_tgz, wd, andClone(testcase_git, testcase_path, andExtractPackages))

  function andExtractPackages (er) {
    if (er) return cb(er)
    asyncMap(testtarballs, function (tgz, done) {
      extract(tgz, wd, done)
    }, andChdir)
  }
  function andChdir (er) {
    if (er) return cb(er)
    process.chdir(testcase_path)
    andLoadNpm()
  }
  function andLoadNpm () {
    var opts = {
      cache: path.resolve(wd, 'cache')
    }
    npm.load(opts, cb)
  }
}

// there are two (sic) valid trees that can result we don't care which one we
// get in npm@2
var oneTree = [
  'npm-git-test@1.0.0', [
    ['dummy-npm-bar@4.0.0', [
      ['dummy-npm-foo@3.0.0', []],
      ['dummy-npm-buzz@3.0.0', []]
    ]],
    ['dummy-npm-buzz@3.0.0', []],
    ['dummy-npm-foo@4.0.0', [
      ['dummy-npm-buzz@2.0.0', []]
    ]]
  ]
]
var otherTree = [
  'npm-git-test@1.0.0', [
    ['dummy-npm-bar@4.0.0', [
      ['dummy-npm-buzz@3.0.0', []],
      ['dummy-npm-foo@3.0.0', []]
    ]],
    ['dummy-npm-buzz@3.0.0', []],
    ['dummy-npm-foo@4.0.0', [
      ['dummy-npm-buzz@2.0.0', []]
    ]]
  ]
]

function toSimple (tree) {
  var deps = []
  Object.keys(tree.dependencies || {}).forEach(function (dep) {
    deps.push(toSimple(tree.dependencies[dep]))
  })
  return [ tree['name'] + '@' + tree['version'], deps ]
}

test('setup', function (t) {
  setup(function (er) {
    t.ifError(er, 'setup ran OK')
    t.end()
  })
})

test('correct versions are installed for git dependency', function (t) {
  t.comment('test for https://github.com/npm/npm/issues/7202')
  npm.commands.install([], function (er) {
    t.ifError(er, 'installed OK')
    npm.commands.ls([], true, function (er, result) {
      t.ifError(er, 'ls OK')
      var simplified = toSimple(result)
      if (deepEqual(simplified, oneTree) || deepEqual(simplified, otherTree)) {
        t.pass('install tree is correct')
      } else {
        t.isDeeply(simplified, oneTree, 'oneTree matches')
        t.isDeeply(simplified, otherTree, 'otherTree matches')
      }
      t.done()
    })
  })
})