From 592ca1693d04f1b6975a257a0a4e086c777c28d3 Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Thu, 29 Aug 2024 16:16:14 -0700 Subject: [PATCH 1/7] advances, fixing reject when timing out --- package-lock.json | 67 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + src/SDK.ts | 37 ++++++++++++++------------ test/SDK.spec.ts | 27 ++++++++++++++++--- 4 files changed, 111 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3951c58..e42cee2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "@types/jasmine": "^3.9.1", "@typescript-eslint/eslint-plugin": "^3.10.1", "@typescript-eslint/parser": "^3.10.1", + "axios-mock-adapter": "^2.0.0", "eslint": "^7.32.0", "eslint-plugin-security": "^1.4.0", "istanbul-instrumenter-loader": "^3.0.1", @@ -4144,6 +4145,48 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/axios-mock-adapter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-2.0.0.tgz", + "integrity": "sha512-D/K0J5Zm6KvaMTnsWrBQZWLzKN9GxUFZEa0mx2qeEHXDeTugCoplWehy8y36dj5vuSjhe1u/Dol8cZ8lzzmDew==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "is-buffer": "^2.0.5" + }, + "peerDependencies": { + "axios": ">= 0.17.0" + } + }, + "node_modules/axios-mock-adapter/node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/axios-mock-adapter/node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, "node_modules/axios-retry": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.9.1.tgz", @@ -16889,6 +16932,30 @@ "proxy-from-env": "^1.1.0" } }, + "axios-mock-adapter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-2.0.0.tgz", + "integrity": "sha512-D/K0J5Zm6KvaMTnsWrBQZWLzKN9GxUFZEa0mx2qeEHXDeTugCoplWehy8y36dj5vuSjhe1u/Dol8cZ8lzzmDew==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "is-buffer": "^2.0.5" + }, + "dependencies": { + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true + } + } + }, "axios-retry": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.9.1.tgz", diff --git a/package.json b/package.json index 689c0eb..f12ea90 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "@types/jasmine": "^3.9.1", "@typescript-eslint/eslint-plugin": "^3.10.1", "@typescript-eslint/parser": "^3.10.1", + "axios-mock-adapter": "^2.0.0", "eslint": "^7.32.0", "eslint-plugin-security": "^1.4.0", "istanbul-instrumenter-loader": "^3.0.1", diff --git a/src/SDK.ts b/src/SDK.ts index 6ab4629..b0aa323 100644 --- a/src/SDK.ts +++ b/src/SDK.ts @@ -1,7 +1,8 @@ import * as hash from "crypto"; -import { ChannelId, LiveChatVersion, OCSDKTelemetryEvent } from "./Common/Enums"; +import { ChannelId, LiveChatVersion, OCSDKTelemetryEvent, SDKError } from "./Common/Enums"; import axios, { AxiosRequestConfig, AxiosResponse } from "axios"; + import { BrowserInfo } from "./Utils/BrowserInfo"; import Constants from "./Common/Constants"; import { CustomContextData } from "./Utils/CustomContextData"; @@ -39,6 +40,7 @@ import ReconnectMappingRecord from "./Model/ReconnectMappingRecord"; import { RequestTimeoutConfig } from "./Common/RequestTimeoutConfig"; import { StringMap } from "./Common/Mappings"; import { Timer } from "./Utils/Timer"; +import { addOcUserAgentHeader } from "./Utils/httpHeadersUtils"; import axiosRetryHandler from "./Utils/axiosRetryHandler"; import { createGetChatTokenEndpoint } from "./Utils/endpointsCreators"; import isExpectedAxiosError from "./Utils/isExpectedAxiosError"; @@ -46,7 +48,6 @@ import sessionInitRetryHandler from "./Utils/SessionInitRetryHandler"; import throwClientHTTPTimeoutError from "./Utils/throwClientHTTPError"; import { uuidv4 } from "./Utils/uuid"; import { waitTimeBetweenRetriesConfigs } from "./Utils/waitTimeBetweenRetriesConfigs"; -import { addOcUserAgentHeader } from "./Utils/httpHeadersUtils"; export default class SDK implements ISDK { private static defaultRequestTimeoutConfig: RequestTimeoutConfig = { @@ -84,6 +85,8 @@ export default class SDK implements ISDK { liveChatVersion: number; sessionId?: string; ocUserAgent: string[]; + HTTPTimeOutErrorMessage = `${SDKError.ClientHTTPTimeoutErrorName}: ${SDKError.ClientHTTPTimeoutErrorMessage}`; + public constructor(private omnichannelConfiguration: IOmnichannelConfiguration, private configuration: ISDKConfiguration = SDK.defaultConfiguration, private logger?: OCSDKLogger) { // Sets to default configuration if passed configuration is empty or is not an object @@ -289,7 +292,7 @@ export default class SDK implements ISDK { const elapsedTimeInMilliseconds = timer.milliSecondsElapsed; this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.GETLWISTATUSFAILED, "Get LWI Details failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) { - throwClientHTTPTimeoutError(); + reject( new Error(this.HTTPTimeOutErrorMessage)); } reject(error); } @@ -396,7 +399,7 @@ export default class SDK implements ISDK { const elapsedTimeInMilliseconds = timer.milliSecondsElapsed; this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.GETCHATTOKENFAILED, "Get Chat Token failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) { - throwClientHTTPTimeoutError(); + reject( new Error(this.HTTPTimeOutErrorMessage)); } reject(error); } @@ -453,7 +456,7 @@ export default class SDK implements ISDK { const elapsedTimeInMilliseconds = timer.milliSecondsElapsed; this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.GETRECONNECTABLECHATS, "Get Reconnectable Chats failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) { - throwClientHTTPTimeoutError(); + reject( new Error(this.HTTPTimeOutErrorMessage)); } reject(error); return; @@ -505,7 +508,7 @@ export default class SDK implements ISDK { const elapsedTimeInMilliseconds = timer.milliSecondsElapsed; this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.GETRECONNECTAVAILABILITY, "Get Reconnect Availability failed", undefined, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) { - throwClientHTTPTimeoutError(); + reject( new Error(this.HTTPTimeOutErrorMessage)); } reject(error); return; @@ -608,7 +611,7 @@ export default class SDK implements ISDK { this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.GETAGENTAVAILABILITYFAILED, "Get agent availability failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) { - throwClientHTTPTimeoutError(); + reject( new Error(this.HTTPTimeOutErrorMessage)); } reject(error); } @@ -709,7 +712,7 @@ export default class SDK implements ISDK { const elapsedTimeInMilliseconds = timer.milliSecondsElapsed; this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.SESSIONINITFAILED, "Session Init failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, data, requestHeaders); if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) { - throwClientHTTPTimeoutError(); + reject( new Error(this.HTTPTimeOutErrorMessage)); } reject(error); } @@ -781,7 +784,7 @@ export default class SDK implements ISDK { const elapsedTimeInMilliseconds = timer.milliSecondsElapsed; this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.SESSIONCLOSEFAILED, "Session close failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); if (error.code === Constants.axiosTimeoutErrorCode) { - throwClientHTTPTimeoutError(); + reject( new Error(this.HTTPTimeOutErrorMessage)); } reject(error); } @@ -848,7 +851,7 @@ export default class SDK implements ISDK { this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.VALIDATEAUTHCHATRECORDFAILED, "Validate Auth Chat Record failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) { - throwClientHTTPTimeoutError(); + reject( new Error(this.HTTPTimeOutErrorMessage)); } if (error.toString() === "Error: Request failed with status code 404") { // backward compatibility @@ -912,7 +915,7 @@ export default class SDK implements ISDK { const elapsedTimeInMilliseconds = timer.milliSecondsElapsed; this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.SUBMITPOSTCHATFAILED, "Submit Post Chat Failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) { - throwClientHTTPTimeoutError(); + reject( new Error(this.HTTPTimeOutErrorMessage)); } reject(error); } @@ -982,7 +985,7 @@ export default class SDK implements ISDK { const elapsedTimeInMilliseconds = timer.milliSecondsElapsed; this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.GETSURVEYINVITELINKFAILED, "Get Survey Invite Link failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) { - throwClientHTTPTimeoutError(); + reject( new Error(this.HTTPTimeOutErrorMessage)); } reject(error); } @@ -1052,7 +1055,7 @@ export default class SDK implements ISDK { const elapsedTimeInMilliseconds = timer.milliSecondsElapsed; this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.GETCHATTRANSCRIPTFAILED, "Get Chat Transcript failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) { - throwClientHTTPTimeoutError(); + reject( new Error(this.HTTPTimeOutErrorMessage)); } reject(error); } @@ -1116,7 +1119,7 @@ export default class SDK implements ISDK { const elapsedTimeInMilliseconds = timer.milliSecondsElapsed; this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.EMAILTRANSCRIPTFAILED, "Email Transcript Failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) { - throwClientHTTPTimeoutError(); + reject( new Error(this.HTTPTimeOutErrorMessage)); } reject(error); } @@ -1168,7 +1171,7 @@ export default class SDK implements ISDK { const elapsedTimeInMilliseconds = timer.milliSecondsElapsed; this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.FETCHDATAMASKINGFAILED, "Fetch Data Masking Failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) { - throwClientHTTPTimeoutError(); + reject( new Error(this.HTTPTimeOutErrorMessage)); } reject(error); } @@ -1231,7 +1234,7 @@ export default class SDK implements ISDK { const elapsedTimeInMilliseconds = timer.milliSecondsElapsed; this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.SECONDARYCHANNELEVENTREQUESTFAILED, "Secondary Channel Event Request Failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) { - throwClientHTTPTimeoutError(); + reject( new Error(this.HTTPTimeOutErrorMessage)); } reject(error); } @@ -1281,7 +1284,7 @@ export default class SDK implements ISDK { this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.SENDTYPINGINDICATORFAILED, "Send Typing Indicator Failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) { - throwClientHTTPTimeoutError(); + reject( new Error(this.HTTPTimeOutErrorMessage)); } reject(error); } diff --git a/test/SDK.spec.ts b/test/SDK.spec.ts index 3163bb5..0ab4fdb 100644 --- a/test/SDK.spec.ts +++ b/test/SDK.spec.ts @@ -22,6 +22,7 @@ import ISubmitPostChatResponseOptionalParams from "../src/Interfaces/ISubmitPost import IValidateAuthChatRecordOptionalParams from "../src/Interfaces/IValidateAuthChatRecordOptionalParams"; import { LocationInfo } from "../src/Utils/LocationInfo"; import { LogLevel } from "../src/Model/LogLevel"; +import MockAdapter from 'axios-mock-adapter'; import OCSDKLogger from "../src/Common/OCSDKLogger"; import { OSInfo } from "../src/Utils/OSInfo"; import SDK from "../src/SDK"; @@ -206,7 +207,7 @@ describe("SDK unit tests", () => { const sdk = new SDK(ochannelConfig as IOmnichannelConfiguration, undefined, ocsdkLogger); try { await sdk.getChatToken(requestId, {}, -1); - throw new Error("Should throw an error"); + fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual("Invalid currentRetryCount"); expect(ocsdkLogger.log).toHaveBeenCalled(); @@ -517,9 +518,7 @@ describe("SDK unit tests", () => { }); describe("Test getChatTranscripts method", () => { - const coolId = "coolId"; - const sessionInitOpt = { authenticatedUserToken: "asdas" }; @@ -630,7 +629,7 @@ describe("SDK unit tests", () => { const sdk = new SDK(ochannelConfig as IOmnichannelConfiguration); const result = sdk.validateAuthChatRecord(requestId, validateAuthChatRecordOptionalParams as IValidateAuthChatRecordOptionalParams); result.then(() => { - throw Error("Promise should reject"); + fail("Promise should reject"); }).catch(() => { expect(axiosInstMockInvalid).toHaveBeenCalled(); done(); @@ -679,6 +678,8 @@ describe("SDK unit tests", () => { let sdk: any; let requestBody: any; let originalTimeout:number; + let mock: MockAdapter; + beforeEach(() => { originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; @@ -690,13 +691,17 @@ describe("SDK unit tests", () => { chatId: "chatId" }; sdk = new SDK(ochannelConfig as IOmnichannelConfiguration); + mock = new MockAdapter(axios); requestBody = { body: "dummy" } + mock.onGet(/.*/).timeout(); + }); it("getChatConfig timeout test", async () => { spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { await sdk.getChatConfig(""); + fail("Should throw an error"); } catch (error: any) { expect(error.code).toEqual("ECONNABORTED "); expect(error.message).toContain("timeout"); @@ -706,7 +711,10 @@ describe("SDK unit tests", () => { it("getChatToken timeout test", async () => { spyOn(axios, "create").and.returnValue(axiosInstMock); try { + mock.onGet(/.*/).timeout(); + await sdk.getChatToken(requestId, {}, 0); + fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); } @@ -716,6 +724,7 @@ describe("SDK unit tests", () => { spyOn(axios, "create").and.returnValue(axiosInstMock); try { await sdk.getReconnectableChats({ authenticatedUserToken : "Token"} as IReconnectableChatsParams); + fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); } @@ -725,6 +734,7 @@ describe("SDK unit tests", () => { spyOn(axios, "create").and.returnValue(axiosInstMock); try { await sdk.getReconnectAvailability("reconnectId"); + fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); } @@ -734,6 +744,7 @@ describe("SDK unit tests", () => { spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { sdk.sessionInit(requestId, defaultOpt as ISessionInitOptionalParams); + fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); } @@ -743,7 +754,9 @@ describe("SDK unit tests", () => { spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { sdk.getAgentAvailability(requestId, defaultOpt as ISessionInitOptionalParams); + fail("Should throw an error"); } catch (error: any) { + expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); } }); @@ -752,6 +765,7 @@ describe("SDK unit tests", () => { spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { sdk.sessionClose(requestId, defaultOpt as ISessionCloseOptionalParams); + fail("Should throw an error"); } catch (error: any) { expect(error.code).toEqual("ECONNABORTED "); expect(error.message).toContain("timeout"); @@ -762,6 +776,7 @@ describe("SDK unit tests", () => { spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { sdk.submitPostChatResponse(requestId, defaultOpt as ISubmitPostChatResponseOptionalParams); + fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); } @@ -771,6 +786,7 @@ describe("SDK unit tests", () => { spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { sdk.getSurveyInviteLink(requestId, defaultOpt as IGetSurveyInviteLinkOptionalParams); + fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); } @@ -780,6 +796,7 @@ describe("SDK unit tests", () => { spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { sdk.getChatTranscripts(requestId, "coolId", "coolId", defaultOpt as IGetChatTranscriptsOptionalParams); + fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); } @@ -789,6 +806,7 @@ describe("SDK unit tests", () => { spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { sdk.makeSecondaryChannelEventRequest(requestId, requestBody, defaultOpt as ISecondaryChannelEventOptionalParams); + fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); } @@ -798,6 +816,7 @@ describe("SDK unit tests", () => { spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { sdk.validateAuthChatRecord(requestId, defaultOpt as IValidateAuthChatRecordOptionalParams); + fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); } From 13ecfc834e21645fcd6001e31e5ea70a24522dae Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Thu, 29 Aug 2024 16:56:12 -0700 Subject: [PATCH 2/7] pending 8 tests to solve --- src/SDK.ts | 2 +- test/SDK.spec.ts | 64 +++++++++++++++++++++++++----------------------- 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/SDK.ts b/src/SDK.ts index b0aa323..0d1f2e9 100644 --- a/src/SDK.ts +++ b/src/SDK.ts @@ -45,7 +45,6 @@ import axiosRetryHandler from "./Utils/axiosRetryHandler"; import { createGetChatTokenEndpoint } from "./Utils/endpointsCreators"; import isExpectedAxiosError from "./Utils/isExpectedAxiosError"; import sessionInitRetryHandler from "./Utils/SessionInitRetryHandler"; -import throwClientHTTPTimeoutError from "./Utils/throwClientHTTPError"; import { uuidv4 } from "./Utils/uuid"; import { waitTimeBetweenRetriesConfigs } from "./Utils/waitTimeBetweenRetriesConfigs"; @@ -208,6 +207,7 @@ export default class SDK implements ISDK { return data; } catch (error) { + console.debug("ELOPEZANAYA :: failing here for chat config"); const elapsedTimeInMilliseconds = timer.milliSecondsElapsed; this.logWithLogger(LogLevel.INFO, OCSDKTelemetryEvent.GETCHATCONFIGFAILED, "Get Chat config failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); return Promise.reject(error); diff --git a/test/SDK.spec.ts b/test/SDK.spec.ts index 0ab4fdb..3990ee7 100644 --- a/test/SDK.spec.ts +++ b/test/SDK.spec.ts @@ -279,6 +279,8 @@ describe("SDK unit tests", () => { let browserName: any; let deviceType: any; let osType: any; + const HTTPTimeOutErrorMessage = SDKError.ClientHTTPTimeoutErrorName + ": " + SDKError.ClientHTTPTimeoutErrorMessage; + beforeEach(() => { locationInfo = { latitude: "1", longitude: "2" }; @@ -405,6 +407,7 @@ describe("SDK unit tests", () => { const sessionInitOpt = { authenticatedUserToken: "asdas" }; + const HTTPTimeOutErrorMessage = SDKError.ClientHTTPTimeoutErrorName + ": " + SDKError.ClientHTTPTimeoutErrorMessage; it("Should return promise", () => { spyOn(axios, "create").and.returnValue(axiosInstMock); @@ -679,8 +682,8 @@ describe("SDK unit tests", () => { let requestBody: any; let originalTimeout:number; let mock: MockAdapter; + const HTTPTimeOutErrorMessage = `${SDKError.ClientHTTPTimeoutErrorName}: ${SDKError.ClientHTTPTimeoutErrorMessage}`; - beforeEach(() => { originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000; //set the environment variable @@ -693,132 +696,133 @@ describe("SDK unit tests", () => { sdk = new SDK(ochannelConfig as IOmnichannelConfiguration); mock = new MockAdapter(axios); requestBody = { body: "dummy" } - mock.onGet(/.*/).timeout(); - }); it("getChatConfig timeout test", async () => { - spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { + mock.onGet(/.*/).timeout(); await sdk.getChatConfig(""); fail("Should throw an error"); } catch (error: any) { - expect(error.code).toEqual("ECONNABORTED "); - expect(error.message).toContain("timeout"); - } + expect(error.code).toEqual("ECONNABORTED"); + expect(error.message).toContain("timeout"); } }); it("getChatToken timeout test", async () => { - spyOn(axios, "create").and.returnValue(axiosInstMock); try { mock.onGet(/.*/).timeout(); - await sdk.getChatToken(requestId, {}, 0); fail("Should throw an error"); } catch (error: any) { - expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); + expect(error.message).toEqual(HTTPTimeOutErrorMessage); } }); it("getReconnectableChats timeout test", async () => { - spyOn(axios, "create").and.returnValue(axiosInstMock); try { + mock.onGet(/.*/).timeout(); await sdk.getReconnectableChats({ authenticatedUserToken : "Token"} as IReconnectableChatsParams); fail("Should throw an error"); } catch (error: any) { - expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); + expect(error.message).toEqual(HTTPTimeOutErrorMessage); } }); it("getReconnectAvailability timeout test", async () => { - spyOn(axios, "create").and.returnValue(axiosInstMock); try { + mock.onGet(/.*/).timeout(); await sdk.getReconnectAvailability("reconnectId"); fail("Should throw an error"); } catch (error: any) { - expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); + expect(error.message).toEqual(HTTPTimeOutErrorMessage); } }); it("sessionInit timeout test", async () => { - spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { + mock.onPost(/.*/).timeout(); sdk.sessionInit(requestId, defaultOpt as ISessionInitOptionalParams); fail("Should throw an error"); } catch (error: any) { - expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); + expect(error.message).toEqual(HTTPTimeOutErrorMessage); } }); it("getAgentAvailability timeout test", async () => { - spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { + mock.onGet(/.*/).timeout(); + sdk.getAgentAvailability(requestId, defaultOpt as ISessionInitOptionalParams); fail("Should throw an error"); } catch (error: any) { - - expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); + expect(error.message).toEqual(HTTPTimeOutErrorMessage); } }); it("sessionClose timeout test", async () => { - spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { + mock.onPost(/.*/).timeout(); + sdk.sessionClose(requestId, defaultOpt as ISessionCloseOptionalParams); fail("Should throw an error"); } catch (error: any) { expect(error.code).toEqual("ECONNABORTED "); expect(error.message).toContain("timeout"); + } }); it("submitPostChatResponse timeout test", async () => { - spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { + mock.onPost(/.*/).timeout(); + sdk.submitPostChatResponse(requestId, defaultOpt as ISubmitPostChatResponseOptionalParams); fail("Should throw an error"); } catch (error: any) { - expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); + expect(error.message).toEqual(HTTPTimeOutErrorMessage); } }); it("getSurveyInviteLink timeout test", async () => { - spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); + try { + mock.onGet(/.*/).timeout(); + sdk.getSurveyInviteLink(requestId, defaultOpt as IGetSurveyInviteLinkOptionalParams); fail("Should throw an error"); } catch (error: any) { - expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); + expect(error.message).toEqual(HTTPTimeOutErrorMessage); } }); it("getChatTranscripts timeout test", async () => { - spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { + mock.onGet(/.*/).timeout(); sdk.getChatTranscripts(requestId, "coolId", "coolId", defaultOpt as IGetChatTranscriptsOptionalParams); fail("Should throw an error"); } catch (error: any) { - expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); + expect(error.message).toEqual(HTTPTimeOutErrorMessage); } }); it("makeSecondaryChannelEventRequest timeout test", async () => { - spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { + mock.onPost(/.*/).timeout(); sdk.makeSecondaryChannelEventRequest(requestId, requestBody, defaultOpt as ISecondaryChannelEventOptionalParams); fail("Should throw an error"); } catch (error: any) { - expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); + expect(error.message).toEqual(HTTPTimeOutErrorMessage); } }); it("validateAuthChatRecord timeout test", async () => { - spyOn(axios, "create").and.returnValue({ async get(endpoint: any) { return dataMock; }}); try { + mock.onPost(/.*/).timeout(); + sdk.validateAuthChatRecord(requestId, defaultOpt as IValidateAuthChatRecordOptionalParams); fail("Should throw an error"); } catch (error: any) { - expect(error.message).toEqual(SDKError.ClientHTTPTimeoutErrorName + ":" + SDKError.ClientHTTPTimeoutErrorMessage); + expect(error.message).toEqual(HTTPTimeOutErrorMessage); } }); From 81d55403587b56641b021fbf4a109f3b4cd2c5df Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Thu, 29 Aug 2024 17:43:39 -0700 Subject: [PATCH 3/7] missing to fix 3 tests --- test/SDK.spec.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/SDK.spec.ts b/test/SDK.spec.ts index 3990ee7..7b88651 100644 --- a/test/SDK.spec.ts +++ b/test/SDK.spec.ts @@ -407,7 +407,6 @@ describe("SDK unit tests", () => { const sessionInitOpt = { authenticatedUserToken: "asdas" }; - const HTTPTimeOutErrorMessage = SDKError.ClientHTTPTimeoutErrorName + ": " + SDKError.ClientHTTPTimeoutErrorMessage; it("Should return promise", () => { spyOn(axios, "create").and.returnValue(axiosInstMock); @@ -741,7 +740,8 @@ describe("SDK unit tests", () => { it("sessionInit timeout test", async () => { try { mock.onPost(/.*/).timeout(); - sdk.sessionInit(requestId, defaultOpt as ISessionInitOptionalParams); + + await sdk.sessionInit(requestId, defaultOpt as ISessionInitOptionalParams); fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual(HTTPTimeOutErrorMessage); @@ -750,9 +750,8 @@ describe("SDK unit tests", () => { it("getAgentAvailability timeout test", async () => { try { - mock.onGet(/.*/).timeout(); - - sdk.getAgentAvailability(requestId, defaultOpt as ISessionInitOptionalParams); + mock.onPost(/.*/).timeout(); + await sdk.getAgentAvailability(requestId, defaultOpt as ISessionInitOptionalParams); fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual(HTTPTimeOutErrorMessage); @@ -763,7 +762,7 @@ describe("SDK unit tests", () => { try { mock.onPost(/.*/).timeout(); - sdk.sessionClose(requestId, defaultOpt as ISessionCloseOptionalParams); + await sdk.sessionClose(requestId, defaultOpt as ISessionCloseOptionalParams); fail("Should throw an error"); } catch (error: any) { expect(error.code).toEqual("ECONNABORTED "); @@ -776,7 +775,7 @@ describe("SDK unit tests", () => { try { mock.onPost(/.*/).timeout(); - sdk.submitPostChatResponse(requestId, defaultOpt as ISubmitPostChatResponseOptionalParams); + await sdk.submitPostChatResponse(requestId, defaultOpt as ISubmitPostChatResponseOptionalParams); fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual(HTTPTimeOutErrorMessage); @@ -787,8 +786,7 @@ describe("SDK unit tests", () => { try { mock.onGet(/.*/).timeout(); - - sdk.getSurveyInviteLink(requestId, defaultOpt as IGetSurveyInviteLinkOptionalParams); + await sdk.getSurveyInviteLink(requestId, defaultOpt as IGetSurveyInviteLinkOptionalParams); fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual(HTTPTimeOutErrorMessage); @@ -798,7 +796,7 @@ describe("SDK unit tests", () => { it("getChatTranscripts timeout test", async () => { try { mock.onGet(/.*/).timeout(); - sdk.getChatTranscripts(requestId, "coolId", "coolId", defaultOpt as IGetChatTranscriptsOptionalParams); + await sdk.getChatTranscripts(requestId, "coolId", "coolId", defaultOpt as IGetChatTranscriptsOptionalParams); fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual(HTTPTimeOutErrorMessage); @@ -808,7 +806,7 @@ describe("SDK unit tests", () => { it("makeSecondaryChannelEventRequest timeout test", async () => { try { mock.onPost(/.*/).timeout(); - sdk.makeSecondaryChannelEventRequest(requestId, requestBody, defaultOpt as ISecondaryChannelEventOptionalParams); + await sdk.makeSecondaryChannelEventRequest(requestId, requestBody, defaultOpt as ISecondaryChannelEventOptionalParams); fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual(HTTPTimeOutErrorMessage); @@ -819,7 +817,7 @@ describe("SDK unit tests", () => { try { mock.onPost(/.*/).timeout(); - sdk.validateAuthChatRecord(requestId, defaultOpt as IValidateAuthChatRecordOptionalParams); + await sdk.validateAuthChatRecord(requestId, defaultOpt as IValidateAuthChatRecordOptionalParams); fail("Should throw an error"); } catch (error: any) { expect(error.message).toEqual(HTTPTimeOutErrorMessage); @@ -827,6 +825,8 @@ describe("SDK unit tests", () => { }); afterEach(() => { + mock.restore(); + mock.reset(); jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; //remove environment variable }); }); From ae3ac299b78ce4eaf7ca7ecc96bee0b62e83fcdd Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Thu, 29 Aug 2024 17:48:35 -0700 Subject: [PATCH 4/7] 2 left... --- test/SDK.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/SDK.spec.ts b/test/SDK.spec.ts index 7b88651..76daec3 100644 --- a/test/SDK.spec.ts +++ b/test/SDK.spec.ts @@ -785,7 +785,7 @@ describe("SDK unit tests", () => { it("getSurveyInviteLink timeout test", async () => { try { - mock.onGet(/.*/).timeout(); + mock.onPost(/.*/).timeout(); await sdk.getSurveyInviteLink(requestId, defaultOpt as IGetSurveyInviteLinkOptionalParams); fail("Should throw an error"); } catch (error: any) { From e5c89b79d4bfbcbaf21c4144d15850ff07ca88ac Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Thu, 29 Aug 2024 18:08:17 -0700 Subject: [PATCH 5/7] all timeout tests passing --- src/SDK.ts | 6 ++++-- test/SDK.spec.ts | 9 ++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/SDK.ts b/src/SDK.ts index 0d1f2e9..ce650f5 100644 --- a/src/SDK.ts +++ b/src/SDK.ts @@ -207,7 +207,6 @@ export default class SDK implements ISDK { return data; } catch (error) { - console.debug("ELOPEZANAYA :: failing here for chat config"); const elapsedTimeInMilliseconds = timer.milliSecondsElapsed; this.logWithLogger(LogLevel.INFO, OCSDKTelemetryEvent.GETCHATCONFIGFAILED, "Get Chat config failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); return Promise.reject(error); @@ -711,6 +710,7 @@ export default class SDK implements ISDK { } catch (error) { const elapsedTimeInMilliseconds = timer.milliSecondsElapsed; this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.SESSIONINITFAILED, "Session Init failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, data, requestHeaders); + if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) { reject( new Error(this.HTTPTimeOutErrorMessage)); } @@ -1052,10 +1052,12 @@ export default class SDK implements ISDK { this.logWithLogger(LogLevel.INFO, OCSDKTelemetryEvent.GETCHATTRANSCRIPTSUCCEEDED, "Get Chat Transcript succeeded", requestId, response, elapsedTimeInMilliseconds, requestPath, method, undefined, undefined, requestHeaders); resolve(data); } catch (error) { + const elapsedTimeInMilliseconds = timer.milliSecondsElapsed; this.logWithLogger(LogLevel.ERROR, OCSDKTelemetryEvent.GETCHATTRANSCRIPTFAILED, "Get Chat Transcript failed", requestId, undefined, elapsedTimeInMilliseconds, requestPath, method, error, undefined, requestHeaders); if (isExpectedAxiosError(error, Constants.axiosTimeoutErrorCode)) { - reject( new Error(this.HTTPTimeOutErrorMessage)); + reject(new Error(this.HTTPTimeOutErrorMessage)); + } reject(error); } diff --git a/test/SDK.spec.ts b/test/SDK.spec.ts index 76daec3..f345d6d 100644 --- a/test/SDK.spec.ts +++ b/test/SDK.spec.ts @@ -765,8 +765,9 @@ describe("SDK unit tests", () => { await sdk.sessionClose(requestId, defaultOpt as ISessionCloseOptionalParams); fail("Should throw an error"); } catch (error: any) { - expect(error.code).toEqual("ECONNABORTED "); - expect(error.message).toContain("timeout"); + console.debug("ELOPEZ ERROR :", error); + expect(error.message).toEqual(HTTPTimeOutErrorMessage); + } }); @@ -815,8 +816,7 @@ describe("SDK unit tests", () => { it("validateAuthChatRecord timeout test", async () => { try { - mock.onPost(/.*/).timeout(); - + mock.onGet(/.*/).timeout(); await sdk.validateAuthChatRecord(requestId, defaultOpt as IValidateAuthChatRecordOptionalParams); fail("Should throw an error"); } catch (error: any) { @@ -826,7 +826,6 @@ describe("SDK unit tests", () => { afterEach(() => { mock.restore(); - mock.reset(); jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; //remove environment variable }); }); From 17f0f5b601191e064b87d2cd6f8d54a7457b3a4b Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Thu, 29 Aug 2024 18:10:45 -0700 Subject: [PATCH 6/7] cleanup --- test/SDK.spec.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/SDK.spec.ts b/test/SDK.spec.ts index f345d6d..6c870fb 100644 --- a/test/SDK.spec.ts +++ b/test/SDK.spec.ts @@ -765,10 +765,7 @@ describe("SDK unit tests", () => { await sdk.sessionClose(requestId, defaultOpt as ISessionCloseOptionalParams); fail("Should throw an error"); } catch (error: any) { - console.debug("ELOPEZ ERROR :", error); expect(error.message).toEqual(HTTPTimeOutErrorMessage); - - } }); From d4d514714903893bc108268663409a9c0952e05f Mon Sep 17 00:00:00 2001 From: Edgar Lopez Date: Thu, 29 Aug 2024 18:27:51 -0700 Subject: [PATCH 7/7] error when returning getchat empty --- src/SDK.ts | 7 +++++++ test/SDK.spec.ts | 14 +++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/SDK.ts b/src/SDK.ts index ce650f5..0a3b493 100644 --- a/src/SDK.ts +++ b/src/SDK.ts @@ -382,6 +382,13 @@ export default class SDK implements ISDK { // Resolves only if it contains chat token response which only happens on status 200 if (data) { + + // check if data is empty, if so, then reject the promise + if (Object.keys(data).length === 0) { + reject(new Error("Empty data received from getChatToken")); + return; + } + data.requestId = requestId; this.logWithLogger(LogLevel.INFO, OCSDKTelemetryEvent.GETCHATTOKENSUCCEEDED, "Get Chat Token succeeded", requestId, response, elapsedTimeInMilliseconds, requestPath, method, undefined, undefined, requestHeaders); resolve(data); diff --git a/test/SDK.spec.ts b/test/SDK.spec.ts index 6c870fb..6b21085 100644 --- a/test/SDK.spec.ts +++ b/test/SDK.spec.ts @@ -41,6 +41,7 @@ describe("SDK unit tests", () => { let dataMock: any; let uuidvSpy: any; let axiosInstMock: any; + let axiosInstEmptyMock:any; let axiosInstMockWithError: any; let ocsdkLogger: any; const logger = { @@ -61,6 +62,7 @@ describe("SDK unit tests", () => { spyOn(axiosRetryHandler, "default").and.callFake(() => {}); axiosInstMock = jasmine.createSpy("axiosInstance").and.returnValue(dataMock); axiosInstMockWithError = jasmine.createSpy("axiosInstance").and.throwError(AxiosError); + axiosInstEmptyMock = jasmine.createSpy("axiosInstance").and.returnValue({data: {}}); }); describe("Test constructor", () => { @@ -223,8 +225,18 @@ describe("SDK unit tests", () => { expect(axios.create).toHaveBeenCalled(); expect(axiosRetryHandler.default).toHaveBeenCalled(); }); - }); + it("Should fail due to empty response", async () => { + spyOn(axios, "create").and.returnValue(axiosInstEmptyMock); + const sdk = new SDK(ochannelConfig as IOmnichannelConfiguration); + try { + await sdk.getChatToken(requestId, {}, 0); + fail("Should throw an error"); + } catch (error:any) { + expect(error.message).toEqual("Empty data received from getChatToken"); + } + }); + }); describe("Test getReconnectableChats method", () => {