• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict'
2
3const cacheFile = require('npm-cache-filename')
4const mkdirp = require('mkdirp')
5const mr = require('npm-registry-mock')
6const path = require('path')
7const qs = require('querystring')
8const test = require('tap').test
9
10const Tacks = require('tacks')
11const File = Tacks.File
12
13const common = require('../common-tap.js')
14
15// this test uses a fresh cache for each test block
16// create them all in common.cache so that we can verify
17// them for root-owned files in sudotest
18let CACHE_DIR
19let cacheBase
20let cachePath
21let cacheCounter = 1
22function setup () {
23  CACHE_DIR = common.cache + '/' + cacheCounter++
24  cacheBase = cacheFile(CACHE_DIR)(common.registry + '/-/all')
25  cachePath = path.join(cacheBase, '.cache.json')
26  mkdirp.sync(cacheBase)
27  fixOwner(CACHE_DIR)
28}
29
30const chownr = require('chownr')
31const fixOwner = (
32  process.getuid && process.getuid() === 0 &&
33  process.env.SUDO_UID && process.env.SUDO_GID
34) ? (path) => chownr.sync(path, +process.env.SUDO_UID, +process.env.SUDO_GID)
35  : () => {}
36
37let server
38
39test('setup', function (t) {
40  mr({port: common.port, throwOnUnmatched: true}, function (err, s) {
41    t.ifError(err, 'registry mocked successfully')
42    server = s
43    t.pass('all set up')
44    t.done()
45  })
46})
47
48test('notifies when there are no results', function (t) {
49  setup()
50  const query = qs.stringify({
51    text: 'none',
52    size: 20,
53    from: 0,
54    quality: 0.65,
55    popularity: 0.98,
56    maintenance: 0.5
57  })
58  server.get(`/-/v1/search?${query}`).once().reply(200, {
59    objects: []
60  })
61  common.npm([
62    'search', 'none',
63    '--registry', common.registry,
64    '--loglevel', 'error',
65    '--cache', CACHE_DIR
66  ], {}, function (err, code, stdout, stderr) {
67    if (err) throw err
68    t.equal(stderr, '', 'no error output')
69    t.equal(code, 0, 'search gives 0 error code even if no matches')
70    t.match(stdout, /No matches found/, 'Useful message on search failure')
71    t.done()
72  })
73})
74
75test('spits out a useful error when no cache nor network', function (t) {
76  setup()
77  const query = qs.stringify({
78    text: 'foo',
79    size: 20,
80    from: 0,
81    quality: 0.65,
82    popularity: 0.98,
83    maintenance: 0.5
84  })
85  server.get(`/-/v1/search?${query}`).once().reply(404, {})
86  server.get('/-/all').many().reply(404, {})
87  const cacheContents = {}
88  const fixture = new Tacks(File(cacheContents))
89  fixture.create(cachePath)
90  fixOwner(cachePath)
91  common.npm([
92    'search', 'foo',
93    '--registry', common.registry,
94    '--loglevel', 'silly',
95    '--json',
96    '--fetch-retry-mintimeout', 0,
97    '--fetch-retry-maxtimeout', 0,
98    '--cache', CACHE_DIR
99  ], {}, function (err, code, stdout, stderr) {
100    if (err) throw err
101    t.equal(code, 1, 'non-zero exit code')
102    t.match(JSON.parse(stdout).error.summary, /No search sources available/)
103    t.match(stderr, /No search sources available/, 'useful error')
104    t.done()
105  })
106})
107
108test('can switch to JSON mode', function (t) {
109  setup()
110  const query = qs.stringify({
111    text: 'oo',
112    size: 20,
113    from: 0,
114    quality: 0.65,
115    popularity: 0.98,
116    maintenance: 0.5
117  })
118  server.get(`/-/v1/search?${query}`).once().reply(200, {
119    objects: [
120      { package: { name: 'cool', version: '1.0.0' } },
121      { package: { name: 'foo', version: '2.0.0' } }
122    ]
123  })
124  common.npm([
125    'search', 'oo',
126    '--json',
127    '--registry', common.registry,
128    '--loglevel', 'error',
129    '--cache', CACHE_DIR
130  ], {}, function (err, code, stdout, stderr) {
131    if (err) throw err
132    t.equal(stderr, '', 'no error output')
133    t.equal(code, 0, 'search gives 0 error code even if no matches')
134    t.similar(JSON.parse(stdout), [
135      {
136        name: 'cool',
137        version: '1.0.0'
138      },
139      {
140        name: 'foo',
141        version: '2.0.0'
142      }
143    ], 'results returned as valid json')
144    t.done()
145  })
146})
147
148test('JSON mode does not notify on empty', function (t) {
149  setup()
150  const query = qs.stringify({
151    text: 'oo',
152    size: 20,
153    from: 0,
154    quality: 0.65,
155    popularity: 0.98,
156    maintenance: 0.5
157  })
158  server.get(`/-/v1/search?${query}`).once().reply(200, {
159    objects: []
160  })
161  common.npm([
162    'search', 'oo',
163    '--json',
164    '--registry', common.registry,
165    '--loglevel', 'error',
166    '--cache', CACHE_DIR
167  ], {}, function (err, code, stdout, stderr) {
168    if (err) throw err
169    t.deepEquals(JSON.parse(stdout), [], 'no notification about no results')
170    t.equal(stderr, '', 'no error output')
171    t.equal(code, 0, 'search gives 0 error code even if no matches')
172    t.done()
173  })
174})
175
176test('can switch to tab separated mode', function (t) {
177  setup()
178  const query = qs.stringify({
179    text: 'oo',
180    size: 20,
181    from: 0,
182    quality: 0.65,
183    popularity: 0.98,
184    maintenance: 0.5
185  })
186  server.get(`/-/v1/search?${query}`).once().reply(200, {
187    objects: [
188      { package: { name: 'cool', version: '1.0.0' } },
189      { package: { name: 'foo', description: 'this\thas\ttabs', version: '2.0.0' } }
190    ]
191  })
192  common.npm([
193    'search', 'oo',
194    '--parseable',
195    '--registry', common.registry,
196    '--loglevel', 'error',
197    '--cache', CACHE_DIR
198  ], {}, function (err, code, stdout, stderr) {
199    if (err) throw err
200    t.equal(stdout, 'cool\t\t\tprehistoric\t1.0.0\t\nfoo\tthis has tabs\t\tprehistoric\t2.0.0\t\n', 'correct output, including replacing tabs in descriptions')
201    t.equal(stderr, '', 'no error output')
202    t.equal(code, 0, 'search gives 0 error code even if no matches')
203    t.done()
204  })
205})
206
207test('tab mode does not notify on empty', function (t) {
208  setup()
209  const query = qs.stringify({
210    text: 'oo',
211    size: 20,
212    from: 0,
213    quality: 0.65,
214    popularity: 0.98,
215    maintenance: 0.5
216  })
217  server.get(`/-/v1/search?${query}`).once().reply(200, {
218    objects: []
219  })
220  common.npm([
221    'search', 'oo',
222    '--parseable',
223    '--registry', common.registry,
224    '--loglevel', 'error',
225    '--cache', CACHE_DIR
226  ], {}, function (err, code, stdout, stderr) {
227    if (err) throw err
228    t.equal(stdout, '', 'no notification about no results')
229    t.equal(stderr, '', 'no error output')
230    t.equal(code, 0, 'search gives 0 error code even if no matches')
231    t.done()
232  })
233})
234
235test('no arguments provided should error', function (t) {
236  setup()
237  common.npm(['search', '--cache', CACHE_DIR], {}, function (err, code, stdout, stderr) {
238    if (err) throw err
239    t.equal(code, 1, 'search finished unsuccessfully')
240
241    t.match(
242      stderr,
243      /search must be called with arguments/,
244      'should have correct error message'
245    )
246    t.end()
247  })
248})
249
250test('cleanup', function (t) {
251  server.done()
252  server.close()
253  t.end()
254})
255