Skip to content

Commit cee61d3

Browse files
committed
add iam unit tests
1 parent 697ccdd commit cee61d3

File tree

4 files changed

+350
-22
lines changed

4 files changed

+350
-22
lines changed

packages/amazonq/test/unit/codewhisperer/util/authUtil.test.ts

Lines changed: 143 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ describe('AuthUtil', async function () {
4242
assert.ok(auth.isInternalAmazonUser())
4343
})
4444

45-
it('identifies SSO session', function () {
46-
;(auth as any).session = { loginType: auth2.LoginTypes.SSO }
45+
it('identifies SSO session', async function () {
46+
await auth.login_sso(constants.internalStartUrl, 'us-east-1')
4747
assert.strictEqual(auth.isSsoSession(), true)
4848
})
4949

50-
it('identifies non-SSO session', function () {
51-
;(auth as any).session = { loginType: auth2.LoginTypes.IAM }
50+
it('identifies non-SSO session', async function () {
51+
await auth.login_iam('accessKey', 'secretKey', 'sessionToken')
5252
assert.strictEqual(auth.isSsoSession(), false)
5353
})
5454
})
@@ -137,6 +137,7 @@ describe('AuthUtil', async function () {
137137
it('returns credentials form for IAM credentials', async function () {
138138
sinon.stub(auth, 'isSsoSession').returns(false)
139139
sinon.stub(auth, 'isConnected').returns(true)
140+
sinon.stub(auth, 'isIamSession').returns(true)
140141

141142
const forms = await auth.getAuthFormIds()
142143
assert.deepStrictEqual(forms, ['credentials'])
@@ -295,11 +296,11 @@ describe('AuthUtil', async function () {
295296
if (!(auth as any).session) {
296297
auth.session = new auth2.SsoLogin(auth.profileName, auth.lspAuth, auth.eventEmitter)
297298
}
298-
const updateProfileStubNext = sinon.stub((auth as any).session, 'updateProfile').resolves()
299+
const updateProfileStub = sinon.stub((auth as any).session, 'updateProfile').resolves()
299300

300301
await auth.migrateSsoConnectionToLsp('test-client')
301302

302-
assert.ok(updateProfileStubNext.calledOnce)
303+
assert.ok(updateProfileStub.calledOnce)
303304
assert.ok(memento.update.calledWith('auth.profiles', undefined))
304305
})
305306

@@ -366,13 +367,13 @@ describe('AuthUtil', async function () {
366367
auth.session = new auth2.SsoLogin(auth.profileName, auth.lspAuth, auth.eventEmitter)
367368
}
368369

369-
const updateProfileStubMore = sinon.stub((auth as any).session, 'updateProfile').resolves()
370+
const updateProfileStub = sinon.stub((auth as any).session, 'updateProfile').resolves()
370371

371372
await auth.migrateSsoConnectionToLsp('test-client')
372373

373-
assert.ok(updateProfileStubMore.calledOnce)
374+
assert.ok(updateProfileStub.calledOnce)
374375
assert.ok(memento.update.calledWith('auth.profiles', undefined))
375-
assert.deepStrictEqual(updateProfileStubMore.firstCall.args[0], {
376+
assert.deepStrictEqual(updateProfileStub.firstCall.args[0], {
376377
startUrl: validProfile.startUrl,
377378
region: validProfile.ssoRegion,
378379
scopes: validProfile.scopes,
@@ -408,4 +409,137 @@ describe('AuthUtil', async function () {
408409
)
409410
})
410411
})
412+
413+
describe('login_iam', function () {
414+
it('creates IAM session and logs in', async function () {
415+
const mockResponse = {
416+
id: 'test-credential-id',
417+
credentials: {
418+
accessKeyId: 'encrypted-access-key',
419+
secretAccessKey: 'encrypted-secret-key',
420+
sessionToken: 'encrypted-session-token',
421+
},
422+
updateCredentialsParams: {
423+
data: 'credential-data',
424+
},
425+
}
426+
427+
const mockIamLogin = {
428+
login: sinon.stub().resolves(mockResponse),
429+
loginType: 'iam',
430+
}
431+
432+
sinon.stub(auth2, 'IamLogin').returns(mockIamLogin as any)
433+
434+
const response = await auth.login_iam('accessKey', 'secretKey', 'sessionToken')
435+
436+
assert.ok(mockIamLogin.login.calledOnce)
437+
assert.ok(
438+
mockIamLogin.login.calledWith({
439+
accessKey: 'accessKey',
440+
secretKey: 'secretKey',
441+
})
442+
)
443+
assert.strictEqual(response, mockResponse)
444+
})
445+
})
446+
447+
describe('getIamCredential', function () {
448+
it('returns IAM credentials from session', async function () {
449+
const mockCredentials = {
450+
accessKeyId: 'test-access-key',
451+
secretAccessKey: 'test-secret-key',
452+
sessionToken: 'test-session-token',
453+
}
454+
455+
const mockSession = {
456+
getCredential: sinon.stub().resolves({
457+
credential: mockCredentials,
458+
updateCredentialsParams: { data: 'test' },
459+
}),
460+
loginType: 'iam',
461+
}
462+
463+
;(auth as any).session = mockSession
464+
465+
const result = await auth.getIamCredential()
466+
467+
assert.ok(mockSession.getCredential.calledOnce)
468+
assert.deepStrictEqual(result, mockCredentials)
469+
})
470+
471+
it('throws error for SSO session', async function () {
472+
const mockSession = {
473+
getCredential: sinon.stub().resolves({
474+
credential: 'sso-token',
475+
updateCredentialsParams: { data: 'test' },
476+
}),
477+
loginType: 'sso',
478+
}
479+
480+
;(auth as any).session = mockSession
481+
482+
try {
483+
await auth.getIamCredential()
484+
assert.fail('Should have thrown an error')
485+
} catch (err) {
486+
assert.strictEqual((err as Error).message, 'Cannot get token with SSO session')
487+
}
488+
})
489+
490+
it('throws error when not logged in', async function () {
491+
;(auth as any).session = undefined
492+
493+
try {
494+
await auth.getIamCredential()
495+
assert.fail('Should have thrown an error')
496+
} catch (err) {
497+
assert.strictEqual((err as Error).message, 'Cannot get credential without logging in.')
498+
}
499+
})
500+
})
501+
502+
describe('isIamSession', function () {
503+
it('returns true for IAM session', function () {
504+
const mockSession = new auth2.IamLogin(auth.profileName, auth.lspAuth, auth.eventEmitter)
505+
;(auth as any).session = mockSession
506+
507+
assert.strictEqual(auth.isIamSession(), true)
508+
})
509+
510+
it('returns false for SSO session', function () {
511+
const mockSession = { loginType: 'sso' }
512+
;(auth as any).session = mockSession
513+
514+
assert.strictEqual(auth.isIamSession(), false)
515+
})
516+
517+
it('returns false when no session', function () {
518+
;(auth as any).session = undefined
519+
520+
assert.strictEqual(auth.isIamSession(), false)
521+
})
522+
})
523+
524+
describe('IAM session state changes', function () {
525+
let mockLspAuth: any
526+
527+
beforeEach(function () {
528+
mockLspAuth = (auth as any).lspAuth
529+
})
530+
531+
it('updates IAM credential when state is refreshed', async function () {
532+
const mockSession = new auth2.IamLogin(auth.profileName, auth.lspAuth, auth.eventEmitter)
533+
sinon.stub(mockSession, 'getCredential').resolves({
534+
credential: { accessKeyId: 'key', secretAccessKey: 'secret' },
535+
updateCredentialsParams: { data: 'fake-data' },
536+
})
537+
;(auth as any).session = mockSession
538+
539+
await (auth as any).stateChangeHandler({ state: 'refreshed' })
540+
541+
assert.ok(mockLspAuth.updateIamCredential.called)
542+
assert.strictEqual(mockLspAuth.updateIamCredential.firstCall.args[0].data, 'fake-data')
543+
})
544+
})
411545
})

packages/core/src/codewhisperer/util/authUtil.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,7 @@ import { showAmazonQWalkthroughOnce } from '../../amazonq/onboardingPage/walkthr
3030
import { setContext } from '../../shared/vscode/setContext'
3131
import { openUrl } from '../../shared/utilities/vsCodeUtils'
3232
import { telemetry } from '../../shared/telemetry/telemetry'
33-
import {
34-
AuthStateEvent,
35-
cacheChangedEvent,
36-
LanguageClientAuth,
37-
Login,
38-
SsoLogin,
39-
IamLogin,
40-
LoginTypes,
41-
} from '../../auth/auth2'
33+
import { AuthStateEvent, cacheChangedEvent, LanguageClientAuth, Login, SsoLogin, IamLogin } from '../../auth/auth2'
4234
import { builderIdStartUrl, internalStartUrl } from '../../auth/sso/constants'
4335
import { VSCODE_EXTENSION_ID } from '../../shared/extensions'
4436
import { RegionProfileManager } from '../region/regionProfileManager'
@@ -116,11 +108,11 @@ export class AuthUtil implements IAuthProvider {
116108
}
117109

118110
isSsoSession(): boolean {
119-
return this.session?.loginType === LoginTypes.SSO || this.session instanceof SsoLogin
111+
return this.session instanceof SsoLogin
120112
}
121113

122114
isIamSession(): boolean {
123-
return this.session?.loginType === LoginTypes.IAM || this.session instanceof IamLogin
115+
return this.session instanceof IamLogin
124116
}
125117

126118
/**
@@ -378,7 +370,11 @@ export class AuthUtil implements IAuthProvider {
378370

379371
private async refreshState(state = this.getAuthState()) {
380372
if (state === 'expired' || state === 'notConnected') {
381-
this.lspAuth.deleteBearerToken()
373+
if (this.isSsoSession()) {
374+
this.lspAuth.deleteBearerToken()
375+
} else if (this.isIamSession()) {
376+
this.lspAuth.deleteIamCredential()
377+
}
382378
if (this.isIdcConnection()) {
383379
await this.regionProfileManager.invalidateProfile(this.regionProfileManager.activeRegionProfile?.arn)
384380
await this.regionProfileManager.clearCache()

0 commit comments

Comments
 (0)