diff --git a/src/common/retryer.js b/src/common/retryer.js index 090865ed00c6d..88c9a834bfef1 100644 --- a/src/common/retryer.js +++ b/src/common/retryer.js @@ -63,10 +63,9 @@ const retryer = async (fetcher, variables, retries = 0) => { } catch (err) { // prettier-ignore // also checking for bad credentials if any tokens gets invalidated - const isBadCredential = err.response.data && err.response.data.message === "Bad credentials"; + const isBadCredential = err.response?.data?.message === "Bad credentials"; const isAccountSuspended = - err.response.data && - err.response.data.message === "Sorry. Your account was suspended."; + err.response?.data?.message === "Sorry. Your account was suspended."; if (isBadCredential || isAccountSuspended) { logger.log(`PAT_${retries + 1} Failed`); diff --git a/src/fetchers/wakatime.js b/src/fetchers/wakatime.js index f69d6ae498eef..558b550fc87d1 100644 --- a/src/fetchers/wakatime.js +++ b/src/fetchers/wakatime.js @@ -21,7 +21,8 @@ const fetchWakatimeStats = async ({ username, api_domain }) => { return data.data; } catch (err) { - if (err.response.status < 200 || err.response.status > 299) { + const status = err.response?.status; + if (status && (status < 200 || status > 299)) { throw new CustomError( `Could not resolve to a User with the login of '${username}'`, "WAKATIME_USER_NOT_FOUND", diff --git a/tests/retryer.test.js b/tests/retryer.test.js index 76630039d5017..91c80b466900b 100644 --- a/tests/retryer.test.js +++ b/tests/retryer.test.js @@ -76,4 +76,49 @@ describe("Test Retryer", () => { expect(err.message).toBe("Downtime due to GitHub API rate limiting"); } }); + + it("retryer should handle network errors without err.response gracefully", async () => { + const fetcherNetworkError = jest.fn(() => { + const error = new Error("ECONNREFUSED"); + // Simulate network error without response object + return Promise.reject(error); + }); + + const res = await retryer(fetcherNetworkError, {}); + + expect(fetcherNetworkError).toBeCalledTimes(1); + expect(res).toBeUndefined(); // err.response is undefined for network errors + }); + + it("retryer should retry on bad credentials error", async () => { + const fetcherBadCreds = jest.fn((_vars, _token, retries) => { + if (retries < 1) { + const error = new Error("Bad credentials"); + error.response = { data: { message: "Bad credentials" } }; + return Promise.reject(error); + } + return Promise.resolve({ data: "ok" }); + }); + + const res = await retryer(fetcherBadCreds, {}); + + expect(fetcherBadCreds).toBeCalledTimes(2); + expect(res).toStrictEqual({ data: "ok" }); + }); + + it("retryer should retry on account suspended error", async () => { + const fetcherSuspended = jest.fn((_vars, _token, retries) => { + if (retries < 1) { + const error = new Error("Account suspended"); + error.response = { data: { message: "Sorry. Your account was suspended." } }; + return Promise.reject(error); + } + return Promise.resolve({ data: "ok" }); + }); + + const res = await retryer(fetcherSuspended, {}); + + expect(fetcherSuspended).toBeCalledTimes(2); + expect(res).toStrictEqual({ data: "ok" }); + }); });