From 92c2c47106dbd383fdfaf27f77245b216204560b Mon Sep 17 00:00:00 2001 From: raj pandey Date: Thu, 16 Apr 2026 15:40:09 +0530 Subject: [PATCH 1/2] fix(core): release sync gate after Sync API failures --- .gitignore | 1 + .talismanrc | 10 +---- jest.config.js | 2 + jest.setup.js | 10 +++++ package-lock.json | 93 ++++++++++++++++++++-------------------- package.json | 11 +++-- src/api.ts | 4 +- src/core/index.ts | 20 +++++---- src/core/inet.ts | 21 +++++++-- test/api.ts | 2 + test/core/index.ts | 3 +- test/core/inet.ts | 22 ++++++++++ test/core/sync-resume.ts | 50 +++++++++++++++++++++ test/core/sync.ts | 28 +++++++++--- 14 files changed, 199 insertions(+), 78 deletions(-) create mode 100644 jest.setup.js create mode 100644 test/core/sync-resume.ts diff --git a/.gitignore b/.gitignore index 3c573a8..3d1f3f1 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ example/**/* dist/ dist/* +.DS_store diff --git a/.talismanrc b/.talismanrc index 0dbc96a..52506d1 100644 --- a/.talismanrc +++ b/.talismanrc @@ -1,12 +1,4 @@ fileignoreconfig: -- filename: .github/workflows/secrets-scan.yml - ignore_detectors: - - filecontent -- filename: .github/workflows/check-version-bump.yml - ignore_detectors: - - filecontent - filename: package-lock.json - checksum: 1525b038bc7500db7a4b489db28529cd0b2ada15afc7110d818fe13657303612 -- filename: .husky/pre-commit - checksum: 1b9367d219802de2e3a8af9c5c698e0c255c00af89339d73bdbb8acf5275079f + checksum: 009fb6f2f26eda48369458fddb51a255ebbb4c73bd66d247d428c3a0faf2ec1b version: "" diff --git a/jest.config.js b/jest.config.js index 14801e0..bf396ef 100644 --- a/jest.config.js +++ b/jest.config.js @@ -102,6 +102,8 @@ module.exports = { // The test environment that will be used for testing testEnvironment: 'node', + setupFilesAfterEnv: ['/jest.setup.js'], + // Options that will be passed to the testEnvironment // testEnvironmentOptions: {}, testPathIgnorePatterns: [ diff --git a/jest.setup.js b/jest.setup.js new file mode 100644 index 0000000..5fc0995 --- /dev/null +++ b/jest.setup.js @@ -0,0 +1,10 @@ +/* eslint-env jest */ +// marked ships ESM; Jest/ts-jest need a stub for tests that load the app entry. +jest.mock('marked', () => ({ + __esModule: true, + default: { + parse: jest.fn(() => ''), + setOptions: jest.fn(), + use: jest.fn(), + }, +})) diff --git a/package-lock.json b/package-lock.json index 9d3bac2..f60facd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,29 +1,29 @@ { "name": "@contentstack/datasync-manager", - "version": "2.4.0-beta.2", + "version": "2.4.0-beta.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@contentstack/datasync-manager", - "version": "2.4.0-beta.2", + "version": "2.4.0-beta.3", "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.1.2", "debug": "^4.4.3", "dns-socket": "^4.2.2", "lodash": "^4.18.1", - "marked": "^17.0.5", + "marked": "^17.0.6", "write-file-atomic": "7.0.1" }, "devDependencies": { "@semantic-release/commit-analyzer": "^9.0.2", "@semantic-release/git": "^10.0.1", - "@semantic-release/npm": "^12.0.1", + "@semantic-release/npm": "^12.0.2", "@semantic-release/release-notes-generator": "^10.0.3", "@types/debug": "0.0.31", "@types/jest": "23.3.14", - "@types/lodash": "4.17.15", + "@types/lodash": "4.17.24", "@types/marked": "^4.3.2", "@types/mkdirp": "0.5.2", "@types/nock": "9.3.1", @@ -789,9 +789,9 @@ } }, "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", "dev": true, "license": "MIT", "engines": { @@ -2024,9 +2024,9 @@ "license": "MIT" }, "node_modules/@types/lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==", + "version": "4.17.24", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", + "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==", "dev": true, "license": "MIT" }, @@ -2452,9 +2452,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.13", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.13.tgz", - "integrity": "sha512-BL2sTuHOdy0YT1lYieUxTw/QMtPBC3pmlJC6xk8BBYVv6vcw3SGdKemQ+Xsx9ik2F/lYDO9tqsFQH1r9PFuHKw==", + "version": "2.10.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.19.tgz", + "integrity": "sha512-qCkNLi2sfBOn8XhZQ0FXsT1Ki/Yo5P90hrkRamVFRS7/KV9hpfA4HkoWNU152+8w0zPjnxo5psx5NL3PSGgv5g==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2479,9 +2479,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "dev": true, "license": "MIT", "dependencies": { @@ -2577,15 +2577,15 @@ } }, "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" }, "engines": { @@ -2665,9 +2665,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001784", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001784.tgz", - "integrity": "sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw==", + "version": "1.0.30001788", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", + "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==", "dev": true, "funding": [ { @@ -3443,9 +3443,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.331", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.331.tgz", - "integrity": "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==", + "version": "1.5.339", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.339.tgz", + "integrity": "sha512-Is+0BBHJ4NrdpAYiperrmp53pLywG/yV/6lIMTAnhxvzj/Cmn5Q/ogSHC6AKe7X+8kPLxxFk0cs5oc/3j/fxIg==", "dev": true, "license": "ISC" }, @@ -6179,9 +6179,9 @@ } }, "node_modules/marked": { - "version": "17.0.5", - "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.5.tgz", - "integrity": "sha512-6hLvc0/JEbRjRgzI6wnT2P1XuM1/RrrDEX0kPt0N7jGm1133g6X7DlxFasUIx+72aKAr904GTxhSLDrd5DIlZg==", + "version": "17.0.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.6.tgz", + "integrity": "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -9742,9 +9742,9 @@ } }, "node_modules/qs": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", - "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -10153,12 +10153,13 @@ } }, "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "dev": true, "license": "MIT", "dependencies": { + "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" @@ -10933,14 +10934,14 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -11592,14 +11593,14 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" diff --git a/package.json b/package.json index e1b45c5..edebe97 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@contentstack/datasync-manager", "author": "Contentstack LLC ", - "version": "2.4.0-beta.2", + "version": "2.4.0-beta.3", "description": "The primary module of Contentstack DataSync. Syncs Contentstack data with your server using Contentstack Sync API", "main": "dist/index.js", "dependencies": { @@ -9,17 +9,17 @@ "debug": "^4.4.3", "dns-socket": "^4.2.2", "lodash": "^4.18.1", - "marked": "^17.0.5", + "marked": "^17.0.6", "write-file-atomic": "7.0.1" }, "devDependencies": { "@semantic-release/commit-analyzer": "^9.0.2", "@semantic-release/git": "^10.0.1", - "@semantic-release/npm": "^12.0.1", + "@semantic-release/npm": "^12.0.2", "@semantic-release/release-notes-generator": "^10.0.3", "@types/debug": "0.0.31", "@types/jest": "23.3.14", - "@types/lodash": "4.17.15", + "@types/lodash": "4.17.24", "@types/marked": "^4.3.2", "@types/mkdirp": "0.5.2", "@types/nock": "9.3.1", @@ -48,6 +48,9 @@ "start": "dist", "tslint": "npx tslint -c tslint.json 'src/**/*.ts' --fix", "test": "PLUGIN_PATH=./test/dummy jest --colors --coverage --verbose", + "mock:sync-api": "node scripts/sync-api-mock-server/server.js", + "mock:run-all-scenarios": "node scripts/sync-api-mock-server/run-all-scenarios.js", + "demo:mock": "node scripts/demo-with-mock.js", "lint": "eslint", "semantic-release": "semantic-release", "husky-check": "npx husky && chmod +x .husky/pre-commit" diff --git a/src/api.ts b/src/api.ts index c6d0595..0b9fddc 100644 --- a/src/api.ts +++ b/src/api.ts @@ -225,7 +225,9 @@ export const get = (req, RETRY = 1) => { httpRequest.setTimeout(options.timeout, () => { debug(MESSAGES.API.REQUEST_TIMEOUT(options.path)) httpRequest.destroy() - reject(new Error('Request timeout')) + const timeoutError: any = new Error('Request timeout') + timeoutError.code = 'ETIMEDOUT' + reject(timeoutError) }) // Enhanced error handling for network and connection errors diff --git a/src/core/index.ts b/src/core/index.ts index 711a408..b0c4bef 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -212,9 +212,16 @@ const check = async () => { const sync = async () => { try { debug(MESSAGES.SYNC_CORE.SYNC_STARTED); - const tokenObject = await getToken(); + let tokenObject: IToken + try { + tokenObject = await getToken() as IToken + } catch (tokenError) { + // check() sets SQ before sync(); fire() is never entered — release the gate + flag.SQ = false + throw tokenError + } debug(MESSAGES.SYNC_CORE.SYNC_TOKEN_OBJECT, tokenObject); - const token: IToken = (tokenObject as IToken) + const token: IToken = tokenObject const request: any = { qs: { environment: process.env.SYNC_ENV || Contentstack.environment || 'development', @@ -251,7 +258,7 @@ export const unlock = (refire?: boolean) => { .catch(flag.requestCache.reject) } } - check() + return check() } /** @@ -373,8 +380,6 @@ const fire = (req: IApiRequest) => { if (parsedError.error_code === 141) { logger.error(MESSAGES.SYNC_CORE.OUTDATED_SYNC_TOKEN) logger.info(MESSAGES.SYNC_CORE.SYNC_TOKEN_RENEWAL) - // Reset flag so next webhook notification can trigger a fresh sync - flag.SQ = false // Reset sync_token so next sync starts fresh with init=true Contentstack.sync_token = undefined } @@ -382,9 +387,8 @@ const fire = (req: IApiRequest) => { // Not a JSON error or not Error 141, continue with normal handling } - if (netConnectivityIssues(error)) { - flag.SQ = false - } + // Any failed sync API attempt must release the gate so the next notify/poke can run + flag.SQ = false return reject(error) }) diff --git a/src/core/inet.ts b/src/core/inet.ts index 424e228..4573491 100644 --- a/src/core/inet.ts +++ b/src/core/inet.ts @@ -81,10 +81,23 @@ export const checkNetConnectivity = () => { } export const netConnectivityIssues = (error) => { - // Include socket hang up and connection reset errors as network connectivity issues - const networkErrorCodes = ['ENOTFOUND', 'ETIMEDOUT', 'ECONNRESET', 'EPIPE', 'EHOSTUNREACH'] - - if (networkErrorCodes.includes(error.code) || error.message?.includes('socket hang up')) { + // Align with retryable codes in api.ts plus client-side timeout (no .code on Request timeout) + const networkErrorCodes = [ + 'ENOTFOUND', + 'ETIMEDOUT', + 'ECONNRESET', + 'EPIPE', + 'EHOSTUNREACH', + 'ECONNREFUSED', + 'ENETUNREACH', + 'EAI_AGAIN', + ] + const msg = typeof error?.message === 'string' ? error.message : '' + + if (networkErrorCodes.includes(error?.code)) { + return true + } + if (msg.includes('socket hang up') || msg.includes('Request timeout')) { return true } diff --git a/test/api.ts b/test/api.ts index 161de39..733588c 100644 --- a/test/api.ts +++ b/test/api.ts @@ -150,3 +150,5 @@ describe('test api - get()', () => { // }) }) + +// Socket timeout + ETIMEDOUT: covered by test/core/inet.ts, scripts/sync-api-mock-server (MOCK_SCENARIO=hang). diff --git a/test/core/index.ts b/test/core/index.ts index 4c51903..b2ea338 100644 --- a/test/core/index.ts +++ b/test/core/index.ts @@ -13,6 +13,7 @@ describe('check lock-unlock', () => { }) test('lock-unlock', () => { expect(lock()).toBeUndefined() - expect(unlock(true)).toBeUndefined() + // unlock(true) would run check() -> sync() and requires init(); use unlock(false) to only exercise the gate + expect(unlock(false)).toBeUndefined() }) }) diff --git a/test/core/inet.ts b/test/core/inet.ts index 66cb183..8468b01 100644 --- a/test/core/inet.ts +++ b/test/core/inet.ts @@ -20,4 +20,26 @@ describe('# inet', () => { expect(netConnectivityIssues({})) .toEqual(false) }) + + describe('netConnectivityIssues', () => { + test('returns false for unrelated errors', () => { + expect(netConnectivityIssues({ message: 'business logic failed' })).toBe(false) + expect(netConnectivityIssues({ code: 'ICTC' })).toBe(false) + }) + + test('returns true for Request timeout message', () => { + expect(netConnectivityIssues({ message: 'Request timeout' })).toBe(true) + }) + + test('returns true for ETIMEDOUT and other retryable codes', () => { + expect(netConnectivityIssues({ code: 'ETIMEDOUT' })).toBe(true) + expect(netConnectivityIssues({ code: 'ECONNREFUSED' })).toBe(true) + expect(netConnectivityIssues({ code: 'ENETUNREACH' })).toBe(true) + expect(netConnectivityIssues({ code: 'EAI_AGAIN' })).toBe(true) + }) + + test('returns true for socket hang up in message', () => { + expect(netConnectivityIssues({ message: 'socket hang up' })).toBe(true) + }) + }) }) diff --git a/test/core/sync-resume.ts b/test/core/sync-resume.ts new file mode 100644 index 0000000..a72e153 --- /dev/null +++ b/test/core/sync-resume.ts @@ -0,0 +1,50 @@ +/*! + * Ensures a failed sync (e.g. Request timeout) does not block a later poke(). + */ +import { join } from 'path' +import { cloneDeep, merge } from 'lodash' +import { setConfig } from '../../src' +import { config as internalConfig } from '../../src/config' +import { init as initCore, poke } from '../../src/core' +import { buildConfigPaths } from '../../src/util/build-paths' +import { setLogger } from '../../src/util/logger' +import { config as mockConfig } from '../dummy/config' +import { assetConnector, contentConnector } from '../dummy/connector-listener-instances' +import { get, init as initApi } from '../../src/api' + +jest.mock('../../src/api', () => { + const actual = jest.requireActual('../../src/api') + + return { + ...actual, + get: jest.fn(), + } +}) + +const configs: any = cloneDeep(merge({}, internalConfig, mockConfig)) + +describe('poke after sync failure', () => { + beforeAll(async () => { + configs.paths = buildConfigPaths() + const testRoot = join(process.cwd(), 'test', 'dummy') + configs.paths.checkpoint = join(testRoot, '.checkpoint') + configs.paths.token = join(testRoot, '.token') + setConfig(configs) + setLogger() + initApi(configs.contentstack) + ;(get as jest.Mock).mockResolvedValue({ items: [], sync_token: 'init-token' }) + await initCore(contentConnector, assetConnector) + }) + + test('second poke calls get again after Request timeout', async () => { + const g = get as jest.Mock + g.mockReset() + g.mockRejectedValueOnce(Object.assign(new Error('Request timeout'), { code: 'ETIMEDOUT' })) + g.mockResolvedValueOnce({ items: [], sync_token: 'recover-token' }) + + await expect(poke()).rejects.toThrow('Request timeout') + await poke() + + expect(g.mock.calls.length).toBe(2) + }) +}) diff --git a/test/core/sync.ts b/test/core/sync.ts index e549378..8098dd6 100644 --- a/test/core/sync.ts +++ b/test/core/sync.ts @@ -1,19 +1,37 @@ +import { join } from 'path' import { cloneDeep, merge } from 'lodash' import { setConfig } from '../../src' import { config as internalConfig } from '../../src/config' -import { poke } from '../../src/core' +import { init as initCore, poke } from '../../src/core' import { buildConfigPaths } from '../../src/util/build-paths' import { setLogger } from '../../src/util/logger' import { config } from '../dummy/config' +import { assetConnector, contentConnector } from '../dummy/connector-listener-instances' +import { get, init as initApi } from '../../src/api' + +jest.mock('../../src/api', () => { + const actual = jest.requireActual('../../src/api') + + return { + ...actual, + get: jest.fn(), + } +}) const configs: any = cloneDeep(merge({}, internalConfig, config)) -beforeAll(() => { - setConfig(configs) +beforeAll(async () => { configs.paths = buildConfigPaths() + const testRoot = join(process.cwd(), 'test', 'dummy') + configs.paths.checkpoint = join(testRoot, '.checkpoint') + configs.paths.token = join(testRoot, '.token') + setConfig(configs) setLogger() + initApi(configs.contentstack) + ;(get as jest.Mock).mockResolvedValue({ items: [], sync_token: 'poke-test' }) + await initCore(contentConnector, assetConnector) }) -test('Poke should work without errors', () => { - expect(poke()).toBeUndefined() +test('Poke should work without errors', async () => { + await expect(poke()).resolves.toBeUndefined() }) From 86ef1d6e71b8eac1eed92320423e5dae3352308e Mon Sep 17 00:00:00 2001 From: raj pandey Date: Thu, 16 Apr 2026 15:56:20 +0530 Subject: [PATCH 2/2] Removed any type from error --- src/api.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/api.ts b/src/api.ts index 0b9fddc..515cbfe 100644 --- a/src/api.ts +++ b/src/api.ts @@ -225,8 +225,9 @@ export const get = (req, RETRY = 1) => { httpRequest.setTimeout(options.timeout, () => { debug(MESSAGES.API.REQUEST_TIMEOUT(options.path)) httpRequest.destroy() - const timeoutError: any = new Error('Request timeout') - timeoutError.code = 'ETIMEDOUT' + const timeoutError = Object.assign(new Error('Request timeout'), { + code: 'ETIMEDOUT', + }) as Error & { code: string } reject(timeoutError) })