1'use strict' 2 3const figgyPudding = require('figgy-pudding') 4const getStream = require('get-stream') 5const qs = require('querystring') 6const test = require('tap').test 7const tnock = require('./util/tnock.js') 8 9const OPTS = figgyPudding({ registry: {} })({ 10 registry: 'https://mock.reg/' 11}) 12 13const REG = OPTS.registry 14const search = require('../index.js') 15 16test('basic test', t => { 17 const query = qs.stringify({ 18 text: 'oo', 19 size: 20, 20 from: 0, 21 quality: 0.65, 22 popularity: 0.98, 23 maintenance: 0.5 24 }) 25 tnock(t, REG).get(`/-/v1/search?${query}`).once().reply(200, { 26 objects: [ 27 { package: { name: 'cool', version: '1.0.0' } }, 28 { package: { name: 'foo', version: '2.0.0' } } 29 ] 30 }) 31 return search('oo', OPTS).then(results => { 32 t.similar(results, [{ 33 name: 'cool', 34 version: '1.0.0' 35 }, { 36 name: 'foo', 37 version: '2.0.0' 38 }], 'got back an array of search results') 39 }) 40}) 41 42test('search.stream', t => { 43 const query = qs.stringify({ 44 text: 'oo', 45 size: 20, 46 from: 0, 47 quality: 0.65, 48 popularity: 0.98, 49 maintenance: 0.5 50 }) 51 tnock(t, REG).get(`/-/v1/search?${query}`).once().reply(200, { 52 objects: [ 53 { package: { name: 'cool', version: '1.0.0', date: new Date().toISOString() } }, 54 { package: { name: 'foo', version: '2.0.0' } } 55 ] 56 }) 57 return getStream.array( 58 search.stream('oo', OPTS) 59 ).then(results => { 60 t.similar(results, [{ 61 name: 'cool', 62 version: '1.0.0' 63 }, { 64 name: 'foo', 65 version: '2.0.0' 66 }], 'has a stream-based API function with identical results') 67 }) 68}) 69 70test('accepts a limit option', t => { 71 const query = qs.stringify({ 72 text: 'oo', 73 size: 3, 74 from: 0, 75 quality: 0.65, 76 popularity: 0.98, 77 maintenance: 0.5 78 }) 79 tnock(t, REG).get(`/-/v1/search?${query}`).once().reply(200, { 80 objects: [ 81 { package: { name: 'cool', version: '1.0.0' } }, 82 { package: { name: 'cool', version: '1.0.0' } }, 83 { package: { name: 'cool', version: '1.0.0' } }, 84 { package: { name: 'cool', version: '1.0.0' } } 85 ] 86 }) 87 return search('oo', OPTS.concat({ limit: 3 })).then(results => { 88 t.equal(results.length, 4, 'returns more results if endpoint does so') 89 }) 90}) 91 92test('accepts a from option', t => { 93 const query = qs.stringify({ 94 text: 'oo', 95 size: 20, 96 from: 1, 97 quality: 0.65, 98 popularity: 0.98, 99 maintenance: 0.5 100 }) 101 tnock(t, REG).get(`/-/v1/search?${query}`).once().reply(200, { 102 objects: [ 103 { package: { name: 'cool', version: '1.0.0' } }, 104 { package: { name: 'cool', version: '1.1.0' } }, 105 { package: { name: 'cool', version: '1.0.0' } }, 106 { package: { name: 'cool', version: '1.0.0' } } 107 ] 108 }) 109 return search('oo', OPTS.concat({ from: 1 })).then(results => { 110 t.equal(results.length, 4, 'returns more results if endpoint does so') 111 }) 112}) 113 114test('accepts quality/mainenance/popularity options', t => { 115 const query = qs.stringify({ 116 text: 'oo', 117 size: 20, 118 from: 0, 119 quality: 1, 120 popularity: 2, 121 maintenance: 3 122 }) 123 tnock(t, REG).get(`/-/v1/search?${query}`).once().reply(200, { 124 objects: [ 125 { package: { name: 'cool', version: '1.0.0' } }, 126 { package: { name: 'cool', version: '1.0.0' } }, 127 { package: { name: 'cool', version: '1.0.0' } }, 128 { package: { name: 'cool', version: '1.0.0' } } 129 ] 130 }) 131 return search('oo', OPTS.concat({ 132 quality: 1, 133 popularity: 2, 134 maintenance: 3 135 })).then(results => { 136 t.equal(results.length, 4, 'returns more results if endpoint does so') 137 }) 138}) 139 140test('sortBy: quality', t => { 141 const query = qs.stringify({ 142 text: 'oo', 143 size: 20, 144 from: 0, 145 quality: 1, 146 popularity: 0, 147 maintenance: 0 148 }) 149 tnock(t, REG).get(`/-/v1/search?${query}`).once().reply(200, { 150 objects: [ 151 { package: { name: 'cool', version: '1.0.0' } }, 152 { package: { name: 'cool', version: '1.0.0' } }, 153 { package: { name: 'cool', version: '1.0.0' } }, 154 { package: { name: 'cool', version: '1.0.0' } } 155 ] 156 }) 157 return search('oo', OPTS.concat({ 158 sortBy: 'quality' 159 })).then(results => { 160 t.equal(results.length, 4, 'returns more results if endpoint does so') 161 }) 162}) 163 164test('sortBy: popularity', t => { 165 const query = qs.stringify({ 166 text: 'oo', 167 size: 20, 168 from: 0, 169 quality: 0, 170 popularity: 1, 171 maintenance: 0 172 }) 173 tnock(t, REG).get(`/-/v1/search?${query}`).once().reply(200, { 174 objects: [ 175 { package: { name: 'cool', version: '1.0.0' } }, 176 { package: { name: 'cool', version: '1.0.0' } }, 177 { package: { name: 'cool', version: '1.0.0' } }, 178 { package: { name: 'cool', version: '1.0.0' } } 179 ] 180 }) 181 return search('oo', OPTS.concat({ 182 sortBy: 'popularity' 183 })).then(results => { 184 t.equal(results.length, 4, 'returns more results if endpoint does so') 185 }) 186}) 187 188test('sortBy: maintenance', t => { 189 const query = qs.stringify({ 190 text: 'oo', 191 size: 20, 192 from: 0, 193 quality: 0, 194 popularity: 0, 195 maintenance: 1 196 }) 197 tnock(t, REG).get(`/-/v1/search?${query}`).once().reply(200, { 198 objects: [ 199 { package: { name: 'cool', version: '1.0.0' } }, 200 { package: { name: 'cool', version: '1.0.0' } }, 201 { package: { name: 'cool', version: '1.0.0' } }, 202 { package: { name: 'cool', version: '1.0.0' } } 203 ] 204 }) 205 return search('oo', OPTS.concat({ 206 sortBy: 'maintenance' 207 })).then(results => { 208 t.equal(results.length, 4, 'returns more results if endpoint does so') 209 }) 210}) 211 212test('sortBy: optimal', t => { 213 const query = qs.stringify({ 214 text: 'oo', 215 size: 20, 216 from: 0, 217 quality: 0.65, 218 popularity: 0.98, 219 maintenance: 0.5 220 }) 221 tnock(t, REG).get(`/-/v1/search?${query}`).once().reply(200, { 222 objects: [ 223 { package: { name: 'cool', version: '1.0.0' } }, 224 { package: { name: 'cool', version: '1.0.0' } }, 225 { package: { name: 'cool', version: '1.0.0' } }, 226 { package: { name: 'cool', version: '1.0.0' } } 227 ] 228 }) 229 return search('oo', OPTS.concat({ 230 sortBy: 'optimal' 231 })).then(results => { 232 t.equal(results.length, 4, 'returns more results if endpoint does so') 233 }) 234}) 235 236test('detailed format', t => { 237 const query = qs.stringify({ 238 text: 'oo', 239 size: 20, 240 from: 0, 241 quality: 0, 242 popularity: 0, 243 maintenance: 1 244 }) 245 const results = [ 246 { 247 package: { name: 'cool', version: '1.0.0' }, 248 score: { 249 final: 0.9237841281241451, 250 detail: { 251 quality: 0.9270640902288084, 252 popularity: 0.8484861649808381, 253 maintenance: 0.9962706951777409 254 } 255 }, 256 searchScore: 100000.914 257 }, 258 { 259 package: { name: 'ok', version: '2.0.0' }, 260 score: { 261 final: 0.9237841281451, 262 detail: { 263 quality: 0.9270602288084, 264 popularity: 0.8461649808381, 265 maintenance: 0.9706951777409 266 } 267 }, 268 searchScore: 1000.91 269 } 270 ] 271 tnock(t, REG).get(`/-/v1/search?${query}`).once().reply(200, { 272 objects: results 273 }) 274 return search('oo', OPTS.concat({ 275 sortBy: 'maintenance', 276 detailed: true 277 })).then(res => { 278 t.deepEqual(res, results, 'return full-format results with opts.detailed') 279 }) 280}) 281 282test('space-separates and URI-encodes multiple search params', t => { 283 const query = qs.stringify({ 284 text: 'foo bar:baz quux?=', 285 size: 1, 286 from: 0, 287 quality: 1, 288 popularity: 2, 289 maintenance: 3 290 }) 291 tnock(t, REG).get(`/-/v1/search?${query}`).reply(200, { objects: [] }) 292 return search(['foo', 'bar:baz', 'quux?='], OPTS.concat({ 293 limit: 1, 294 quality: 1, 295 popularity: 2, 296 maintenance: 3 297 })).then( 298 () => t.ok(true, 'sent parameters correctly urlencoded') 299 ) 300}) 301