From 570d8f8157f0e4416f2f3623a82228e2a414b288 Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Thu, 8 Jul 2021 19:05:19 -0500 Subject: [PATCH 01/19] feat: implement register consent flow --- src/model/registerConsent.interface.ts | 76 ++++++ src/model/registerConsent.model.ts | 339 +++++++++++++++++++++++++ src/shared/api-error.ts | 50 ++++ src/shared/http-response-error.ts | 58 +++++ 4 files changed, 523 insertions(+) create mode 100644 src/model/registerConsent.interface.ts create mode 100644 src/model/registerConsent.model.ts create mode 100644 src/shared/api-error.ts create mode 100644 src/shared/http-response-error.ts diff --git a/src/model/registerConsent.interface.ts b/src/model/registerConsent.interface.ts new file mode 100644 index 00000000..a91b8b21 --- /dev/null +++ b/src/model/registerConsent.interface.ts @@ -0,0 +1,76 @@ +/***** + License + -------------- + Copyright © 2020 Mojaloop Foundation + The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 (the "License") + and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed + on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + - Kevin Leyow - kevin.leyow@modusbox.com + -------------- + ******/ + import { + ControlledStateMachine, + PersistentModelConfig, StateData +} from '~/model/persistent.model' +import { Method } from 'javascript-state-machine' +import { ThirdpartyRequests, MojaloopRequests } from '@mojaloop/sdk-standard-components'; +import { + thirdparty as tpAPI +} from '@mojaloop/api-snippets' +import { PubSub } from '~/shared/pub-sub' +import { ParticipantsTypeIDPutResponse } from '../../../api-snippets/src/thirdparty/index'; + +export enum RegisterConsentPhase { + waitOnParticipantResponseFromALS = 'waitOnParticipantResponseFromALS', +} + +export interface RegisterConsentStateMachine extends ControlledStateMachine { + verifyConsent: Method + onVerifyConsent: Method + registerAuthoritativeSourceWithALS: Method + onRegisterAuthoritativeSourceWithALS: Method + sendConsentCallbackToDFSP: Method + onSendConsentCallbackToDFSP: Method +} + +export interface RegisterConsentModelConfig extends PersistentModelConfig { + subscriber: PubSub + thirdpartyRequests: ThirdpartyRequests + mojaloopRequests: MojaloopRequests + requestProcessingTimeoutSeconds: number + authServiceParticipantFSPId: string + alsEndpoint: string +} + +export interface RegisterConsentData extends StateData { + // the DFSP requesting the registering of the Consent object + participantDFSPId: string + + // initial /POST consents request + consentPostRequestAUTH: tpAPI.Schemas.ConsentsPostRequestAUTH + + // request sent to ALS + participantsTypeIDPostRequest: tpAPI.Schemas.ParticipantsTypeIDSubIDPostRequest + participantsTypeIDPutResponse: tpAPI.Schemas.ParticipantsTypeIDPutResponse + + // response to DFSP letting them know Consent is verified and stored + consentsIDPutResponseVerified: tpAPI.Schemas.ConsentsIDPutResponseVerified + + errorInformation?: tpAPI.Schemas.ErrorInformation +} diff --git a/src/model/registerConsent.model.ts b/src/model/registerConsent.model.ts new file mode 100644 index 00000000..01b28852 --- /dev/null +++ b/src/model/registerConsent.model.ts @@ -0,0 +1,339 @@ +/***** + License + -------------- + Copyright © 2020 Mojaloop Foundation + The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 (the "License") + and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed + on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + - Kevin Leyow - kevin.leyow@modusbox.com + -------------- + ******/ + +import { PubSub } from '~/shared/pub-sub' +import { PersistentModel } from '~/model/persistent.model' +import { StateMachineConfig } from 'javascript-state-machine' +import { ThirdpartyRequests, MojaloopRequests, Errors } from '@mojaloop/sdk-standard-components'; +import inspect from '~/shared/inspect' +import { + RegisterConsentData, + RegisterConsentStateMachine, + RegisterConsentModelConfig +} from './registerConsent.interface' +import { Message } from '~/shared/pub-sub' +import deferredJob from '~/shared/deferred-job' +import { RegisterConsentPhase } from './registerConsent.interface' +import { reformatError } from '~/shared/api-error' +import axios from 'axios'; +import { + v1_1 as fspiopAPI, + thirdparty as tpAPI +} from '@mojaloop/api-snippets' + +export class RegisterConsentModel + extends PersistentModel { + protected config: RegisterConsentModelConfig + + constructor ( + data: RegisterConsentData, + config: RegisterConsentModelConfig + ) { + const spec: StateMachineConfig = { + init: 'start', + transitions: [ + { name: 'verifyConsent', from: 'start', to: 'consentVerified' }, + { name: 'registerAuthoritativeSourceWithALS', from: 'consentVerified', to: 'registeredAsAuthoritativeSource' }, + { name: 'sendConsentCallbackToDFSP', from: 'registeredAsAuthoritativeSource', to: 'callbackSent' }, + ], + methods: { + // specific transitions handlers methods + onVerifyConsent: () => this.onVerifyConsent(), + onRegisterAuthoritativeSourceWithALS: () => this.onRegisterAuthoritativeSourceWithALS(), + onSendConsentCallbackToDFSP: () => this.onSendConsentCallbackToDFSP(), + } + } + super(data, config, spec) + this.config = { ...config } + } + + // getters + get subscriber (): PubSub { + return this.config.subscriber + } + + get mojaloopRequests (): MojaloopRequests { + return this.config.mojaloopRequests + } + + get thirdpartyRequests (): ThirdpartyRequests { + return this.config.thirdpartyRequests + } + + // utility function to check if an error after a transition which + // pub/subs for a response that can return a mojaloop error + async checkModelDataForErrorInformation(): Promise { + if (this.data.errorInformation) { + await this.fsm.error(this.data.errorInformation) + } + } + + static notificationChannel (phase: RegisterConsentPhase, id: string): string { + if (!id) { + throw new Error('RegisterConsentModel.notificationChannel: \'id\' parameter is required') + } + // channel name + return `RegisterConsent_${phase}_${id}` + } + + static async triggerWorkflow ( + phase: RegisterConsentPhase, + id: string, + pubSub: PubSub, + message: Message + ): Promise { + const channel = RegisterConsentModel.notificationChannel(phase, id) + return deferredJob(pubSub, channel).trigger(message) + } + + + async onVerifyConsent (): Promise { + const { consentPostRequestAUTH, participantDFSPId } = this.data + + try { + // not sure what functions to use or if they are ready + // for now we are just going do nothing here. + // todo: update transition to + // - verify consent + // - store consent + // - throw errors if there are error in verifying the Consent + } catch (error) { + this.logger.push({ error }).error('start -> requestIsValid') + + let mojaloopError + // if error is planned and is a MojaloopApiErrorCode we send back that code + if ((error as Errors.MojaloopApiErrorCode).code) { + mojaloopError = reformatError(error, this.logger) + } else { + // if error is not planned send back a generalized error + mojaloopError = reformatError( + Errors.MojaloopApiErrorCodes.TP_ACCOUNT_LINKING_ERROR, + this.logger + ) + } + + await this.thirdpartyRequests.putConsentsError( + consentPostRequestAUTH.consentId, + mojaloopError as unknown as fspiopAPI.Schemas.ErrorInformationObject, + participantDFSPId + ) + + // throw error to stop state machine + throw error + } + } + + async onRegisterAuthoritativeSourceWithALS (): Promise { + const { consentPostRequestAUTH, participantDFSPId } = this.data + + // catch any unplanned errors and notify DFSP + try { + const waitOnParticipantResponseFromALSResponse = RegisterConsentModel.notificationChannel( + RegisterConsentPhase.waitOnParticipantResponseFromALS, + consentPostRequestAUTH.consentId + ) + + await deferredJob(this.subscriber, waitOnParticipantResponseFromALSResponse) + .init(async (channel) => { + // todo: sdk-standard-components needs a postParticipantsTypeId function + // building the request from scratch for now + const alsParticipantURI = `${this.config.alsEndpoint}/participants/CONSENT/${consentPostRequestAUTH.consentId}` + const axiosConfig = { + headers: { + 'Content-Type': 'application/vnd.interoperability.participants+json;version=1.0', + 'Accept': 'application/vnd.interoperability.participants+json;version=1.0', + 'FSPIOP-Source': this.config.authServiceParticipantFSPId, + Date: (new Date()).toUTCString() + } + } + const payload: fspiopAPI.Schemas.ParticipantsTypeIDSubIDPostRequest = { + 'fspId': this.config.authServiceParticipantFSPId + } + + const res = await axios.post(alsParticipantURI, payload, axiosConfig) + + this.logger.push({ res, channel }) + .log('POST /participants/{Type}/{ID} call sent to ALS, listening on response') + }) + .job(async (message: Message): Promise => { + try { + type PutResponse = + fspiopAPI.Schemas.ParticipantsTypeIDPutResponse + type PutResponseOrError = PutResponse & fspiopAPI.Schemas.ErrorInformationObject + const putResponse = message as unknown as PutResponseOrError + + if (putResponse.errorInformation) { + // if the ALS sends back any error, inform the DFSP + // that the consent verification has failed + // todo: more detailed error handling depending on ALS error response + // todo: need to create auth-service specific errors + const mojaloopError = reformatError( + Errors.MojaloopApiErrorCodes.TP_ACCOUNT_LINKING_ERROR, + this.logger + ) + + await this.thirdpartyRequests.putConsentsError( + consentPostRequestAUTH.consentId, + mojaloopError as unknown as fspiopAPI.Schemas.ErrorInformationObject, + participantDFSPId + ) + // store the error so we can transition to an errored state + this.data.errorInformation = mojaloopError.errorInformation as unknown as fspiopAPI.Schemas.ErrorInformation + } else { + this.data.participantsTypeIDPutResponse = { ...message as unknown as PutResponse } + } + } catch (error) { + return Promise.reject(error) + } + }) + .wait(this.config.requestProcessingTimeoutSeconds * 1000) + } catch (error) { + // we send back an account linking error despite the actual error + const mojaloopError = reformatError( + Errors.MojaloopApiErrorCodes.TP_ACCOUNT_LINKING_ERROR, + this.logger + ) + + // if the flow fails to run for any reason notify the DFSP that the account + // linking process has failed + await this.thirdpartyRequests.putConsentsError( + consentPostRequestAUTH.consentId, + mojaloopError as unknown as fspiopAPI.Schemas.ErrorInformationObject, + participantDFSPId + ) + + // throw the actual error + throw error + } + } + + async onSendConsentCallbackToDFSP (): Promise { + const { consentPostRequestAUTH, participantDFSPId } = this.data + + try { + // copy credential and update status + const verifiedCredential: tpAPI.Schemas.VerifiedCredential = { + ...consentPostRequestAUTH.credential, + status: 'VERIFIED' + } + + const consentsIDPutResponseVerified: tpAPI.Schemas.ConsentsIDPutResponseVerified = { + scopes: consentPostRequestAUTH.scopes, + credential: verifiedCredential + } + + await this.thirdpartyRequests.putConsents( + consentPostRequestAUTH.consentId, + consentsIDPutResponseVerified, + participantDFSPId + ) + } catch (error) { + this.logger.push({ error }).error('start -> requestIsValid') + + let mojaloopError + // if error is planned and is a MojaloopApiErrorCode we send back that code + if ((error as Errors.MojaloopApiErrorCode).code) { + mojaloopError = reformatError(error, this.logger) + } else { + // if error is not planned send back a generalized error + mojaloopError = reformatError( + Errors.MojaloopApiErrorCodes.TP_ACCOUNT_LINKING_ERROR, + this.logger + ) + } + + await this.thirdpartyRequests.putConsentsError( + consentPostRequestAUTH.consentId, + mojaloopError as unknown as fspiopAPI.Schemas.ErrorInformationObject, + participantDFSPId + ) + + // throw error to stop state machine + throw error + } + } + + async run (): Promise { + const data = this.data + try { + // run transitions based on incoming state + switch (data.currentState) { + case 'start': + await this.fsm.verifyConsent() + return this.run() + + case 'consentVerified': + await this.fsm.registerAuthoritativeSourceWithALS() + // check if the ALS sent back an error + await this.checkModelDataForErrorInformation() + return this.run() + + case 'registeredAsAuthoritativeSource': + await this.fsm.sendConsentCallbackToDFSP() + // flow is finished + return + default: + this.logger.info('State machine in errored state') + return + } + } catch (err) { + this.logger.info(`Error running RegisterConsentModel : ${inspect(err)}`) + + // as this function is recursive, we don't want to error the state machine multiple times + if (data.currentState !== 'errored') { + // err should not have a RegisterConsentState property here! + if (err.RegisterConsentState) { + this.logger.info('State machine is broken') + } + // transition to errored state + await this.fsm.error(err) + + // avoid circular ref between RegisterConsentState.lastError and err + err.RegisterConsentState = { ...this.data } + } + throw err + } + } +} + +export async function create ( + data: RegisterConsentData, + config: RegisterConsentModelConfig +): Promise { + // create a new model + const model = new RegisterConsentModel(data, config) + + // enforce to finish any transition to state specified by data.currentState or spec.init + await model.fsm.state + return model +} + + +export default { + RegisterConsentModel, + create, +} diff --git a/src/shared/api-error.ts b/src/shared/api-error.ts new file mode 100644 index 00000000..4a75f4fe --- /dev/null +++ b/src/shared/api-error.ts @@ -0,0 +1,50 @@ +import { HTTPResponseError, ResponseErrorData } from '~/shared/http-response-error' +import { Errors, Logger as SDKLogger } from '@mojaloop/sdk-standard-components' + +export interface MojaloopApiErrorCode { + code: string + message: string, + httpStatusCode?: number +} + +export function reformatError ( + err: Error | MojaloopApiErrorCode, logger: SDKLogger.Logger +): Errors.MojaloopApiErrorObject { + // default 500 error + let mojaloopErrorCode: MojaloopApiErrorCode = Errors.MojaloopApiErrorCodes.INTERNAL_SERVER_ERROR + if (err instanceof HTTPResponseError) { + const e: ResponseErrorData = err.getData() + if (e.res && (e.res.body || e.res.data)) { + if (e.res.body) { + try { + const bodyObj = JSON.parse(e.res.body) + mojaloopErrorCode = Errors.MojaloopApiErrorCodeFromCode(`${bodyObj?.statusCode}`) + } catch (ex) { + // do nothing, only log problems + logger.push({ exception: ex }).error('Error parsing error message body as JSON') + } + } else if (e.res.data) { + mojaloopErrorCode = Errors.MojaloopApiErrorCodeFromCode(`${e.res.data?.statusCode}`) + } + } + // check are we having valid MojaloopApiErrorCodes object thrown + } else { + // error is valid when it is defined on the common list + const code = (err as MojaloopApiErrorCode).code + const exist = typeof Errors.MojaloopApiErrorCodeFromCode(code) !== 'undefined' + if (exist) { + mojaloopErrorCode = (err as MojaloopApiErrorCode) + } + } + + return new Errors.MojaloopFSPIOPError( + err, + mojaloopErrorCode.message || err.message, + null as unknown as string, + mojaloopErrorCode + ).toApiErrorObject() +} + +export default { + reformatError +} diff --git a/src/shared/http-response-error.ts b/src/shared/http-response-error.ts new file mode 100644 index 00000000..aa0598c0 --- /dev/null +++ b/src/shared/http-response-error.ts @@ -0,0 +1,58 @@ +/***** + License + -------------- + Copyright © 2020 Mojaloop Foundation + The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 (the "License") + and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed + on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + - James Bush + - Paweł Marzec + -------------- + ******/ + +import inspect from '~/shared/inspect' + +export interface ResponseErrorData { + msg: string + res?: { + body?: string + data?: {[key:string]: unknown } + } +} + +export class HTTPResponseError extends Error { + protected errorData: ErrorData + + constructor (params: ErrorData) { + super(params.msg) + this.errorData = params + } + + getData (): ErrorData { + return this.errorData + } + + toString (): string { + return inspect(this.errorData) + } + + toJSON (): string { + return JSON.stringify(this.errorData) + } +} From 7f632b2a50dc023d1d5d33fafb760cdfe4597c9b Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Thu, 8 Jul 2021 22:53:51 -0500 Subject: [PATCH 02/19] chore: change fspId and add model unit tests --- config/development.json | 2 +- config/integration.json | 2 +- config/production.json | 2 +- config/test.json | 2 +- src/model/registerConsent.interface.ts | 10 +- src/model/registerConsent.model.ts | 26 +- test/unit/model/registerConsent.model.test.ts | 371 ++++++++++++++++++ 7 files changed, 388 insertions(+), 27 deletions(-) create mode 100644 test/unit/model/registerConsent.model.test.ts diff --git a/config/development.json b/config/development.json index 6e803964..95b319e2 100644 --- a/config/development.json +++ b/config/development.json @@ -1,7 +1,7 @@ { "PORT": 4004, "HOST": "0.0.0.0", - "PARTICIPANT_ID": "auth_service", + "PARTICIPANT_ID": "centralAuth", "REDIS": { "PORT": 6379, "HOST": "redis", diff --git a/config/integration.json b/config/integration.json index 6e803964..95b319e2 100644 --- a/config/integration.json +++ b/config/integration.json @@ -1,7 +1,7 @@ { "PORT": 4004, "HOST": "0.0.0.0", - "PARTICIPANT_ID": "auth_service", + "PARTICIPANT_ID": "centralAuth", "REDIS": { "PORT": 6379, "HOST": "redis", diff --git a/config/production.json b/config/production.json index 6e803964..95b319e2 100644 --- a/config/production.json +++ b/config/production.json @@ -1,7 +1,7 @@ { "PORT": 4004, "HOST": "0.0.0.0", - "PARTICIPANT_ID": "auth_service", + "PARTICIPANT_ID": "centralAuth", "REDIS": { "PORT": 6379, "HOST": "redis", diff --git a/config/test.json b/config/test.json index 299dca10..19eccf19 100644 --- a/config/test.json +++ b/config/test.json @@ -1,7 +1,7 @@ { "PORT": 4004, "HOST": "0.0.0.0", - "PARTICIPANT_ID": "auth_service", + "PARTICIPANT_ID": "centralAuth", "REDIS": { "PORT": 6379, "HOST": "localhost", diff --git a/src/model/registerConsent.interface.ts b/src/model/registerConsent.interface.ts index a91b8b21..a3161e31 100644 --- a/src/model/registerConsent.interface.ts +++ b/src/model/registerConsent.interface.ts @@ -34,7 +34,6 @@ import { thirdparty as tpAPI } from '@mojaloop/api-snippets' import { PubSub } from '~/shared/pub-sub' -import { ParticipantsTypeIDPutResponse } from '../../../api-snippets/src/thirdparty/index'; export enum RegisterConsentPhase { waitOnParticipantResponseFromALS = 'waitOnParticipantResponseFromALS', @@ -63,14 +62,7 @@ export interface RegisterConsentData extends StateData { participantDFSPId: string // initial /POST consents request - consentPostRequestAUTH: tpAPI.Schemas.ConsentsPostRequestAUTH - - // request sent to ALS - participantsTypeIDPostRequest: tpAPI.Schemas.ParticipantsTypeIDSubIDPostRequest - participantsTypeIDPutResponse: tpAPI.Schemas.ParticipantsTypeIDPutResponse - - // response to DFSP letting them know Consent is verified and stored - consentsIDPutResponseVerified: tpAPI.Schemas.ConsentsIDPutResponseVerified + consentsPostRequestAUTH: tpAPI.Schemas.ConsentsPostRequestAUTH errorInformation?: tpAPI.Schemas.ErrorInformation } diff --git a/src/model/registerConsent.model.ts b/src/model/registerConsent.model.ts index 01b28852..1b779c3b 100644 --- a/src/model/registerConsent.model.ts +++ b/src/model/registerConsent.model.ts @@ -112,7 +112,7 @@ export class RegisterConsentModel async onVerifyConsent (): Promise { - const { consentPostRequestAUTH, participantDFSPId } = this.data + const { consentsPostRequestAUTH, participantDFSPId } = this.data try { // not sure what functions to use or if they are ready @@ -137,7 +137,7 @@ export class RegisterConsentModel } await this.thirdpartyRequests.putConsentsError( - consentPostRequestAUTH.consentId, + consentsPostRequestAUTH.consentId, mojaloopError as unknown as fspiopAPI.Schemas.ErrorInformationObject, participantDFSPId ) @@ -148,20 +148,20 @@ export class RegisterConsentModel } async onRegisterAuthoritativeSourceWithALS (): Promise { - const { consentPostRequestAUTH, participantDFSPId } = this.data + const { consentsPostRequestAUTH, participantDFSPId } = this.data // catch any unplanned errors and notify DFSP try { const waitOnParticipantResponseFromALSResponse = RegisterConsentModel.notificationChannel( RegisterConsentPhase.waitOnParticipantResponseFromALS, - consentPostRequestAUTH.consentId + consentsPostRequestAUTH.consentId ) await deferredJob(this.subscriber, waitOnParticipantResponseFromALSResponse) .init(async (channel) => { // todo: sdk-standard-components needs a postParticipantsTypeId function // building the request from scratch for now - const alsParticipantURI = `${this.config.alsEndpoint}/participants/CONSENT/${consentPostRequestAUTH.consentId}` + const alsParticipantURI = `${this.config.alsEndpoint}/participants/CONSENT/${consentsPostRequestAUTH.consentId}` const axiosConfig = { headers: { 'Content-Type': 'application/vnd.interoperability.participants+json;version=1.0', @@ -197,14 +197,12 @@ export class RegisterConsentModel ) await this.thirdpartyRequests.putConsentsError( - consentPostRequestAUTH.consentId, + consentsPostRequestAUTH.consentId, mojaloopError as unknown as fspiopAPI.Schemas.ErrorInformationObject, participantDFSPId ) // store the error so we can transition to an errored state this.data.errorInformation = mojaloopError.errorInformation as unknown as fspiopAPI.Schemas.ErrorInformation - } else { - this.data.participantsTypeIDPutResponse = { ...message as unknown as PutResponse } } } catch (error) { return Promise.reject(error) @@ -221,7 +219,7 @@ export class RegisterConsentModel // if the flow fails to run for any reason notify the DFSP that the account // linking process has failed await this.thirdpartyRequests.putConsentsError( - consentPostRequestAUTH.consentId, + consentsPostRequestAUTH.consentId, mojaloopError as unknown as fspiopAPI.Schemas.ErrorInformationObject, participantDFSPId ) @@ -232,22 +230,22 @@ export class RegisterConsentModel } async onSendConsentCallbackToDFSP (): Promise { - const { consentPostRequestAUTH, participantDFSPId } = this.data + const { consentsPostRequestAUTH, participantDFSPId } = this.data try { // copy credential and update status const verifiedCredential: tpAPI.Schemas.VerifiedCredential = { - ...consentPostRequestAUTH.credential, + ...consentsPostRequestAUTH.credential, status: 'VERIFIED' } const consentsIDPutResponseVerified: tpAPI.Schemas.ConsentsIDPutResponseVerified = { - scopes: consentPostRequestAUTH.scopes, + scopes: consentsPostRequestAUTH.scopes, credential: verifiedCredential } await this.thirdpartyRequests.putConsents( - consentPostRequestAUTH.consentId, + consentsPostRequestAUTH.consentId, consentsIDPutResponseVerified, participantDFSPId ) @@ -267,7 +265,7 @@ export class RegisterConsentModel } await this.thirdpartyRequests.putConsentsError( - consentPostRequestAUTH.consentId, + consentsPostRequestAUTH.consentId, mojaloopError as unknown as fspiopAPI.Schemas.ErrorInformationObject, participantDFSPId ) diff --git a/test/unit/model/registerConsent.model.test.ts b/test/unit/model/registerConsent.model.test.ts new file mode 100644 index 00000000..d85a4398 --- /dev/null +++ b/test/unit/model/registerConsent.model.test.ts @@ -0,0 +1,371 @@ +/***** + License + -------------- + Copyright © 2020 Mojaloop Foundation + The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 (the "License") + and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed + on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the +Gates Foundation organization for an example). Those individuals should have +their names indented and be marked with a '-'. Email address can be added +optionally within square brackets . +* Gates Foundation +- Name Surname + +- Kevin Leyow - kevin.leyow@modusbox.com +-------------- +******/ + +import { + v1_1 as fspiopAPI, + thirdparty as tpAPI +} from '@mojaloop/api-snippets' +import { KVS } from '~/shared/kvs' +import { + Message, + NotificationCallback, + PubSub +} from '~/shared/pub-sub' +import { ThirdpartyRequests, MojaloopRequests } from '@mojaloop/sdk-standard-components'; +import { + RegisterConsentModel, + create +} from '~/model/registerConsent.model' +import { RedisConnectionConfig } from '~/shared/redis-connection' +import { mocked } from 'ts-jest/utils' + +import mockLogger from 'test/unit/mockLogger' +import sortedArray from 'test/unit/sortedArray' +import { RegisterConsentModelConfig, RegisterConsentData, RegisterConsentPhase } from '~/model/registerConsent.interface' +import config from '~/shared/config'; +import axios from 'axios'; + +// mock KVS default exported class +jest.mock('~/shared/kvs') + +// mock PubSub default exported class +jest.mock('~/shared/pub-sub') + +jest.mock('axios') + +const consentsPostRequestAUTH: tpAPI.Schemas.ConsentsPostRequestAUTH = { + consentId: 'b51ec534-ee48-4575-b6a9-ead2955b8069', + scopes: [ + { + accountId: 'dfsp.username.5678', + actions: [ + 'accounts.transfer', + 'accounts.getBalance' + ] + } + ], + credential: { + credentialType: 'FIDO', + status: 'PENDING', + payload: { + id: 'X8aQc8WgIOiYzoRIKbTYJdlzMZ_8zo3ZiIL3Rvh_ONfr9kZtudCwYO49tWVkjgJGyJSpoo6anRBVJGda0Lri3Q', + rawId: Buffer.from([ + 95, 198, 144, 115, 197, 160, 32, 232, 152, 206, 132, 72, 41, 180, 216, 37, 217, 115, 49, 159, 252, 206, + 141, 217, 136, 130, 247, 70, 248, 127, 56, 215, 235, 246, 70, 109, 185, 208, 176, 96, 238, 61, 181, + 101, 100, 142, 2, 70, 200, 148, 169, 162, 142, 154, 157, 16, 85, 36, 103, 90, 208, 186, 226, 221] + ).toString('base64'), + response: { + // clientDataJSON needs to be utf-8 not base64 + clientDataJSON: Buffer.from( + [123, 34, 116, 121, 112, 101, 34, 58, 34, 119, 101, 98, + 97, 117, 116, 104, 110, 46, 99, 114, 101, 97, 116, 101, 34, 44, 34, 99, 104, 97, 108, 108, 101, 110, + 103, 101, 34, 58, 34, 77, 103, 65, 51, 65, 68, 103, 65, 78, 81, 66, 106, 65, 68, 73, 65, 90, 65, 65, + 53, 65, 68, 107, 65, 89, 81, 65, 48, 65, 71, 77, 65, 77, 81, 65, 53, 65, 71, 81, 65, 77, 81, 66, 104, + 65, 68, 103, 65, 78, 119, 66, 107, 65, 68, 77, 65, 78, 65, 66, 109, 65, 71, 81, 65, 77, 65, 66, 106, + 65, 68, 69, 65, 77, 65, 66, 104, 65, 71, 81, 65, 77, 65, 66, 105, 65, 68, 85, 65, 77, 103, 65, 51, + 65, 68, 73, 65, 77, 81, 66, 106, 65, 71, 89, 65, 77, 119, 66, 106, 65, 68, 103, 65, 77, 65, 65, 121, + 65, 68, 103, 65, 79, 65, 66, 106, 65, 68, 73, 65, 79, 81, 66, 107, 65, 71, 69, 65, 78, 81, 66, 105, + 65, 68, 65, 65, 90, 81, 66, 105, 65, 71, 85, 65, 90, 103, 65, 50, 65, 68, 99, 65, 79, 65, 65, 122, + 65, 68, 81, 65, 77, 65, 65, 34, 44, 34, 111, 114, 105, 103, 105, 110, 34, 58, 34, 104, 116, 116, 112, + 58, 47, 47, 108, 111, 99, 97, 108, 104, 111, 115, 116, 58, 53, 48, 48, 48, 34, 44, 34, 99, 114, 111, + 115, 115, 79, 114, 105, 103, 105, 110, 34, 58, 102, 97, 108, 115, 101, 125] + ).toString('utf-8'), + attestationObject: Buffer.from( + [163, 99, 102, 109, 116, 102, 112, 97, 99, 107, 101, 100, 103, 97, 116, + 116, 83, 116, 109, 116, 163, 99, 97, 108, 103, 38, 99, 115, 105, 103, 88, 71, 48, 69, 2, 32, 30, 175, + 73, 42, 152, 191, 108, 89, 231, 187, 75, 149, 87, 233, 58, 38, 49, 223, 5, 193, 112, 89, 20, 66, 92, + 149, 165, 122, 56, 51, 36, 181, 2, 33, 0, 246, 136, 227, 141, 25, 119, 155, 56, 44, 106, 223, 181, + 42, 190, 233, 177, 11, 247, 73, 207, 86, 183, 83, 209, 117, 13, 172, 85, 48, 89, 243, 127, 99, 120, + 53, 99, 129, 89, 2, 193, 48, 130, 2, 189, 48, 130, 1, 165, 160, 3, 2, 1, 2, 2, 4, 11, 5, 205, 83, 48, + 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 11, 5, 0, 48, 46, 49, 44, 48, 42, 6, 3, 85, 4, 3, 19, 35, + 89, 117, 98, 105, 99, 111, 32, 85, 50, 70, 32, 82, 111, 111, 116, 32, 67, 65, 32, 83, 101, 114, 105, + 97, 108, 32, 52, 53, 55, 50, 48, 48, 54, 51, 49, 48, 32, 23, 13, 49, 52, 48, 56, 48, 49, 48, 48, 48, + 48, 48, 48, 90, 24, 15, 50, 48, 53, 48, 48, 57, 48, 52, 48, 48, 48, 48, 48, 48, 90, 48, 110, 49, 11, + 48, 9, 6, 3, 85, 4, 6, 19, 2, 83, 69, 49, 18, 48, 16, 6, 3, 85, 4, 10, 12, 9, 89, 117, 98, 105, 99, + 111, 32, 65, 66, 49, 34, 48, 32, 6, 3, 85, 4, 11, 12, 25, 65, 117, 116, 104, 101, 110, 116, 105, 99, + 97, 116, 111, 114, 32, 65, 116, 116, 101, 115, 116, 97, 116, 105, 111, 110, 49, 39, 48, 37, 6, 3, 85, + 4, 3, 12, 30, 89, 117, 98, 105, 99, 111, 32, 85, 50, 70, 32, 69, 69, 32, 83, 101, 114, 105, 97, 108, + 32, 49, 56, 52, 57, 50, 57, 54, 49, 57, 48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, + 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 33, 26, 111, 177, 181, 137, 37, 203, 10, 193, 24, 95, 124, + 42, 227, 168, 180, 136, 16, 20, 121, 177, 30, 255, 245, 85, 224, 125, 151, 81, 189, 43, 23, 106, 37, + 45, 238, 89, 236, 227, 133, 153, 32, 91, 179, 234, 40, 191, 143, 215, 252, 125, 167, 92, 5, 66, 114, + 174, 72, 88, 229, 145, 252, 90, 163, 108, 48, 106, 48, 34, 6, 9, 43, 6, 1, 4, 1, 130, 196, 10, 2, 4, + 21, 49, 46, 51, 46, 54, 46, 49, 46, 52, 46, 49, 46, 52, 49, 52, 56, 50, 46, 49, 46, 49, 48, 19, 6, + 11, 43, 6, 1, 4, 1, 130, 229, 28, 2, 1, 1, 4, 4, 3, 2, 4, 48, 48, 33, 6, 11, 43, 6, 1, 4, 1, 130, + 229, 28, 1, 1, 4, 4, 18, 4, 16, 20, 154, 32, 33, 142, 246, 65, 51, 150, 184, 129, 248, 213, 183, 241, + 245, 48, 12, 6, 3, 85, 29, 19, 1, 1, 255, 4, 2, 48, 0, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, + 11, 5, 0, 3, 130, 1, 1, 0, 62, 254, 163, 223, 61, 42, 224, 114, 87, 143, 126, 4, 208, 221, 90, 75, + 104, 219, 1, 175, 232, 99, 46, 24, 180, 224, 184, 115, 67, 24, 145, 25, 108, 24, 75, 235, 193, 213, + 51, 162, 61, 119, 139, 177, 4, 8, 193, 185, 170, 65, 78, 117, 118, 133, 91, 9, 54, 151, 24, 179, 72, + 175, 92, 239, 108, 176, 48, 134, 114, 214, 31, 184, 189, 155, 134, 161, 10, 166, 130, 206, 140, 45, + 78, 240, 144, 237, 80, 84, 24, 254, 83, 212, 206, 30, 98, 122, 40, 243, 114, 3, 9, 88, 208, 143, 250, + 89, 27, 196, 24, 128, 225, 142, 138, 12, 237, 26, 133, 128, 127, 144, 150, 113, 65, 122, 11, 69, 50, + 21, 179, 141, 193, 71, 42, 36, 73, 118, 64, 180, 232, 107, 254, 196, 241, 84, 99, 155, 133, 184, 232, + 128, 20, 150, 54, 36, 56, 53, 89, 1, 43, 252, 135, 124, 11, 68, 236, 125, 167, 148, 210, 6, 84, 178, + 154, 220, 29, 186, 92, 80, 123, 240, 202, 109, 243, 82, 188, 205, 222, 116, 13, 46, 167, 225, 8, 36, + 162, 206, 57, 79, 144, 77, 29, 153, 65, 94, 58, 124, 69, 181, 254, 40, 122, 155, 203, 220, 105, 142, + 139, 220, 213, 180, 121, 138, 92, 237, 53, 222, 138, 53, 9, 2, 10, 20, 183, 38, 191, 191, 57, 167, + 68, 7, 156, 185, 143, 91, 157, 202, 9, 183, 195, 235, 188, 189, 162, 175, 105, 3, 104, 97, 117, 116, + 104, 68, 97, 116, 97, 88, 196, 73, 150, 13, 229, 136, 14, 140, 104, 116, 52, 23, 15, 100, 118, 96, + 91, 143, 228, 174, 185, 162, 134, 50, 199, 153, 92, 243, 186, 131, 29, 151, 99, 65, 0, 0, 0, 4, 20, + 154, 32, 33, 142, 246, 65, 51, 150, 184, 129, 248, 213, 183, 241, 245, 0, 64, 95, 198, 144, 115, 197, + 160, 32, 232, 152, 206, 132, 72, 41, 180, 216, 37, 217, 115, 49, 159, 252, 206, 141, 217, 136, 130, + 247, 70, 248, 127, 56, 215, 235, 246, 70, 109, 185, 208, 176, 96, 238, 61, 181, 101, 100, 142, 2, 70, + 200, 148, 169, 162, 142, 154, 157, 16, 85, 36, 103, 90, 208, 186, 226, 221, 165, 1, 2, 3, 38, 32, 1, + 33, 88, 32, 116, 102, 143, 113, 2, 62, 213, 231, 68, 238, 236, 120, 252, 23, 149, 168, 208, 13, 192, + 150, 130, 41, 177, 80, 210, 99, 104, 156, 91, 215, 146, 194, 34, 88, 32, 126, 253, 183, 67, 182, 134, + 115, 195, 130, 235, 165, 161, 137, 246, 121, 39, 81, 237, 198, 154, 101, 223, 197, 126, 121, 164, + 226, 252, 142, 1, 54, 155] + ).toString('base64') + }, + type: 'public-key' + } + } +} + +const participantsTypeIDPutResponse: fspiopAPI.Schemas.ParticipantsTypeIDPutResponse = { + 'fspId': config.PARTICIPANT_ID +} + +describe('RegisterConsentModel', () => { + const connectionConfig: RedisConnectionConfig = { + port: 6789, + host: 'localhost', + logger: mockLogger() + } + let modelConfig: RegisterConsentModelConfig + let publisher: PubSub + + beforeEach(async () => { + let subId = 0 + let handler: NotificationCallback + + publisher = new PubSub(connectionConfig) + await publisher.connect() + + modelConfig = { + key: 'cache-key', + kvs: new KVS(connectionConfig), + subscriber: new PubSub(connectionConfig), + logger: connectionConfig.logger, + mojaloopRequests: {} as unknown as MojaloopRequests, + thirdpartyRequests: { + putConsents: jest.fn(() => Promise.resolve({ statusCode: 200 })), + putConsentsError: jest.fn(() => Promise.resolve({ statusCode: 200 })) + } as unknown as ThirdpartyRequests, + authServiceParticipantFSPId: config.PARTICIPANT_ID, + alsEndpoint: config.SHARED.ALS_ENDPOINT!, + requestProcessingTimeoutSeconds: 3 + } + mocked(modelConfig.subscriber.subscribe).mockImplementationOnce( + (_channel: string, cb: NotificationCallback) => { + handler = cb + return ++subId + } + ) + + mocked(publisher.publish).mockImplementationOnce( + async (channel: string, message: Message) => handler(channel, message, subId) + ) + await modelConfig.kvs.connect() + await modelConfig.subscriber.connect() + }) + + afterEach(async () => { + await publisher.disconnect() + await modelConfig.kvs.disconnect() + await modelConfig.subscriber.disconnect() + }) + + function checkRegisterConsentModelLayout (RegisterConsentModel: RegisterConsentModel, optData?: RegisterConsentData) { + expect(RegisterConsentModel).toBeTruthy() + expect(RegisterConsentModel.data).toBeDefined() + expect(RegisterConsentModel.fsm.state).toEqual(optData?.currentState || 'start') + + // check new getters + expect(RegisterConsentModel.subscriber).toEqual(modelConfig.subscriber) + expect(RegisterConsentModel.thirdpartyRequests).toEqual(modelConfig.thirdpartyRequests) + + // check is fsm correctly constructed + expect(typeof RegisterConsentModel.fsm.init).toEqual('function') + expect(typeof RegisterConsentModel.fsm.verifyConsent).toEqual('function') + expect(typeof RegisterConsentModel.fsm.registerAuthoritativeSourceWithALS).toEqual('function') + expect(typeof RegisterConsentModel.fsm.sendConsentCallbackToDFSP).toEqual('function') + + // check fsm notification handler + expect(typeof RegisterConsentModel.onVerifyConsent).toEqual('function') + expect(typeof RegisterConsentModel.onRegisterAuthoritativeSourceWithALS).toEqual('function') + expect(typeof RegisterConsentModel.onSendConsentCallbackToDFSP).toEqual('function') + + expect(sortedArray(RegisterConsentModel.fsm.allStates())).toEqual([ + 'callbackSent', + 'consentVerified', + 'errored', + 'none', + 'registeredAsAuthoritativeSource', + 'start' + ]) + expect(sortedArray(RegisterConsentModel.fsm.allTransitions())).toEqual([ + 'error', + 'init', + 'registerAuthoritativeSourceWithALS', + 'sendConsentCallbackToDFSP', + 'verifyConsent', + ]) + } + + it('module layout', () => { + expect(typeof RegisterConsentModel).toEqual('function') + expect(typeof create).toEqual('function') + }) + + + describe('verifyConsent', () => { + const registerConsentData: RegisterConsentData = { + currentState: 'start', + participantDFSPId: 'dfspA', + consentsPostRequestAUTH + } + + it('should be well constructed', async () => { + const model = await create(registerConsentData, modelConfig) + checkRegisterConsentModelLayout(model, registerConsentData) + }) + + it('registerAuthoritativeSourceWithALS() should transition start to consentVerified state when successful', async () => { + const model = await create(registerConsentData, modelConfig) + await model.fsm.verifyConsent() + + // check that the fsm was able to transition properly + expect(model.data.currentState).toEqual('consentVerified') + }) + }) + + describe('registerAuthoritativeSourceWithALS', () => { + const registerConsentData: RegisterConsentData = { + currentState: 'consentVerified', + participantDFSPId: 'dfspA', + consentsPostRequestAUTH + } + + const genericALSErrorResponse: fspiopAPI.Schemas.ErrorInformationObject = { + errorInformation: { + errorCode: '3000', + errorDescription: 'Generic error' + } + } + + const genericErrorResponse: fspiopAPI.Schemas.ErrorInformationObject = { + errorInformation: { + errorCode: '7200', + errorDescription: 'Generic Thirdparty account linking error' + } + } + + it('should be well constructed', async () => { + const model = await create(registerConsentData, modelConfig) + checkRegisterConsentModelLayout(model, registerConsentData) + }) + + it('registerAuthoritativeSourceWithALS() should transition consentVerified to registeredAsAuthoritativeSource state when successful', async () => { + const model = await create(registerConsentData, modelConfig) + // defer publication to notification channel + setImmediate(() => publisher.publish( + RegisterConsentModel.notificationChannel( + RegisterConsentPhase.waitOnParticipantResponseFromALS, + registerConsentData.consentsPostRequestAUTH.consentId + ), + participantsTypeIDPutResponse as unknown as Message + )) + await model.fsm.registerAuthoritativeSourceWithALS() + + // check that the fsm was able to transition properly + expect(model.data.currentState).toEqual('registeredAsAuthoritativeSource') + + // check we made a call to the als + expect(axios.post).toBeCalledWith( + `${config.SHARED.ALS_ENDPOINT}/participants/CONSENT/${consentsPostRequestAUTH.consentId}`, + { fspId: "centralAuth"}, + expect.any(Object) + ) + }) + + it('should handle a PUT /participants/CONSENT/{ID}/error response', async () => { + setImmediate(() => publisher.publish( + RegisterConsentModel.notificationChannel( + RegisterConsentPhase.waitOnParticipantResponseFromALS, + registerConsentData.consentsPostRequestAUTH.consentId + ), + genericALSErrorResponse as unknown as Message + )) + + const model = await create(registerConsentData, modelConfig) + await model.fsm.registerAuthoritativeSourceWithALS() + + // check it sends an error back to DFSP + expect(model.thirdpartyRequests.putConsentsError).toBeCalledWith( + "b51ec534-ee48-4575-b6a9-ead2955b8069", + genericErrorResponse, + "dfspA" + ) + }) + }) + + describe('sendConsentCallbackToDFSP', () => { + const registerConsentData: RegisterConsentData = { + currentState: 'registeredAsAuthoritativeSource', + participantDFSPId: 'dfspA', + consentsPostRequestAUTH + } + + it('should be well constructed', async () => { + const model = await create(registerConsentData, modelConfig) + checkRegisterConsentModelLayout(model, registerConsentData) + }) + + it('sendConsentCallbackToDFSP() should transition registeredAsAuthoritativeSource to callbackSent state when successful', async () => { + const model = await create(registerConsentData, modelConfig) + await model.fsm.sendConsentCallbackToDFSP() + + // check that the fsm was able to transition properly + expect(model.data.currentState).toEqual('callbackSent') + + // check we made a call to thirdpartyRequests.putConsents + expect(model.thirdpartyRequests.putConsents).toBeCalledWith( + 'b51ec534-ee48-4575-b6a9-ead2955b8069', + { + scopes: consentsPostRequestAUTH.scopes, + credential: { + ...consentsPostRequestAUTH.credential, + status: 'VERIFIED' + } + }, + 'dfspA' + ) + }) + }) +}) From 06ac6b875e37054c4b92931201bbbb1019e26a95 Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Thu, 8 Jul 2021 23:04:17 -0500 Subject: [PATCH 03/19] chore: lint god --- src/model/registerConsent.interface.ts | 4 ++-- src/model/registerConsent.model.ts | 25 ++++++++++++------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/model/registerConsent.interface.ts b/src/model/registerConsent.interface.ts index a3161e31..200c19fe 100644 --- a/src/model/registerConsent.interface.ts +++ b/src/model/registerConsent.interface.ts @@ -24,12 +24,12 @@ - Kevin Leyow - kevin.leyow@modusbox.com -------------- ******/ - import { +import { ControlledStateMachine, PersistentModelConfig, StateData } from '~/model/persistent.model' import { Method } from 'javascript-state-machine' -import { ThirdpartyRequests, MojaloopRequests } from '@mojaloop/sdk-standard-components'; +import { ThirdpartyRequests, MojaloopRequests } from '@mojaloop/sdk-standard-components' import { thirdparty as tpAPI } from '@mojaloop/api-snippets' diff --git a/src/model/registerConsent.model.ts b/src/model/registerConsent.model.ts index 1b779c3b..e1348cc1 100644 --- a/src/model/registerConsent.model.ts +++ b/src/model/registerConsent.model.ts @@ -25,21 +25,22 @@ -------------- ******/ -import { PubSub } from '~/shared/pub-sub' +import { PubSub, Message } from '~/shared/pub-sub' import { PersistentModel } from '~/model/persistent.model' import { StateMachineConfig } from 'javascript-state-machine' -import { ThirdpartyRequests, MojaloopRequests, Errors } from '@mojaloop/sdk-standard-components'; +import { ThirdpartyRequests, MojaloopRequests, Errors } from '@mojaloop/sdk-standard-components' import inspect from '~/shared/inspect' import { RegisterConsentData, RegisterConsentStateMachine, RegisterConsentModelConfig + , RegisterConsentPhase } from './registerConsent.interface' -import { Message } from '~/shared/pub-sub' + import deferredJob from '~/shared/deferred-job' -import { RegisterConsentPhase } from './registerConsent.interface' + import { reformatError } from '~/shared/api-error' -import axios from 'axios'; +import axios from 'axios' import { v1_1 as fspiopAPI, thirdparty as tpAPI @@ -58,13 +59,13 @@ export class RegisterConsentModel transitions: [ { name: 'verifyConsent', from: 'start', to: 'consentVerified' }, { name: 'registerAuthoritativeSourceWithALS', from: 'consentVerified', to: 'registeredAsAuthoritativeSource' }, - { name: 'sendConsentCallbackToDFSP', from: 'registeredAsAuthoritativeSource', to: 'callbackSent' }, + { name: 'sendConsentCallbackToDFSP', from: 'registeredAsAuthoritativeSource', to: 'callbackSent' } ], methods: { // specific transitions handlers methods onVerifyConsent: () => this.onVerifyConsent(), onRegisterAuthoritativeSourceWithALS: () => this.onRegisterAuthoritativeSourceWithALS(), - onSendConsentCallbackToDFSP: () => this.onSendConsentCallbackToDFSP(), + onSendConsentCallbackToDFSP: () => this.onSendConsentCallbackToDFSP() } } super(data, config, spec) @@ -86,7 +87,7 @@ export class RegisterConsentModel // utility function to check if an error after a transition which // pub/subs for a response that can return a mojaloop error - async checkModelDataForErrorInformation(): Promise { + async checkModelDataForErrorInformation (): Promise { if (this.data.errorInformation) { await this.fsm.error(this.data.errorInformation) } @@ -110,7 +111,6 @@ export class RegisterConsentModel return deferredJob(pubSub, channel).trigger(message) } - async onVerifyConsent (): Promise { const { consentsPostRequestAUTH, participantDFSPId } = this.data @@ -165,13 +165,13 @@ export class RegisterConsentModel const axiosConfig = { headers: { 'Content-Type': 'application/vnd.interoperability.participants+json;version=1.0', - 'Accept': 'application/vnd.interoperability.participants+json;version=1.0', + Accept: 'application/vnd.interoperability.participants+json;version=1.0', 'FSPIOP-Source': this.config.authServiceParticipantFSPId, Date: (new Date()).toUTCString() } } const payload: fspiopAPI.Schemas.ParticipantsTypeIDSubIDPostRequest = { - 'fspId': this.config.authServiceParticipantFSPId + fspId: this.config.authServiceParticipantFSPId } const res = await axios.post(alsParticipantURI, payload, axiosConfig) @@ -330,8 +330,7 @@ export async function create ( return model } - export default { RegisterConsentModel, - create, + create } From 1f36fc1937875e974cf52126f85bd3dd6cd42c6e Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Fri, 9 Jul 2021 20:16:14 -0500 Subject: [PATCH 04/19] chore: add integration tests --- config/integration.json | 4 +- docker-compose.yml | 4 +- .../thirdparty_pisp/callback_map.json | 3 +- .../spec_files/rules_callback/default.json | 3 +- src/model/registerConsent.model.ts | 11 +- src/server/handlers/consents.ts | 48 ++-- .../handlers/participants/{Type}/{ID}.ts | 28 ++- .../participants/{Type}/{ID}/error.ts | 23 +- src/shared/config.ts | 8 +- .../server/workflows/registerConsent.test.ts | 226 ++++++++++++++++++ 10 files changed, 310 insertions(+), 48 deletions(-) create mode 100644 test/integration/server/workflows/registerConsent.test.ts diff --git a/config/integration.json b/config/integration.json index 95b319e2..33cc3ec9 100644 --- a/config/integration.json +++ b/config/integration.json @@ -13,8 +13,8 @@ "COLOR": true }, "SHARED": { - "PEER_ENDPOINT": "0.0.0.0:4003", - "ALS_ENDPOINT": "0.0.0.0:4002", + "PEER_ENDPOINT": "ml-testing-toolkit:5000", + "ALS_ENDPOINT": "ml-testing-toolkit:5000", "QUOTES_ENDPOINT": "0.0.0.0:3002", "TRANSFERS_ENDPOINT": "0.0.0.0:3000", "BULK_TRANSFERS_ENDPOINT": "", diff --git a/docker-compose.yml b/docker-compose.yml index b15d1cd5..3a9870ae 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,6 +22,8 @@ services: volumes: - ./scripts/wait4.js:/opt/auth-service/scripts/wait4.js - ./scripts/wait4.config.js:/opt/auth-service/scripts/wait4.config.js + # environment: + # - NODE_ENV=integration healthcheck: test: wget -q http://localhost:4004/health -O /dev/null || exit 1 timeout: 20s @@ -65,7 +67,7 @@ services: - "./docker/ml-testing-toolkit/spec_files:/opt/mojaloop-testing-toolkit/spec_files" - "./docker/ml-testing-toolkit/secrets:/opt/mojaloop-testing-toolkit/secrets" ports: - - "15000:5000" + - "5000:5000" - "5050:5050" command: npm start networks: diff --git a/docker/ml-testing-toolkit/spec_files/api_definitions/thirdparty_pisp/callback_map.json b/docker/ml-testing-toolkit/spec_files/api_definitions/thirdparty_pisp/callback_map.json index 0967ef42..2c63c085 100644 --- a/docker/ml-testing-toolkit/spec_files/api_definitions/thirdparty_pisp/callback_map.json +++ b/docker/ml-testing-toolkit/spec_files/api_definitions/thirdparty_pisp/callback_map.json @@ -1 +1,2 @@ -{} +{ +} diff --git a/docker/ml-testing-toolkit/spec_files/rules_callback/default.json b/docker/ml-testing-toolkit/spec_files/rules_callback/default.json index fe51488c..0d4f101c 100644 --- a/docker/ml-testing-toolkit/spec_files/rules_callback/default.json +++ b/docker/ml-testing-toolkit/spec_files/rules_callback/default.json @@ -1 +1,2 @@ -[] +[ +] diff --git a/src/model/registerConsent.model.ts b/src/model/registerConsent.model.ts index e1348cc1..790bf8ef 100644 --- a/src/model/registerConsent.model.ts +++ b/src/model/registerConsent.model.ts @@ -120,7 +120,7 @@ export class RegisterConsentModel // todo: update transition to // - verify consent // - store consent - // - throw errors if there are error in verifying the Consent + // - throw errors if there are errors in verifying the consent } catch (error) { this.logger.push({ error }).error('start -> requestIsValid') @@ -161,21 +161,18 @@ export class RegisterConsentModel .init(async (channel) => { // todo: sdk-standard-components needs a postParticipantsTypeId function // building the request from scratch for now - const alsParticipantURI = `${this.config.alsEndpoint}/participants/CONSENT/${consentsPostRequestAUTH.consentId}` + const alsParticipantURI = `http://${this.config.alsEndpoint}/participants/CONSENT/${consentsPostRequestAUTH.consentId}` const axiosConfig = { headers: { - 'Content-Type': 'application/vnd.interoperability.participants+json;version=1.0', - Accept: 'application/vnd.interoperability.participants+json;version=1.0', + 'Accept': 'application/vnd.interoperability.participants+json;version=1.1', 'FSPIOP-Source': this.config.authServiceParticipantFSPId, - Date: (new Date()).toUTCString() + 'Date': (new Date()).toUTCString() } } const payload: fspiopAPI.Schemas.ParticipantsTypeIDSubIDPostRequest = { fspId: this.config.authServiceParticipantFSPId } - const res = await axios.post(alsParticipantURI, payload, axiosConfig) - this.logger.push({ res, channel }) .log('POST /participants/{Type}/{ID} call sent to ALS, listening on response') }) diff --git a/src/server/handlers/consents.ts b/src/server/handlers/consents.ts index 5a103434..067191c2 100644 --- a/src/server/handlers/consents.ts +++ b/src/server/handlers/consents.ts @@ -27,33 +27,53 @@ -------------- ******/ -import { Request, ResponseToolkit, ResponseObject } from '@hapi/hapi' +import { Request, ResponseObject } from '@hapi/hapi' import { Enum } from '@mojaloop/central-services-shared' import { logger } from '~/shared/logger' import inspect from '~/shared/inspect' +import { RegisterConsentModel } from '~/model/registerConsent.model' +import { create } from '~/model/registerConsent.model' +import { RegisterConsentModelConfig, RegisterConsentData } from '~/model/registerConsent.interface' +import { StateResponseToolkit } from '../plugins/state' +import { thirdparty as tpAPI } from '@mojaloop/api-snippets' +import config from '~/shared/config' /** The HTTP request `POST /consents` is used to create a consent object. * Called by `DFSP` to register a Consent object. */ export async function post ( _context: unknown, - _request: Request, - h: ResponseToolkit): Promise { - // const payload: tpAPI.Schemas.ConsentsPostRequest = request.payload as tpAPI.Schemas.ConsentsPostRequestAUTH - // const consentId = payload.consentId - // const initiatorId = request.headers[Enum.Http.Headers.FSPIOP.SOURCE] - // The auth-service is now the authoritative source for the Consent object. - // The auth-service's fspId is retrieved from the destination header. - // const participantId = request.headers[Enum.Http.Headers.FSPIOP.DESTINATION] + request: Request, + h: StateResponseToolkit): Promise { + const payload: tpAPI.Schemas.ConsentsPostRequestAUTH = request.payload as tpAPI.Schemas.ConsentsPostRequestAUTH + const consentId = payload.consentId + const initiatorId = request.headers[Enum.Http.Headers.FSPIOP.SOURCE] + + const data: RegisterConsentData = { + dfspId: h.getDFSPId(), + currentState: 'start', + consentsPostRequestAUTH: payload, + participantDFSPId: initiatorId + } + + // if the request is valid then DFSP returns response via PUT /consentRequests/{ID} call. + const modelConfig: RegisterConsentModelConfig = { + kvs: h.getKVS(), + subscriber: h.getSubscriber(), + key: consentId, + logger: logger, + thirdpartyRequests: h.getThirdpartyRequests(), + mojaloopRequests: h.getMojaloopRequests(), + authServiceParticipantFSPId: config.PARTICIPANT_ID, + alsEndpoint: config.SHARED.ALS_ENDPOINT, + requestProcessingTimeoutSeconds: config.REQUEST_PROCESSING_TIMEOUT_SECONDS + } setImmediate(async (): Promise => { try { - // TODO: 1) auth-service needs to check signature against challenge. - // 2) send a POST /participants/CONSENT/{ID} to the ALS and receive - // a PUT /participants/CONSENT/{ID} - // 3) store the consent object and finally send back a - // PUT /consents/{ID} request. Wooo...state machine + const model: RegisterConsentModel = await create(data, modelConfig) + await model.run() } catch (error) { // the model catches all planned, catches unplanned errors, // handles callbacks and also rethrows the error to stop the state machine diff --git a/src/server/handlers/participants/{Type}/{ID}.ts b/src/server/handlers/participants/{Type}/{ID}.ts index 7362638a..114bcaac 100644 --- a/src/server/handlers/participants/{Type}/{ID}.ts +++ b/src/server/handlers/participants/{Type}/{ID}.ts @@ -21,27 +21,31 @@ - Kevin Leyow -------------- ******/ -import { Request, ResponseObject, ResponseToolkit } from '@hapi/hapi' +import { Request, ResponseObject } from '@hapi/hapi' import { Enum } from '@mojaloop/central-services-shared' +import { RegisterConsentPhase } from '~/model/registerConsent.interface' +import { RegisterConsentModel } from '~/model/registerConsent.model' +import { thirdparty as tpAPI } from '@mojaloop/api-snippets' +import { StateResponseToolkit } from '~/server/plugins/state' +import { Message } from '~/shared/pub-sub' /** * Handles a inbound PUT /participants/{Type}/{ID} request */ -async function put (_context: unknown, _request: Request, h: ResponseToolkit): Promise { +async function put (_context: unknown, request: Request, h: StateResponseToolkit): Promise { // PUT /participants/{Type}/{ID} is a response to POST /participants/{Type}/{ID} // when the ALS is able to register the auth-service as the authoritative // owner of a Consent object - // const consentRequestId = request.params.ID - // const payload = request.payload as tpAPI.Schemas.ParticipantsTypeIDPutResponse - /* - RegisterConsentModel.triggerWorkflow( - RegisterConsentPhase.registerAuthServiceConsentWithALS, - consentRequestId, - h.getPublisher(), - payload as unknown as Message - ) - */ + const consentId = request.params.ID + const payload = request.payload as tpAPI.Schemas.ParticipantsTypeIDPutResponse + + RegisterConsentModel.triggerWorkflow( + RegisterConsentPhase.waitOnParticipantResponseFromALS, + consentId, + h.getPublisher(), + payload as unknown as Message + ) return h.response({}).code(Enum.Http.ReturnCodes.OK.CODE) } diff --git a/src/server/handlers/participants/{Type}/{ID}/error.ts b/src/server/handlers/participants/{Type}/{ID}/error.ts index 1dc97a75..eee86957 100644 --- a/src/server/handlers/participants/{Type}/{ID}/error.ts +++ b/src/server/handlers/participants/{Type}/{ID}/error.ts @@ -21,25 +21,30 @@ - Kevin Leyow -------------- ******/ -import { Request, ResponseObject, ResponseToolkit } from '@hapi/hapi' -import { Enum } from '@mojaloop/central-services-shared' + import { Request, ResponseObject } from '@hapi/hapi' + import { Enum } from '@mojaloop/central-services-shared' + import { RegisterConsentPhase } from '~/model/registerConsent.interface' + import { RegisterConsentModel } from '~/model/registerConsent.model' + import { v1_1 as fspiopAPI } from '@mojaloop/api-snippets' + import { StateResponseToolkit } from '~/server/plugins/state' + import { Message } from '~/shared/pub-sub' /** * Handles a inbound PUT /participants/{Type}/{ID}/error request */ -async function put (_context: unknown, _request: Request, h: ResponseToolkit): Promise { +async function put (_context: unknown, request: Request, h: StateResponseToolkit): Promise { // PUT /participants/{Type}/{ID}/error is a response to POST /participants/{Type}/{ID} // when some went wrong with the ALS - // const consentRequestId = request.params.ID - // const payload = request.payload as fspiopAPI.Schemas.ErrorInformation - /* + const consentId = request.params.ID + const payload = request.payload as fspiopAPI.Schemas.ErrorInformation + RegisterConsentModel.triggerWorkflow( - RegisterConsentPhase.registerAuthServiceConsentWithALS, - consentRequestId, + RegisterConsentPhase.waitOnParticipantResponseFromALS, + consentId, h.getPublisher(), payload as unknown as Message ) - */ + return h.response({}).code(Enum.Http.ReturnCodes.OK.CODE) } diff --git a/src/shared/config.ts b/src/shared/config.ts index f096c609..cdc83b60 100644 --- a/src/shared/config.ts +++ b/src/shared/config.ts @@ -43,6 +43,7 @@ interface ServiceConfig { PARTICIPANT_ID: string; DATABASE: DatabaseConfig; ENV: string; + REQUEST_PROCESSING_TIMEOUT_SECONDS: number; REDIS: { HOST: string; PORT: number; @@ -55,7 +56,7 @@ interface ServiceConfig { }; SHARED: { PEER_ENDPOINT: string; - ALS_ENDPOINT?: string; + ALS_ENDPOINT: string; QUOTES_ENDPOINT?: string; TRANSFERS_ENDPOINT?: string; SERVICES_ENDPOINT?: string; @@ -114,6 +115,11 @@ const ConvictConfig = Convict({ env: 'PARTICIPANT_ID', arg: 'participantId' }, + REQUEST_PROCESSING_TIMEOUT_SECONDS: { + doc: 'The timeout for waiting for a response to a request', + env: 'REQUEST_PROCESSING_TIMEOUT_SECONDS', + default: 30 + }, REDIS: { HOST: { doc: 'The Redis Hostname/IP address to connect.', diff --git a/test/integration/server/workflows/registerConsent.test.ts b/test/integration/server/workflows/registerConsent.test.ts new file mode 100644 index 00000000..032f9918 --- /dev/null +++ b/test/integration/server/workflows/registerConsent.test.ts @@ -0,0 +1,226 @@ +/***** + License + -------------- + Copyright © 2020 Mojaloop Foundation + The Mojaloop files are made available by the Mojaloop Foundation under the + Apache License, Version 2.0 (the 'License') and you may not use these files + except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop + files are distributed onan 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + ANY KIND, either express or implied. See the License for the specific language + governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + - Kenneth Zeng + -------------- + ******/ + +import axios from 'axios' +import headers from '~/../test/data/headers.json' +import { thirdparty as tpAPI } from '@mojaloop/api-snippets' + +interface MLTestingToolkitRequest { + timestamp: string + method: string + path: string + headers: Record + body: Record +} + +// test data from Lewis +// here is how the client should convert ArrayBuffer to base64 strings using Browser's btoa function +// take a look on `reduce` version +// https://stackoverflow.com/questions/9267899/arraybuffer-to-base64-encoded-string +// in nodejs we use only Buffer.from([...]).toString('base64') +const consentsPostRequestAUTH: tpAPI.Schemas.ConsentsPostRequestAUTH = { + consentId: 'b51ec534-ee48-4575-b6a9-ead2955b8069', + scopes: [ + { + accountId: 'dfsp.username.5678', + actions: [ + 'accounts.transfer', + 'accounts.getBalance' + ] + } + ], + credential: { + credentialType: 'FIDO', + status: 'PENDING', + payload: { + id: 'X8aQc8WgIOiYzoRIKbTYJdlzMZ_8zo3ZiIL3Rvh_ONfr9kZtudCwYO49tWVkjgJGyJSpoo6anRBVJGda0Lri3Q', + rawId: Buffer.from([ + 95, 198, 144, 115, 197, 160, 32, 232, 152, 206, 132, 72, 41, 180, 216, 37, 217, 115, 49, 159, 252, 206, + 141, 217, 136, 130, 247, 70, 248, 127, 56, 215, 235, 246, 70, 109, 185, 208, 176, 96, 238, 61, 181, + 101, 100, 142, 2, 70, 200, 148, 169, 162, 142, 154, 157, 16, 85, 36, 103, 90, 208, 186, 226, 221] + ).toString('base64'), + response: { + // clientDataJSON needs to be utf-8 not base64 + clientDataJSON: Buffer.from( + [123, 34, 116, 121, 112, 101, 34, 58, 34, 119, 101, 98, + 97, 117, 116, 104, 110, 46, 99, 114, 101, 97, 116, 101, 34, 44, 34, 99, 104, 97, 108, 108, 101, 110, + 103, 101, 34, 58, 34, 77, 103, 65, 51, 65, 68, 103, 65, 78, 81, 66, 106, 65, 68, 73, 65, 90, 65, 65, + 53, 65, 68, 107, 65, 89, 81, 65, 48, 65, 71, 77, 65, 77, 81, 65, 53, 65, 71, 81, 65, 77, 81, 66, 104, + 65, 68, 103, 65, 78, 119, 66, 107, 65, 68, 77, 65, 78, 65, 66, 109, 65, 71, 81, 65, 77, 65, 66, 106, + 65, 68, 69, 65, 77, 65, 66, 104, 65, 71, 81, 65, 77, 65, 66, 105, 65, 68, 85, 65, 77, 103, 65, 51, + 65, 68, 73, 65, 77, 81, 66, 106, 65, 71, 89, 65, 77, 119, 66, 106, 65, 68, 103, 65, 77, 65, 65, 121, + 65, 68, 103, 65, 79, 65, 66, 106, 65, 68, 73, 65, 79, 81, 66, 107, 65, 71, 69, 65, 78, 81, 66, 105, + 65, 68, 65, 65, 90, 81, 66, 105, 65, 71, 85, 65, 90, 103, 65, 50, 65, 68, 99, 65, 79, 65, 65, 122, + 65, 68, 81, 65, 77, 65, 65, 34, 44, 34, 111, 114, 105, 103, 105, 110, 34, 58, 34, 104, 116, 116, 112, + 58, 47, 47, 108, 111, 99, 97, 108, 104, 111, 115, 116, 58, 53, 48, 48, 48, 34, 44, 34, 99, 114, 111, + 115, 115, 79, 114, 105, 103, 105, 110, 34, 58, 102, 97, 108, 115, 101, 125] + ).toString('utf-8'), + attestationObject: Buffer.from( + [163, 99, 102, 109, 116, 102, 112, 97, 99, 107, 101, 100, 103, 97, 116, + 116, 83, 116, 109, 116, 163, 99, 97, 108, 103, 38, 99, 115, 105, 103, 88, 71, 48, 69, 2, 32, 30, 175, + 73, 42, 152, 191, 108, 89, 231, 187, 75, 149, 87, 233, 58, 38, 49, 223, 5, 193, 112, 89, 20, 66, 92, + 149, 165, 122, 56, 51, 36, 181, 2, 33, 0, 246, 136, 227, 141, 25, 119, 155, 56, 44, 106, 223, 181, + 42, 190, 233, 177, 11, 247, 73, 207, 86, 183, 83, 209, 117, 13, 172, 85, 48, 89, 243, 127, 99, 120, + 53, 99, 129, 89, 2, 193, 48, 130, 2, 189, 48, 130, 1, 165, 160, 3, 2, 1, 2, 2, 4, 11, 5, 205, 83, 48, + 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 11, 5, 0, 48, 46, 49, 44, 48, 42, 6, 3, 85, 4, 3, 19, 35, + 89, 117, 98, 105, 99, 111, 32, 85, 50, 70, 32, 82, 111, 111, 116, 32, 67, 65, 32, 83, 101, 114, 105, + 97, 108, 32, 52, 53, 55, 50, 48, 48, 54, 51, 49, 48, 32, 23, 13, 49, 52, 48, 56, 48, 49, 48, 48, 48, + 48, 48, 48, 90, 24, 15, 50, 48, 53, 48, 48, 57, 48, 52, 48, 48, 48, 48, 48, 48, 90, 48, 110, 49, 11, + 48, 9, 6, 3, 85, 4, 6, 19, 2, 83, 69, 49, 18, 48, 16, 6, 3, 85, 4, 10, 12, 9, 89, 117, 98, 105, 99, + 111, 32, 65, 66, 49, 34, 48, 32, 6, 3, 85, 4, 11, 12, 25, 65, 117, 116, 104, 101, 110, 116, 105, 99, + 97, 116, 111, 114, 32, 65, 116, 116, 101, 115, 116, 97, 116, 105, 111, 110, 49, 39, 48, 37, 6, 3, 85, + 4, 3, 12, 30, 89, 117, 98, 105, 99, 111, 32, 85, 50, 70, 32, 69, 69, 32, 83, 101, 114, 105, 97, 108, + 32, 49, 56, 52, 57, 50, 57, 54, 49, 57, 48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, + 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 33, 26, 111, 177, 181, 137, 37, 203, 10, 193, 24, 95, 124, + 42, 227, 168, 180, 136, 16, 20, 121, 177, 30, 255, 245, 85, 224, 125, 151, 81, 189, 43, 23, 106, 37, + 45, 238, 89, 236, 227, 133, 153, 32, 91, 179, 234, 40, 191, 143, 215, 252, 125, 167, 92, 5, 66, 114, + 174, 72, 88, 229, 145, 252, 90, 163, 108, 48, 106, 48, 34, 6, 9, 43, 6, 1, 4, 1, 130, 196, 10, 2, 4, + 21, 49, 46, 51, 46, 54, 46, 49, 46, 52, 46, 49, 46, 52, 49, 52, 56, 50, 46, 49, 46, 49, 48, 19, 6, + 11, 43, 6, 1, 4, 1, 130, 229, 28, 2, 1, 1, 4, 4, 3, 2, 4, 48, 48, 33, 6, 11, 43, 6, 1, 4, 1, 130, + 229, 28, 1, 1, 4, 4, 18, 4, 16, 20, 154, 32, 33, 142, 246, 65, 51, 150, 184, 129, 248, 213, 183, 241, + 245, 48, 12, 6, 3, 85, 29, 19, 1, 1, 255, 4, 2, 48, 0, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, + 11, 5, 0, 3, 130, 1, 1, 0, 62, 254, 163, 223, 61, 42, 224, 114, 87, 143, 126, 4, 208, 221, 90, 75, + 104, 219, 1, 175, 232, 99, 46, 24, 180, 224, 184, 115, 67, 24, 145, 25, 108, 24, 75, 235, 193, 213, + 51, 162, 61, 119, 139, 177, 4, 8, 193, 185, 170, 65, 78, 117, 118, 133, 91, 9, 54, 151, 24, 179, 72, + 175, 92, 239, 108, 176, 48, 134, 114, 214, 31, 184, 189, 155, 134, 161, 10, 166, 130, 206, 140, 45, + 78, 240, 144, 237, 80, 84, 24, 254, 83, 212, 206, 30, 98, 122, 40, 243, 114, 3, 9, 88, 208, 143, 250, + 89, 27, 196, 24, 128, 225, 142, 138, 12, 237, 26, 133, 128, 127, 144, 150, 113, 65, 122, 11, 69, 50, + 21, 179, 141, 193, 71, 42, 36, 73, 118, 64, 180, 232, 107, 254, 196, 241, 84, 99, 155, 133, 184, 232, + 128, 20, 150, 54, 36, 56, 53, 89, 1, 43, 252, 135, 124, 11, 68, 236, 125, 167, 148, 210, 6, 84, 178, + 154, 220, 29, 186, 92, 80, 123, 240, 202, 109, 243, 82, 188, 205, 222, 116, 13, 46, 167, 225, 8, 36, + 162, 206, 57, 79, 144, 77, 29, 153, 65, 94, 58, 124, 69, 181, 254, 40, 122, 155, 203, 220, 105, 142, + 139, 220, 213, 180, 121, 138, 92, 237, 53, 222, 138, 53, 9, 2, 10, 20, 183, 38, 191, 191, 57, 167, + 68, 7, 156, 185, 143, 91, 157, 202, 9, 183, 195, 235, 188, 189, 162, 175, 105, 3, 104, 97, 117, 116, + 104, 68, 97, 116, 97, 88, 196, 73, 150, 13, 229, 136, 14, 140, 104, 116, 52, 23, 15, 100, 118, 96, + 91, 143, 228, 174, 185, 162, 134, 50, 199, 153, 92, 243, 186, 131, 29, 151, 99, 65, 0, 0, 0, 4, 20, + 154, 32, 33, 142, 246, 65, 51, 150, 184, 129, 248, 213, 183, 241, 245, 0, 64, 95, 198, 144, 115, 197, + 160, 32, 232, 152, 206, 132, 72, 41, 180, 216, 37, 217, 115, 49, 159, 252, 206, 141, 217, 136, 130, + 247, 70, 248, 127, 56, 215, 235, 246, 70, 109, 185, 208, 176, 96, 238, 61, 181, 101, 100, 142, 2, 70, + 200, 148, 169, 162, 142, 154, 157, 16, 85, 36, 103, 90, 208, 186, 226, 221, 165, 1, 2, 3, 38, 32, 1, + 33, 88, 32, 116, 102, 143, 113, 2, 62, 213, 231, 68, 238, 236, 120, 252, 23, 149, 168, 208, 13, 192, + 150, 130, 41, 177, 80, 210, 99, 104, 156, 91, 215, 146, 194, 34, 88, 32, 126, 253, 183, 67, 182, 134, + 115, 195, 130, 235, 165, 161, 137, 246, 121, 39, 81, 237, 198, 154, 101, 223, 197, 126, 121, 164, + 226, 252, 142, 1, 54, 155] + ).toString('base64') + }, + type: 'public-key' + } + } +} + +describe('Inbound POST /consents', (): void => { + const ttkRequestsHistoryUri = `http://localhost:5050/api/history/requests` + + beforeEach(async(): Promise => { + // clear the request history in TTK between tests. + await axios.delete(ttkRequestsHistoryUri, {}) + }) + + describe('Happy Path', (): void => { + describe('POST /consents from DFSP should create the model and start the workflow', (): void => { + const axiosConfig = { + headers: { + 'Content-Type': 'application/json', + 'FSPIOP-Source': 'switch', + Date: 'Thu, 24 Jan 2019 10:23:12 GMT', + 'FSPIOP-Destination': 'als' + } + } + + it('should send POST /participants/CONSENT/{ID} to the ALS', async (): Promise => { + // Endpoint + const scenariosURI = 'http://localhost:4004/consents' + const payload = consentsPostRequestAUTH + const response = await axios.post(scenariosURI, payload, { + headers: headers + }) + + // auth-service should return Accepted code + expect(response.status).toEqual(202) + + // wait a bit for the auth-service to process the request + await new Promise(resolve => setTimeout(resolve, 200)) + + // check that the auth-service has sent a POST /participants/{Type}/{ID} to the ALS (TTK) + const requestsHistory: MLTestingToolkitRequest[] = (await axios.get(ttkRequestsHistoryUri, axiosConfig)).data + var postParticipantsTypeIdToALS = requestsHistory.filter(req => { + return req.method === 'post' && req.path === '/participants/CONSENT/b51ec534-ee48-4575-b6a9-ead2955b8069' + }) + expect(postParticipantsTypeIdToALS.length).toEqual(1) + + const historyPayload = postParticipantsTypeIdToALS[0].body as tpAPI.Schemas.ParticipantsTypeIDSubIDPostRequest + expect(historyPayload).toEqual({ + fspId: 'centralAuth' + }) + }) + }) + + describe('PUT /participants/{Type}/{ID} from ALS should continue the flow', (): void => { + it('should send PUT /consents/{ID} on receiving successful callback from ALS', async (): Promise => { + // Endpoint + const axiosConfig = { + headers: { + 'Content-Type': 'application/vnd.interoperability.participants+json;version=1.1', + 'Accept': 'application/vnd.interoperability.participants+json;version=1.1', + 'FSPIOP-Source': 'als', + Date: 'Thu, 24 Jan 2019 10:23:12 GMT', + 'FSPIOP-Destination': 'centralAuth' + } + } + // simulate ALS sending successful callback to auth-service + const putParticipantsTypeIdFromALS = { + fspId: 'centralAuth' + } + const putScenarioUri = `http://localhost:4004/participants/CONSENT/b51ec534-ee48-4575-b6a9-ead2955b8069` + + const responseToPutParticipantsTypeId = await axios.put(putScenarioUri, putParticipantsTypeIdFromALS, axiosConfig) + expect(responseToPutParticipantsTypeId.status).toEqual(200) + + // wait a bit for the DFSP adapter to process the request + await new Promise(resolve => setTimeout(resolve, 200)) + + // check that the auth-service has sent a PUT /consents/{ID} to the DFSP (TTK) + const requestsHistory: MLTestingToolkitRequest[] = (await axios.get(ttkRequestsHistoryUri, axiosConfig)).data + var putConsentsIdToDFSP = requestsHistory.filter(req => { + return req.method === 'put' && req.path === '/consents/b51ec534-ee48-4575-b6a9-ead2955b8069' + }) + expect(putConsentsIdToDFSP.length).toEqual(1) + + const historyPayload = putConsentsIdToDFSP[0].body as tpAPI.Schemas.ConsentRequestsIDPutResponseOTP + expect(historyPayload).toEqual({ + scopes: consentsPostRequestAUTH.scopes, + credential: { + ...consentsPostRequestAUTH.credential, + status: 'VERIFIED' + } + }) + }) + }) + }) +}) From cdbee6090b1bc0dfb66ac59f2058cc9cc3566ae1 Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Fri, 9 Jul 2021 20:39:07 -0500 Subject: [PATCH 05/19] chore: fix lint --- src/model/registerConsent.model.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/model/registerConsent.model.ts b/src/model/registerConsent.model.ts index 790bf8ef..23ecde62 100644 --- a/src/model/registerConsent.model.ts +++ b/src/model/registerConsent.model.ts @@ -112,15 +112,15 @@ export class RegisterConsentModel } async onVerifyConsent (): Promise { + // not sure what functions to use or if they are ready + // for now we are just going do nothing here. + // todo: update transition to + // - verify consent + // - store consent + // - throw errors if there are errors in verifying the consent + /* const { consentsPostRequestAUTH, participantDFSPId } = this.data - try { - // not sure what functions to use or if they are ready - // for now we are just going do nothing here. - // todo: update transition to - // - verify consent - // - store consent - // - throw errors if there are errors in verifying the consent } catch (error) { this.logger.push({ error }).error('start -> requestIsValid') @@ -145,6 +145,7 @@ export class RegisterConsentModel // throw error to stop state machine throw error } + */ } async onRegisterAuthoritativeSourceWithALS (): Promise { @@ -164,9 +165,9 @@ export class RegisterConsentModel const alsParticipantURI = `http://${this.config.alsEndpoint}/participants/CONSENT/${consentsPostRequestAUTH.consentId}` const axiosConfig = { headers: { - 'Accept': 'application/vnd.interoperability.participants+json;version=1.1', + Accept: 'application/vnd.interoperability.participants+json;version=1.1', 'FSPIOP-Source': this.config.authServiceParticipantFSPId, - 'Date': (new Date()).toUTCString() + Date: (new Date()).toUTCString() } } const payload: fspiopAPI.Schemas.ParticipantsTypeIDSubIDPostRequest = { From ca6e4456854ecbc790ad8977babdfbdddfa83f4c Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Fri, 9 Jul 2021 20:50:40 -0500 Subject: [PATCH 06/19] chore: fix unit tests --- test/unit/model/registerConsent.model.test.ts | 2 +- test/unit/server/handlers/consents.test.ts | 27 ++++++++++++++++--- .../handlers/participants/{Type}/{ID}.test.ts | 18 ++++++++++--- .../participants/{Type}/{ID}/error.test.ts | 17 +++++++++--- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/test/unit/model/registerConsent.model.test.ts b/test/unit/model/registerConsent.model.test.ts index d85a4398..893ee89a 100644 --- a/test/unit/model/registerConsent.model.test.ts +++ b/test/unit/model/registerConsent.model.test.ts @@ -308,7 +308,7 @@ describe('RegisterConsentModel', () => { // check we made a call to the als expect(axios.post).toBeCalledWith( - `${config.SHARED.ALS_ENDPOINT}/participants/CONSENT/${consentsPostRequestAUTH.consentId}`, + `http://${config.SHARED.ALS_ENDPOINT}/participants/CONSENT/${consentsPostRequestAUTH.consentId}`, { fspId: "centralAuth"}, expect.any(Object) ) diff --git a/test/unit/server/handlers/consents.test.ts b/test/unit/server/handlers/consents.test.ts index 79ad6c2b..2dfefe22 100644 --- a/test/unit/server/handlers/consents.test.ts +++ b/test/unit/server/handlers/consents.test.ts @@ -26,10 +26,10 @@ - Kevin Leyow -------------- ******/ -import { Request, ResponseToolkit } from '@hapi/hapi' +import { Request } from '@hapi/hapi' import { Enum } from '@mojaloop/central-services-shared' -import { h } from 'test/data/data' import ConsentsHandler from '~/server/handlers/consents' +import { StateResponseToolkit } from '~/server/plugins/state' jest.mock('~/domain/errors') @@ -134,10 +134,31 @@ const consentsPostRequestAUTH = { describe('server/handlers/consents', (): void => { it('Should return 202 success code', async (): Promise => { const request = consentsPostRequestAUTH + const pubSubMock = { + subscribe: jest.fn() + } + const toolkit = { + getSubscriber: jest.fn(() => pubSubMock), + response: jest.fn(() => ({ + code: jest.fn((code: number) => ({ + statusCode: code + })) + })), + getDFSPId: jest.fn(() => 'centralAuth'), + getThirdpartyRequests: jest.fn(() => ({ + putConsents: jest.fn(), + putConsentsError: jest.fn() + })), + getMojaloopRequests: jest.fn(), + getKVS: jest.fn(() => ({ + set: jest.fn() + })) + } + const response = await ConsentsHandler.post( null, request as unknown as Request, - h as ResponseToolkit + toolkit as unknown as StateResponseToolkit ) expect(response.statusCode).toBe(Enum.Http.ReturnCodes.ACCEPTED.CODE) diff --git a/test/unit/server/handlers/participants/{Type}/{ID}.test.ts b/test/unit/server/handlers/participants/{Type}/{ID}.test.ts index 3d94bdd6..06fd9012 100644 --- a/test/unit/server/handlers/participants/{Type}/{ID}.test.ts +++ b/test/unit/server/handlers/participants/{Type}/{ID}.test.ts @@ -24,10 +24,10 @@ - Kevin Leyow -------------- ******/ -import { Request, ResponseToolkit } from '@hapi/hapi' +import { Request } from '@hapi/hapi' import { Enum } from '@mojaloop/central-services-shared' -import { h } from 'test/data/data' import ParticipantsTypeIDHandler from '~/server/handlers/participants/{Type}/{ID}' +import { StateResponseToolkit } from '../../../../../../src/server/plugins/state'; jest.mock('~/domain/errors') @@ -48,10 +48,22 @@ const participantsTypeIDPutResponse = { describe('server/handlers/consents', (): void => { it('Should return 200 success code', async (): Promise => { const request = participantsTypeIDPutResponse + const pubSubMock = { + publish: jest.fn() + } + const toolkit = { + getPublisher: jest.fn(() => pubSubMock), + response: jest.fn(() => ({ + code: jest.fn((code: number) => ({ + statusCode: code + })) + })) + } + const response = await ParticipantsTypeIDHandler.put( null, request as unknown as Request, - h as ResponseToolkit + toolkit as unknown as StateResponseToolkit ) expect(response.statusCode).toBe(Enum.Http.ReturnCodes.OK.CODE) diff --git a/test/unit/server/handlers/participants/{Type}/{ID}/error.test.ts b/test/unit/server/handlers/participants/{Type}/{ID}/error.test.ts index 444b9a0c..50558bef 100644 --- a/test/unit/server/handlers/participants/{Type}/{ID}/error.test.ts +++ b/test/unit/server/handlers/participants/{Type}/{ID}/error.test.ts @@ -24,10 +24,10 @@ - Kevin Leyow -------------- ******/ -import { Request, ResponseToolkit } from '@hapi/hapi' +import { Request } from '@hapi/hapi' import { Enum } from '@mojaloop/central-services-shared' -import { h } from 'test/data/data' import ParticipantsTypeIDErrorHandler from '~/server/handlers/participants/{Type}/{ID}/error' +import { StateResponseToolkit } from '~/server/plugins/state'; jest.mock('~/domain/errors') @@ -51,10 +51,21 @@ const errorInformationResponse = { describe('server/handlers/consents', (): void => { it('Should return 200 success code', async (): Promise => { const request = errorInformationResponse + const pubSubMock = { + publish: jest.fn() + } + const toolkit = { + getPublisher: jest.fn(() => pubSubMock), + response: jest.fn(() => ({ + code: jest.fn((code: number) => ({ + statusCode: code + })) + })) + } const response = await ParticipantsTypeIDErrorHandler.put( null, request as unknown as Request, - h as ResponseToolkit + toolkit as unknown as StateResponseToolkit ) expect(response.statusCode).toBe(Enum.Http.ReturnCodes.OK.CODE) From d983982cf1af18f3475153551f798b7ee450c269 Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Fri, 9 Jul 2021 20:57:42 -0500 Subject: [PATCH 07/19] chore: increase wait time --- test/integration/server/workflows/registerConsent.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/server/workflows/registerConsent.test.ts b/test/integration/server/workflows/registerConsent.test.ts index 032f9918..f3bd967c 100644 --- a/test/integration/server/workflows/registerConsent.test.ts +++ b/test/integration/server/workflows/registerConsent.test.ts @@ -165,7 +165,7 @@ describe('Inbound POST /consents', (): void => { expect(response.status).toEqual(202) // wait a bit for the auth-service to process the request - await new Promise(resolve => setTimeout(resolve, 200)) + await new Promise(resolve => setTimeout(resolve, 500)) // check that the auth-service has sent a POST /participants/{Type}/{ID} to the ALS (TTK) const requestsHistory: MLTestingToolkitRequest[] = (await axios.get(ttkRequestsHistoryUri, axiosConfig)).data @@ -203,7 +203,7 @@ describe('Inbound POST /consents', (): void => { expect(responseToPutParticipantsTypeId.status).toEqual(200) // wait a bit for the DFSP adapter to process the request - await new Promise(resolve => setTimeout(resolve, 200)) + await new Promise(resolve => setTimeout(resolve, 500)) // check that the auth-service has sent a PUT /consents/{ID} to the DFSP (TTK) const requestsHistory: MLTestingToolkitRequest[] = (await axios.get(ttkRequestsHistoryUri, axiosConfig)).data From 0296f7c8174fe98f4afb9dae5d4aefa70586a22f Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Fri, 9 Jul 2021 22:49:32 -0500 Subject: [PATCH 08/19] chore: add debug logging --- src/model/registerConsent.model.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/model/registerConsent.model.ts b/src/model/registerConsent.model.ts index 23ecde62..d9562c4d 100644 --- a/src/model/registerConsent.model.ts +++ b/src/model/registerConsent.model.ts @@ -173,6 +173,9 @@ export class RegisterConsentModel const payload: fspiopAPI.Schemas.ParticipantsTypeIDSubIDPostRequest = { fspId: this.config.authServiceParticipantFSPId } + console.log({ alsParticipantURI, payload, axiosConfig }) + this.logger.push({ alsParticipantURI, payload, axiosConfig }) + .log('Testing') const res = await axios.post(alsParticipantURI, payload, axiosConfig) this.logger.push({ res, channel }) .log('POST /participants/{Type}/{ID} call sent to ALS, listening on response') From f75accbf25579bf3959835051f1cde319558c02e Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Sat, 10 Jul 2021 12:55:04 -0500 Subject: [PATCH 09/19] chore: improve coverage --- src/model/registerConsent.model.ts | 6 +- test/unit/model/registerConsent.model.test.ts | 121 ++++++++++++++++++ 2 files changed, 124 insertions(+), 3 deletions(-) diff --git a/src/model/registerConsent.model.ts b/src/model/registerConsent.model.ts index d9562c4d..48588361 100644 --- a/src/model/registerConsent.model.ts +++ b/src/model/registerConsent.model.ts @@ -173,9 +173,9 @@ export class RegisterConsentModel const payload: fspiopAPI.Schemas.ParticipantsTypeIDSubIDPostRequest = { fspId: this.config.authServiceParticipantFSPId } - console.log({ alsParticipantURI, payload, axiosConfig }) - this.logger.push({ alsParticipantURI, payload, axiosConfig }) - .log('Testing') + //console.log({ alsParticipantURI, payload, axiosConfig }) + //this.logger.push({ alsParticipantURI, payload, axiosConfig }) + // .log('Testing') const res = await axios.post(alsParticipantURI, payload, axiosConfig) this.logger.push({ res, channel }) .log('POST /participants/{Type}/{ID} call sent to ALS, listening on response') diff --git a/test/unit/model/registerConsent.model.test.ts b/test/unit/model/registerConsent.model.test.ts index 893ee89a..a7ac7163 100644 --- a/test/unit/model/registerConsent.model.test.ts +++ b/test/unit/model/registerConsent.model.test.ts @@ -48,6 +48,7 @@ import sortedArray from 'test/unit/sortedArray' import { RegisterConsentModelConfig, RegisterConsentData, RegisterConsentPhase } from '~/model/registerConsent.interface' import config from '~/shared/config'; import axios from 'axios'; +import shouldNotBeExecuted from '../shouldNotBeExecuted'; // mock KVS default exported class jest.mock('~/shared/kvs') @@ -209,6 +210,7 @@ describe('RegisterConsentModel', () => { // check new getters expect(RegisterConsentModel.subscriber).toEqual(modelConfig.subscriber) expect(RegisterConsentModel.thirdpartyRequests).toEqual(modelConfig.thirdpartyRequests) + expect(RegisterConsentModel.mojaloopRequests).toEqual(modelConfig.mojaloopRequests) // check is fsm correctly constructed expect(typeof RegisterConsentModel.fsm.init).toEqual('function') @@ -243,6 +245,23 @@ describe('RegisterConsentModel', () => { expect(typeof create).toEqual('function') }) + describe('notificationChannel', () => { + it('should generate proper channel name', () => { + const id = '123' + expect(RegisterConsentModel.notificationChannel( + RegisterConsentPhase.waitOnParticipantResponseFromALS, + id)).toEqual('RegisterConsent_waitOnParticipantResponseFromALS_123') + }) + + it('input validation', () => { + expect( + () => RegisterConsentModel.notificationChannel( + RegisterConsentPhase.waitOnParticipantResponseFromALS, + null as unknown as string + ) + ).toThrow() + }) + }) describe('verifyConsent', () => { const registerConsentData: RegisterConsentData = { @@ -326,6 +345,11 @@ describe('RegisterConsentModel', () => { const model = await create(registerConsentData, modelConfig) await model.fsm.registerAuthoritativeSourceWithALS() + // check for errors + await model.checkModelDataForErrorInformation() + // check that the fsm was able to transition properly + expect(model.data.currentState).toEqual('errored') + // check it sends an error back to DFSP expect(model.thirdpartyRequests.putConsentsError).toBeCalledWith( "b51ec534-ee48-4575-b6a9-ead2955b8069", @@ -367,5 +391,102 @@ describe('RegisterConsentModel', () => { 'dfspA' ) }) + + it('sendConsentCallbackToDFSP() should transition registeredAsAuthoritativeSource to errored state when unsuccessful', async () => { + const error = new Error('the-exception') + mocked(modelConfig.thirdpartyRequests.putConsents).mockImplementationOnce( + () => { + throw error + } + ) + + const model = await create(registerConsentData, modelConfig) + + try { + await model.fsm.sendConsentCallbackToDFSP() + shouldNotBeExecuted() + } catch (error) { + expect(error.message).toEqual('the-exception') + } + + // check we send an error callback to DFSP + expect(model.thirdpartyRequests.putConsentsError).toBeCalledWith( + 'b51ec534-ee48-4575-b6a9-ead2955b8069', + { + errorInformation: { + errorCode: '7200', + errorDescription: 'Generic Thirdparty account linking error' + } + }, + 'dfspA' + ) + }) + }) + + // run this test last since it can interfere with other tests because of the + // timed pubsub publishing + describe('run workflow', () => { + const registerConsentData: RegisterConsentData = { + currentState: 'registeredAsAuthoritativeSource', + participantDFSPId: 'dfspA', + consentsPostRequestAUTH + } + + it('start', async () => { + const model = await create(registerConsentData, modelConfig) + const waitOnParticipantResponseFromALSChannel = RegisterConsentModel.notificationChannel( + RegisterConsentPhase.waitOnParticipantResponseFromALS, + registerConsentData.consentsPostRequestAUTH.consentId + ) + + setImmediate(() => { + publisher.publish( + waitOnParticipantResponseFromALSChannel, + participantsTypeIDPutResponse as unknown as Message + ) + }) + + await model.run() + + // check that the fsm was able complete the workflow + expect(model.data.currentState).toEqual('callbackSent') + + mocked(modelConfig.logger.info).mockReset() + + }) + + it('errored', async () => { + const model = await create({ ...registerConsentData, currentState: 'error' }, modelConfig) + + const result = await model.run() + + expect(mocked(modelConfig.logger.info)).toBeCalledWith('State machine in errored state') + + expect(result).toBeUndefined() + }) + + it('exceptions', async () => { + const error = { message: 'error from requests.putConsentRequests', consentReqState: 'broken' } + mocked(axios.post).mockImplementationOnce( + () => { + throw error + } + ) + const model = await create(registerConsentData, modelConfig) + + expect(async () => await model.run()).rejects.toEqual(error) + }) + + it('exceptions - Error', async () => { + const error = new Error('the-exception') + mocked(axios.post).mockImplementationOnce( + () => { + throw error + } + ) + const model = await create({ ...registerConsentData, currentState: 'start' }, modelConfig) + + expect(model.run()).rejects.toEqual(error) + }) }) }) From cc01462b86fc859b16adf5503b94e69d9a2f11ad Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Sat, 10 Jul 2021 14:19:02 -0500 Subject: [PATCH 10/19] chore: import tests --- .circleci/config.yml | 2 +- src/model/registerConsent.model.ts | 10 +-- test/unit/shared/api-error.test.ts | 74 ++++++++++++++++++++ test/unit/shared/http-response-error.test.ts | 49 +++++++++++++ 4 files changed, 129 insertions(+), 6 deletions(-) create mode 100644 test/unit/shared/api-error.test.ts create mode 100644 test/unit/shared/http-response-error.test.ts diff --git a/.circleci/config.yml b/.circleci/config.yml index 3e011f61..d451a5bb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -207,7 +207,7 @@ jobs: name: Execute integration tests command: | [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - npm -s run test:integration + npm run test:integration environment: NODE_ENV: "integration" - run: diff --git a/src/model/registerConsent.model.ts b/src/model/registerConsent.model.ts index 48588361..9d23e462 100644 --- a/src/model/registerConsent.model.ts +++ b/src/model/registerConsent.model.ts @@ -173,9 +173,9 @@ export class RegisterConsentModel const payload: fspiopAPI.Schemas.ParticipantsTypeIDSubIDPostRequest = { fspId: this.config.authServiceParticipantFSPId } - //console.log({ alsParticipantURI, payload, axiosConfig }) - //this.logger.push({ alsParticipantURI, payload, axiosConfig }) - // .log('Testing') + console.log({ alsParticipantURI, payload, axiosConfig }) + this.logger.push({ alsParticipantURI, payload, axiosConfig }) + .log('Testing') const res = await axios.post(alsParticipantURI, payload, axiosConfig) this.logger.push({ res, channel }) .log('POST /participants/{Type}/{ID} call sent to ALS, listening on response') @@ -211,6 +211,7 @@ export class RegisterConsentModel }) .wait(this.config.requestProcessingTimeoutSeconds * 1000) } catch (error) { + this.logger.push({ error }).error('consentVerified -> registeredAsAuthoritativeSource') // we send back an account linking error despite the actual error const mojaloopError = reformatError( Errors.MojaloopApiErrorCodes.TP_ACCOUNT_LINKING_ERROR, @@ -251,8 +252,7 @@ export class RegisterConsentModel participantDFSPId ) } catch (error) { - this.logger.push({ error }).error('start -> requestIsValid') - + this.logger.push({ error }).error('registeredAsAuthoritativeSource -> callbackSent') let mojaloopError // if error is planned and is a MojaloopApiErrorCode we send back that code if ((error as Errors.MojaloopApiErrorCode).code) { diff --git a/test/unit/shared/api-error.test.ts b/test/unit/shared/api-error.test.ts new file mode 100644 index 00000000..9fd5dcc5 --- /dev/null +++ b/test/unit/shared/api-error.test.ts @@ -0,0 +1,74 @@ +import { HTTPResponseError, ResponseErrorData } from '~/shared/http-response-error' +import { Errors, Logger as SDKLogger } from '@mojaloop/sdk-standard-components' +import { reformatError } from '~/shared/api-error' + +import mockLogger from '../mockLogger' + +describe('api-error', () => { + describe('reformatError', () => { + let logger: SDKLogger.Logger + + beforeEach(() => { + logger = mockLogger() + }) + + it('should reformat any Error', () => { + const err = new Error('any-error') + const result = reformatError(err, logger) + expect(result).toEqual({ + errorInformation: { + errorCode: '2001', + errorDescription: 'Internal server error' + } + }) + }) + + it('should reformat HTTPResponseError', () => { + const err = new HTTPResponseError( + { msg: 'some-message', res: { data: { statusCode: 7200 } } } + ) + const result = reformatError(err, logger) + expect(result).toEqual({ + errorInformation: { + errorCode: '7200', + errorDescription: 'Generic Thirdparty account linking error' + } + }) + }) + + it('should report json parsing errors', () => { + const err = new HTTPResponseError( + { msg: 'some-message', res: { body: '[' } } + ) + const result = reformatError(err, logger) + expect(result).toEqual({ + errorInformation: { + errorCode: '2001', + errorDescription: 'Internal server error' + } + }) + expect(logger.push).toHaveBeenCalledWith({ exception: expect.anything() }) + }) + + it('should preserve existing MojaloopApiErrorCode', () => { + const err = Errors.MojaloopApiErrorCodes.TP_FSP_OTP_VALIDATION_ERROR + const result = reformatError(err, logger) + expect(result).toEqual({ + errorInformation: { + errorCode: '7206', + errorDescription: 'FSP failed to validate OTP' + } + }) + }) + + it('should handle non-existing MojaloopApiErrorCode', () => { + const result = reformatError({ code: '-1', message: 'zzz' }, logger) + expect(result).toEqual({ + errorInformation: { + errorCode: '2001', + errorDescription: 'Internal server error' + } + }) + }) + }) +}) diff --git a/test/unit/shared/http-response-error.test.ts b/test/unit/shared/http-response-error.test.ts new file mode 100644 index 00000000..a9620130 --- /dev/null +++ b/test/unit/shared/http-response-error.test.ts @@ -0,0 +1,49 @@ +/***** + License + -------------- + Copyright © 2020 Mojaloop Foundation + The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 (the "License") + and you may not use these files except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed + on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and limitations under the License. + Contributors + -------------- + This is the official list of the Mojaloop project contributors for this file. + Names of the original copyright holders (individuals or organizations) + should be listed with a '*' in the first column. People who have + contributed from an organization can be listed under the organization + that actually holds the copyright for their contributions (see the + Gates Foundation organization for an example). Those individuals should have + their names indented and be marked with a '-'. Email address can be added + optionally within square brackets . + * Gates Foundation + - Name Surname + + - Paweł Marzec + -------------- + ******/ +import { ResponseErrorData, HTTPResponseError } from '~/shared/http-response-error' +import inspect from '~/shared/inspect' +describe('HTTPResponseError', () => { + const data: ResponseErrorData = { + msg: 'the-message' + } + + it('should be properly created', () => { + const err = new HTTPResponseError(data) + expect(err).toBeDefined() + expect(err.getData()).toEqual(data) + }) + + it('should properly implement toString', () => { + const err = new HTTPResponseError(data) + expect(err.toString()).toEqual(inspect(data)) + }) + + it('should properly implement toJSON', () => { + const err = new HTTPResponseError(data) + expect(err.toJSON()).toEqual(JSON.stringify(data)) + }) +}) From 88893e178ce5b2cc133e0593d2539760827f4dbd Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Sat, 10 Jul 2021 15:09:09 -0500 Subject: [PATCH 11/19] chore: tests failing for unknown reason on ci --- .circleci/config.yml | 2 +- src/model/registerConsent.model.ts | 20 ++++++------------- test/unit/model/registerConsent.model.test.ts | 2 +- test/unit/server/handlers/consents.test.ts | 1 - 4 files changed, 8 insertions(+), 17 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d451a5bb..3e011f61 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -207,7 +207,7 @@ jobs: name: Execute integration tests command: | [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - npm run test:integration + npm -s run test:integration environment: NODE_ENV: "integration" - run: diff --git a/src/model/registerConsent.model.ts b/src/model/registerConsent.model.ts index 9d23e462..31eee18a 100644 --- a/src/model/registerConsent.model.ts +++ b/src/model/registerConsent.model.ts @@ -173,9 +173,7 @@ export class RegisterConsentModel const payload: fspiopAPI.Schemas.ParticipantsTypeIDSubIDPostRequest = { fspId: this.config.authServiceParticipantFSPId } - console.log({ alsParticipantURI, payload, axiosConfig }) - this.logger.push({ alsParticipantURI, payload, axiosConfig }) - .log('Testing') + const res = await axios.post(alsParticipantURI, payload, axiosConfig) this.logger.push({ res, channel }) .log('POST /participants/{Type}/{ID} call sent to ALS, listening on response') @@ -253,17 +251,11 @@ export class RegisterConsentModel ) } catch (error) { this.logger.push({ error }).error('registeredAsAuthoritativeSource -> callbackSent') - let mojaloopError - // if error is planned and is a MojaloopApiErrorCode we send back that code - if ((error as Errors.MojaloopApiErrorCode).code) { - mojaloopError = reformatError(error, this.logger) - } else { - // if error is not planned send back a generalized error - mojaloopError = reformatError( - Errors.MojaloopApiErrorCodes.TP_ACCOUNT_LINKING_ERROR, - this.logger - ) - } + // we send back an account linking error despite the actual error + const mojaloopError = reformatError( + Errors.MojaloopApiErrorCodes.TP_ACCOUNT_LINKING_ERROR, + this.logger + ) await this.thirdpartyRequests.putConsentsError( consentsPostRequestAUTH.consentId, diff --git a/test/unit/model/registerConsent.model.test.ts b/test/unit/model/registerConsent.model.test.ts index a7ac7163..82385eda 100644 --- a/test/unit/model/registerConsent.model.test.ts +++ b/test/unit/model/registerConsent.model.test.ts @@ -466,7 +466,7 @@ describe('RegisterConsentModel', () => { }) it('exceptions', async () => { - const error = { message: 'error from requests.putConsentRequests', consentReqState: 'broken' } + const error = { message: 'error from axios.post', consentReqState: 'broken' } mocked(axios.post).mockImplementationOnce( () => { throw error diff --git a/test/unit/server/handlers/consents.test.ts b/test/unit/server/handlers/consents.test.ts index 2dfefe22..fbe9d254 100644 --- a/test/unit/server/handlers/consents.test.ts +++ b/test/unit/server/handlers/consents.test.ts @@ -130,7 +130,6 @@ const consentsPostRequestAUTH = { } } - describe('server/handlers/consents', (): void => { it('Should return 202 success code', async (): Promise => { const request = consentsPostRequestAUTH From 4921803ba2f57b111a970f3d48f88c8519d89edc Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Sat, 10 Jul 2021 15:41:32 -0500 Subject: [PATCH 12/19] chore: improve coverage --- test/unit/model/registerConsent.model.test.ts | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/test/unit/model/registerConsent.model.test.ts b/test/unit/model/registerConsent.model.test.ts index 82385eda..a0833846 100644 --- a/test/unit/model/registerConsent.model.test.ts +++ b/test/unit/model/registerConsent.model.test.ts @@ -263,6 +263,26 @@ describe('RegisterConsentModel', () => { }) }) + describe('checkModelDataForErrorInformation', () => { + it('should transistion fsm to errored state if errorInformation is truthy', async () => { + const registerConsentData: RegisterConsentData = { + currentState: 'start', + participantDFSPId: 'dfspA', + consentsPostRequestAUTH, + errorInformation: { + errorCode: '3000', + errorDescription: 'Generic error' + } + } + const model = await create(registerConsentData, modelConfig) + await model.checkModelDataForErrorInformation() + + // check that the fsm was able to transition properly + expect(model.data.currentState).toEqual('errored') + }) + }) + + describe('verifyConsent', () => { const registerConsentData: RegisterConsentData = { currentState: 'start', @@ -278,7 +298,6 @@ describe('RegisterConsentModel', () => { it('registerAuthoritativeSourceWithALS() should transition start to consentVerified state when successful', async () => { const model = await create(registerConsentData, modelConfig) await model.fsm.verifyConsent() - // check that the fsm was able to transition properly expect(model.data.currentState).toEqual('consentVerified') }) @@ -321,6 +340,7 @@ describe('RegisterConsentModel', () => { participantsTypeIDPutResponse as unknown as Message )) await model.fsm.registerAuthoritativeSourceWithALS() + await model.checkModelDataForErrorInformation() // check that the fsm was able to transition properly expect(model.data.currentState).toEqual('registeredAsAuthoritativeSource') @@ -447,12 +467,9 @@ describe('RegisterConsentModel', () => { }) await model.run() - // check that the fsm was able complete the workflow expect(model.data.currentState).toEqual('callbackSent') - mocked(modelConfig.logger.info).mockReset() - }) it('errored', async () => { From 157bb84ec59bf81aecd25493a8d8bcaf637ecc37 Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Sat, 10 Jul 2021 18:34:14 -0500 Subject: [PATCH 13/19] chore: more changes --- src/model/registerConsent.model.ts | 1 - test/unit/model/registerConsent.model.test.ts | 32 ++++++------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/src/model/registerConsent.model.ts b/src/model/registerConsent.model.ts index 31eee18a..6d3be50e 100644 --- a/src/model/registerConsent.model.ts +++ b/src/model/registerConsent.model.ts @@ -173,7 +173,6 @@ export class RegisterConsentModel const payload: fspiopAPI.Schemas.ParticipantsTypeIDSubIDPostRequest = { fspId: this.config.authServiceParticipantFSPId } - const res = await axios.post(alsParticipantURI, payload, axiosConfig) this.logger.push({ res, channel }) .log('POST /participants/{Type}/{ID} call sent to ALS, listening on response') diff --git a/test/unit/model/registerConsent.model.test.ts b/test/unit/model/registerConsent.model.test.ts index a0833846..9187bdee 100644 --- a/test/unit/model/registerConsent.model.test.ts +++ b/test/unit/model/registerConsent.model.test.ts @@ -152,6 +152,13 @@ const participantsTypeIDPutResponse: fspiopAPI.Schemas.ParticipantsTypeIDPutResp 'fspId': config.PARTICIPANT_ID } +const genericErrorResponse: fspiopAPI.Schemas.ErrorInformationObject = { + errorInformation: { + errorCode: '7200', + errorDescription: 'Generic Thirdparty account linking error' + } +} + describe('RegisterConsentModel', () => { const connectionConfig: RedisConnectionConfig = { port: 6789, @@ -264,7 +271,7 @@ describe('RegisterConsentModel', () => { }) describe('checkModelDataForErrorInformation', () => { - it('should transistion fsm to errored state if errorInformation is truthy', async () => { + it('should transition fsm to errored state if errorInformation is truthy', async () => { const registerConsentData: RegisterConsentData = { currentState: 'start', participantDFSPId: 'dfspA', @@ -317,13 +324,6 @@ describe('RegisterConsentModel', () => { } } - const genericErrorResponse: fspiopAPI.Schemas.ErrorInformationObject = { - errorInformation: { - errorCode: '7200', - errorDescription: 'Generic Thirdparty account linking error' - } - } - it('should be well constructed', async () => { const model = await create(registerConsentData, modelConfig) checkRegisterConsentModelLayout(model, registerConsentData) @@ -483,8 +483,8 @@ describe('RegisterConsentModel', () => { }) it('exceptions', async () => { - const error = { message: 'error from axios.post', consentReqState: 'broken' } - mocked(axios.post).mockImplementationOnce( + const error = { message: 'error from modelConfig.thirdpartyRequests.putConsents', consentReqState: 'broken' } + mocked(modelConfig.thirdpartyRequests.putConsents).mockImplementationOnce( () => { throw error } @@ -493,17 +493,5 @@ describe('RegisterConsentModel', () => { expect(async () => await model.run()).rejects.toEqual(error) }) - - it('exceptions - Error', async () => { - const error = new Error('the-exception') - mocked(axios.post).mockImplementationOnce( - () => { - throw error - } - ) - const model = await create({ ...registerConsentData, currentState: 'start' }, modelConfig) - - expect(model.run()).rejects.toEqual(error) - }) }) }) From 96f0618a2464e0ca50f309693b658f0739d192eb Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Sat, 10 Jul 2021 18:43:02 -0500 Subject: [PATCH 14/19] chore: improve coverage --- test/unit/model/registerConsent.model.test.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/unit/model/registerConsent.model.test.ts b/test/unit/model/registerConsent.model.test.ts index 9187bdee..30009e17 100644 --- a/test/unit/model/registerConsent.model.test.ts +++ b/test/unit/model/registerConsent.model.test.ts @@ -493,5 +493,16 @@ describe('RegisterConsentModel', () => { expect(async () => await model.run()).rejects.toEqual(error) }) + + it('exceptions - Error', async () => { + const error = new Error('the-exception') + mocked(modelConfig.thirdpartyRequests.putConsents).mockImplementationOnce( + () => { + throw error + } + ) + const model = await create({ ...registerConsentData, currentState: 'registeredAsAuthoritativeSource' }, modelConfig) + expect(model.run()).rejects.toEqual(error) + }) }) }) From 6871a2bb0d04c7354d6073fefde8cffae8e305b6 Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Sat, 10 Jul 2021 19:05:15 -0500 Subject: [PATCH 15/19] chore: more coverage --- test/unit/model/registerConsent.model.test.ts | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/test/unit/model/registerConsent.model.test.ts b/test/unit/model/registerConsent.model.test.ts index 30009e17..48d53300 100644 --- a/test/unit/model/registerConsent.model.test.ts +++ b/test/unit/model/registerConsent.model.test.ts @@ -447,7 +447,7 @@ describe('RegisterConsentModel', () => { // timed pubsub publishing describe('run workflow', () => { const registerConsentData: RegisterConsentData = { - currentState: 'registeredAsAuthoritativeSource', + currentState: 'start', participantDFSPId: 'dfspA', consentsPostRequestAUTH } @@ -473,7 +473,17 @@ describe('RegisterConsentModel', () => { }) it('errored', async () => { - const model = await create({ ...registerConsentData, currentState: 'error' }, modelConfig) + const model = await create({ ...registerConsentData, currentState: 'errored' }, modelConfig) + + const result = await model.run() + + expect(mocked(modelConfig.logger.info)).toBeCalledWith('State machine in errored state') + + expect(result).toBeUndefined() + }) + + it('wrong state', async () => { + const model = await create({ ...registerConsentData, currentState: 'adga' }, modelConfig) const result = await model.run() @@ -482,19 +492,19 @@ describe('RegisterConsentModel', () => { expect(result).toBeUndefined() }) - it('exceptions', async () => { + it('exceptions - sendConsentCallbackToDFSP stage', async () => { const error = { message: 'error from modelConfig.thirdpartyRequests.putConsents', consentReqState: 'broken' } mocked(modelConfig.thirdpartyRequests.putConsents).mockImplementationOnce( () => { throw error } ) - const model = await create(registerConsentData, modelConfig) + const model = await create({ ...registerConsentData, currentState: 'registeredAsAuthoritativeSource' }, modelConfig) expect(async () => await model.run()).rejects.toEqual(error) }) - it('exceptions - Error', async () => { + it('exceptions - Error - sendConsentCallbackToDFSP stage', async () => { const error = new Error('the-exception') mocked(modelConfig.thirdpartyRequests.putConsents).mockImplementationOnce( () => { @@ -504,5 +514,28 @@ describe('RegisterConsentModel', () => { const model = await create({ ...registerConsentData, currentState: 'registeredAsAuthoritativeSource' }, modelConfig) expect(model.run()).rejects.toEqual(error) }) + + it('exceptions - registerAuthoritativeSourceWithALS stage', async () => { + const error = { message: 'error from axios.post', consentReqState: 'broken' } + mocked(axios.post).mockImplementationOnce( + () => { + throw error + } + ) + const model = await create({ ...registerConsentData, currentState: 'consentVerified' }, modelConfig) + + expect(async () => await model.run()).rejects.toEqual(error) + }) + + it('exceptions - Error - registerAuthoritativeSourceWithALS stage', async () => { + const error = new Error('the-exception') + mocked(axios.post).mockImplementationOnce( + () => { + throw error + } + ) + const model = await create({ ...registerConsentData, currentState: 'consentVerified' }, modelConfig) + expect(model.run()).rejects.toEqual(error) + }) }) }) From b8dd2c190ddbff10de2f89f57f80ea66a014c3ca Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Sat, 10 Jul 2021 19:45:19 -0500 Subject: [PATCH 16/19] chore: more changes --- package-lock.json | 1890 +++++++++++--------- package.json | 2 +- test/unit/server/handlers/consents.test.ts | 10 + 3 files changed, 1030 insertions(+), 872 deletions(-) diff --git a/package-lock.json b/package-lock.json index eafe1c8a..35607858 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1248,48 +1248,77 @@ "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==" }, "@jest/console": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.3.0.tgz", - "integrity": "sha512-/5Pn6sJev0nPUcAdpJHMVIsA8sKizL2ZkcKPE5+dJrCccks7tcM7c9wbgHudBJbxXLoTbqsHkG1Dofoem4F09w==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", + "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.6.2", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^26.3.0", - "jest-util": "^26.3.0", + "jest-message-util": "^26.6.2", + "jest-util": "^26.6.2", "slash": "^3.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + } } }, "@jest/core": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.4.0.tgz", - "integrity": "sha512-mpXm4OjWQbz7qbzGIiSqvfNZ1FxX6ywWgLtdSD2luPORt5zKPtqcdDnX7L8RdfMaj1znDBgN2+gB094ZIr7vnA==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", "dev": true, "requires": { - "@jest/console": "^26.3.0", - "@jest/reporters": "^26.4.0", - "@jest/test-result": "^26.3.0", - "@jest/transform": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/console": "^26.6.2", + "@jest/reporters": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.4", - "jest-changed-files": "^26.3.0", - "jest-config": "^26.4.0", - "jest-haste-map": "^26.3.0", - "jest-message-util": "^26.3.0", + "jest-changed-files": "^26.6.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.4.0", - "jest-resolve-dependencies": "^26.4.0", - "jest-runner": "^26.4.0", - "jest-runtime": "^26.4.0", - "jest-snapshot": "^26.4.0", - "jest-util": "^26.3.0", - "jest-validate": "^26.4.0", - "jest-watcher": "^26.3.0", + "jest-resolve": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "jest-watcher": "^26.6.2", "micromatch": "^4.0.2", "p-each-series": "^2.1.0", "rimraf": "^3.0.0", @@ -1297,12 +1326,39 @@ "strip-ansi": "^6.0.0" }, "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -1324,53 +1380,112 @@ } }, "@jest/environment": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.3.0.tgz", - "integrity": "sha512-EW+MFEo0DGHahf83RAaiqQx688qpXgl99wdb8Fy67ybyzHwR1a58LHcO376xQJHfmoXTu89M09dH3J509cx2AA==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", + "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", "dev": true, "requires": { - "@jest/fake-timers": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", "@types/node": "*", - "jest-mock": "^26.3.0" + "jest-mock": "^26.6.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + } } }, "@jest/fake-timers": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.3.0.tgz", - "integrity": "sha512-ZL9ytUiRwVP8ujfRepffokBvD2KbxbqMhrXSBhSdAhISCw3gOkuntisiSFv+A6HN0n0fF4cxzICEKZENLmW+1A==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", + "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.6.2", "@sinonjs/fake-timers": "^6.0.1", "@types/node": "*", - "jest-message-util": "^26.3.0", - "jest-mock": "^26.3.0", - "jest-util": "^26.3.0" + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + } } }, "@jest/globals": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.4.0.tgz", - "integrity": "sha512-QKwoVAeL9d0xaEM9ebPvfc+bolN04F+o3zM2jswGDBiiNjCogZ3LvOaqumRdDyz6kLmbx+UhgMBAVuLunbXZ2A==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", + "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", "dev": true, "requires": { - "@jest/environment": "^26.3.0", - "@jest/types": "^26.3.0", - "expect": "^26.4.0" + "@jest/environment": "^26.6.2", + "@jest/types": "^26.6.2", + "expect": "^26.6.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + } } }, "@jest/reporters": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.4.0.tgz", - "integrity": "sha512-14OPAAuYhgRBSNxAocVluX6ksdMdK/EuP9NmtBXU9g1uKaVBrPnohn/CVm6iMot1a9iU8BCxa5715YRf8FEg/A==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^26.3.0", - "@jest/test-result": "^26.3.0", - "@jest/transform": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", @@ -1381,22 +1496,51 @@ "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.0.2", - "jest-haste-map": "^26.3.0", - "jest-resolve": "^26.4.0", - "jest-util": "^26.3.0", - "jest-worker": "^26.3.0", - "node-notifier": "^7.0.0", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "node-notifier": "^8.0.0", "slash": "^3.0.0", "source-map": "^0.6.0", "string-length": "^4.0.1", "terminal-link": "^2.0.0", - "v8-to-istanbul": "^5.0.1" + "v8-to-istanbul": "^7.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + } } }, "@jest/source-map": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.3.0.tgz", - "integrity": "sha512-hWX5IHmMDWe1kyrKl7IhFwqOuAreIwHhbe44+XH2ZRHjrKIh0LO5eLQ/vxHFeAfRwJapmxuqlGAEYLadDq6ZGQ==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", "dev": true, "requires": { "callsites": "^3.0.0", @@ -1405,51 +1549,95 @@ } }, "@jest/test-result": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.3.0.tgz", - "integrity": "sha512-a8rbLqzW/q7HWheFVMtghXV79Xk+GWwOK1FrtimpI5n1la2SY0qHri3/b0/1F0Ve0/yJmV8pEhxDfVwiUBGtgg==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", "dev": true, "requires": { - "@jest/console": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + } } }, "@jest/test-sequencer": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.4.0.tgz", - "integrity": "sha512-9Z7lCShS7vERp+DRwIVNH/6sHMWwJK1DPnGCpGeVLGJJWJ4Y08sQI3vIKdmKHu2KmwlUBpRM+BFf7NlVUkl5XA==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", "dev": true, "requires": { - "@jest/test-result": "^26.3.0", + "@jest/test-result": "^26.6.2", "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.3.0", - "jest-runner": "^26.4.0", - "jest-runtime": "^26.4.0" + "jest-haste-map": "^26.6.2", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" } }, "@jest/transform": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.3.0.tgz", - "integrity": "sha512-Isj6NB68QorGoFWvcOjlUhpkT56PqNIsXKR7XfvoDlCANn/IANlh8DrKAA2l2JKC3yWSMH5wS0GwuQM20w3b2A==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^26.3.0", + "@jest/types": "^26.6.2", "babel-plugin-istanbul": "^6.0.0", "chalk": "^4.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.3.0", + "jest-haste-map": "^26.6.2", "jest-regex-util": "^26.0.0", - "jest-util": "^26.3.0", + "jest-util": "^26.6.2", "micromatch": "^4.0.2", "pirates": "^4.0.1", "slash": "^3.0.0", "source-map": "^0.6.1", "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + } } }, "@jest/types": { @@ -2617,9 +2805,9 @@ "dev": true }, "@types/prettier": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.0.2.tgz", - "integrity": "sha512-IkVfat549ggtkZUthUzEX49562eGikhSYeVGX97SkMFn+sTZrgRewXjQ4tPKFPCykZHkX1Zfd9OoELGqKU2jJA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog==", "dev": true }, "@types/promise-timeout": { @@ -4444,6 +4632,12 @@ "integrity": "sha1-cDhrG0jidz0NYxZqVa/5TvRFahI=", "dev": true }, + "cjs-module-lexer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", + "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", + "dev": true + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -5391,9 +5585,9 @@ } }, "decimal.js": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", - "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==", + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", "dev": true }, "decode-uri-component": { @@ -5790,9 +5984,9 @@ "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==" }, "emittery": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.1.tgz", - "integrity": "sha512-d34LN4L6h18Bzz9xpoku2nPwKxCPlPMr3EEKTkoEBi+1/+b0lcRkRJ1UVyyZaKNeqGR3swcGl6s390DNO4YVgQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", + "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", "dev": true }, "emoji-regex": { @@ -6703,26 +6897,38 @@ } }, "expect": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.4.0.tgz", - "integrity": "sha512-dbYDJhFcqQsamlos6nEwAMe+ahdckJBk5fmw1DYGLQGabGSlUuT+Fm2jHYw5119zG3uIhP+lCQbjJhFEdZMJtg==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.6.2", "ansi-styles": "^4.0.0", "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.4.0", - "jest-message-util": "^26.3.0", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", "jest-regex-util": "^26.0.0" }, "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, @@ -6740,12 +6946,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true } } }, @@ -8397,12 +8597,6 @@ "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", "dev": true }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -8531,9 +8725,9 @@ } }, "is-docker": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", - "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, "optional": true }, @@ -8650,9 +8844,9 @@ } }, "is-potential-custom-element-name": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", - "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true }, "is-regex": { @@ -8810,12 +9004,12 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } } } @@ -8836,29 +9030,41 @@ "integrity": "sha512-BwhYxQ1OPenBPXC735RgfB+ZUG8H3kjsx8hrYTgWnoy6TPipEy4fiicyhT2lxRKAXq9pG7CfFT8a2HLr6Hmwxg==" }, "jest": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-26.0.1.tgz", - "integrity": "sha512-29Q54kn5Bm7ZGKIuH2JRmnKl85YRigp0o0asTc6Sb6l2ch1DCXIeZTLLFy9ultJvhkTqbswF5DEx4+RlkmCxWg==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", + "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", "dev": true, "requires": { - "@jest/core": "^26.0.1", + "@jest/core": "^26.6.3", "import-local": "^3.0.2", - "jest-cli": "^26.0.1" + "jest-cli": "^26.6.3" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, @@ -8895,30 +9101,44 @@ "dev": true }, "jest-cli": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.4.0.tgz", - "integrity": "sha512-kw2Pr3V2x9/WzSDGsbz/MJBNlCoPMxMudrIavft4bqRlv5tASjU51tyO+1Os1LdW2dAnLQZYsxFUZ8oWPyssGQ==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", "dev": true, "requires": { - "@jest/core": "^26.4.0", - "@jest/test-result": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.4", "import-local": "^3.0.2", "is-ci": "^2.0.0", - "jest-config": "^26.4.0", - "jest-util": "^26.3.0", - "jest-validate": "^26.4.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", "prompts": "^2.0.1", - "yargs": "^15.3.1" + "yargs": "^15.4.1" + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" } }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "requires": { "emoji-regex": "^8.0.0", @@ -8974,16 +9194,29 @@ } }, "jest-changed-files": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.3.0.tgz", - "integrity": "sha512-1C4R4nijgPltX6fugKxM4oQ18zimS7LqQ+zTTY8lMCMFPrxqBFb7KJH0Z2fRQJvw2Slbaipsqq7s1mgX5Iot+g==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.6.2", "execa": "^4.0.0", "throat": "^5.0.0" }, "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -8996,9 +9229,9 @@ } }, "execa": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", - "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, "requires": { "cross-spawn": "^7.0.0", @@ -9013,9 +9246,9 @@ } }, "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "requires": { "pump": "^3.0.0" @@ -9036,16 +9269,6 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -9073,97 +9296,42 @@ } }, "jest-config": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.4.0.tgz", - "integrity": "sha512-MxsvrBug8YY+C4QcUBtmgnHyFeW7w3Ouk/w9eplCDN8VJGVyBEZFe8Lxzfp2pSqh0Dqurqv8Oik2YkbekGUlxg==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.4.0", - "@jest/types": "^26.3.0", - "babel-jest": "^26.3.0", + "@jest/test-sequencer": "^26.6.3", + "@jest/types": "^26.6.2", + "babel-jest": "^26.6.3", "chalk": "^4.0.0", "deepmerge": "^4.2.2", "glob": "^7.1.1", "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^26.3.0", - "jest-environment-node": "^26.3.0", + "jest-environment-jsdom": "^26.6.2", + "jest-environment-node": "^26.6.2", "jest-get-type": "^26.3.0", - "jest-jasmine2": "^26.4.0", + "jest-jasmine2": "^26.6.3", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.4.0", - "jest-util": "^26.3.0", - "jest-validate": "^26.4.0", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", "micromatch": "^4.0.2", - "pretty-format": "^26.4.0" + "pretty-format": "^26.6.2" }, "dependencies": { - "@jest/transform": { + "@jest/types": { "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", - "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^26.6.2", - "babel-plugin-istanbul": "^6.0.0", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-util": "^26.6.2", - "micromatch": "^4.0.2", - "pirates": "^4.0.1", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - } - } - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" } }, "babel-jest": { @@ -9180,21 +9348,6 @@ "chalk": "^4.0.0", "graceful-fs": "^4.2.4", "slash": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - } } }, "babel-plugin-jest-hoist": { @@ -9219,109 +9372,18 @@ "babel-preset-current-node-syntax": "^1.0.0" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "jest-haste-map": { + "jest-util": { "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", - "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", "dev": true, "requires": { "@jest/types": "^26.6.2", - "@types/graceful-fs": "^4.1.2", "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", + "chalk": "^4.0.0", "graceful-fs": "^4.2.4", - "jest-regex-util": "^26.0.0", - "jest-serializer": "^26.6.2", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "micromatch": "^4.0.2", - "sane": "^4.0.3", - "walker": "^1.0.7" - }, - "dependencies": { - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - } - } - }, - "jest-serializer": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", - "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", - "dev": true, - "requires": { - "@types/node": "*", - "graceful-fs": "^4.2.4" - } - }, - "jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - } - }, - "pretty-format": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.0.tgz", - "integrity": "sha512-mEEwwpCseqrUtuMbrJG4b824877pM5xald3AkilJ47Po2YLr97/siejYQHqj2oDQBeJNbu+Q0qUuekJ8F0NAPg==", - "dev": true, - "requires": { - "@jest/types": "^26.3.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" } } } @@ -10782,96 +10844,132 @@ } }, "jest-each": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.4.0.tgz", - "integrity": "sha512-+cyBh1ehs6thVT/bsZVG+WwmRn2ix4Q4noS9yLZgM10yGWPW12/TDvwuOV2VZXn1gi09/ZwJKJWql6YW1C9zNw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", + "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.6.2", "chalk": "^4.0.0", "jest-get-type": "^26.3.0", - "jest-util": "^26.3.0", - "pretty-format": "^26.4.0" + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "pretty-format": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.0.tgz", - "integrity": "sha512-mEEwwpCseqrUtuMbrJG4b824877pM5xald3AkilJ47Po2YLr97/siejYQHqj2oDQBeJNbu+Q0qUuekJ8F0NAPg==", + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", "dev": true, "requires": { - "@jest/types": "^26.3.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" } } } }, "jest-environment-jsdom": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.3.0.tgz", - "integrity": "sha512-zra8He2btIMJkAzvLaiZ9QwEPGEetbxqmjEBQwhH3CA+Hhhu0jSiEJxnJMbX28TGUvPLxBt/zyaTLrOPF4yMJA==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", + "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", "dev": true, "requires": { - "@jest/environment": "^26.3.0", - "@jest/fake-timers": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", "@types/node": "*", - "jest-mock": "^26.3.0", - "jest-util": "^26.3.0", - "jsdom": "^16.2.2" - } - }, + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2", + "jsdom": "^16.4.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + } + } + }, "jest-environment-node": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.3.0.tgz", - "integrity": "sha512-c9BvYoo+FGcMj5FunbBgtBnbR5qk3uky8PKyRVpSfe2/8+LrNQMiXX53z6q2kY+j15SkjQCOSL/6LHnCPLVHNw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", + "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", "dev": true, "requires": { - "@jest/environment": "^26.3.0", - "@jest/fake-timers": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", "@types/node": "*", - "jest-mock": "^26.3.0", - "jest-util": "^26.3.0" + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + } } }, "jest-get-type": { @@ -10880,12 +10978,12 @@ "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==" }, "jest-haste-map": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.3.0.tgz", - "integrity": "sha512-DHWBpTJgJhLLGwE5Z1ZaqLTYqeODQIZpby0zMBsCU9iRFHYyhklYqP4EiG73j5dkbaAdSZhgB938mL51Q5LeZA==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", + "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.6.2", "@types/graceful-fs": "^4.1.2", "@types/node": "*", "anymatch": "^3.0.3", @@ -10893,81 +10991,94 @@ "fsevents": "^2.1.2", "graceful-fs": "^4.2.4", "jest-regex-util": "^26.0.0", - "jest-serializer": "^26.3.0", - "jest-util": "^26.3.0", - "jest-worker": "^26.3.0", + "jest-serializer": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", "micromatch": "^4.0.2", "sane": "^4.0.3", "walker": "^1.0.7" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + } } }, "jest-jasmine2": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.4.0.tgz", - "integrity": "sha512-cGBxwzDDKB09EPJ4pE69BMDv+2lO442IB1xQd+vL3cua2OKdeXQK6iDlQKoRX/iP0RgU5T8sn9yahLcx/+ox8Q==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", + "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", "dev": true, "requires": { "@babel/traverse": "^7.1.0", - "@jest/environment": "^26.3.0", - "@jest/source-map": "^26.3.0", - "@jest/test-result": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/environment": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", - "expect": "^26.4.0", + "expect": "^26.6.2", "is-generator-fn": "^2.0.0", - "jest-each": "^26.4.0", - "jest-matcher-utils": "^26.4.0", - "jest-message-util": "^26.3.0", - "jest-runtime": "^26.4.0", - "jest-snapshot": "^26.4.0", - "jest-util": "^26.3.0", - "pretty-format": "^26.4.0", + "jest-each": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2", "throat": "^5.0.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", "dev": true, "requires": { - "color-name": "~1.1.4" + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "pretty-format": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.0.tgz", - "integrity": "sha512-mEEwwpCseqrUtuMbrJG4b824877pM5xald3AkilJ47Po2YLr97/siejYQHqj2oDQBeJNbu+Q0qUuekJ8F0NAPg==", + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", "dev": true, "requires": { - "@jest/types": "^26.3.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" } } } @@ -11103,171 +11214,88 @@ } }, "jest-leak-detector": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.4.0.tgz", - "integrity": "sha512-7EXKKEKnAWUPyiVtGZzJflbPOtYUdlNoevNVOkAcPpdR8xWiYKPGNGA6sz25S+8YhZq3rmkQJYAh3/P0VnoRwA==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", + "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", "dev": true, "requires": { "jest-get-type": "^26.3.0", - "pretty-format": "^26.4.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "pretty-format": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.0.tgz", - "integrity": "sha512-mEEwwpCseqrUtuMbrJG4b824877pM5xald3AkilJ47Po2YLr97/siejYQHqj2oDQBeJNbu+Q0qUuekJ8F0NAPg==", - "dev": true, - "requires": { - "@jest/types": "^26.3.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - } + "pretty-format": "^26.6.2" } }, "jest-matcher-utils": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.4.0.tgz", - "integrity": "sha512-u+xdCdq+F262DH+PutJKXLGr2H5P3DImdJCir51PGSfi3TtbLQ5tbzKaN8BkXbiTIU6ayuAYBWTlU1nyckVdzA==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", "dev": true, "requires": { "chalk": "^4.0.0", - "jest-diff": "^26.4.0", + "jest-diff": "^26.6.2", "jest-get-type": "^26.3.0", - "pretty-format": "^26.4.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "diff-sequences": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.3.0.tgz", - "integrity": "sha512-5j5vdRcw3CNctePNYN0Wy2e/JbWT6cAYnXv5OuqPhDpyCGc0uLu2TK0zOCJWNB9kOIfYMSpIulRaDgIi4HJ6Ig==", - "dev": true - }, - "jest-diff": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.4.0.tgz", - "integrity": "sha512-wwC38HlOW+iTq6j5tkj/ZamHn6/nrdcEOc/fKaVILNtN2NLWGdkfRaHWwfNYr5ehaLvuoG2LfCZIcWByVj0gjg==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.3.0", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.4.0" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "pretty-format": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.0.tgz", - "integrity": "sha512-mEEwwpCseqrUtuMbrJG4b824877pM5xald3AkilJ47Po2YLr97/siejYQHqj2oDQBeJNbu+Q0qUuekJ8F0NAPg==", - "dev": true, - "requires": { - "@jest/types": "^26.3.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - } + "pretty-format": "^26.6.2" } }, "jest-message-util": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.3.0.tgz", - "integrity": "sha512-xIavRYqr4/otGOiLxLZGj3ieMmjcNE73Ui+LdSW/Y790j5acqCsAdDiLIbzHCZMpN07JOENRWX5DcU+OQ+TjTA==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.3.0", - "@types/stack-utils": "^1.0.1", + "@jest/types": "^26.6.2", + "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", "micromatch": "^4.0.2", + "pretty-format": "^26.6.2", "slash": "^3.0.0", "stack-utils": "^2.0.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + } } }, "jest-mock": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.3.0.tgz", - "integrity": "sha512-PeaRrg8Dc6mnS35gOo/CbZovoDPKAeB1FICZiuagAgGvbWdNNyjQjkOaGUa/3N3JtpQ/Mh9P4A2D4Fv51NnP8Q==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", + "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.6.2", "@types/node": "*" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + } } }, "jest-mock-process": { @@ -11289,94 +11317,191 @@ "dev": true }, "jest-resolve": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.4.0.tgz", - "integrity": "sha512-bn/JoZTEXRSlEx3+SfgZcJAVuTMOksYq9xe9O6s4Ekg84aKBObEaVXKOEilULRqviSLAYJldnoWV9c07kwtiCg==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.6.2", "chalk": "^4.0.0", "graceful-fs": "^4.2.4", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.3.0", + "jest-util": "^26.6.2", "read-pkg-up": "^7.0.1", - "resolve": "^1.17.0", + "resolve": "^1.18.1", "slash": "^3.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + } } }, "jest-resolve-dependencies": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.4.0.tgz", - "integrity": "sha512-hznK/hlrlhu8hwdbieRdHFKmcV83GW8t30libt/v6j1L3IEzb8iN21SaWzV8KRAAK4ijiU0kuge0wnHn+0rytQ==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", + "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.6.2", "jest-regex-util": "^26.0.0", - "jest-snapshot": "^26.4.0" + "jest-snapshot": "^26.6.2" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + } } }, "jest-runner": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.4.0.tgz", - "integrity": "sha512-XF+tnUGolnPriu6Gg+HHWftspMjD5NkTV2mQppQnpZe39GcUangJ0al7aBGtA3GbVAcRd048DQiJPmsQRdugjw==", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", + "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", "dev": true, "requires": { - "@jest/console": "^26.3.0", - "@jest/environment": "^26.3.0", - "@jest/test-result": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.7.1", "exit": "^0.1.2", "graceful-fs": "^4.2.4", - "jest-config": "^26.4.0", + "jest-config": "^26.6.3", "jest-docblock": "^26.0.0", - "jest-haste-map": "^26.3.0", - "jest-leak-detector": "^26.4.0", - "jest-message-util": "^26.3.0", - "jest-resolve": "^26.4.0", - "jest-runtime": "^26.4.0", - "jest-util": "^26.3.0", - "jest-worker": "^26.3.0", + "jest-haste-map": "^26.6.2", + "jest-leak-detector": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", "source-map-support": "^0.5.6", "throat": "^5.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + } } }, "jest-runtime": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.4.0.tgz", - "integrity": "sha512-1fjZgGpkyQBUTo59Vi19I4IcsBwzY6uwVFNjUmR06iIi3XRErkY28yimi4IUDRrofQErqcDEw2n3DF9WmQ6vEg==", - "dev": true, - "requires": { - "@jest/console": "^26.3.0", - "@jest/environment": "^26.3.0", - "@jest/fake-timers": "^26.3.0", - "@jest/globals": "^26.4.0", - "@jest/source-map": "^26.3.0", - "@jest/test-result": "^26.3.0", - "@jest/transform": "^26.3.0", - "@jest/types": "^26.3.0", + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", + "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/globals": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", "@types/yargs": "^15.0.0", "chalk": "^4.0.0", + "cjs-module-lexer": "^0.6.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.2.4", - "jest-config": "^26.4.0", - "jest-haste-map": "^26.3.0", - "jest-message-util": "^26.3.0", - "jest-mock": "^26.3.0", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.4.0", - "jest-snapshot": "^26.4.0", - "jest-util": "^26.3.0", - "jest-validate": "^26.4.0", + "jest-resolve": "^26.6.2", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", "slash": "^3.0.0", "strip-bom": "^4.0.0", - "yargs": "^15.3.1" + "yargs": "^15.4.1" }, "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", @@ -11384,12 +11509,11 @@ "dev": true }, "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, @@ -11425,10 +11549,24 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "requires": { "emoji-regex": "^8.0.0", @@ -11490,9 +11628,9 @@ } }, "jest-serializer": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.3.0.tgz", - "integrity": "sha512-IDRBQBLPlKa4flg77fqg0n/pH87tcRKwe8zxOVTWISxGpPHYkRZ1dXKyh04JOja7gppc60+soKVZ791mruVdow==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", + "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", "dev": true, "requires": { "@types/node": "*", @@ -11500,100 +11638,50 @@ } }, "jest-snapshot": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.4.0.tgz", - "integrity": "sha512-vFGmNGWHMBomrlOpheTMoqihymovuH3GqfmaEIWoPpsxUXyxT3IlbxI5I4m2vg0uv3HUJYg5JoGrkgMzVsAwCg==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", + "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", "dev": true, "requires": { "@babel/types": "^7.0.0", - "@jest/types": "^26.3.0", + "@jest/types": "^26.6.2", + "@types/babel__traverse": "^7.0.4", "@types/prettier": "^2.0.0", "chalk": "^4.0.0", - "expect": "^26.4.0", + "expect": "^26.6.2", "graceful-fs": "^4.2.4", - "jest-diff": "^26.4.0", + "jest-diff": "^26.6.2", "jest-get-type": "^26.3.0", - "jest-haste-map": "^26.3.0", - "jest-matcher-utils": "^26.4.0", - "jest-message-util": "^26.3.0", - "jest-resolve": "^26.4.0", + "jest-haste-map": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", "natural-compare": "^1.4.0", - "pretty-format": "^26.4.0", + "pretty-format": "^26.6.2", "semver": "^7.3.2" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "diff-sequences": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.3.0.tgz", - "integrity": "sha512-5j5vdRcw3CNctePNYN0Wy2e/JbWT6cAYnXv5OuqPhDpyCGc0uLu2TK0zOCJWNB9kOIfYMSpIulRaDgIi4HJ6Ig==", - "dev": true - }, - "jest-diff": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.4.0.tgz", - "integrity": "sha512-wwC38HlOW+iTq6j5tkj/ZamHn6/nrdcEOc/fKaVILNtN2NLWGdkfRaHWwfNYr5ehaLvuoG2LfCZIcWByVj0gjg==", + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", "dev": true, "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.3.0", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.4.0" + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" } }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "pretty-format": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.0.tgz", - "integrity": "sha512-mEEwwpCseqrUtuMbrJG4b824877pM5xald3AkilJ47Po2YLr97/siejYQHqj2oDQBeJNbu+Q0qUuekJ8F0NAPg==", + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { - "@jest/types": "^26.3.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" + "lru-cache": "^6.0.0" } - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true } } }, @@ -11612,95 +11700,88 @@ } }, "jest-validate": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.4.0.tgz", - "integrity": "sha512-t56Z/FRMrLP6mpmje7/YgHy0wOzcuc6i3LBXz6kjmsUWYN62OuMdC86Vg9/dX59SvyitSqqegOrx+h7BkNXeaQ==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", + "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", "dev": true, "requires": { - "@jest/types": "^26.3.0", + "@jest/types": "^26.6.2", "camelcase": "^6.0.0", "chalk": "^4.0.0", "jest-get-type": "^26.3.0", "leven": "^3.1.0", - "pretty-format": "^26.4.0" + "pretty-format": "^26.6.2" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" } }, "camelcase": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz", - "integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true - }, - "pretty-format": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.4.0.tgz", - "integrity": "sha512-mEEwwpCseqrUtuMbrJG4b824877pM5xald3AkilJ47Po2YLr97/siejYQHqj2oDQBeJNbu+Q0qUuekJ8F0NAPg==", - "dev": true, - "requires": { - "@jest/types": "^26.3.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } } } }, "jest-watcher": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.3.0.tgz", - "integrity": "sha512-XnLdKmyCGJ3VoF6G/p5ohbJ04q/vv5aH9ENI+i6BL0uu9WWB6Z7Z2lhQQk0d2AVZcRGp1yW+/TsoToMhBFPRdQ==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", + "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", "dev": true, "requires": { - "@jest/test-result": "^26.3.0", - "@jest/types": "^26.3.0", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "jest-util": "^26.3.0", + "jest-util": "^26.6.2", "string-length": "^4.0.1" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + } + } } }, "jest-worker": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.3.0.tgz", - "integrity": "sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", "dev": true, "requires": { "@types/node": "*", @@ -11757,44 +11838,107 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "jsdom": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", - "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.6.0.tgz", + "integrity": "sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg==", "dev": true, "requires": { - "abab": "^2.0.3", - "acorn": "^7.1.1", + "abab": "^2.0.5", + "acorn": "^8.2.4", "acorn-globals": "^6.0.0", "cssom": "^0.4.4", - "cssstyle": "^2.2.0", + "cssstyle": "^2.3.0", "data-urls": "^2.0.0", - "decimal.js": "^10.2.0", + "decimal.js": "^10.2.1", "domexception": "^2.0.1", - "escodegen": "^1.14.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", "html-encoding-sniffer": "^2.0.1", - "is-potential-custom-element-name": "^1.0.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", "nwsapi": "^2.2.0", - "parse5": "5.1.1", - "request": "^2.88.2", - "request-promise-native": "^1.0.8", - "saxes": "^5.0.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", "symbol-tree": "^3.2.4", - "tough-cookie": "^3.0.1", + "tough-cookie": "^4.0.0", "w3c-hr-time": "^1.0.2", "w3c-xmlserializer": "^2.0.0", "webidl-conversions": "^6.1.0", "whatwg-encoding": "^1.0.5", "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0", - "ws": "^7.2.3", + "whatwg-url": "^8.5.0", + "ws": "^7.4.5", "xml-name-validator": "^3.0.0" }, "dependencies": { + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "acorn": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", + "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==", + "dev": true + }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, "webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", "dev": true + }, + "whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } } } }, @@ -13502,9 +13646,9 @@ "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=" }, "node-notifier": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-7.0.2.tgz", - "integrity": "sha512-ux+n4hPVETuTL8+daJXTOC6uKLgMsl1RYfFv7DKRzyvzBapqco0rZZ9g72ZN8VS6V+gvNYHYa/ofcCY8fkJWsA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz", + "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==", "dev": true, "optional": true, "requires": { @@ -13512,23 +13656,19 @@ "is-wsl": "^2.2.0", "semver": "^7.3.2", "shellwords": "^0.1.1", - "uuid": "^8.2.0", + "uuid": "^8.3.0", "which": "^2.0.2" }, "dependencies": { "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true, - "optional": true - }, - "uuid": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz", - "integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, - "optional": true + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } }, "which": { "version": "2.0.2", @@ -14869,9 +15009,9 @@ "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" }, "p-each-series": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", - "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", "dev": true }, "p-finally": { @@ -15100,9 +15240,9 @@ "integrity": "sha512-hb50xDyEo8boMtyzB1IdVE4KcTNVbIirk/ZqC8na1irOf/70DyZS30y1FIIAUe9jyHJk9s2QoZ4aBNHR9NXHsg==" }, "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, "parse5-htmlparser2-tree-adapter": { @@ -17210,9 +17350,9 @@ "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" }, "stack-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.2.tgz", - "integrity": "sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", "dev": true, "requires": { "escape-string-regexp": "^2.0.0" @@ -17449,9 +17589,9 @@ "dev": true }, "string-length": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", - "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, "requires": { "char-regex": "^1.0.2", @@ -17599,9 +17739,9 @@ } }, "supports-hyperlinks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", - "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", "dev": true, "requires": { "has-flag": "^4.0.0", @@ -18148,14 +18288,22 @@ } }, "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", "dev": true, "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "dependencies": { + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + } } }, "tr46": { @@ -18660,9 +18808,9 @@ "dev": true }, "v8-to-istanbul": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-5.0.1.tgz", - "integrity": "sha512-mbDNjuDajqYe3TXFk5qxcQy8L1msXNE37WTlLoqqpBfRsimbNcrlhQlDPntmECEcUvdC+AQ8CyMMf6EUx1r74Q==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz", + "integrity": "sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.1", @@ -19178,9 +19326,9 @@ } }, "ws": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", - "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", "dev": true }, "xdg-basedir": { diff --git a/package.json b/package.json index 2846af2b..4ca9a1a8 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "eslint-plugin-promise": "5.1.0", "eslint-plugin-standard": "4.1.0", "husky": "^7.0.1", - "jest": "26.0.1", + "jest": "^26.6.3", "jest-cucumber": "^2.0.11", "jest-junit": "10.0.0", "jest-mock-process": "^1.4.1", diff --git a/test/unit/server/handlers/consents.test.ts b/test/unit/server/handlers/consents.test.ts index fbe9d254..f0e42bbf 100644 --- a/test/unit/server/handlers/consents.test.ts +++ b/test/unit/server/handlers/consents.test.ts @@ -32,6 +32,16 @@ import ConsentsHandler from '~/server/handlers/consents' import { StateResponseToolkit } from '~/server/plugins/state' jest.mock('~/domain/errors') +jest.mock('~/model/registerConsent.model', () => ({ + RegisterConsentModel: { + notificationChannel: jest.fn(() => 'the-mocked-channel') + }, + create: jest.fn(async () => ({ + // this result will be tested + run: jest.fn() + })) +})) + const consentsPostRequestAUTH = { headers: { From 78c688d3bcdebeb02d7e2034d6f32090e8eeee37 Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Sat, 10 Jul 2021 20:22:09 -0500 Subject: [PATCH 17/19] chore: fix integration tests --- docker-compose.yml | 4 ++-- scripts/wait4.js | 0 2 files changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 scripts/wait4.js diff --git a/docker-compose.yml b/docker-compose.yml index 3a9870ae..3e8dbfd1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,8 +22,8 @@ services: volumes: - ./scripts/wait4.js:/opt/auth-service/scripts/wait4.js - ./scripts/wait4.config.js:/opt/auth-service/scripts/wait4.config.js - # environment: - # - NODE_ENV=integration + environment: + - NODE_ENV=integration healthcheck: test: wget -q http://localhost:4004/health -O /dev/null || exit 1 timeout: 20s diff --git a/scripts/wait4.js b/scripts/wait4.js old mode 100644 new mode 100755 From a2ffb30640d436a9b9f4eedf9728883d0664c93e Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Sun, 11 Jul 2021 19:23:41 -0500 Subject: [PATCH 18/19] chore: more tests --- .../server/handlers/participants/{Type}/{ID}.test.ts | 11 +++++++++++ .../handlers/participants/{Type}/{ID}/error.test.ts | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/test/unit/server/handlers/participants/{Type}/{ID}.test.ts b/test/unit/server/handlers/participants/{Type}/{ID}.test.ts index 06fd9012..8413b00b 100644 --- a/test/unit/server/handlers/participants/{Type}/{ID}.test.ts +++ b/test/unit/server/handlers/participants/{Type}/{ID}.test.ts @@ -28,6 +28,8 @@ import { Request } from '@hapi/hapi' import { Enum } from '@mojaloop/central-services-shared' import ParticipantsTypeIDHandler from '~/server/handlers/participants/{Type}/{ID}' import { StateResponseToolkit } from '../../../../../../src/server/plugins/state'; +import { RegisterConsentModel } from '../../../../../../src/model/registerConsent.model'; +import { RegisterConsentPhase } from '../../../../../../src/model/registerConsent.interface'; jest.mock('~/domain/errors') @@ -47,6 +49,7 @@ const participantsTypeIDPutResponse = { describe('server/handlers/consents', (): void => { it('Should return 200 success code', async (): Promise => { + jest.useFakeTimers() const request = participantsTypeIDPutResponse const pubSubMock = { publish: jest.fn() @@ -67,5 +70,13 @@ describe('server/handlers/consents', (): void => { ) expect(response.statusCode).toBe(Enum.Http.ReturnCodes.OK.CODE) + jest.runAllImmediates() + expect(toolkit.getPublisher).toBeCalledTimes(1) + + const channel = RegisterConsentModel.notificationChannel( + RegisterConsentPhase.waitOnParticipantResponseFromALS, + request.params.ID + ) + expect(pubSubMock.publish).toBeCalledWith(channel, request.payload) }) }) diff --git a/test/unit/server/handlers/participants/{Type}/{ID}/error.test.ts b/test/unit/server/handlers/participants/{Type}/{ID}/error.test.ts index 50558bef..1ac480df 100644 --- a/test/unit/server/handlers/participants/{Type}/{ID}/error.test.ts +++ b/test/unit/server/handlers/participants/{Type}/{ID}/error.test.ts @@ -28,6 +28,8 @@ import { Request } from '@hapi/hapi' import { Enum } from '@mojaloop/central-services-shared' import ParticipantsTypeIDErrorHandler from '~/server/handlers/participants/{Type}/{ID}/error' import { StateResponseToolkit } from '~/server/plugins/state'; +import { RegisterConsentModel } from '../../../../../../../src/model/registerConsent.model'; +import { RegisterConsentPhase } from '../../../../../../../src/model/registerConsent.interface'; jest.mock('~/domain/errors') @@ -50,6 +52,7 @@ const errorInformationResponse = { describe('server/handlers/consents', (): void => { it('Should return 200 success code', async (): Promise => { + jest.useFakeTimers() const request = errorInformationResponse const pubSubMock = { publish: jest.fn() @@ -69,5 +72,13 @@ describe('server/handlers/consents', (): void => { ) expect(response.statusCode).toBe(Enum.Http.ReturnCodes.OK.CODE) + jest.runAllImmediates() + expect(toolkit.getPublisher).toBeCalledTimes(1) + + const channel = RegisterConsentModel.notificationChannel( + RegisterConsentPhase.waitOnParticipantResponseFromALS, + request.params.ID + ) + expect(pubSubMock.publish).toBeCalledWith(channel, request.payload) }) }) From b307cad8be4eb99fba11f8441935fbfd6756945a Mon Sep 17 00:00:00 2001 From: Kevin Leyow Date: Tue, 13 Jul 2021 08:07:42 -0500 Subject: [PATCH 19/19] chore: fix imports --- test/unit/server/handlers/participants/{Type}/{ID}.test.ts | 6 +++--- .../server/handlers/participants/{Type}/{ID}/error.test.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/unit/server/handlers/participants/{Type}/{ID}.test.ts b/test/unit/server/handlers/participants/{Type}/{ID}.test.ts index 8413b00b..f498a5ce 100644 --- a/test/unit/server/handlers/participants/{Type}/{ID}.test.ts +++ b/test/unit/server/handlers/participants/{Type}/{ID}.test.ts @@ -27,9 +27,9 @@ import { Request } from '@hapi/hapi' import { Enum } from '@mojaloop/central-services-shared' import ParticipantsTypeIDHandler from '~/server/handlers/participants/{Type}/{ID}' -import { StateResponseToolkit } from '../../../../../../src/server/plugins/state'; -import { RegisterConsentModel } from '../../../../../../src/model/registerConsent.model'; -import { RegisterConsentPhase } from '../../../../../../src/model/registerConsent.interface'; +import { StateResponseToolkit } from '~/server/plugins/state' +import { RegisterConsentModel } from '~/model/registerConsent.model' +import { RegisterConsentPhase } from '~/model/registerConsent.interface' jest.mock('~/domain/errors') diff --git a/test/unit/server/handlers/participants/{Type}/{ID}/error.test.ts b/test/unit/server/handlers/participants/{Type}/{ID}/error.test.ts index 1ac480df..4aaf7821 100644 --- a/test/unit/server/handlers/participants/{Type}/{ID}/error.test.ts +++ b/test/unit/server/handlers/participants/{Type}/{ID}/error.test.ts @@ -27,9 +27,9 @@ import { Request } from '@hapi/hapi' import { Enum } from '@mojaloop/central-services-shared' import ParticipantsTypeIDErrorHandler from '~/server/handlers/participants/{Type}/{ID}/error' -import { StateResponseToolkit } from '~/server/plugins/state'; -import { RegisterConsentModel } from '../../../../../../../src/model/registerConsent.model'; -import { RegisterConsentPhase } from '../../../../../../../src/model/registerConsent.interface'; +import { StateResponseToolkit } from '~/server/plugins/state' +import { RegisterConsentModel } from '~/model/registerConsent.model' +import { RegisterConsentPhase } from '~/model/registerConsent.interface' jest.mock('~/domain/errors')