diff options
Diffstat (limited to 'deps/npm/test/tap/search.js')
-rw-r--r-- | deps/npm/test/tap/search.js | 545 |
1 files changed, 309 insertions, 236 deletions
diff --git a/deps/npm/test/tap/search.js b/deps/npm/test/tap/search.js index 138517c3bd..dcc46df78e 100644 --- a/deps/npm/test/tap/search.js +++ b/deps/npm/test/tap/search.js @@ -1,81 +1,185 @@ -var fs = require('graceful-fs') var path = require('path') - var mkdirp = require('mkdirp') -var mr = require('npm-registry-mock') var osenv = require('osenv') var rimraf = require('rimraf') +var cacheFile = require('npm-cache-filename') var test = require('tap').test +var Tacks = require('tacks') +var File = Tacks.File var common = require('../common-tap.js') -var pkg = path.resolve(__dirname, 'search') -var cache = path.resolve(pkg, 'cache') -var registryCache = path.resolve(cache, 'localhost_1337', '-', 'all') -var cacheJsonFile = path.resolve(registryCache, '.cache.json') - -var timeMock = { - epoch: 1411727900, - future: 1411727900 + 100, - all: 1411727900 + 25, - since: 0 // filled by since server callback -} +var PKG_DIR = path.resolve(__dirname, 'search') +var CACHE_DIR = path.resolve(PKG_DIR, 'cache') +var cacheBase = cacheFile(CACHE_DIR)(common.registry + '/-/all') +var cachePath = path.join(cacheBase, '.cache.json') -var EXEC_OPTS = {} +test('setup', function (t) { + t.pass('all set up') + t.done() +}) -var mocks = { - /* Since request, always response with an _update time > the time requested */ - sinceFuture: function (server) { - server.filteringPathRegEx(/startkey=[^&]*/g, function (s) { - var _allMock = JSON.parse(JSON.stringify(allMock)) - timeMock.since = _allMock._updated = s.replace('startkey=', '') - server.get('/-/all/since?stale=update_after&' + s) - .reply(200, _allMock) - return s - }) - }, - allFutureUpdatedOnly: function (server) { - server.get('/-/all') - .reply(200, stringifyUpdated(timeMock.future)) - }, - all: function (server) { - server.get('/-/all') - .reply(200, allMock) +test('notifies when there are no results', function (t) { + setup() + var now = Date.now() + var cacheContents = { + '_updated': now, + bar: { name: 'bar', version: '1.0.0' }, + cool: { name: 'cool', version: '1.0.0' }, + foo: { name: 'foo', version: '2.0.0' }, + other: { name: 'other', version: '1.0.0' } } -} + var fixture = new Tacks(File(cacheContents)) + fixture.create(cachePath) + common.npm([ + 'search', 'nomatcheswhatsoeverfromthis', + '--registry', common.registry, + '--loglevel', 'error', + '--cache', CACHE_DIR + ], {}, function (err, code, stdout, stderr) { + if (err) throw err + t.equal(stderr, '', 'no error output') + t.equal(code, 0, 'search gives 0 error code even if no matches') + t.match(stdout, /No matches found/, 'Useful message on search failure') + t.done() + }) +}) -test('No previous cache, init cache triggered by first search', function (t) { - cleanup() +test('spits out a useful error when no cache nor network', function (t) { + setup() + var cacheContents = {} + var fixture = new Tacks(File(cacheContents)) + fixture.create(cachePath) + common.npm([ + 'search', 'foo', + '--registry', common.registry, + '--loglevel', 'silly', + '--fetch-retry-mintimeout', 0, + '--fetch-retry-maxtimeout', 0, + '--cache', CACHE_DIR + ], {}, function (err, code, stdout, stderr) { + if (err) throw err + t.equal(code, 1, 'non-zero exit code') + t.equal(stdout, '', 'no stdout output') + t.match(stderr, /No search sources available/, 'useful error') + t.done() + }) +}) - mr({ port: common.port, plugin: mocks.allFutureUpdatedOnly }, function (err, s) { - t.ifError(err, 'mock registry started') - common.npm([ - 'search', 'do not do extra search work on my behalf', - '--registry', common.registry, - '--cache', cache, - '--loglevel', 'silent', - '--color', 'always' - ], - EXEC_OPTS, - function (err, code) { - s.close() - t.equal(code, 0, 'search finished successfully') - t.ifErr(err, 'search finished successfully') +test('can switch to JSON mode', function (t) { + setup() + var now = Date.now() + var cacheContents = { + '_updated': now, + bar: { name: 'bar', version: '1.0.0' }, + cool: { name: 'cool', version: '1.0.0' }, + foo: { name: 'foo', version: '2.0.0' }, + other: { name: 'other', version: '1.0.0' } + } + var fixture = new Tacks(File(cacheContents)) + fixture.create(cachePath) + common.npm([ + 'search', 'oo', + '--json', + '--registry', common.registry, + '--loglevel', 'error', + '--cache', CACHE_DIR + ], {}, function (err, code, stdout, stderr) { + if (err) throw err + t.deepEquals(JSON.parse(stdout), [ + { name: 'cool', version: '1.0.0' }, + { name: 'foo', version: '2.0.0' } + ], 'results returned as valid json') + t.equal(stderr, '', 'no error output') + t.equal(code, 0, 'search gives 0 error code even if no matches') + t.done() + }) +}) - t.ok( - fs.existsSync(cacheJsonFile), - cacheJsonFile + ' expected to have been created' - ) +test('JSON mode does not notify on empty', function (t) { + setup() + var now = Date.now() + var cacheContents = { + '_updated': now, + bar: { name: 'bar', version: '1.0.0' }, + cool: { name: 'cool', version: '1.0.0' }, + foo: { name: 'foo', version: '2.0.0' }, + other: { name: 'other', version: '1.0.0' } + } + var fixture = new Tacks(File(cacheContents)) + fixture.create(cachePath) + common.npm([ + 'search', 'nomatcheswhatsoeverfromthis', + '--json', + '--registry', common.registry, + '--loglevel', 'error', + '--cache', CACHE_DIR + ], {}, function (err, code, stdout, stderr) { + if (err) throw err + t.deepEquals(JSON.parse(stdout), [], 'no notification about no results') + t.equal(stderr, '', 'no error output') + t.equal(code, 0, 'search gives 0 error code even if no matches') + t.done() + }) +}) - var cacheData = JSON.parse(fs.readFileSync(cacheJsonFile, 'utf8')) - t.equal(cacheData._updated, String(timeMock.future)) - t.end() - }) +test('can switch to tab separated mode', function (t) { + setup() + var now = Date.now() + var cacheContents = { + '_updated': now, + bar: { name: 'bar', version: '1.0.0' }, + cool: { name: 'cool', version: '1.0.0' }, + foo: { name: 'foo', description: 'this\thas\ttabs', version: '2.0.0' }, + other: { name: 'other', version: '1.0.0' } + } + var fixture = new Tacks(File(cacheContents)) + fixture.create(cachePath) + common.npm([ + 'search', 'oo', + '--parseable', + '--registry', common.registry, + '--loglevel', 'error', + '--cache', CACHE_DIR + ], {}, function (err, code, stdout, stderr) { + if (err) throw err + t.equal(stdout, 'cool\t\t\tprehistoric\t\t\nfoo\tthis has tabs\t\tprehistoric\t\t\n', 'correct output, including replacing tabs in descriptions') + t.equal(stderr, '', 'no error output') + t.equal(code, 0, 'search gives 0 error code even if no matches') + t.done() + }) +}) + +test('tab mode does not notify on empty', function (t) { + setup() + var now = Date.now() + var cacheContents = { + '_updated': now, + bar: { name: 'bar', version: '1.0.0' }, + cool: { name: 'cool', version: '1.0.0' }, + foo: { name: 'foo', version: '2.0.0' }, + other: { name: 'other', version: '1.0.0' } + } + var fixture = new Tacks(File(cacheContents)) + fixture.create(cachePath) + common.npm([ + 'search', 'nomatcheswhatsoeverfromthis', + '--parseable', + '--registry', common.registry, + '--loglevel', 'error', + '--cache', CACHE_DIR + ], {}, function (err, code, stdout, stderr) { + if (err) throw err + t.equal(stdout, '', 'no notification about no results') + t.equal(stderr, '', 'no error output') + t.equal(code, 0, 'search gives 0 error code even if no matches') + t.done() }) }) test('no arguments provided should error', function (t) { - common.npm(['search'], [], function (err, code, stdout, stderr) { + cleanup() + common.npm(['search'], {}, function (err, code, stdout, stderr) { if (err) throw err t.equal(code, 1, 'search finished unsuccessfully') @@ -88,83 +192,157 @@ test('no arguments provided should error', function (t) { }) }) -test('previous cache, _updated set, should trigger since request', function (t) { - setupCache() - - function m (server) { - [ mocks.all, mocks.sinceFuture ].forEach(function (m) { - m(server) - }) - } - mr({ port: common.port, plugin: m }, function (err, s) { - t.ifError(err, 'mock registry started') - common.npm([ - 'search', 'do not do extra search work on my behalf', - '--registry', common.registry, - '--cache', cache, - '--loglevel', 'silly', - '--color', 'always' - ], - EXEC_OPTS, - function (err, code) { - s.close() - t.equal(code, 0, 'search finished successfully') - t.ifErr(err, 'search finished successfully') - - var cacheData = JSON.parse(fs.readFileSync(cacheJsonFile, 'utf8')) - t.equal( - cacheData._updated, - timeMock.since, - 'cache update time gotten from since response' - ) - t.end() - }) - }) -}) - var searches = [ { - term: 'f36b6a6123da50959741e2ce4d634f96ec668c56', - description: 'non-regex', - location: 241 + term: 'cool', + description: 'non-regex search', + location: 103 + }, + { + term: '/cool/', + description: 'regex search', + location: 103 }, { - term: '/f36b6a6123da50959741e2ce4d634f96ec668c56/', - description: 'regex', - location: 241 + term: 'cool', + description: 'searches name field', + location: 103 + }, + { + term: 'ool', + description: 'excludes matches for --searchexclude', + location: 205, + inject: { + other: { name: 'other', description: 'this is a simple tool' } + }, + extraOpts: ['--searchexclude', 'cool'] + }, + { + term: 'neat lib', + description: 'searches description field', + location: 141, + inject: { + cool: { + name: 'cool', version: '5.0.0', description: 'this is a neat lib' + } + } + }, + { + term: 'foo', + description: 'skips description field with --no-description', + location: 80, + inject: { + cool: { + name: 'cool', version: '5.0.0', description: 'foo bar!' + } + }, + extraOpts: ['--no-description'] + }, + { + term: 'zkat', + description: 'searches maintainers by name', + location: 155, + inject: { + cool: { + name: 'cool', + version: '5.0.0', + maintainers: [{ + name: 'zkat' + }] + } + } + }, + { + term: '=zkat', + description: 'searches maintainers unambiguously by =name', + location: 154, + inject: { + bar: { name: 'bar', description: 'zkat thing', version: '1.0.0' }, + cool: { + name: 'cool', + version: '5.0.0', + maintainers: [{ + name: 'zkat' + }] + } + } + }, + { + term: 'github.com', + description: 'searches projects by url', + location: 205, + inject: { + bar: { + name: 'bar', + url: 'gitlab.com/bar', + // For historical reasons, `url` is only present if `versions` is there + versions: ['1.0.0'], + version: '1.0.0' + }, + cool: { + name: 'cool', + version: '5.0.0', + versions: ['1.0.0'], + url: 'github.com/cool/cool' + } + } + }, + { + term: 'monad', + description: 'searches projects by keywords', + location: 197, + inject: { + cool: { + name: 'cool', + version: '5.0.0', + keywords: ['monads'] + } + } } ] searches.forEach(function (search) { - test(search.description + ' search in color', function (t) { - cleanup() - mr({ port: common.port, plugin: mocks.all }, function (er, s) { - common.npm([ - 'search', search.term, - '--registry', common.registry, - '--cache', cache, - '--loglevel', 'silent', - '--color', 'always' - ], - EXEC_OPTS, - function (err, code, stdout) { - s.close() - t.equal(code, 0, 'search finished successfully') - t.ifErr(err, 'search finished successfully') - // \033 == \u001B - var markStart = '\u001B\\[[0-9][0-9]m' - var markEnd = '\u001B\\[0m' + test(search.description, function (t) { + setup() + var now = Date.now() + var cacheContents = { + '_updated': now, + bar: { name: 'bar', version: '1.0.0' }, + cool: { name: 'cool', version: '5.0.0' }, + foo: { name: 'foo', version: '2.0.0' }, + other: { name: 'other', version: '1.0.0' } + } + for (var k in search.inject) { + cacheContents[k] = search.inject[k] + } + var fixture = new Tacks(File(cacheContents)) + fixture.create(cachePath) + common.npm([ + 'search', search.term, + '--registry', common.registry, + '--cache', CACHE_DIR, + '--loglevel', 'error', + '--color', 'always' + ].concat(search.extraOpts || []), + {}, + function (err, code, stdout, stderr) { + t.equal(stderr, '', 'no error output') + t.notEqual(stdout, '', 'got output') + t.equal(code, 0, 'search finished successfully') + t.ifErr(err, 'search finished successfully') + // \033 == \u001B + var markStart = '\u001B\\[[0-9][0-9]m' + var markEnd = '\u001B\\[0m' - var re = new RegExp(markStart + '.*?' + markEnd) + var re = new RegExp(markStart + '.*?' + markEnd) - var cnt = stdout.search(re) - t.equal( - cnt, - search.location, - search.description + ' search for ' + search.term - ) - t.end() - }) + var cnt = stdout.search(re) + t.equal( + cnt, + search.location, + search.description + ' search for ' + search.term + ) + t.end() }) }) }) @@ -174,117 +352,12 @@ test('cleanup', function (t) { t.end() }) -function cleanup () { - process.chdir(osenv.tmpdir()) - rimraf.sync(pkg) -} - -function setupCache () { +function setup () { cleanup() - mkdirp.sync(cache) - mkdirp.sync(registryCache) - var res = fs.writeFileSync(cacheJsonFile, stringifyUpdated(timeMock.epoch)) - if (res) throw new Error('Creating cache file failed') + mkdirp.sync(cacheBase) } -function stringifyUpdated (time) { - return JSON.stringify({ _updated: String(time) }) -} - -var allMock = { - '_updated': timeMock.all, - 'generator-frontcow': { - 'name': 'generator-frontcow', - 'description': 'f36b6a6123da50959741e2ce4d634f96ec668c56 This is a fake description to ensure we do not accidentally search the real npm registry or use some kind of cache', - 'dist-tags': { - 'latest': '0.1.19' - }, - 'maintainers': [ - { - 'name': 'bcabanes', - 'email': 'contact@benjamincabanes.com' - } - ], - 'homepage': 'https://github.com/bcabanes/generator-frontcow', - 'keywords': [ - 'sass', - 'frontend', - 'yeoman-generator', - 'atomic', - 'design', - 'sass', - 'foundation', - 'foundation5', - 'atomic design', - 'bourbon', - 'polyfill', - 'font awesome' - ], - 'repository': { - 'type': 'git', - 'url': 'https://github.com/bcabanes/generator-frontcow' - }, - 'author': { - 'name': 'ben', - 'email': 'contact@benjamincabanes.com', - 'url': 'https://github.com/bcabanes' - }, - 'bugs': { - 'url': 'https://github.com/bcabanes/generator-frontcow/issues' - }, - 'license': 'MIT', - 'readmeFilename': 'README.md', - 'time': { - 'modified': '2014-10-03T02:26:18.406Z' - }, - 'versions': { - '0.1.19': 'latest' - } - }, - 'marko': { - 'name': 'marko', - 'description': 'Marko is an extensible, streaming, asynchronous, high performance, HTML-based templating language that can be used in Node.js or in the browser.', - 'dist-tags': { - 'latest': '1.2.16' - }, - 'maintainers': [ - { - 'name': 'pnidem', - 'email': 'pnidem@gmail.com' - }, - { - 'name': 'philidem', - 'email': 'phillip.idem@gmail.com' - } - ], - 'homepage': 'https://github.com/raptorjs/marko', - 'keywords': [ - 'templating', - 'template', - 'async', - 'streaming' - ], - 'repository': { - 'type': 'git', - 'url': 'https://github.com/raptorjs/marko.git' - }, - 'author': { - 'name': 'Patrick Steele-Idem', - 'email': 'pnidem@gmail.com' - }, - 'bugs': { - 'url': 'https://github.com/raptorjs/marko/issues' - }, - 'license': 'Apache License v2.0', - 'readmeFilename': 'README.md', - 'users': { - 'pnidem': true - }, - 'time': { - 'modified': '2014-10-03T02:27:31.775Z' - }, - 'versions': { - '1.2.16': 'latest' - } - } +function cleanup () { + process.chdir(osenv.tmpdir()) + rimraf.sync(PKG_DIR) } |