1const t = require('tap') 2 3const { load: loadMockNpm } = require('../../fixtures/mock-npm.js') 4const MockRegistry = require('@npmcli/mock-registry') 5 6const token = 'test-auth-token' 7const auth = { '//registry.npmjs.org/:_authToken': 'test-auth-token' } 8 9t.test('completion', async t => { 10 const { access } = await loadMockNpm(t, { command: 'access' }) 11 const testComp = (argv, expect) => { 12 const res = access.completion({ conf: { argv: { remain: argv } } }) 13 t.resolves(res, expect, argv.join(' ')) 14 } 15 16 testComp(['npm', 'access'], ['list', 'get', 'set', 'grant', 'revoke']) 17 testComp(['npm', 'access', 'list'], ['packages', 'collaborators']) 18 testComp(['npm', 'access', 'ls'], ['packages', 'collaborators']) 19 testComp(['npm', 'access', 'get'], ['status']) 20 testComp(['npm', 'access', 'set'], [ 21 'status=public', 22 'status=private', 23 'mfa=none', 24 'mfa=publish', 25 'mfa=automation', 26 '2fa=none', 27 '2fa=publish', 28 '2fa=automation', 29 ]) 30 testComp(['npm', 'access', 'grant'], ['read-only', 'read-write']) 31 testComp(['npm', 'access', 'revoke'], []) 32 testComp(['npm', 'access', 'grant', ''], []) 33 34 await t.rejects( 35 access.completion({ conf: { argv: { remain: ['npm', 'access', 'foobar'] } } }), 36 { message: 'foobar not recognized' } 37 ) 38}) 39 40t.test('command required', async t => { 41 const { npm } = await loadMockNpm(t) 42 await t.rejects(npm.exec('access', []), { code: 'EUSAGE' }) 43}) 44 45t.test('unrecognized command', async t => { 46 const { npm } = await loadMockNpm(t) 47 await t.rejects( 48 npm.exec('access', ['blerg']), { code: 'EUSAGE' }) 49}) 50 51t.test('subcommand required', async t => { 52 const { npm } = await loadMockNpm(t) 53 await t.rejects(npm.exec('access', ['get']), { code: 'EUSAGE' }) 54}) 55 56t.test('unrecognized subcommand', async t => { 57 const { npm } = await loadMockNpm(t) 58 await t.rejects(npm.exec('access', ['list', 'blerg']), { code: 'EUSAGE' }) 59}) 60 61t.test('grant', t => { 62 t.test('invalid permissions', async t => { 63 const { npm } = await loadMockNpm(t) 64 await t.rejects(npm.exec('access', ['grant', 'other']), { code: 'EUSAGE' }) 65 }) 66 67 t.test('no permissions', async t => { 68 const { npm } = await loadMockNpm(t) 69 await t.rejects(npm.exec('access', ['grant', 'read-only']), { code: 'EUSAGE' }) 70 }) 71 72 t.test('read-only', async t => { 73 const authToken = 'abcd1234' 74 const { npm } = await loadMockNpm(t, { 75 config: { 76 '//registry.npmjs.org/:_authToken': authToken, 77 }, 78 }) 79 const registry = new MockRegistry({ 80 tap: t, 81 registry: npm.config.get('registry'), 82 authorization: authToken, 83 }) 84 const permissions = 'read-only' 85 registry.setPermissions({ spec: '@npmcli/test-package', team: '@npm:test-team', permissions }) 86 await npm.exec('access', ['grant', permissions, '@npm:test-team', '@npmcli/test-package']) 87 }) 88 t.end() 89}) 90 91t.test('revoke', t => { 92 t.test('success', async t => { 93 const authToken = 'abcd1234' 94 const { npm } = await loadMockNpm(t, { 95 config: { 96 '//registry.npmjs.org/:_authToken': authToken, 97 }, 98 }) 99 const registry = new MockRegistry({ 100 tap: t, 101 registry: npm.config.get('registry'), 102 authorization: authToken, 103 }) 104 registry.removePermissions({ spec: '@npmcli/test-package', team: '@npm:test-team' }) 105 await npm.exec('access', ['revoke', '@npm:test-team', '@npmcli/test-package']) 106 }) 107 t.end() 108}) 109 110t.test('list', t => { 111 const packages = { 112 '@npmcli/test-package': 'read', 113 '@npmcli/other-package': 'write', 114 } 115 const collaborators = { 116 npm: 'write', 117 github: 'read', 118 } 119 t.test('invalid subcommand', async t => { 120 const { npm } = await loadMockNpm(t) 121 await t.rejects(npm.exec('access', ['list', 'other'], { code: 'EUSAGE' })) 122 }) 123 124 t.test('packages explicit user', async t => { 125 const { npm, outputs } = await loadMockNpm(t) 126 const registry = new MockRegistry({ 127 tap: t, 128 registry: npm.config.get('registry'), 129 }) 130 registry.getPackages({ team: '@npm:test-team', packages }) 131 await npm.exec('access', ['list', 'packages', '@npm:test-team']) 132 t.same(outputs, [ 133 ['@npmcli/other-package: read-write'], 134 ['@npmcli/test-package: read-only'], 135 ]) 136 }) 137 138 t.test('packages infer user', async t => { 139 const { npm, outputs } = await loadMockNpm(t, { config: { ...auth } }) 140 const registry = new MockRegistry({ 141 tap: t, 142 registry: npm.config.get('registry'), 143 authorization: token, 144 }) 145 registry.whoami({ username: 'npm' }) 146 registry.getPackages({ team: 'npm', packages }) 147 await npm.exec('access', ['list', 'packages']) 148 t.same(outputs, [ 149 ['@npmcli/other-package: read-write'], 150 ['@npmcli/test-package: read-only'], 151 ]) 152 }) 153 154 t.test('packages json', async t => { 155 const { npm, joinedOutput } = await loadMockNpm(t, { config: { json: true } }) 156 const registry = new MockRegistry({ 157 tap: t, 158 registry: npm.config.get('registry'), 159 }) 160 registry.getPackages({ team: '@npm:test-team', packages }) 161 await npm.exec('access', ['list', 'packages', '@npm:test-team']) 162 t.same(JSON.parse(joinedOutput()), { 163 '@npmcli/test-package': 'read-only', 164 '@npmcli/other-package': 'read-write', 165 }) 166 }) 167 168 t.test('collaborators explicit package', async t => { 169 const { npm, outputs } = await loadMockNpm(t) 170 const registry = new MockRegistry({ 171 tap: t, 172 registry: npm.config.get('registry'), 173 }) 174 registry.getCollaborators({ spec: '@npmcli/test-package', collaborators }) 175 await npm.exec('access', ['list', 'collaborators', '@npmcli/test-package']) 176 t.same(outputs, [ 177 ['github: read-only'], 178 ['npm: read-write'], 179 ]) 180 }) 181 182 t.test('collaborators user', async t => { 183 const { npm, outputs } = await loadMockNpm(t) 184 const registry = new MockRegistry({ 185 tap: t, 186 registry: npm.config.get('registry'), 187 }) 188 registry.getCollaborators({ spec: '@npmcli/test-package', collaborators }) 189 await npm.exec('access', ['list', 'collaborators', '@npmcli/test-package', 'npm']) 190 t.same(outputs, [ 191 ['npm: read-write'], 192 ]) 193 }) 194 t.end() 195}) 196 197t.test('get', t => { 198 t.test('invalid subcommand', async t => { 199 const { npm } = await loadMockNpm(t) 200 await t.rejects(npm.exec('access', ['get', 'other'], { code: 'EUSAGE' })) 201 }) 202 203 t.test('status explicit package', async t => { 204 const { npm, outputs } = await loadMockNpm(t) 205 const registry = new MockRegistry({ 206 tap: t, 207 registry: npm.config.get('registry'), 208 }) 209 registry.getVisibility({ spec: '@npmcli/test-package', visibility: { public: true } }) 210 await npm.exec('access', ['get', 'status', '@npmcli/test-package']) 211 t.same(outputs, [['@npmcli/test-package: public']]) 212 }) 213 t.test('status implicit package', async t => { 214 const { npm, outputs } = await loadMockNpm(t, { 215 prefixDir: { 216 'package.json': JSON.stringify({ name: '@npmcli/test-package' }), 217 }, 218 }) 219 const registry = new MockRegistry({ 220 tap: t, 221 registry: npm.config.get('registry'), 222 }) 223 registry.getVisibility({ spec: '@npmcli/test-package', visibility: { public: true } }) 224 await npm.exec('access', ['get', 'status']) 225 t.same(outputs, [['@npmcli/test-package: public']]) 226 }) 227 t.test('status no package', async t => { 228 const { npm } = await loadMockNpm(t) 229 await t.rejects( 230 npm.exec('access', ['get', 'status']), 231 { code: 'ENOENT' } 232 ) 233 }) 234 t.test('status invalid package', async t => { 235 const { npm } = await loadMockNpm(t, { 236 prefixDir: { 'package.json': '[not:valid_json}' }, 237 }) 238 await t.rejects( 239 npm.exec('access', ['get', 'status']), 240 { code: 'EJSONPARSE' } 241 ) 242 }) 243 t.test('status json', async t => { 244 const { npm, joinedOutput } = await loadMockNpm(t, { config: { json: true } }) 245 const registry = new MockRegistry({ 246 tap: t, 247 registry: npm.config.get('registry'), 248 }) 249 registry.getVisibility({ spec: '@npmcli/test-package', visibility: { public: true } }) 250 await npm.exec('access', ['get', 'status', '@npmcli/test-package']) 251 t.same(JSON.parse(joinedOutput()), { '@npmcli/test-package': 'public' }) 252 }) 253 t.end() 254}) 255 256t.test('set', t => { 257 t.test('status=public', async t => { 258 const { npm, outputs } = await loadMockNpm(t) 259 const registry = new MockRegistry({ 260 tap: t, 261 registry: npm.config.get('registry'), 262 }) 263 registry.setAccess({ spec: '@npmcli/test-package', body: { access: 'public' } }) 264 registry.getVisibility({ spec: '@npmcli/test-package', visibility: { public: true } }) 265 await npm.exec('access', ['set', 'status=public', '@npmcli/test-package']) 266 t.same(outputs, [['@npmcli/test-package: public']]) 267 }) 268 t.test('status=private', async t => { 269 const { npm, outputs } = await loadMockNpm(t) 270 const registry = new MockRegistry({ 271 tap: t, 272 registry: npm.config.get('registry'), 273 }) 274 registry.setAccess({ spec: '@npmcli/test-package', body: { access: 'restricted' } }) 275 registry.getVisibility({ spec: '@npmcli/test-package', visibility: { public: false } }) 276 await npm.exec('access', ['set', 'status=private', '@npmcli/test-package']) 277 t.same(outputs, [['@npmcli/test-package: private']]) 278 }) 279 t.test('status=invalid', async t => { 280 const { npm } = await loadMockNpm(t) 281 await t.rejects( 282 npm.exec('access', ['set', 'status=invalid', '@npmcli/test-package']), 283 { code: 'EUSAGE' } 284 ) 285 }) 286 t.test('status non scoped package', async t => { 287 const { npm } = await loadMockNpm(t) 288 await t.rejects( 289 npm.exec('access', ['set', 'status=public', 'npm']), 290 { code: 'EUSAGE' } 291 ) 292 }) 293 t.test('mfa=none', async t => { 294 const { npm } = await loadMockNpm(t) 295 const registry = new MockRegistry({ 296 tap: t, 297 registry: npm.config.get('registry'), 298 }) 299 registry.setAccess({ spec: '@npmcli/test-package', 300 body: { 301 publish_requires_tfa: false, 302 } }) 303 await npm.exec('access', ['set', 'mfa=none', '@npmcli/test-package']) 304 }) 305 t.test('mfa=publish', async t => { 306 const { npm } = await loadMockNpm(t) 307 const registry = new MockRegistry({ 308 tap: t, 309 registry: npm.config.get('registry'), 310 }) 311 registry.setAccess({ spec: '@npmcli/test-package', 312 body: { 313 publish_requires_tfa: true, 314 automation_token_overrides_tfa: false, 315 } }) 316 await npm.exec('access', ['set', 'mfa=publish', '@npmcli/test-package']) 317 }) 318 t.test('mfa=automation', async t => { 319 const { npm } = await loadMockNpm(t) 320 const registry = new MockRegistry({ 321 tap: t, 322 registry: npm.config.get('registry'), 323 }) 324 registry.setAccess({ spec: '@npmcli/test-package', 325 body: { 326 publish_requires_tfa: true, 327 automation_token_overrides_tfa: true, 328 } }) 329 await npm.exec('access', ['set', 'mfa=automation', '@npmcli/test-package']) 330 }) 331 t.test('mfa=invalid', async t => { 332 const { npm } = await loadMockNpm(t) 333 await t.rejects( 334 npm.exec('access', ['set', 'mfa=invalid']), 335 { code: 'EUSAGE' } 336 ) 337 }) 338 t.test('2fa=none', async t => { 339 const { npm } = await loadMockNpm(t) 340 const registry = new MockRegistry({ 341 tap: t, 342 registry: npm.config.get('registry'), 343 }) 344 registry.setAccess({ spec: '@npmcli/test-package', 345 body: { 346 publish_requires_tfa: false, 347 } }) 348 await npm.exec('access', ['set', '2fa=none', '@npmcli/test-package']) 349 }) 350 t.test('2fa=publish', async t => { 351 const { npm } = await loadMockNpm(t) 352 const registry = new MockRegistry({ 353 tap: t, 354 registry: npm.config.get('registry'), 355 }) 356 registry.setAccess({ spec: '@npmcli/test-package', 357 body: { 358 publish_requires_tfa: true, 359 automation_token_overrides_tfa: false, 360 } }) 361 await npm.exec('access', ['set', '2fa=publish', '@npmcli/test-package']) 362 }) 363 t.test('2fa=automation', async t => { 364 const { npm } = await loadMockNpm(t) 365 const registry = new MockRegistry({ 366 tap: t, 367 registry: npm.config.get('registry'), 368 }) 369 registry.setAccess({ spec: '@npmcli/test-package', 370 body: { 371 publish_requires_tfa: true, 372 automation_token_overrides_tfa: true, 373 } }) 374 await npm.exec('access', ['set', '2fa=automation', '@npmcli/test-package']) 375 }) 376 t.test('2fa=invalid', async t => { 377 const { npm } = await loadMockNpm(t) 378 await t.rejects( 379 npm.exec('access', ['set', '2fa=invalid']), 380 { code: 'EUSAGE' } 381 ) 382 }) 383 384 t.end() 385}) 386