diff --git a/.eslintignore b/.eslintignore index 9d9dbd383..f617d3fdd 100644 --- a/.eslintignore +++ b/.eslintignore @@ -12,3 +12,5 @@ node_modules /samples/generated/express-direct-auth-dynamic .eslintrc.js dist +/test/apps/app/public/oidc-app.js +/test/apps/app/target diff --git a/lib/idx/idxState/v1/generateIdxAction.ts b/lib/idx/idxState/v1/generateIdxAction.ts index 51d4f6099..7f75649f6 100644 --- a/lib/idx/idxState/v1/generateIdxAction.ts +++ b/lib/idx/idxState/v1/generateIdxAction.ts @@ -64,8 +64,7 @@ const generateDirectFetch = function generateDirectFetch(authClient: OktaAuthIdx idxResponse.stepUp = true; } - // Throw IDX response if request did not succeed. This behavior will be removed in version 7.0: OKTA-481844 - throw idxResponse; + return idxResponse; } }; }; diff --git a/lib/idx/register.ts b/lib/idx/register.ts index 86f27eb94..6edaed42e 100644 --- a/lib/idx/register.ts +++ b/lib/idx/register.ts @@ -34,14 +34,10 @@ export async function register( autoRemediate: false }); if (!options.activationToken && enabledFeatures && !enabledFeatures.includes(IdxFeature.REGISTRATION)) { - const error = new AuthSdkError('Registration is not supported based on your current org configuration.'); - throw error; - // return { status: IdxStatus.FAILURE, error } as unknown as IdxTransaction; // TODO: wny not just throw the error? + throw new AuthSdkError('Registration is not supported based on your current org configuration.'); } if (options.activationToken && availableSteps?.some(({ name }) => name === 'identify')) { - const error = new AuthSdkError('activationToken is not supported based on your current org configuration.'); - throw error; - // return { status: IdxStatus.FAILURE, error } as unknown as IdxTransaction; // TODO: wny not just throw the error? + throw new AuthSdkError('activationToken is not supported based on your current org configuration.'); } } diff --git a/lib/idx/remediate.ts b/lib/idx/remediate.ts index 2bd7c8dcc..a1972a705 100644 --- a/lib/idx/remediate.ts +++ b/lib/idx/remediate.ts @@ -25,7 +25,7 @@ import { filterValuesForRemediation, getRemediator, getNextStep, - handleIdxError + handleFailedResponse } from './util'; export interface RemediateActionWithOptionalParams { @@ -97,11 +97,9 @@ export async function remediate( let optionsWithoutExecutedAction = removeActionFromOptions(options, action); if (typeof idxResponse.actions[action] === 'function') { - try { - idxResponse = await idxResponse.actions[action](params); - idxResponse = { ...idxResponse, requestDidSucceed: true }; - } catch (e) { - return handleIdxError(authClient, e, options); + idxResponse = await idxResponse.actions[action](params); + if (idxResponse.requestDidSucceed === false) { + return handleFailedResponse(authClient, idxResponse, options); } if (action === 'cancel') { return { idxResponse, canceled: true }; @@ -117,14 +115,10 @@ export async function remediate( // search for action in remediation list const remediationAction = neededToProceed.find(({ name }) => name === action); if (remediationAction) { - try { - idxResponse = await idxResponse.proceed(action, params); - idxResponse = { ...idxResponse, requestDidSucceed: true }; + idxResponse = await idxResponse.proceed(action, params); + if (idxResponse.requestDidSucceed === false) { + return handleFailedResponse(authClient, idxResponse, options); } - catch (e) { - return handleIdxError(authClient, e, options); - } - return remediate(authClient, idxResponse, values, optionsWithoutExecutedAction); // recursive call } } @@ -137,16 +131,17 @@ export async function remediate( } if (!remediator) { + // With options.step, remediator is not required if (options.step) { values = filterValuesForRemediation(idxResponse, options.step, values); // include only requested values - try { - idxResponse = await idxResponse.proceed(options.step, values); - idxResponse = { ...idxResponse, requestDidSucceed: true }; - return { idxResponse }; - } catch(e) { - return handleIdxError(authClient, e, options); + idxResponse = await idxResponse.proceed(options.step, values); + if (idxResponse.requestDidSucceed === false) { + return handleFailedResponse(authClient, idxResponse, options); } + return { idxResponse }; } + + // With default flow, remediator is not required if (flow === 'default') { return { idxResponse }; } @@ -167,28 +162,28 @@ export async function remediate( const name = remediator.getName(); const data = remediator.getData(); - try { - idxResponse = await idxResponse.proceed(name, data); - idxResponse = { ...idxResponse, requestDidSucceed: true }; - // We may want to trim the values bag for the next remediation - // Let the remediator decide what the values should be (default to current values) - values = remediator.getValuesAfterProceed(); - options = { ...options, step: undefined }; // do not re-use the step - - // generic remediator should not auto proceed in pending status - // return nextStep directly - if (options.useGenericRemediator && !idxResponse.interactionCode && !isTerminalResponse(idxResponse)) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const gr = getRemediator(idxResponse.neededToProceed, values, options)!; - const nextStep = getNextStep(authClient, gr, idxResponse); - return { - idxResponse, - nextStep, - }; - } - - return remediate(authClient, idxResponse, values, options); // recursive call - } catch (e) { - return handleIdxError(authClient, e, options); + + idxResponse = await idxResponse.proceed(name, data); + if (idxResponse.requestDidSucceed === false) { + return handleFailedResponse(authClient, idxResponse, options); } + // We may want to trim the values bag for the next remediation + // Let the remediator decide what the values should be (default to current values) + values = remediator.getValuesAfterProceed(); + options = { ...options, step: undefined }; // do not re-use the step + + // generic remediator should not auto proceed in pending status + // return nextStep directly + if (options.useGenericRemediator && !idxResponse.interactionCode && !isTerminalResponse(idxResponse)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const gr = getRemediator(idxResponse.neededToProceed, values, options)!; + const nextStep = getNextStep(authClient, gr, idxResponse); + return { + idxResponse, + nextStep, + }; + } + + return remediate(authClient, idxResponse, values, options); // recursive call + } diff --git a/lib/idx/run.ts b/lib/idx/run.ts index 9713726c2..40ae6cb3b 100644 --- a/lib/idx/run.ts +++ b/lib/idx/run.ts @@ -29,7 +29,7 @@ import { Tokens, APIError, } from '../types'; -import { IdxMessage, IdxResponse, isIdxResponse } from './types/idx-js'; +import { IdxMessage, IdxResponse } from './types/idx-js'; import { getSavedTransactionMeta, saveTransactionMeta } from './transactionMeta'; import { getAvailableSteps, getEnabledFeatures, getMessagesFromResponse, isTerminalResponse } from './util'; declare interface RunData { @@ -296,22 +296,6 @@ async function finalizeData(authClient, data: RunData): Promise { }; } -function handleError(err, data: RunData): RunData { - let { error, status, shouldClearTransaction } = data; - - // current version of idx-js will throw/reject IDX responses. Handle these differently than regular errors - if (isIdxResponse(err)) { - error = err; - status = IdxStatus.FAILURE; - shouldClearTransaction = true; - } else { - // error is not an IDX response, throw it like a regular error - throw err; - } - - return { ...data, error, status, shouldClearTransaction }; -} - export async function run( authClient: OktaAuthIdxInterface, options: RunOptions = {}, @@ -322,12 +306,8 @@ export async function run( }; data = initializeData(authClient, data); - try { - data = await getDataFromIntrospect(authClient, data); - data = await getDataFromRemediate(authClient, data); - } catch (err) { - data = handleError(err, data); - } + data = await getDataFromIntrospect(authClient, data); + data = await getDataFromRemediate(authClient, data); data = await finalizeData(authClient, data); const { diff --git a/lib/idx/util.ts b/lib/idx/util.ts index 63bc129dd..36b7f3004 100644 --- a/lib/idx/util.ts +++ b/lib/idx/util.ts @@ -3,7 +3,7 @@ import * as remediators from './remediators'; import { RemediationValues, Remediator, RemediatorConstructor } from './remediators'; import { GenericRemediator } from './remediators/GenericRemediator'; import { IdxFeature, NextStep, RemediateOptions, RemediationResponse, RunOptions } from './types'; -import { IdxMessage, IdxRemediation, IdxRemediationValue, IdxResponse, isIdxResponse } from './types/idx-js'; +import { IdxMessage, IdxRemediation, IdxRemediationValue, IdxResponse } from './types/idx-js'; import { OktaAuthIdxInterface } from '../types'; export function isTerminalResponse(idxResponse: IdxResponse) { @@ -254,17 +254,11 @@ export function getNextStep( }; } -export function handleIdxError(authClient: OktaAuthIdxInterface, e, options = {}): RemediationResponse { - // Handle idx messages - let idxResponse = isIdxResponse(e) ? e : null; - if (!idxResponse) { - // Thrown error terminates the interaction with idx - throw e; - } - idxResponse = { - ...idxResponse, - requestDidSucceed: false - }; +export function handleFailedResponse( + authClient: OktaAuthIdxInterface, + idxResponse: IdxResponse, + options = {} +): RemediationResponse { const terminal = isTerminalResponse(idxResponse); const remediator = getRemediator(idxResponse.neededToProceed, {}, options); const nextStep = remediator && getNextStep(authClient, remediator, idxResponse); diff --git a/scripts/lint.sh b/scripts/lint.sh index c030415c2..464ebbcb6 100644 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -5,9 +5,14 @@ source ${OKTA_HOME}/${REPO}/scripts/setup.sh export TEST_SUITE_TYPE="checkstyle" export TEST_RESULT_FILE_DIR="${REPO}/build2" +if ! yarn tsd; then + echo "tsd failed! Exiting..." + exit ${PUBLISH_TYPE_AND_RESULT_DIR_BUT_ALWAYS_FAIL} +fi + if ! yarn lint:report; then echo "lint failed! Exiting..." - exit ${TEST_FAILURE} + exit ${PUBLISH_TYPE_AND_RESULT_DIR_BUT_ALWAYS_FAIL} fi mkdir -p ${TEST_RESULT_FILE_DIR} diff --git a/scripts/unit.sh b/scripts/unit.sh index a241ea439..383f2c6d9 100644 --- a/scripts/unit.sh +++ b/scripts/unit.sh @@ -4,10 +4,12 @@ source ${OKTA_HOME}/${REPO}/scripts/setup.sh "v14.18.0" export TEST_SUITE_TYPE="junit" export TEST_RESULT_FILE_DIR="${REPO}/build2/reports/unit" +echo ${TEST_SUITE_TYPE} > ${TEST_SUITE_TYPE_FILE} +echo ${TEST_RESULT_FILE_DIR} > ${TEST_RESULT_FILE_DIR_FILE} if ! yarn test:unit; then echo "unit failed! Exiting..." - exit ${TEST_FAILURE} + exit ${PUBLISH_TYPE_AND_RESULT_DIR_BUT_ALWAYS_FAIL} fi if ! yarn test:types; then @@ -17,19 +19,17 @@ fi if ! yarn test:bundle:esm:browser; then echo "validate ESM browser bundle failed! Exiting..." - exit ${TEST_FAILURE} + exit ${PUBLISH_TYPE_AND_RESULT_DIR_BUT_ALWAYS_FAIL} fi if ! yarn test:bundle:esm:node; then echo "validate ESM node bundle failed! Exiting..." - exit ${TEST_FAILURE} + exit ${PUBLISH_TYPE_AND_RESULT_DIR_BUT_ALWAYS_FAIL} fi if ! yarn test:bundle:cjs; then echo "validate cjs bundle failed! Exiting..." - exit ${TEST_FAILURE} + exit ${PUBLISH_TYPE_AND_RESULT_DIR_BUT_ALWAYS_FAIL} fi -echo ${TEST_SUITE_TYPE} > ${TEST_SUITE_TYPE_FILE} -echo ${TEST_RESULT_FILE_DIR} > ${TEST_RESULT_FILE_DIR_FILE} exit ${PUBLISH_TYPE_AND_RESULT_DIR} diff --git a/test/apps/app/.eslintignore b/test/apps/app/.eslintignore index 85a1daf9d..bbd7f0e79 100644 --- a/test/apps/app/.eslintignore +++ b/test/apps/app/.eslintignore @@ -1 +1,2 @@ -/public \ No newline at end of file +/public +/target \ No newline at end of file diff --git a/test/spec/idx/authenticate.ts b/test/spec/idx/authenticate.ts index 56a463618..c1f7dfe80 100644 --- a/test/spec/idx/authenticate.ts +++ b/test/spec/idx/authenticate.ts @@ -239,6 +239,7 @@ describe('idx/authenticate', () => { it('returns terminal error when invalid password is provided', async () => { const { authClient } = testContext; const errorResponse = IdxResponseFactory.build({ + requestDidSucceed: false, rawIdxState: RawIdxResponseFactory.build({ messages: IdxMessagesFactory.build({ value: [ @@ -249,7 +250,7 @@ describe('idx/authenticate', () => { }); const identifyResponse = IdentifyResponseFactory.build(); - identifyResponse.proceed = jest.fn().mockRejectedValue(errorResponse); + identifyResponse.proceed = jest.fn().mockResolvedValue(errorResponse); jest.spyOn(mocked.introspect, 'introspect').mockResolvedValue(identifyResponse); const res = await authenticate(authClient, { username: 'myuser', password: 'invalid-password' }); @@ -266,6 +267,7 @@ describe('idx/authenticate', () => { it('returns terminal error when user account is deactivated or is not assigned to the application', async () => { const { authClient } = testContext; const errorResponse = IdxResponseFactory.build({ + requestDidSucceed: false, rawIdxState: RawIdxResponseFactory.build({ messages: IdxMessagesFactory.build({ value: [ @@ -275,7 +277,7 @@ describe('idx/authenticate', () => { }) }); const identifyResponse = IdentifyResponseFactory.build(); - identifyResponse.proceed = jest.fn().mockRejectedValue(errorResponse); + identifyResponse.proceed = jest.fn().mockResolvedValue(errorResponse); jest.spyOn(mocked.introspect, 'introspect').mockResolvedValue(identifyResponse); const res = await authenticate(authClient, { username: 'myuser' }); @@ -299,6 +301,7 @@ describe('idx/authenticate', () => { }) }); const idxResponse = IdxResponseFactory.build({ + requestDidSucceed: false, rawIdxState: errorResponse }); jest.spyOn(mocked.introspect, 'introspect').mockResolvedValue(idxResponse); @@ -317,6 +320,7 @@ describe('idx/authenticate', () => { it('returns terminal error when user account is locked or suspeneded', async () => { const { authClient } = testContext; const errorResponse = IdxResponseFactory.build({ + requestDidSucceed: false, rawIdxState: RawIdxResponseFactory.build({ messages: IdxMessagesFactory.build({ value: [ @@ -326,7 +330,7 @@ describe('idx/authenticate', () => { }) }); const identifyResponse = IdentifyResponseFactory.build(); - identifyResponse.proceed = jest.fn().mockRejectedValue(errorResponse); + identifyResponse.proceed = jest.fn().mockResolvedValue(errorResponse); jest.spyOn(mocked.introspect, 'introspect').mockResolvedValue(identifyResponse); const res = await authenticate(authClient, { username: 'myuser' }); @@ -671,6 +675,7 @@ describe('idx/authenticate', () => { ] }); const errorInvalidCodeResponse = IdxResponseFactory.build({ + requestDidSucceed: false, neededToProceed: [ challengeAuthenticatorRemediation ], @@ -788,7 +793,7 @@ describe('idx/authenticate', () => { verifyPhoneResponse, errorInvalidCodeResponse } = testContext; - jest.spyOn(verifyPhoneResponse, 'proceed').mockRejectedValue(errorInvalidCodeResponse); + jest.spyOn(verifyPhoneResponse, 'proceed').mockResolvedValue(errorInvalidCodeResponse); jest.spyOn(mocked.introspect, 'introspect').mockResolvedValue(verifyPhoneResponse); const verificationCode = 'invalid-test-code'; const res = await authenticate(authClient, { @@ -859,6 +864,7 @@ describe('idx/authenticate', () => { ] }); const errorInvalidPhoneResponse = IdxResponseFactory.build({ + requestDidSucceed: false, neededToProceed: [ PhoneAuthenticatorEnrollmentDataRemediationFactory.build() ], @@ -1047,7 +1053,7 @@ describe('idx/authenticate', () => { errorInvalidPhoneResponse } = testContext; - jest.spyOn(phoneEnrollmentDataResponse, 'proceed').mockRejectedValue(errorInvalidPhoneResponse); + jest.spyOn(phoneEnrollmentDataResponse, 'proceed').mockResolvedValueOnce(errorInvalidPhoneResponse); jest.spyOn(mocked.introspect, 'introspect') .mockResolvedValueOnce(phoneEnrollmentDataResponse); @@ -1155,6 +1161,7 @@ describe('idx/authenticate', () => { ] }); const errorInvalidCodeResponse = IdxResponseFactory.build({ + requestDidSucceed: false, neededToProceed: [ challengeAuthenticatorRemediation ], @@ -1230,7 +1237,7 @@ describe('idx/authenticate', () => { verifyEmailResponse, errorInvalidCodeResponse } = testContext; - jest.spyOn(verifyEmailResponse, 'proceed').mockRejectedValue(errorInvalidCodeResponse); + jest.spyOn(verifyEmailResponse, 'proceed').mockResolvedValue(errorInvalidCodeResponse); jest.spyOn(mocked.introspect, 'introspect').mockResolvedValue(verifyEmailResponse); const verificationCode = 'invalid-test-code'; const res = await authenticate(authClient, { @@ -1301,6 +1308,7 @@ describe('idx/authenticate', () => { ] }); const errorInvalidCodeResponse = IdxResponseFactory.build({ + requestDidSucceed: false, rawIdxState: RawIdxResponseFactory.build({ messages: IdxMessagesFactory.build({ value: [ @@ -1404,7 +1412,7 @@ describe('idx/authenticate', () => { verifyAuthenticatorResponse, errorInvalidCodeResponse } = testContext; - jest.spyOn(verifyAuthenticatorResponse, 'proceed').mockRejectedValue(errorInvalidCodeResponse); + jest.spyOn(verifyAuthenticatorResponse, 'proceed').mockResolvedValue(errorInvalidCodeResponse); jest.spyOn(mocked.introspect, 'introspect').mockResolvedValue(verifyAuthenticatorResponse); const verificationCode = 'invalid-test-code'; const res = await authenticate(authClient, { @@ -1476,6 +1484,7 @@ describe('idx/authenticate', () => { ] }); const errorInvalidCodeResponse = IdxResponseFactory.build({ + requestDidSucceed: false, rawIdxState: RawIdxResponseFactory.build({ messages: IdxMessagesFactory.build({ value: [ @@ -1557,7 +1566,7 @@ describe('idx/authenticate', () => { errorInvalidCodeResponse } = testContext; - jest.spyOn(enrollGoogleAuthenticatorResponse, 'proceed').mockRejectedValue(errorInvalidCodeResponse); + jest.spyOn(enrollGoogleAuthenticatorResponse, 'proceed').mockResolvedValue(errorInvalidCodeResponse); jest.spyOn(mocked.introspect, 'introspect') .mockResolvedValueOnce(enrollGoogleAuthenticatorResponse); @@ -1742,6 +1751,7 @@ describe('idx/authenticate', () => { verifyAuthenticatorResponse, } = testContext; const errorInvalidCodeResponse = IdxResponseFactory.build({ + requestDidSucceed: false, rawIdxState: RawIdxResponseFactory.build({ messages: IdxMessagesFactory.build({ value: [ @@ -1753,7 +1763,7 @@ describe('idx/authenticate', () => { VerifyOktaVerifyAuthenticatorRemediationFactory.build(), ] }); - jest.spyOn(verifyAuthenticatorResponse, 'proceed').mockRejectedValue(errorInvalidCodeResponse); + jest.spyOn(verifyAuthenticatorResponse, 'proceed').mockResolvedValue(errorInvalidCodeResponse); jest.spyOn(mocked.introspect, 'introspect').mockResolvedValue(verifyAuthenticatorResponse); const verificationCode = 'invalid-test-code'; const res = await authenticate(authClient, { diff --git a/test/spec/idx/idxState/unit/v1/generateIdxAction.test.js b/test/spec/idx/idxState/unit/v1/generateIdxAction.test.js index b3f817ddb..947d7171b 100644 --- a/test/spec/idx/idxState/unit/v1/generateIdxAction.test.js +++ b/test/spec/idx/idxState/unit/v1/generateIdxAction.test.js @@ -109,9 +109,6 @@ describe('generateIdxAction', () => { const actionFunction = generateIdxAction(sdk, mockIdxResponse.remediation.value[0]); return actionFunction() .then( result => { - fail('mock call should have failed', result); - }) - .catch( result => { expect( http.mock.calls.length ).toBe(1); expect( http.mock.calls[0][0] ).toEqual( sdk ); expect( http.mock.calls[0][1] ).toEqual( { diff --git a/test/spec/idx/poll.ts b/test/spec/idx/poll.ts index 21bc2e3bd..db87969cb 100644 --- a/test/spec/idx/poll.ts +++ b/test/spec/idx/poll.ts @@ -115,6 +115,7 @@ describe('idx/poll', () => { }); const sessionExpiredResponse = IdxResponseFactory.build({ + requestDidSucceed: false, rawIdxState: RawIdxResponseFactory.build({ messages: IdxMessagesFactory.build({ value: [ @@ -222,10 +223,10 @@ describe('idx/poll', () => { ]); jest.spyOn(mocked.introspect, 'introspect') .mockResolvedValueOnce(enrollPollResponse) - .mockRejectedValueOnce(sessionExpiredResponse); + .mockResolvedValueOnce(sessionExpiredResponse); const transaction = await poll(authClient, { refresh: 100 }); - expect(transaction.status).toEqual(IdxStatus.FAILURE); + expect(transaction.status).toEqual(IdxStatus.TERMINAL); }); it('propagates other errors', async () => { diff --git a/test/spec/idx/recoverPassword.ts b/test/spec/idx/recoverPassword.ts index 6b3161385..b52b25407 100644 --- a/test/spec/idx/recoverPassword.ts +++ b/test/spec/idx/recoverPassword.ts @@ -373,6 +373,7 @@ describe('idx/recoverPassword', () => { } = testContext; const idxError = IdxResponseFactory.build({ + requestDidSucceed: false, rawIdxState: RawIdxResponseFactory.build({ messages: IdxMessagesFactory.build({ value: [ @@ -382,7 +383,7 @@ describe('idx/recoverPassword', () => { }) }); - jest.spyOn(identifyRecoveryResponse, 'proceed').mockRejectedValue(idxError); + jest.spyOn(identifyRecoveryResponse, 'proceed').mockResolvedValue(idxError); jest.spyOn(mocked.introspect, 'introspect') .mockResolvedValueOnce(identifyResponse); diff --git a/test/spec/idx/register.ts b/test/spec/idx/register.ts index 9b6e9aab8..87264a1a1 100644 --- a/test/spec/idx/register.ts +++ b/test/spec/idx/register.ts @@ -1281,6 +1281,7 @@ describe('idx/register', () => { } = testContext; const errorResponse = IdxResponseFactory.build({ + requestDidSucceed: false, rawIdxState: RawIdxResponseFactory.build({ messages: IdxMessagesFactory.build({ value: [ @@ -1293,7 +1294,7 @@ describe('idx/register', () => { ] }); - jest.spyOn(phoneEnrollmentDataResponse, 'proceed').mockRejectedValue(errorResponse); + jest.spyOn(phoneEnrollmentDataResponse, 'proceed').mockResolvedValue(errorResponse); jest.spyOn(mocked.introspect, 'introspect') .mockResolvedValueOnce(phoneEnrollmentDataResponse); diff --git a/test/spec/idx/remediate.ts b/test/spec/idx/remediate.ts index cdfae529b..8cc035480 100644 --- a/test/spec/idx/remediate.ts +++ b/test/spec/idx/remediate.ts @@ -19,7 +19,7 @@ describe('idx/remediate', () => { rawIdxState: {} } as unknown as IdxResponse; const errorResponse = { fakeErrorResponse: true }; - jest.spyOn(util, 'handleIdxError').mockReturnValue(errorResponse); + jest.spyOn(util, 'handleFailedResponse').mockReturnValue(errorResponse); const remediator = { canRemediate: jest.fn(), getNextStep: jest.fn(), @@ -83,8 +83,7 @@ describe('idx/remediate', () => { const res = await remediate(authClient, idxResponse, { resend: true }, {}); expect(res).toEqual({ idxResponse: { - ...responseFromAction, - requestDidSucceed: true + ...responseFromAction }, nextStep: {} }); @@ -94,7 +93,7 @@ describe('idx/remediate', () => { }); it('will handle exceptions', async () => { - const { authClient, errorResponse } = testContext; + const { authClient } = testContext; const error = new Error('my test error'); const idxResponse = { neededToProceed: [{ @@ -105,9 +104,14 @@ describe('idx/remediate', () => { }, rawIdxState: {} } as unknown as IdxResponse; - const res = await remediate(authClient, idxResponse, { resend: true }, {}); - expect(res).toBe(errorResponse); - expect(util.handleIdxError).toHaveBeenCalledWith(authClient, error, {}); + let errorThrown = false; + try { + await remediate(authClient, idxResponse, { resend: true }, {}); + } catch (e) { + errorThrown = true; + expect(e).toBe(error); + } + expect(errorThrown).toBe(true); }); }); @@ -136,8 +140,7 @@ describe('idx/remediate', () => { const res = await remediate(authClient, idxResponse, {}, { actions: ['some-action'] }); expect(res).toEqual({ idxResponse: { - ...responseFromAction, - requestDidSucceed: true + ...responseFromAction }, nextStep: {} }); @@ -146,7 +149,7 @@ describe('idx/remediate', () => { expect(util.getRemediator).toHaveBeenNthCalledWith(2, responseFromAction.neededToProceed, {}, { actions: [] }); }); it('will handle exceptions', async () => { - const { authClient, errorResponse } = testContext; + const { authClient } = testContext; const error = new Error('my test error'); const idxResponse = { neededToProceed: [{ @@ -157,10 +160,14 @@ describe('idx/remediate', () => { }, rawIdxState: {} } as unknown as IdxResponse; - const options = { actions: ['some-action'] }; - const res = await remediate(authClient, idxResponse, { resend: true }, options); - expect(res).toEqual(errorResponse); - expect(util.handleIdxError).toHaveBeenCalledWith(authClient, error, options); + let errorThrown = false; + try { + await remediate(authClient, idxResponse, { resend: true }, { actions: ['some-action']}); + } catch (e) { + errorThrown = true; + expect(e).toBe(error); + } + expect(errorThrown).toBe(true); }); }); @@ -183,8 +190,7 @@ describe('idx/remediate', () => { const res = await remediate(authClient, idxResponse, {}, { actions: ['some-remediation'] }); expect(res).toEqual({ idxResponse: { - ...responseFromRemediation, - requestDidSucceed: true + ...responseFromRemediation }, nextStep: {} }); @@ -194,7 +200,6 @@ describe('idx/remediate', () => { }); it('will handle exceptions', async () => { let { authClient, idxResponse } = testContext; - const { errorResponse } = testContext; const error = new Error('my test error'); idxResponse = { ...idxResponse, @@ -203,10 +208,14 @@ describe('idx/remediate', () => { name: 'some-remediation' }], } as unknown as IdxResponse; - const options = { actions: ['some-remediation'] }; - const res = await remediate(authClient, idxResponse, {}, options); - expect(res).toBe(errorResponse); - expect(util.handleIdxError).toHaveBeenCalledWith(authClient, error, options); + let errorThrown = false; + try { + await remediate(authClient, idxResponse, {}, { actions: ['some-remediation']}); + } catch (e) { + errorThrown = true; + expect(e).toBe(error); + } + expect(errorThrown).toBe(true); }); }); @@ -266,8 +275,7 @@ describe('idx/remediate', () => { expect(actionFn).toHaveBeenCalledWith({ foo: 'bar' }); expect(res).toEqual({ idxResponse: { - ...responseFromAction, - requestDidSucceed: true + ...responseFromAction }, nextStep: {} }); @@ -277,7 +285,6 @@ describe('idx/remediate', () => { }); it('will handle exceptions', async () => { let { authClient, idxResponse } = testContext; - const { errorResponse } = testContext; const error = new Error('my test error'); const actionFn = jest.fn().mockRejectedValue(error); idxResponse = { @@ -293,10 +300,14 @@ describe('idx/remediate', () => { name: 'some-action', params: { foo: 'bar' } }; - const options = { actions: [action] }; - const res = await remediate(authClient, idxResponse, {}, options); - expect(res).toBe(errorResponse); - expect(util.handleIdxError).toHaveBeenCalledWith(authClient, error, options); + let errorThrown = false; + try { + await remediate(authClient, idxResponse, {}, { actions: [action]}); + } catch (e) { + errorThrown = true; + expect(e).toBe(error); + } + expect(errorThrown).toBe(true); expect(actionFn).toHaveBeenCalledWith({ foo: 'bar' }); }); }); @@ -323,8 +334,7 @@ describe('idx/remediate', () => { const res = await remediate(authClient, idxResponse, {}, { actions: [action] }); expect(res).toEqual({ idxResponse: { - ...responseFromRemediation, - requestDidSucceed: true + ...responseFromRemediation }, nextStep: {} }); @@ -334,7 +344,6 @@ describe('idx/remediate', () => { }); it('will handle exceptions', async () => { let { authClient, idxResponse } = testContext; - const { errorResponse } = testContext; const error = new Error('my test error'); idxResponse = { ...idxResponse, @@ -346,10 +355,14 @@ describe('idx/remediate', () => { const action = { name: 'some-remediation' }; - const options = { actions: [action] }; - const res = await remediate(authClient, idxResponse, {}, options); - expect(res).toBe(errorResponse); - expect(util.handleIdxError).toHaveBeenCalledWith(authClient, error, options); + let errorThrown = false; + try { + await remediate(authClient, idxResponse, {}, { actions: [action]}); + } catch (e) { + errorThrown = true; + expect(e).toBe(error); + } + expect(errorThrown).toBe(true); }); }); @@ -415,8 +428,7 @@ describe('idx/remediate', () => { const res = await remediate(authClient, idxResponse, {}, { step: 'some-remediation' }); expect(res).toEqual({ idxResponse: { - ...responseFromRemediation, - requestDidSucceed: true + ...responseFromRemediation }, }); expect(util.getRemediator).toHaveBeenCalledTimes(1); @@ -425,7 +437,6 @@ describe('idx/remediate', () => { }); it('will handle exceptions', async () => { let { authClient, idxResponse } = testContext; - const { errorResponse } = testContext; const error = new Error('my test error'); idxResponse = { ...idxResponse, @@ -437,10 +448,14 @@ describe('idx/remediate', () => { }] }], } as unknown as IdxResponse; - const options = { step: 'some-remediation' }; - const res = await remediate(authClient, idxResponse, {}, options); - expect(res).toBe(errorResponse); - expect(util.handleIdxError).toHaveBeenCalledWith(authClient, error, options); + let errorThrown = false; + try { + await remediate(authClient, idxResponse, {}, { step: 'some-remediation' }); + } catch (e) { + errorThrown = true; + expect(e).toBe(error); + } + expect(errorThrown).toBe(true); expect(idxResponse.proceed).toHaveBeenCalledWith('some-remediation', {}); }); }); @@ -509,8 +524,7 @@ describe('idx/remediate', () => { const res = await remediate(authClient, idxResponse, {}, {}); expect(res).toEqual({ idxResponse: { - ...responseFromProceed, - requestDidSucceed: true + ...responseFromProceed }, nextStep: {} }); @@ -521,8 +535,8 @@ describe('idx/remediate', () => { }); - it('handles thrown exceptions', async () => { - let { authClient, idxResponse, remediator, errorResponse } = testContext; + it('will bubble up thrown exceptions', async () => { + let { authClient, idxResponse, remediator } = testContext; jest.spyOn(util, 'isTerminalResponse'); const name = 'fubar'; const data = { foo: 'bar' }; @@ -533,8 +547,13 @@ describe('idx/remediate', () => { remediator.getValuesAfterProceed.mockReturnValue(valuesAfterProceed); const errorFromProceed = new Error('test error'); idxResponse.proceed.mockRejectedValue(errorFromProceed); - const res = await remediate(authClient, idxResponse, {}, {}); - expect(res).toEqual(errorResponse); + let errorThrown; + try { + await remediate(authClient, idxResponse, {}, {}); + } catch (e) { + errorThrown = e; + } + expect(errorThrown).toBe(errorFromProceed); expect(idxResponse.proceed).toHaveBeenCalledWith(name, data); expect(util.isTerminalResponse).toHaveBeenCalledTimes(1); }); @@ -589,7 +608,6 @@ describe('idx/remediate', () => { expect(res).toEqual({ idxResponse: { ...responseFromProceed, - requestDidSucceed: true, }, terminal: true, }); diff --git a/test/spec/idx/util.ts b/test/spec/idx/util.ts index e340b5331..17bf47d83 100644 --- a/test/spec/idx/util.ts +++ b/test/spec/idx/util.ts @@ -5,7 +5,7 @@ import { isTerminalResponse, getRemediator, getNextStep, - handleIdxError + handleFailedResponse } from '../../../lib/idx/util'; import { IdxResponseFactory, @@ -416,7 +416,7 @@ describe('idx/util', () => { }); }); - describe('handleIdxError', () => { + describe('handleFailedResponse', () => { let testContext; beforeEach(() => { const authClient = {} as OktaAuthIdxInterface; @@ -433,26 +433,15 @@ describe('idx/util', () => { }; }); - it('By default, it will throw unrecognized errors', () => { - const { authClient } = testContext; - const error = new Error('my test error'); - const fn = () => { - handleIdxError(authClient, error); - }; - expect(fn).toThrowError(error); - }); - it('For terminal IDX responses, it augments the object with requestDidSucceed = false and returns it', () => { + it('For terminal IDX responses, it returns it with terminal flag', () => { const { authClient, idxResponse } = testContext; - const res = handleIdxError(authClient, idxResponse); + const res = handleFailedResponse(authClient, idxResponse); expect(res).toEqual({ - idxResponse: { - ...idxResponse, - requestDidSucceed: false - }, + idxResponse, terminal: true, }); }); - it('non-terminal IDX responses, no remediator: it augments the object with requestDidSucceed = false and returns it', () => { + it('non-terminal IDX responses, no remediator: it returns it', () => { const { authClient, idxResponse } = testContext; idxResponse.neededToProceed.push({ name: 'some-remediation' @@ -462,15 +451,12 @@ describe('idx/util', () => { foo: FooRemediator, }; const options = { remediators }; - const res = handleIdxError(authClient, idxResponse, options); + const res = handleFailedResponse(authClient, idxResponse, options); expect(res).toEqual({ - idxResponse: { - ...idxResponse, - requestDidSucceed: false - }, + idxResponse }); }); - it('non-terminal IDX response and a remediator: it augments the object with requestDidSucceed = false and returns it along with next step info', () => { + it('non-terminal IDX response and a remediator: returns it along with next step info', () => { const { authClient, idxResponse } = testContext; const context = { fake: true }; idxResponse.context = context; @@ -489,12 +475,9 @@ describe('idx/util', () => { foo: FooRemediator, }; const options = { remediators }; - const res = handleIdxError(authClient, idxResponse, options); + const res = handleFailedResponse(authClient, idxResponse, options); expect(res).toEqual({ - idxResponse: { - ...idxResponse, - requestDidSucceed: false - }, + idxResponse, nextStep }); expect(mockGetNextStep).toHaveBeenCalledWith(authClient, context);