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