• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict'
2
3const common = require('../common-tap.js')
4const getStream = require('get-stream')
5const mkdirp = require('mkdirp')
6const mr = require('npm-registry-mock')
7const npm = require('../../')
8const path = require('path')
9const Tacks = require('tacks')
10const test = require('tap').test
11
12const {File} = Tacks
13
14const _createEntryStream = require('../../lib/search/all-package-metadata.js')._createEntryStream
15
16let server
17
18// this test uses a fresh cache for each test block
19// create them all in common.cache so that we can verify
20// them for root-owned files in sudotest
21let CACHE_DIR
22let cacheCounter = 1
23function setup () {
24  CACHE_DIR = common.cache + '/' + cacheCounter++
25  mkdirp.sync(CACHE_DIR)
26  fixOwner(CACHE_DIR)
27}
28
29const chownr = require('chownr')
30const fixOwner = (
31  process.getuid && process.getuid() === 0 &&
32  process.env.SUDO_UID && process.env.SUDO_GID
33) ? (path) => chownr.sync(path, +process.env.SUDO_UID, +process.env.SUDO_GID)
34  : () => {}
35
36test('setup', t => {
37  mr({port: common.port, throwOnUnmatched: true}, (err, s) => {
38    t.ifError(err, 'registry mocked successfully')
39    npm.load({ cache: CACHE_DIR, registry: common.registry }, err => {
40      t.ifError(err, 'npm loaded successfully')
41      server = s
42      t.pass('all set up')
43      t.done()
44    })
45  })
46})
47
48test('createEntryStream full request', t => {
49  setup()
50  const cachePath = path.join(CACHE_DIR, '.cache.json')
51  const dataTime = +(new Date())
52  server.get('/-/all').once().reply(200, {
53    '_updated': dataTime,
54    'bar': { name: 'bar', version: '1.0.0' },
55    'foo': { name: 'foo', version: '1.0.0' }
56  }, {
57    date: 1234 // should never be used.
58  })
59  return _createEntryStream(cachePath, 600, {
60    registry: common.registry
61  }).then(({
62    entryStream: stream,
63    latest,
64    newEntries
65  }) => {
66    t.equals(latest, dataTime, '`latest` correctly extracted')
67    t.ok(newEntries, 'new entries need to be written to cache')
68    t.ok(stream, 'returned a stream')
69    return getStream.array(stream)
70  }).then(results => {
71    t.deepEquals(results, [{
72      name: 'bar',
73      version: '1.0.0'
74    }, {
75      name: 'foo',
76      version: '1.0.0'
77    }])
78    server.done()
79  })
80})
81
82test('createEntryStream cache only', function (t) {
83  setup()
84  const now = Date.now()
85  const cacheTime = now - 100000
86  const cachePath = path.join(CACHE_DIR, '.cache.json')
87  const fixture = new Tacks(File({
88    '_updated': cacheTime,
89    bar: { name: 'bar', version: '1.0.0' },
90    cool: { name: 'cool', version: '1.0.0' },
91    foo: { name: 'foo', version: '2.0.0' },
92    other: { name: 'other', version: '1.0.0' }
93  }))
94  fixture.create(cachePath)
95  fixOwner(cachePath)
96  return _createEntryStream(cachePath, 600, {
97    registry: common.registry
98  }).then(({
99    entryStream: stream,
100    latest,
101    newEntries
102  }) => {
103    t.equals(latest, cacheTime, '`latest` is cache time')
104    t.ok(stream, 'returned a stream')
105    t.notOk(newEntries, 'cache only means no need to write to cache')
106    return getStream.array(stream)
107  }).then(results => {
108    t.deepEquals(
109      results.map(function (pkg) { return pkg.name }),
110      ['bar', 'cool', 'foo', 'other'],
111      'packages deduped and sorted'
112    )
113    server.done()
114  })
115})
116
117test('createEntryStream merged stream', function (t) {
118  setup()
119  const now = Date.now()
120  const cacheTime = now - 6000000
121  server.get('/-/all/since?stale=update_after&startkey=' + cacheTime).once().reply(200, {
122    'bar': { name: 'bar', version: '2.0.0' },
123    'car': { name: 'car', version: '1.0.0' },
124    'foo': { name: 'foo', version: '1.0.0' }
125  }, {
126    date: (new Date(now)).toISOString()
127  })
128  const cachePath = path.join(CACHE_DIR, '.cache.json')
129  const fixture = new Tacks(File({
130    '_updated': cacheTime,
131    bar: { name: 'bar', version: '1.0.0' },
132    cool: { name: 'cool', version: '1.0.0' },
133    foo: { name: 'foo', version: '2.0.0' },
134    other: { name: 'other', version: '1.0.0' }
135  }))
136  fixture.create(cachePath)
137  fixOwner(cachePath)
138  return _createEntryStream(cachePath, 600, {
139    registry: common.registry
140  }).then(({
141    entryStream: stream,
142    latest,
143    newEntries
144  }) => {
145    t.equals(latest, now, '`latest` correctly extracted from header')
146    t.ok(stream, 'returned a stream')
147    t.ok(newEntries, 'cache update means entries should be written')
148    return getStream.array(stream)
149  }).then(results => {
150    t.deepEquals(
151      results.map(function (pkg) { return pkg.name }),
152      ['bar', 'car', 'cool', 'foo', 'other'],
153      'packages deduped and sorted'
154    )
155    t.deepEquals(results[0], {
156      name: 'bar',
157      version: '2.0.0'
158    }, 'update stream version wins on dedupe')
159    t.deepEquals(results[3], {
160      name: 'foo',
161      version: '1.0.0'
162    }, 'update stream version wins on dedupe even when the newer one has a lower semver.')
163    server.done()
164  })
165})
166
167test('createEntryStream no sources', function (t) {
168  setup()
169  const cachePath = path.join(CACHE_DIR, '.cache.json')
170  server.get('/-/all').once().reply(404, {})
171  return _createEntryStream(cachePath, 600, {
172    registry: common.registry
173  }).then(({
174    entryStream: stream,
175    latest,
176    newEntries
177  }) => {
178    throw new Error('should not succeed')
179  }, err => {
180    t.ok(err, 'no sources, got an error')
181    t.match(err.message, /No search sources available/, 'useful error message')
182  }).then(() => {
183    server.done()
184  })
185})
186
187test('cleanup', function (t) {
188  server.close()
189  t.done()
190})
191