Skip to content

Commit b8f25ed

Browse files
committed
fixes tests
1 parent eb09565 commit b8f25ed

3 files changed

Lines changed: 112 additions & 55 deletions

File tree

lib/commands/profile.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ class Profile extends BaseCommand {
249249
log.info('profile', 'Determine if tfa is pending')
250250
const userInfo = await get(conf)
251251

252-
if (!userInfo.tfa.pending && userInfo.tfa.mode === mode) {
252+
if (!userInfo?.tfa?.pending && userInfo?.tfa?.mode === mode) {
253253
output.standard('Two factor authentication is already enabled and set to ' + mode)
254254
return
255255
}
@@ -312,7 +312,9 @@ class Profile extends BaseCommand {
312312
}
313313

314314
log.info('profile', 'Setting two-factor authentication to ' + mode)
315-
const challenge = await otplease(this.npm, conf, c => set(info, c))
315+
const challenge = await otplease(this.npm, conf, async c => {
316+
return await set(info, c)
317+
})
316318

317319
if (challenge.tfa && challenge.tfa.mode) {
318320
output.standard('Two factor authentication mode changed to: ' + mode)

lib/utils/auth.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ const otplease = async (npm, opts, fn) => {
1010
if (!process.stdin.isTTY || !process.stdout.isTTY) {
1111
throw err
1212
}
13-
1413
// web otp
1514
if (err.code === 'EOTP' && err.body?.authUrl && err.body?.doneUrl) {
1615
const { token: otp } = await webAuthOpener(

test/lib/commands/profile.js

Lines changed: 108 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
1+
const { inspect } = require('node:util')
12
const t = require('tap')
23
const mockNpm = require('../../fixtures/mock-npm')
4+
const tmock = require('../../fixtures/tmock')
35

4-
const mockProfile = async (t, { npmProfile, readUserInfo, qrcode, config, ...opts } = {}) => {
6+
const mockProfile = async (t, {
7+
npmProfile, readUserInfo, qrcode, config, isTTY, ...opts } = {}) => {
8+
const mockReadUserInfo = {
9+
'{LIB}/utils/read-user-info.js': readUserInfo || {
10+
async password () {},
11+
async otp () {},
12+
},
13+
}
514
const mocks = {
615
'npm-profile': npmProfile || {
716
async get () {},
817
async set () {},
918
async createToken () {},
1019
},
1120
'qrcode-terminal': qrcode || { generate: (url, cb) => cb() },
12-
'{LIB}/utils/read-user-info.js': readUserInfo || {
13-
async password () {},
14-
async otp () {},
15-
},
21+
...mockReadUserInfo,
22+
'{LIB}/utils/auth.js': tmock(t, '{LIB}/utils/auth.js', mockReadUserInfo),
1623
}
1724

1825
const mock = await mockNpm(t, {
@@ -22,6 +29,10 @@ const mockProfile = async (t, { npmProfile, readUserInfo, qrcode, config, ...opt
2229
color: false,
2330
...config,
2431
},
32+
globals: {
33+
'process.stdin.isTTY': isTTY,
34+
'process.stdout.isTTY': isTTY,
35+
},
2536
mocks: {
2637
...mocks,
2738
...opts.mocks,
@@ -516,6 +527,12 @@ t.test('enable-2fa', async t => {
516527
t.match(pass, 'bar', 'should use password for basic auth')
517528
return {}
518529
},
530+
async get () {
531+
return {
532+
userProfile,
533+
tfa: null,
534+
}
535+
},
519536
}
520537

521538
const { npm, profile } = await mockProfile(t, {
@@ -543,6 +560,12 @@ t.test('enable-2fa', async t => {
543560
async createToken () {
544561
return {}
545562
},
563+
async get () {
564+
return {
565+
...userProfile,
566+
tfa: null,
567+
}
568+
},
546569
}
547570

548571
const { npm, profile } = await mockProfile(t, {
@@ -577,7 +600,9 @@ t.test('enable-2fa', async t => {
577600
})
578601

579602
t.test('from basic auth, asks for otp', async t => {
580-
t.plan(9)
603+
t.plan(10)
604+
605+
let setCallCount = 0
581606

582607
const npmProfile = {
583608
async createToken (pass) {
@@ -588,18 +613,36 @@ t.test('enable-2fa', async t => {
588613
return userProfile
589614
},
590615
async set (newProfile) {
591-
t.match(
592-
newProfile,
593-
{
616+
setCallCount++
617+
if (setCallCount === 1) {
618+
t.match(
619+
newProfile,
620+
{
621+
tfa: {
622+
mode: 'auth-only',
623+
},
624+
},
625+
'should set tfa mode on first call'
626+
)
627+
const err = new Error('One-time password required')
628+
err.code = 'EOTP'
629+
throw err
630+
} else if (setCallCount === 2) {
631+
t.match(
632+
newProfile,
633+
{
634+
tfa: {
635+
mode: 'auth-only',
636+
},
637+
},
638+
'should set tfa mode'
639+
)
640+
return {
641+
...userProfile,
594642
tfa: {
595643
mode: 'auth-only',
596644
},
597-
},
598-
'should set tfa mode'
599-
)
600-
return {
601-
...userProfile,
602-
tfa: null,
645+
}
603646
}
604647
},
605648
}
@@ -612,14 +655,15 @@ t.test('enable-2fa', async t => {
612655
async otp (label) {
613656
t.equal(
614657
label,
615-
'Enter one-time password: ',
658+
'This operation requires a one-time password.\nEnter OTP:',
616659
'should ask for otp confirmation'
617660
)
618661
return '123456'
619662
},
620663
}
621664

622665
const { npm, profile, result } = await mockProfile(t, {
666+
isTTY: true,
623667
npmProfile,
624668
readUserInfo,
625669
})
@@ -817,12 +861,12 @@ t.test('enable-2fa', async t => {
817861

818862
t.equal(
819863
result(),
820-
'Two factor authentication mode changed to: auth-and-writes',
864+
'Two factor authentication is already enabled and set to auth-and-writes',
821865
'should output success msg'
822866
)
823867
})
824868

825-
t.test('missing tfa from user profile', async t => {
869+
t.test('errors when tfa is return null (not otpauth URL) and tfa is not setup already (with auth-only)', async t => {
826870
const npmProfile = {
827871
async get () {
828872
return {
@@ -847,7 +891,7 @@ t.test('enable-2fa', async t => {
847891
},
848892
}
849893

850-
const { npm, profile, result } = await mockProfile(t, {
894+
const { npm, profile } = await mockProfile(t, {
851895
npmProfile,
852896
readUserInfo,
853897
})
@@ -856,16 +900,15 @@ t.test('enable-2fa', async t => {
856900
return { token: 'token' }
857901
}
858902

859-
await profile.exec(['enable-2fa', 'auth-only'])
860-
861-
t.equal(
862-
result(),
863-
'Two factor authentication mode changed to: auth-only',
864-
'should output success msg'
865-
)
903+
await t.rejects(async () => {
904+
await profile.exec(['enable-2fa', 'auth-only'])
905+
}, new Error(
906+
'Unknown error enabling two-factor authentication. Expected otpauth URL' +
907+
', got: ' + inspect(null)
908+
))
866909
})
867910

868-
t.test('defaults to auth-and-writes permission if no mode specified', async t => {
911+
t.test('errors when tfa is return null (not otpauth URL) and tfa is not setup already', async t => {
869912
const npmProfile = {
870913
async get () {
871914
return {
@@ -890,7 +933,7 @@ t.test('enable-2fa', async t => {
890933
},
891934
}
892935

893-
const { npm, profile, result } = await mockProfile(t, {
936+
const { npm, profile } = await mockProfile(t, {
894937
npmProfile,
895938
readUserInfo,
896939
})
@@ -899,12 +942,12 @@ t.test('enable-2fa', async t => {
899942
return { token: 'token' }
900943
}
901944

902-
await profile.exec(['enable-2fa'])
903-
t.equal(
904-
result(),
905-
'Two factor authentication mode changed to: auth-and-writes',
906-
'should enable 2fa with auth-and-writes permission'
907-
)
945+
await t.rejects(async () => {
946+
await profile.exec(['enable-2fa'])
947+
}, new Error(
948+
'Unknown error enabling two-factor authentication. Expected otpauth URL' +
949+
', got: ' + inspect(null)
950+
))
908951
})
909952
})
910953

@@ -929,23 +972,33 @@ t.test('disable-2fa', async t => {
929972
})
930973

931974
t.test('requests otp', async t => {
932-
const npmProfile = t => ({
933-
async get () {
934-
return userProfile
935-
},
936-
async set (newProfile) {
937-
t.same(
938-
newProfile,
939-
{
940-
tfa: {
941-
password: 'password1234',
942-
mode: 'disable',
943-
},
944-
},
945-
'should send the new info for setting in profile'
946-
)
947-
},
948-
})
975+
const OTP_ERROR = Object.assign(new Error('One-time password required'), { code: 'EOTP' })
976+
977+
const npmProfile = (t) => {
978+
let setCallCount = 0
979+
return {
980+
async get () {
981+
return userProfile
982+
},
983+
async set (newProfile) {
984+
setCallCount++
985+
if (setCallCount === 1) {
986+
throw OTP_ERROR
987+
} else if (setCallCount === 2) {
988+
t.same(
989+
newProfile,
990+
{
991+
tfa: {
992+
password: 'password1234',
993+
mode: 'disable',
994+
},
995+
},
996+
'should send the new info for setting in profile'
997+
)
998+
}
999+
},
1000+
}
1001+
}
9491002

9501003
const readUserInfo = t => ({
9511004
async password () {
@@ -955,7 +1008,7 @@ t.test('disable-2fa', async t => {
9551008
async otp (label) {
9561009
t.equal(
9571010
label,
958-
'Enter one-time password: ',
1011+
'This operation requires a one-time password.\nEnter OTP:',
9591012
'should ask for otp confirmation'
9601013
)
9611014
return '1234'
@@ -968,6 +1021,7 @@ t.test('disable-2fa', async t => {
9681021
const { profile, result } = await mockProfile(t, {
9691022
npmProfile: npmProfile(t),
9701023
readUserInfo: readUserInfo(t),
1024+
isTTY: true,
9711025
})
9721026

9731027
await profile.exec(['disable-2fa'])
@@ -983,6 +1037,7 @@ t.test('disable-2fa', async t => {
9831037
npmProfile: npmProfile(t),
9841038
readUserInfo: readUserInfo(t),
9851039
config,
1040+
isTTY: true,
9861041
})
9871042

9881043
await profile.exec(['disable-2fa'])
@@ -999,6 +1054,7 @@ t.test('disable-2fa', async t => {
9991054
npmProfile: npmProfile(t),
10001055
readUserInfo: readUserInfo(t),
10011056
config,
1057+
isTTY: true,
10021058
})
10031059

10041060
await profile.exec(['disable-2fa'])

0 commit comments

Comments
 (0)