• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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