1'use strict' 2 3const figgyPudding = require('figgy-pudding') 4const getStream = require('get-stream') 5const {test} = require('tap') 6const tnock = require('./util/tnock.js') 7 8const access = require('../index.js') 9 10const REG = 'http://localhost:1337' 11const OPTS = figgyPudding({})({ 12 registry: REG 13}) 14 15test('access public', t => { 16 tnock(t, REG).post( 17 '/-/package/%40foo%2Fbar/access', {access: 'public'} 18 ).reply(200) 19 return access.public('@foo/bar', OPTS).then(ret => { 20 t.deepEqual(ret, true, 'request succeeded') 21 }) 22}) 23 24test('access restricted', t => { 25 tnock(t, REG).post( 26 '/-/package/%40foo%2Fbar/access', {access: 'restricted'} 27 ).reply(200) 28 return access.restricted('@foo/bar', OPTS).then(ret => { 29 t.deepEqual(ret, true, 'request succeeded') 30 }) 31}) 32 33test('access 2fa-required', t => { 34 tnock(t, REG).post('/-/package/%40foo%2Fbar/access', { 35 publish_requires_tfa: true 36 }).reply(200, {ok: true}) 37 return access.tfaRequired('@foo/bar', OPTS).then(ret => { 38 t.deepEqual(ret, true, 'request succeeded') 39 }) 40}) 41 42test('access 2fa-not-required', t => { 43 tnock(t, REG).post('/-/package/%40foo%2Fbar/access', { 44 publish_requires_tfa: false 45 }).reply(200, {ok: true}) 46 return access.tfaNotRequired('@foo/bar', OPTS).then(ret => { 47 t.deepEqual(ret, true, 'request succeeded') 48 }) 49}) 50 51test('access grant basic read-write', t => { 52 tnock(t, REG).put('/-/team/myorg/myteam/package', { 53 package: '@foo/bar', 54 permissions: 'read-write' 55 }).reply(201) 56 return access.grant( 57 '@foo/bar', 'myorg:myteam', 'read-write', OPTS 58 ).then(ret => { 59 t.deepEqual(ret, true, 'request succeeded') 60 }) 61}) 62 63test('access grant basic read-only', t => { 64 tnock(t, REG).put('/-/team/myorg/myteam/package', { 65 package: '@foo/bar', 66 permissions: 'read-only' 67 }).reply(201) 68 return access.grant( 69 '@foo/bar', 'myorg:myteam', 'read-only', OPTS 70 ).then(ret => { 71 t.deepEqual(ret, true, 'request succeeded') 72 }) 73}) 74 75test('access grant bad perm', t => { 76 return access.grant( 77 '@foo/bar', 'myorg:myteam', 'unknown', OPTS 78 ).then(ret => { 79 throw new Error('should not have succeeded') 80 }, err => { 81 t.match( 82 err.message, 83 /must be.*read-write.*read-only/, 84 'only read-write and read-only are accepted' 85 ) 86 }) 87}) 88 89test('access grant no entity', t => { 90 return access.grant( 91 '@foo/bar', undefined, 'read-write', OPTS 92 ).then(ret => { 93 throw new Error('should not have succeeded') 94 }, err => { 95 t.match( 96 err.message, 97 /Expected string/, 98 'passing undefined entity gives useful error' 99 ) 100 }) 101}) 102 103test('access grant basic unscoped', t => { 104 tnock(t, REG).put('/-/team/myorg/myteam/package', { 105 package: 'bar', 106 permissions: 'read-write' 107 }).reply(201) 108 return access.grant( 109 'bar', 'myorg:myteam', 'read-write', OPTS 110 ).then(ret => { 111 t.deepEqual(ret, true, 'request succeeded') 112 }) 113}) 114 115test('access revoke basic', t => { 116 tnock(t, REG).delete('/-/team/myorg/myteam/package', { 117 package: '@foo/bar' 118 }).reply(200) 119 return access.revoke('@foo/bar', 'myorg:myteam', OPTS).then(ret => { 120 t.deepEqual(ret, true, 'request succeeded') 121 }) 122}) 123 124test('access revoke basic unscoped', t => { 125 tnock(t, REG).delete('/-/team/myorg/myteam/package', { 126 package: 'bar' 127 }).reply(200, {accessChanged: true}) 128 return access.revoke('bar', 'myorg:myteam', OPTS).then(ret => { 129 t.deepEqual(ret, true, 'request succeeded') 130 }) 131}) 132 133test('ls-packages on team', t => { 134 const serverPackages = { 135 '@foo/bar': 'write', 136 '@foo/util': 'read', 137 '@foo/other': 'shrödinger' 138 } 139 const clientPackages = { 140 '@foo/bar': 'read-write', 141 '@foo/util': 'read-only', 142 '@foo/other': 'shrödinger' 143 } 144 tnock(t, REG).get( 145 '/-/team/myorg/myteam/package?format=cli' 146 ).reply(200, serverPackages) 147 return access.lsPackages('myorg:myteam', OPTS).then(data => { 148 t.deepEqual(data, clientPackages, 'got client package info') 149 }) 150}) 151 152test('ls-packages on org', t => { 153 const serverPackages = { 154 '@foo/bar': 'write', 155 '@foo/util': 'read', 156 '@foo/other': 'shrödinger' 157 } 158 const clientPackages = { 159 '@foo/bar': 'read-write', 160 '@foo/util': 'read-only', 161 '@foo/other': 'shrödinger' 162 } 163 tnock(t, REG).get( 164 '/-/org/myorg/package?format=cli' 165 ).reply(200, serverPackages) 166 return access.lsPackages('myorg', OPTS).then(data => { 167 t.deepEqual(data, clientPackages, 'got client package info') 168 }) 169}) 170 171test('ls-packages on user', t => { 172 const serverPackages = { 173 '@foo/bar': 'write', 174 '@foo/util': 'read', 175 '@foo/other': 'shrödinger' 176 } 177 const clientPackages = { 178 '@foo/bar': 'read-write', 179 '@foo/util': 'read-only', 180 '@foo/other': 'shrödinger' 181 } 182 const srv = tnock(t, REG) 183 srv.get('/-/org/myuser/package?format=cli').reply(404, {error: 'not found'}) 184 srv.get('/-/user/myuser/package?format=cli').reply(200, serverPackages) 185 return access.lsPackages('myuser', OPTS).then(data => { 186 t.deepEqual(data, clientPackages, 'got client package info') 187 }) 188}) 189 190test('ls-packages error on team', t => { 191 tnock(t, REG).get('/-/team/myorg/myteam/package?format=cli').reply(404) 192 return access.lsPackages('myorg:myteam', OPTS).then( 193 () => { throw new Error('should not have succeeded') }, 194 err => t.equal(err.code, 'E404', 'spit out 404 directly if team provided') 195 ) 196}) 197 198test('ls-packages error on user', t => { 199 const srv = tnock(t, REG) 200 srv.get('/-/org/myuser/package?format=cli').reply(404, {error: 'not found'}) 201 srv.get('/-/user/myuser/package?format=cli').reply(404, {error: 'not found'}) 202 return access.lsPackages('myuser', OPTS).then( 203 () => { throw new Error('should not have succeeded') }, 204 err => t.equal(err.code, 'E404', 'spit out 404 if both reqs fail') 205 ) 206}) 207 208test('ls-packages bad response', t => { 209 tnock(t, REG).get( 210 '/-/team/myorg/myteam/package?format=cli' 211 ).reply(200, JSON.stringify(null)) 212 return access.lsPackages('myorg:myteam', OPTS).then(data => { 213 t.deepEqual(data, null, 'succeeds with null') 214 }) 215}) 216 217test('ls-packages stream', t => { 218 const serverPackages = { 219 '@foo/bar': 'write', 220 '@foo/util': 'read', 221 '@foo/other': 'shrödinger' 222 } 223 const clientPackages = [ 224 ['@foo/bar', 'read-write'], 225 ['@foo/util', 'read-only'], 226 ['@foo/other', 'shrödinger'] 227 ] 228 tnock(t, REG).get( 229 '/-/team/myorg/myteam/package?format=cli' 230 ).reply(200, serverPackages) 231 return getStream.array( 232 access.lsPackages.stream('myorg:myteam', OPTS) 233 ).then(data => { 234 t.deepEqual(data, clientPackages, 'got streamed client package info') 235 }) 236}) 237 238test('ls-collaborators', t => { 239 const serverCollaborators = { 240 'myorg:myteam': 'write', 241 'myorg:anotherteam': 'read', 242 'myorg:thirdteam': 'special-case' 243 } 244 const clientCollaborators = { 245 'myorg:myteam': 'read-write', 246 'myorg:anotherteam': 'read-only', 247 'myorg:thirdteam': 'special-case' 248 } 249 tnock(t, REG).get( 250 '/-/package/%40foo%2Fbar/collaborators?format=cli' 251 ).reply(200, serverCollaborators) 252 return access.lsCollaborators('@foo/bar', OPTS).then(data => { 253 t.deepEqual(data, clientCollaborators, 'got collaborators') 254 }) 255}) 256 257test('ls-collaborators stream', t => { 258 const serverCollaborators = { 259 'myorg:myteam': 'write', 260 'myorg:anotherteam': 'read', 261 'myorg:thirdteam': 'special-case' 262 } 263 const clientCollaborators = [ 264 ['myorg:myteam', 'read-write'], 265 ['myorg:anotherteam', 'read-only'], 266 ['myorg:thirdteam', 'special-case'] 267 ] 268 tnock(t, REG).get( 269 '/-/package/%40foo%2Fbar/collaborators?format=cli' 270 ).reply(200, serverCollaborators) 271 return getStream.array( 272 access.lsCollaborators.stream('@foo/bar', OPTS) 273 ).then(data => { 274 t.deepEqual(data, clientCollaborators, 'got collaborators') 275 }) 276}) 277 278test('ls-collaborators w/scope', t => { 279 const serverCollaborators = { 280 'myorg:myteam': 'write', 281 'myorg:anotherteam': 'read', 282 'myorg:thirdteam': 'special-case' 283 } 284 const clientCollaborators = { 285 'myorg:myteam': 'read-write', 286 'myorg:anotherteam': 'read-only', 287 'myorg:thirdteam': 'special-case' 288 } 289 tnock(t, REG).get( 290 '/-/package/%40foo%2Fbar/collaborators?format=cli&user=zkat' 291 ).reply(200, serverCollaborators) 292 return access.lsCollaborators('@foo/bar', 'zkat', OPTS).then(data => { 293 t.deepEqual(data, clientCollaborators, 'got collaborators') 294 }) 295}) 296 297test('ls-collaborators w/o scope', t => { 298 const serverCollaborators = { 299 'myorg:myteam': 'write', 300 'myorg:anotherteam': 'read', 301 'myorg:thirdteam': 'special-case' 302 } 303 const clientCollaborators = { 304 'myorg:myteam': 'read-write', 305 'myorg:anotherteam': 'read-only', 306 'myorg:thirdteam': 'special-case' 307 } 308 tnock(t, REG).get( 309 '/-/package/bar/collaborators?format=cli&user=zkat' 310 ).reply(200, serverCollaborators) 311 return access.lsCollaborators('bar', 'zkat', OPTS).then(data => { 312 t.deepEqual(data, clientCollaborators, 'got collaborators') 313 }) 314}) 315 316test('ls-collaborators bad response', t => { 317 tnock(t, REG).get( 318 '/-/package/%40foo%2Fbar/collaborators?format=cli' 319 ).reply(200, JSON.stringify(null)) 320 return access.lsCollaborators('@foo/bar', null, OPTS).then(data => { 321 t.deepEqual(data, null, 'succeeds with null') 322 }) 323}) 324 325test('error on non-registry specs', t => { 326 const resolve = () => { throw new Error('should not succeed') } 327 const reject = err => t.match( 328 err.message, /spec.*must be a registry spec/, 'registry spec required' 329 ) 330 return Promise.all([ 331 access.public('foo/bar').then(resolve, reject), 332 access.restricted('foo/bar').then(resolve, reject), 333 access.grant('foo/bar', 'myorg', 'myteam', 'read-only').then(resolve, reject), 334 access.revoke('foo/bar', 'myorg', 'myteam').then(resolve, reject), 335 access.lsCollaborators('foo/bar').then(resolve, reject), 336 access.tfaRequired('foo/bar').then(resolve, reject), 337 access.tfaNotRequired('foo/bar').then(resolve, reject) 338 ]) 339}) 340 341test('edit', t => { 342 t.equal(typeof access.edit, 'function', 'access.edit exists') 343 t.throws(() => { 344 access.edit() 345 }, /Not implemented/, 'directly throws NIY message') 346 t.done() 347}) 348