From 3eed509bd3cec22e74f46bab492b5fa1de466204 Mon Sep 17 00:00:00 2001 From: Levko Kravets Date: Tue, 14 May 2024 21:28:30 +0300 Subject: [PATCH] Convert E2E tests to Typescript (#257) * Convert E2E tests to typescript Signed-off-by: Levko Kravets * Adjust Typescript configs for building and linting scenarios Signed-off-by: Levko Kravets * Update tests Signed-off-by: Levko Kravets --------- Signed-off-by: Levko Kravets --- .eslintrc | 7 + package-lock.json | 243 +++++++++++------- package.json | 10 +- tests/e2e/.mocharc.js | 2 +- tests/e2e/{arrow.test.js => arrow.test.ts} | 47 ++-- ...ed_fetch.test.js => batched_fetch.test.ts} | 42 +-- ...{cloudfetch.test.js => cloudfetch.test.ts} | 34 ++- ...{data_types.test.js => data_types.test.ts} | 33 +-- .../{iterators.test.js => iterators.test.ts} | 20 +- tests/e2e/{proxy.test.js => proxy.test.ts} | 46 ++-- ...eters.test.js => query_parameters.test.ts} | 15 +- ...tion.test.js => staging_ingestion.test.ts} | 33 ++- .../{timeouts.test.js => timeouts.test.ts} | 20 +- tests/e2e/utils/config.js | 26 -- tests/e2e/utils/config.ts | 62 +++++ tests/e2e/utils/logger.js | 22 -- tsconfig.build.json | 8 + tsconfig.json | 15 +- 18 files changed, 413 insertions(+), 272 deletions(-) rename tests/e2e/{arrow.test.js => arrow.test.ts} (77%) rename tests/e2e/{batched_fetch.test.js => batched_fetch.test.ts} (71%) rename tests/e2e/{cloudfetch.test.js => cloudfetch.test.ts} (79%) rename tests/e2e/{data_types.test.js => data_types.test.ts} (93%) rename tests/e2e/{iterators.test.js => iterators.test.ts} (78%) rename tests/e2e/{proxy.test.js => proxy.test.ts} (62%) rename tests/e2e/{query_parameters.test.js => query_parameters.test.ts} (94%) rename tests/e2e/{staging_ingestion.test.js => staging_ingestion.test.ts} (86%) rename tests/e2e/{timeouts.test.js => timeouts.test.ts} (65%) delete mode 100644 tests/e2e/utils/config.js create mode 100644 tests/e2e/utils/config.ts delete mode 100644 tests/e2e/utils/logger.js create mode 100644 tsconfig.build.json diff --git a/.eslintrc b/.eslintrc index 33499af2..c027bbd0 100644 --- a/.eslintrc +++ b/.eslintrc @@ -24,6 +24,13 @@ } ] } + }, + { + "files": ["*.test.js", "*.test.ts"], + "rules": { + "no-unused-expressions": "off", + "@typescript-eslint/no-unused-expressions": "off" + } } ] } diff --git a/package-lock.json b/package-lock.json index e3a63561..e3cca1b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,11 +22,13 @@ }, "devDependencies": { "@types/chai": "^4.3.14", + "@types/http-proxy": "^1.17.14", "@types/lz4": "^0.6.4", "@types/mocha": "^10.0.6", "@types/node": "^18.11.9", "@types/node-fetch": "^2.6.4", "@types/node-int64": "^0.4.29", + "@types/sinon": "^17.0.3", "@types/thrift": "^0.10.11", "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^5.44.0", @@ -44,7 +46,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "prettier": "^2.8.4", - "sinon": "^14.0.0", + "sinon": "^17.0.1", "ts-node": "^10.9.2", "typescript": "^4.9.3" }, @@ -867,34 +869,43 @@ } }, "node_modules/@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "dependencies": { "type-detect": "4.0.8" } }, "node_modules/@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.7.0" + "@sinonjs/commons": "^3.0.0" } }, "node_modules/@sinonjs/samsam": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", - "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.6.0", + "@sinonjs/commons": "^2.0.0", "lodash.get": "^4.4.2", "type-detect": "^4.0.8" } }, + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, "node_modules/@sinonjs/text-encoding": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", @@ -946,6 +957,15 @@ "resolved": "https://registry.npmjs.org/@types/command-line-usage/-/command-line-usage-5.0.2.tgz", "integrity": "sha512-n7RlEEJ+4x4TS7ZQddTmNSxP+zziEG0TNsMfiRIxcIVXt71ENJ9ojeXmGO3wPoTdn7pJcU2xc3CJYMktNT6DPg==" }, + "node_modules/@types/http-proxy": { + "version": "1.17.14", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", + "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -1015,6 +1035,21 @@ "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", "dev": true }, + "node_modules/@types/sinon": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.3.tgz", + "integrity": "sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==", + "dev": true, + "dependencies": { + "@types/sinonjs__fake-timers": "*" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", + "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", + "dev": true + }, "node_modules/@types/thrift": { "version": "0.10.11", "resolved": "https://registry.npmjs.org/@types/thrift/-/thrift-0.10.11.tgz", @@ -3806,12 +3841,6 @@ "node": ">=8" } }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "dev": true - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -4035,9 +4064,9 @@ } }, "node_modules/just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", "dev": true }, "node_modules/kuler": { @@ -4409,16 +4438,16 @@ } }, "node_modules/nise": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", - "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", + "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": ">=5", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" } }, "node_modules/node-fetch": { @@ -4979,13 +5008,10 @@ "dev": true }, "node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dev": true, - "dependencies": { - "isarray": "0.0.1" - } + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", + "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", + "dev": true }, "node_modules/path-type": { "version": "4.0.0", @@ -5487,16 +5513,16 @@ } }, "node_modules/sinon": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.0.tgz", - "integrity": "sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz", + "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^9.1.2", - "@sinonjs/samsam": "^6.1.1", - "diff": "^5.0.0", - "nise": "^5.1.1", + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.5", "supports-color": "^7.2.0" }, "funding": { @@ -5504,6 +5530,15 @@ "url": "https://opencollective.com/sinon" } }, + "node_modules/sinon/node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -7007,32 +7042,43 @@ } }, "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "requires": { "type-detect": "4.0.8" } }, "@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", "dev": true, "requires": { - "@sinonjs/commons": "^1.7.0" + "@sinonjs/commons": "^3.0.0" } }, "@sinonjs/samsam": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.1.1.tgz", - "integrity": "sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", "dev": true, "requires": { - "@sinonjs/commons": "^1.6.0", + "@sinonjs/commons": "^2.0.0", "lodash.get": "^4.4.2", "type-detect": "^4.0.8" + }, + "dependencies": { + "@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + } } }, "@sinonjs/text-encoding": { @@ -7086,6 +7132,15 @@ "resolved": "https://registry.npmjs.org/@types/command-line-usage/-/command-line-usage-5.0.2.tgz", "integrity": "sha512-n7RlEEJ+4x4TS7ZQddTmNSxP+zziEG0TNsMfiRIxcIVXt71ENJ9ojeXmGO3wPoTdn7pJcU2xc3CJYMktNT6DPg==" }, + "@types/http-proxy": { + "version": "1.17.14", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", + "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -7155,6 +7210,21 @@ "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", "dev": true }, + "@types/sinon": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.3.tgz", + "integrity": "sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==", + "dev": true, + "requires": { + "@types/sinonjs__fake-timers": "*" + } + }, + "@types/sinonjs__fake-timers": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", + "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", + "dev": true + }, "@types/thrift": { "version": "0.10.11", "resolved": "https://registry.npmjs.org/@types/thrift/-/thrift-0.10.11.tgz", @@ -9146,12 +9216,6 @@ "is-docker": "^2.0.0" } }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "dev": true - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -9326,9 +9390,9 @@ } }, "just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", "dev": true }, "kuler": { @@ -9626,16 +9690,16 @@ "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==" }, "nise": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.1.tgz", - "integrity": "sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", + "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", "dev": true, "requires": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": ">=5", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" } }, "node-fetch": { @@ -10055,13 +10119,10 @@ "dev": true }, "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dev": true, - "requires": { - "isarray": "0.0.1" - } + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", + "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", + "dev": true }, "path-type": { "version": "4.0.0", @@ -10417,17 +10478,25 @@ } }, "sinon": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-14.0.0.tgz", - "integrity": "sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz", + "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==", "dev": true, "requires": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^9.1.2", - "@sinonjs/samsam": "^6.1.1", - "diff": "^5.0.0", - "nise": "^5.1.1", + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.5", "supports-color": "^7.2.0" + }, + "dependencies": { + "diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true + } } }, "slash": { diff --git a/package.json b/package.json index 48aa580c..f379670a 100644 --- a/package.json +++ b/package.json @@ -16,12 +16,12 @@ "e2e": "nyc --reporter=lcov --report-dir=${NYC_REPORT_DIR:-coverage_e2e} mocha --config tests/e2e/.mocharc.js", "test": "nyc --reporter=lcov --report-dir=${NYC_REPORT_DIR:-coverage_unit} mocha --config tests/unit/.mocharc.js", "update-version": "node bin/update-version.js && prettier --write ./lib/version.ts", - "build": "npm run update-version && tsc", - "watch": "tsc -w", + "build": "npm run update-version && tsc --project tsconfig.build.json", + "watch": "tsc --project tsconfig.build.json --watch", "type-check": "tsc --noEmit", "prettier": "prettier . --check", "prettier:fix": "prettier . --write", - "lint": "eslint lib/** --ext .js,.ts", + "lint": "eslint lib/** tests/e2e/** --ext .js,.ts", "lint:fix": "eslint lib/** --ext .js,.ts --fix" }, "repository": { @@ -48,11 +48,13 @@ "license": "Apache 2.0", "devDependencies": { "@types/chai": "^4.3.14", + "@types/http-proxy": "^1.17.14", "@types/lz4": "^0.6.4", "@types/mocha": "^10.0.6", "@types/node": "^18.11.9", "@types/node-fetch": "^2.6.4", "@types/node-int64": "^0.4.29", + "@types/sinon": "^17.0.3", "@types/thrift": "^0.10.11", "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^5.44.0", @@ -70,7 +72,7 @@ "mocha": "^10.2.0", "nyc": "^15.1.0", "prettier": "^2.8.4", - "sinon": "^14.0.0", + "sinon": "^17.0.1", "ts-node": "^10.9.2", "typescript": "^4.9.3" }, diff --git a/tests/e2e/.mocharc.js b/tests/e2e/.mocharc.js index bf412d3d..f410891b 100644 --- a/tests/e2e/.mocharc.js +++ b/tests/e2e/.mocharc.js @@ -1,6 +1,6 @@ 'use strict'; -const allSpecs = 'tests/e2e/**/*.test.js'; +const allSpecs = 'tests/e2e/**/*.test.ts'; const argvSpecs = process.argv.slice(4); diff --git a/tests/e2e/arrow.test.js b/tests/e2e/arrow.test.ts similarity index 77% rename from tests/e2e/arrow.test.js rename to tests/e2e/arrow.test.ts index f1589433..12d87e27 100644 --- a/tests/e2e/arrow.test.js +++ b/tests/e2e/arrow.test.ts @@ -1,19 +1,22 @@ -const { expect } = require('chai'); -const sinon = require('sinon'); -const config = require('./utils/config'); -const logger = require('./utils/logger')(config.logger); -const { DBSQLClient } = require('../../lib'); -const ArrowResultHandler = require('../../lib/result/ArrowResultHandler').default; -const ArrowResultConverter = require('../../lib/result/ArrowResultConverter').default; -const ResultSlicer = require('../../lib/result/ResultSlicer').default; +import { expect } from 'chai'; +import sinon from 'sinon'; +import { DBSQLClient } from '../../lib'; +import { ClientConfig } from '../../lib/contracts/IClientContext'; +import IDBSQLSession from '../../lib/contracts/IDBSQLSession'; +import ArrowResultHandler from '../../lib/result/ArrowResultHandler'; +import ArrowResultConverter from '../../lib/result/ArrowResultConverter'; +import ResultSlicer from '../../lib/result/ResultSlicer'; + +import config from './utils/config'; const fixtures = require('../fixtures/compatibility'); const { expected: expectedColumn } = require('../fixtures/compatibility/column'); const { expected: expectedArrow } = require('../fixtures/compatibility/arrow'); const { expected: expectedArrowNativeTypes } = require('../fixtures/compatibility/arrow_native_types'); + const { fixArrowResult } = fixtures; -async function openSession(customConfig) { +async function openSession(customConfig: Partial = {}) { const client = new DBSQLClient(); const clientConfig = client.getConfig(); @@ -29,23 +32,23 @@ async function openSession(customConfig) { }); return connection.openSession({ - initialCatalog: config.database[0], - initialSchema: config.database[1], + initialCatalog: config.catalog, + initialSchema: config.schema, }); } -async function execute(session, statement) { +async function execute(session: IDBSQLSession, statement: string) { const operation = await session.executeStatement(statement); const result = await operation.fetchAll(); await operation.close(); return result; } -async function deleteTable(session, tableName) { +async function deleteTable(session: IDBSQLSession, tableName: string) { await execute(session, `DROP TABLE IF EXISTS ${tableName}`); } -async function initializeTable(session, tableName) { +async function initializeTable(session: IDBSQLSession, tableName: string) { await deleteTable(session, tableName); const createTable = fixtures.createTableSql.replace(/\$\{table_name\}/g, tableName); @@ -58,15 +61,15 @@ async function initializeTable(session, tableName) { describe('Arrow support', () => { const tableName = `dbsql_nodejs_sdk_e2e_arrow_${config.tableSuffix}`; - function createTest(testBody, customConfig) { + function createTest( + testBody: (session: IDBSQLSession) => void | Promise, + customConfig: Partial = {}, + ) { return async () => { const session = await openSession(customConfig); try { await initializeTable(session, tableName); await testBody(session); - } catch (error) { - logger(error); - throw error; } finally { await deleteTable(session, tableName); await session.close(); @@ -82,6 +85,7 @@ describe('Arrow support', () => { const result = await operation.fetchAll(); expect(result).to.deep.equal(expectedColumn); + // @ts-expect-error TS2339: Property getResultHandler does not exist on type IOperation const resultHandler = await operation.getResultHandler(); expect(resultHandler).to.be.instanceof(ResultSlicer); expect(resultHandler.source).to.be.not.instanceof(ArrowResultConverter); @@ -103,6 +107,7 @@ describe('Arrow support', () => { const result = await operation.fetchAll(); expect(fixArrowResult(result)).to.deep.equal(expectedArrow); + // @ts-expect-error TS2339: Property getResultHandler does not exist on type IOperation const resultHandler = await operation.getResultHandler(); expect(resultHandler).to.be.instanceof(ResultSlicer); expect(resultHandler.source).to.be.instanceof(ArrowResultConverter); @@ -126,6 +131,7 @@ describe('Arrow support', () => { const result = await operation.fetchAll(); expect(fixArrowResult(result)).to.deep.equal(expectedArrowNativeTypes); + // @ts-expect-error TS2339: Property getResultHandler does not exist on type IOperation const resultHandler = await operation.getResultHandler(); expect(resultHandler).to.be.instanceof(ResultSlicer); expect(resultHandler.source).to.be.instanceof(ArrowResultConverter); @@ -155,16 +161,20 @@ describe('Arrow support', () => { `); // We use some internals here to check that server returned response with multiple batches + // @ts-expect-error TS2339: Property getResultHandler does not exist on type IOperation const resultHandler = await operation.getResultHandler(); expect(resultHandler).to.be.instanceof(ResultSlicer); expect(resultHandler.source).to.be.instanceof(ArrowResultConverter); expect(resultHandler.source.source).to.be.instanceof(ArrowResultHandler); + // @ts-expect-error TS2339: Property _data does not exist on type IOperation sinon.spy(operation._data, 'fetchNext'); const result = await resultHandler.fetchNext({ limit: rowsCount }); + // @ts-expect-error TS2339: Property _data does not exist on type IOperation expect(operation._data.fetchNext.callCount).to.be.eq(1); + // @ts-expect-error TS2339: Property _data does not exist on type IOperation const rawData = await operation._data.fetchNext.firstCall.returnValue; // We don't know exact count of batches returned, it depends on server's configuration, // but with much enough rows there should be more than one result batch @@ -181,6 +191,7 @@ describe('Arrow support', () => { const result = await operation.fetchAll(); expect(fixArrowResult(result)).to.deep.equal(expectedArrow); + // @ts-expect-error TS2339: Property getResultHandler does not exist on type IOperation const resultHandler = await operation.getResultHandler(); expect(resultHandler).to.be.instanceof(ResultSlicer); expect(resultHandler.source).to.be.instanceof(ArrowResultConverter); diff --git a/tests/e2e/batched_fetch.test.js b/tests/e2e/batched_fetch.test.ts similarity index 71% rename from tests/e2e/batched_fetch.test.js rename to tests/e2e/batched_fetch.test.ts index ec5cd51f..9dfd1203 100644 --- a/tests/e2e/batched_fetch.test.js +++ b/tests/e2e/batched_fetch.test.ts @@ -1,10 +1,11 @@ -const { expect } = require('chai'); -const sinon = require('sinon'); -const config = require('./utils/config'); -const logger = require('./utils/logger')(config.logger); -const { DBSQLClient } = require('../../lib'); +import { expect } from 'chai'; +import sinon from 'sinon'; +import { DBSQLClient } from '../../lib'; +import { ClientConfig } from '../../lib/contracts/IClientContext'; -async function openSession(customConfig) { +import config from './utils/config'; + +async function openSession(customConfig: Partial = {}) { const client = new DBSQLClient(); const clientConfig = client.getConfig(); @@ -20,8 +21,8 @@ async function openSession(customConfig) { }); return connection.openSession({ - initialCatalog: config.database[0], - initialSchema: config.database[1], + initialCatalog: config.catalog, + initialSchema: config.schema, }); } @@ -34,15 +35,15 @@ describe('Data fetching', () => { it('fetch chunks should return a max row set of chunkSize', async () => { const session = await openSession({ arrowEnabled: false }); + // @ts-expect-error TS2339: Property context does not exist on type IDBSQLSession sinon.spy(session.context.driver, 'fetchResults'); try { // set `maxRows` to null to disable direct results so all the data are fetched through `driver.fetchResults` const operation = await session.executeStatement(query, { maxRows: null }); - let chunkedOp = await operation - .fetchChunk({ maxRows: 10, disableBuffering: true }) - .catch((error) => logger(error)); - expect(chunkedOp.length).to.be.equal(10); + const chunk = await operation.fetchChunk({ maxRows: 10, disableBuffering: true }); + expect(chunk.length).to.be.equal(10); // we explicitly requested only one chunk + // @ts-expect-error TS2339: Property context does not exist on type IDBSQLSession expect(session.context.driver.fetchResults.callCount).to.equal(1); } finally { await session.close(); @@ -62,8 +63,11 @@ describe('Data fetching', () => { let chunkCount = 0; while (hasMoreRows) { - let chunkedOp = await operation.fetchChunk({ maxRows: 300 }); + // eslint-disable-next-line no-await-in-loop + const chunkedOp = await operation.fetchChunk({ maxRows: 300 }); chunkCount += 1; + + // eslint-disable-next-line no-await-in-loop hasMoreRows = await operation.hasMoreRows(); const isLastChunk = !hasMoreRows; @@ -78,13 +82,15 @@ describe('Data fetching', () => { it('fetch all should fetch all records', async () => { const session = await openSession({ arrowEnabled: false }); + // @ts-expect-error TS2339: Property context does not exist on type IDBSQLSession sinon.spy(session.context.driver, 'fetchResults'); try { // set `maxRows` to null to disable direct results so all the data are fetched through `driver.fetchResults` const operation = await session.executeStatement(query, { maxRows: null }); - let all = await operation.fetchAll({ maxRows: 200 }); + const all = await operation.fetchAll({ maxRows: 200 }); expect(all.length).to.be.equal(1000); // 1000/200 = 5 chunks + one extra request to ensure that there's no more data + // @ts-expect-error TS2339: Property context does not exist on type IDBSQLSession expect(session.context.driver.fetchResults.callCount).to.equal(6); } finally { await session.close(); @@ -93,13 +99,15 @@ describe('Data fetching', () => { it('should fetch all records if they fit within directResults response', async () => { const session = await openSession({ arrowEnabled: false }); + // @ts-expect-error TS2339: Property context does not exist on type IDBSQLSession sinon.spy(session.context.driver, 'fetchResults'); try { // here `maxRows` enables direct results with limit of the first batch const operation = await session.executeStatement(query, { maxRows: 1000 }); - let all = await operation.fetchAll(); + const all = await operation.fetchAll(); expect(all.length).to.be.equal(1000); // all the data returned immediately from direct results, so no additional requests + // @ts-expect-error TS2339: Property context does not exist on type IDBSQLSession expect(session.context.driver.fetchResults.callCount).to.equal(0); } finally { await session.close(); @@ -108,15 +116,17 @@ describe('Data fetching', () => { it('should fetch all records if only part of them fit within directResults response', async () => { const session = await openSession({ arrowEnabled: false }); + // @ts-expect-error TS2339: Property context does not exist on type IDBSQLSession sinon.spy(session.context.driver, 'fetchResults'); try { // here `maxRows` enables direct results with limit of the first batch const operation = await session.executeStatement(query, { maxRows: 200 }); // here `maxRows` sets limit for `driver.fetchResults` - let all = await operation.fetchAll({ maxRows: 200 }); + const all = await operation.fetchAll({ maxRows: 200 }); expect(all.length).to.be.equal(1000); // 1 chunk returned immediately from direct results + 4 remaining chunks + one extra chunk to ensure // that there's no more data + // @ts-expect-error TS2339: Property context does not exist on type IDBSQLSession expect(session.context.driver.fetchResults.callCount).to.equal(5); } finally { await session.close(); diff --git a/tests/e2e/cloudfetch.test.js b/tests/e2e/cloudfetch.test.ts similarity index 79% rename from tests/e2e/cloudfetch.test.js rename to tests/e2e/cloudfetch.test.ts index 04416d46..5ac46296 100644 --- a/tests/e2e/cloudfetch.test.js +++ b/tests/e2e/cloudfetch.test.ts @@ -1,12 +1,14 @@ -const { expect } = require('chai'); -const sinon = require('sinon'); -const config = require('./utils/config'); -const { DBSQLClient } = require('../../lib'); -const CloudFetchResultHandler = require('../../lib/result/CloudFetchResultHandler').default; -const ArrowResultConverter = require('../../lib/result/ArrowResultConverter').default; -const ResultSlicer = require('../../lib/result/ResultSlicer').default; - -async function openSession(customConfig) { +import { expect } from 'chai'; +import sinon from 'sinon'; +import { DBSQLClient } from '../../lib'; +import { ClientConfig } from '../../lib/contracts/IClientContext'; +import CloudFetchResultHandler from '../../lib/result/CloudFetchResultHandler'; +import ArrowResultConverter from '../../lib/result/ArrowResultConverter'; +import ResultSlicer from '../../lib/result/ResultSlicer'; + +import config from './utils/config'; + +async function openSession(customConfig: Partial = {}) { const client = new DBSQLClient(); const clientConfig = client.getConfig(); @@ -22,8 +24,8 @@ async function openSession(customConfig) { }); return connection.openSession({ - initialCatalog: config.database[0], - initialSchema: config.database[1], + initialCatalog: config.catalog, + initialSchema: config.schema, }); } @@ -54,6 +56,7 @@ describe('CloudFetch', () => { await operation.finished(); // Check if we're actually getting data via CloudFetch + // @ts-expect-error TS2339: Property getResultHandler does not exist on type IOperation const resultHandler = await operation.getResultHandler(); expect(resultHandler).to.be.instanceof(ResultSlicer); expect(resultHandler.source).to.be.instanceof(ArrowResultConverter); @@ -69,10 +72,12 @@ describe('CloudFetch', () => { expect(cfResultHandler.pendingLinks.length).to.be.equal(0); expect(cfResultHandler.downloadTasks.length).to.be.equal(0); + // @ts-expect-error TS2339: Property _data does not exist on type IOperation sinon.spy(operation._data, 'fetchNext'); const chunk = await operation.fetchChunk({ maxRows: 100000, disableBuffering: true }); // Count links returned from server + // @ts-expect-error TS2339: Property _data does not exist on type IOperation const resultSet = await operation._data.fetchNext.firstCall.returnValue; const resultLinksCount = resultSet?.resultLinks?.length ?? 0; @@ -82,9 +87,11 @@ describe('CloudFetch', () => { expect(cfResultHandler.downloadTasks.length).to.be.equal(cloudFetchConcurrentDownloads - 1); let fetchedRowCount = chunk.length; + // eslint-disable-next-line no-await-in-loop while (await operation.hasMoreRows()) { - const chunk = await operation.fetchChunk({ maxRows: 100000, disableBuffering: true }); - fetchedRowCount += chunk.length; + // eslint-disable-next-line no-await-in-loop + const ch = await operation.fetchChunk({ maxRows: 100000, disableBuffering: true }); + fetchedRowCount += ch.length; } expect(fetchedRowCount).to.be.equal(queriedRowsCount); @@ -114,6 +121,7 @@ describe('CloudFetch', () => { await operation.finished(); // Check if we're actually getting data via CloudFetch + // @ts-expect-error TS2339: Property getResultHandler does not exist on type IOperation const resultHandler = await operation.getResultHandler(); expect(resultHandler).to.be.instanceof(ResultSlicer); expect(resultHandler.source).to.be.instanceof(ArrowResultConverter); diff --git a/tests/e2e/data_types.test.js b/tests/e2e/data_types.test.ts similarity index 93% rename from tests/e2e/data_types.test.js rename to tests/e2e/data_types.test.ts index 041e357e..fd8e6697 100644 --- a/tests/e2e/data_types.test.js +++ b/tests/e2e/data_types.test.ts @@ -1,10 +1,12 @@ -const { expect } = require('chai'); -const sinon = require('sinon'); -const config = require('./utils/config'); -const logger = require('./utils/logger')(config.logger); -const { DBSQLClient } = require('../../lib'); +import { expect } from 'chai'; +import sinon from 'sinon'; +import { DBSQLClient } from '../../lib'; +import { ClientConfig } from '../../lib/contracts/IClientContext'; +import IDBSQLSession from '../../lib/contracts/IDBSQLSession'; -async function openSession(customConfig) { +import config from './utils/config'; + +async function openSession(customConfig: Partial = {}) { const client = new DBSQLClient(); const clientConfig = client.getConfig(); @@ -20,21 +22,21 @@ async function openSession(customConfig) { }); return connection.openSession({ - initialCatalog: config.database[0], - initialSchema: config.database[1], + initialCatalog: config.catalog, + initialSchema: config.schema, }); } -const execute = async (session, statement) => { +const execute = async (session: IDBSQLSession, statement: string) => { const operation = await session.executeStatement(statement); const result = await operation.fetchAll(); await operation.close(); return result; }; -function removeTrailingMetadata(columns) { +function removeTrailingMetadata(columns: Array) { const result = []; - for (let i = 0; i < columns.length; i++) { + for (let i = 0; i < columns.length; i += 1) { const col = columns[i]; if (col.col_name === '') { break; @@ -187,9 +189,6 @@ describe('Data types', () => { dat: '2014-01-17', }, ]); - } catch (error) { - logger(error); - throw error; } finally { await execute(session, `DROP TABLE IF EXISTS ${table}`); await session.close(); @@ -231,9 +230,6 @@ describe('Data types', () => { month_interval: '0-1', }, ]); - } catch (error) { - logger(error); - throw error; } finally { await execute(session, `DROP TABLE IF EXISTS ${table}`); await session.close(); @@ -356,9 +352,6 @@ describe('Data types', () => { }, }, ]); - } catch (error) { - logger(error); - throw error; } finally { await execute(session, `DROP TABLE IF EXISTS ${table}`); await execute(session, `DROP TABLE IF EXISTS ${helperTable}`); diff --git a/tests/e2e/iterators.test.js b/tests/e2e/iterators.test.ts similarity index 78% rename from tests/e2e/iterators.test.js rename to tests/e2e/iterators.test.ts index ee69af24..aa0e4752 100644 --- a/tests/e2e/iterators.test.js +++ b/tests/e2e/iterators.test.ts @@ -1,9 +1,11 @@ -const { expect } = require('chai'); -const sinon = require('sinon'); -const config = require('./utils/config'); -const { DBSQLClient } = require('../../lib'); +import { expect } from 'chai'; +import sinon from 'sinon'; +import { DBSQLClient } from '../../lib'; +import { ClientConfig } from '../../lib/contracts/IClientContext'; -async function openSession(customConfig) { +import config from './utils/config'; + +async function openSession(customConfig: Partial) { const client = new DBSQLClient(); const clientConfig = client.getConfig(); @@ -19,12 +21,12 @@ async function openSession(customConfig) { }); return connection.openSession({ - initialCatalog: config.database[0], - initialSchema: config.database[1], + initialCatalog: config.catalog, + initialSchema: config.schema, }); } -function arrayChunks(arr, chunkSize) { +function arrayChunks(arr: Array, chunkSize: number): Array> { const result = []; while (arr.length > 0) { @@ -38,6 +40,7 @@ function arrayChunks(arr, chunkSize) { describe('Iterators', () => { it('should iterate over all chunks', async () => { const session = await openSession({ arrowEnabled: false }); + // @ts-expect-error TS2339: Property context does not exist on type IDBSQLSession sinon.spy(session.context.driver, 'fetchResults'); try { const expectedRowsCount = 10; @@ -65,6 +68,7 @@ describe('Iterators', () => { it('should iterate over all rows', async () => { const session = await openSession({ arrowEnabled: false }); + // @ts-expect-error TS2339: Property context does not exist on type IDBSQLSession sinon.spy(session.context.driver, 'fetchResults'); try { const expectedRowsCount = 10; diff --git a/tests/e2e/proxy.test.js b/tests/e2e/proxy.test.ts similarity index 62% rename from tests/e2e/proxy.test.js rename to tests/e2e/proxy.test.ts index ae67c110..cd6e8ae4 100644 --- a/tests/e2e/proxy.test.js +++ b/tests/e2e/proxy.test.ts @@ -1,22 +1,34 @@ -const { expect } = require('chai'); -const sinon = require('sinon'); -const httpProxy = require('http-proxy'); -const https = require('https'); -const config = require('./utils/config'); -const { DBSQLClient } = require('../../lib'); +import { expect } from 'chai'; +import sinon from 'sinon'; +import httpProxy from 'http-proxy'; +import { IncomingHttpHeaders, ClientRequest, OutgoingHttpHeaders } from 'http'; +import https from 'https'; +import { DBSQLClient } from '../../lib'; +import { ProxyOptions } from '../../lib/connection/contracts/IConnectionOptions'; + +import config from './utils/config'; class HttpProxyMock { - constructor(target, port) { - this.requests = []; + public readonly requests: Array<{ + method: string; + url: string; + requestHeaders: OutgoingHttpHeaders; + responseHeaders: IncomingHttpHeaders; + }> = []; + + public readonly config: ProxyOptions; + + public readonly target = `https://${config.host}`; + public readonly proxy: httpProxy; + + constructor(target: string, port: number) { this.config = { protocol: 'http', host: 'localhost', port, }; - this.target = `https://${config.host}`; - this.proxy = httpProxy.createServer({ target: this.target, agent: new https.Agent({ @@ -25,21 +37,23 @@ class HttpProxyMock { }); this.proxy.on('proxyRes', (proxyRes) => { - const req = proxyRes.req; + const req = (proxyRes as any).req as ClientRequest; this.requests.push({ method: req.method?.toUpperCase(), url: `${req.protocol}//${req.host}${req.path}`, requestHeaders: { ...req.getHeaders() }, - responseHeaders: proxyRes.headers, + responseHeaders: { ...proxyRes.headers }, }); }); this.proxy.listen(port); + // eslint-disable-next-line no-console console.log(`Proxy listening at ${this.config.host}:${this.config.port} -> ${this.target}`); } close() { this.proxy.close(() => { + // eslint-disable-next-line no-console console.log(`Proxy stopped at ${this.config.host}:${this.config.port}`); }); } @@ -53,7 +67,9 @@ describe('Proxy', () => { // Our proxy mock is HTTP -> HTTPS, but DBSQLClient is hard-coded to use HTTPS. // Here we override default behavior to make DBSQLClient work with HTTP proxy + // @ts-expect-error TS2341: Property getConnectionOptions is private const originalGetConnectionOptions = client.getConnectionOptions; + // @ts-expect-error TS2341: Property getConnectionOptions is private client.getConnectionOptions = (...args) => { const result = originalGetConnectionOptions.apply(client, args); result.https = false; @@ -71,9 +87,9 @@ describe('Proxy', () => { proxy: proxy.config, }); - const session = await connection.openSession({ - initialCatalog: config.database[0], - initialSchema: config.database[1], + await connection.openSession({ + initialCatalog: config.catalog, + initialSchema: config.schema, }); expect(proxy.requests.length).to.be.gte(1); diff --git a/tests/e2e/query_parameters.test.js b/tests/e2e/query_parameters.test.ts similarity index 94% rename from tests/e2e/query_parameters.test.js rename to tests/e2e/query_parameters.test.ts index 6de4f587..d9b1a470 100644 --- a/tests/e2e/query_parameters.test.js +++ b/tests/e2e/query_parameters.test.ts @@ -1,8 +1,9 @@ -const { expect, AssertionError } = require('chai'); -const Int64 = require('node-int64'); -const config = require('./utils/config'); -const { DBSQLClient, DBSQLParameter, DBSQLParameterType } = require('../../lib'); -const ParameterError = require('../../lib/errors/ParameterError').default; +import { expect, AssertionError } from 'chai'; +import Int64 from 'node-int64'; +import { DBSQLClient, DBSQLParameter, DBSQLParameterType } from '../../lib'; +import ParameterError from '../../lib/errors/ParameterError'; + +import config from './utils/config'; const openSession = async () => { const client = new DBSQLClient(); @@ -14,8 +15,8 @@ const openSession = async () => { }); return connection.openSession({ - initialCatalog: config.database[0], - initialSchema: config.database[1], + initialCatalog: config.catalog, + initialSchema: config.schema, }); }; diff --git a/tests/e2e/staging_ingestion.test.js b/tests/e2e/staging_ingestion.test.ts similarity index 86% rename from tests/e2e/staging_ingestion.test.js rename to tests/e2e/staging_ingestion.test.ts index 01aa0318..7721dc75 100644 --- a/tests/e2e/staging_ingestion.test.js +++ b/tests/e2e/staging_ingestion.test.ts @@ -1,25 +1,16 @@ -const { expect } = require('chai'); -const fs = require('fs'); -const path = require('path'); -const os = require('os'); -const uuid = require('uuid'); -const config = require('./utils/config'); -const { DBSQLClient } = require('../../lib'); -const StagingError = require('../../lib/errors/StagingError').default; +import { expect } from 'chai'; +import fs from 'fs'; +import path from 'path'; +import os from 'os'; +import * as uuid from 'uuid'; +import { DBSQLClient } from '../../lib'; +import StagingError from '../../lib/errors/StagingError'; -describe('Staging Test', () => { - const catalog = config.database[0]; - const schema = config.database[1]; - const volume = config.volume; +import config from './utils/config'; +describe('Staging Test', () => { const localPath = fs.mkdtempSync(path.join(os.tmpdir(), 'databricks-sql-tests-')); - before(() => { - expect(catalog).to.not.be.undefined; - expect(schema).to.not.be.undefined; - expect(volume).to.not.be.undefined; - }); - after(() => { fs.rmSync(localPath, { recursive: true, @@ -28,6 +19,8 @@ describe('Staging Test', () => { }); it('put staging data and receive it', async () => { + const { catalog, schema, volume } = config; + const client = new DBSQLClient(); await client.connect({ host: config.host, @@ -59,6 +52,8 @@ describe('Staging Test', () => { }); it('put staging data and remove it', async () => { + const { catalog, schema, volume } = config; + const client = new DBSQLClient(); await client.connect({ host: config.host, @@ -101,6 +96,8 @@ describe('Staging Test', () => { }); it('delete non-existent data', async () => { + const { catalog, schema, volume } = config; + const client = new DBSQLClient(); await client.connect({ host: config.host, diff --git a/tests/e2e/timeouts.test.js b/tests/e2e/timeouts.test.ts similarity index 65% rename from tests/e2e/timeouts.test.js rename to tests/e2e/timeouts.test.ts index a8b4b517..5f40cfbb 100644 --- a/tests/e2e/timeouts.test.js +++ b/tests/e2e/timeouts.test.ts @@ -1,9 +1,11 @@ -const { expect, AssertionError } = require('chai'); -const sinon = require('sinon'); -const config = require('./utils/config'); -const { DBSQLClient } = require('../../lib'); +import { expect, AssertionError } from 'chai'; +import sinon from 'sinon'; +import { DBSQLClient } from '../../lib'; +import { ClientConfig } from '../../lib/contracts/IClientContext'; -async function openSession(socketTimeout, customConfig) { +import config from './utils/config'; + +async function openSession(socketTimeout: number | undefined, customConfig: Partial = {}) { const client = new DBSQLClient(); const clientConfig = client.getConfig(); @@ -20,8 +22,8 @@ async function openSession(socketTimeout, customConfig) { }); return connection.openSession({ - initialCatalog: config.database[0], - initialSchema: config.database[1], + initialCatalog: config.catalog, + initialSchema: config.schema, }); } @@ -33,7 +35,7 @@ describe('Timeouts', () => { await openSession(undefined, { socketTimeout }); expect.fail('It should throw an error'); } catch (error) { - if (error instanceof AssertionError) { + if (error instanceof AssertionError || !(error instanceof Error)) { throw error; } expect(error.message).to.be.eq('Request timed out'); @@ -45,7 +47,7 @@ describe('Timeouts', () => { await openSession(socketTimeout); expect.fail('It should throw an error'); } catch (error) { - if (error instanceof AssertionError) { + if (error instanceof AssertionError || !(error instanceof Error)) { throw error; } expect(error.message).to.be.eq('Request timed out'); diff --git a/tests/e2e/utils/config.js b/tests/e2e/utils/config.js deleted file mode 100644 index bc3cff8e..00000000 --- a/tests/e2e/utils/config.js +++ /dev/null @@ -1,26 +0,0 @@ -let overrides = {}; -try { - overrides = require('./config.local'); -} catch (e) {} - -const catalog = process.env.E2E_CATALOG || undefined; -const schema = process.env.E2E_SCHEMA || undefined; - -// Create file named `config.local.js` in the same directory and override config there -module.exports = { - // Where to log: CONSOLE, FILE, QUIET - logger: 'CONSOLE', - // Host, like ****.cloud.databricks.com - host: process.env.E2E_HOST, - // API path: /sql/2.0/warehouses/**************** - path: process.env.E2E_PATH, - // Access token: dapi******************************** - token: process.env.E2E_ACCESS_TOKEN, - // Catalog and database to use for testing; specify both or leave array empty to use defaults - database: catalog || schema ? [catalog, schema] : [], - // Volume to use for testing - volume: process.env.E2E_VOLUME, - // Suffix used for tables that will be created during tests - tableSuffix: process.env.E2E_TABLE_SUFFIX, - ...overrides, -}; diff --git a/tests/e2e/utils/config.ts b/tests/e2e/utils/config.ts new file mode 100644 index 00000000..b960a887 --- /dev/null +++ b/tests/e2e/utils/config.ts @@ -0,0 +1,62 @@ +// Create file named `config.local.js` in the same directory and override config there + +interface E2EConfig { + // Host, like ****.cloud.databricks.com + host: string; + // API path: /sql/2.0/warehouses/**************** + path: string; + // Access token: dapi******************************** + token: string; + // Catalog and schema to use for testing + catalog: string; + schema: string; + // UC Volume to use for testing + volume: string; + // Suffix used for tables that will be created during tests + tableSuffix: string; +} + +function validateConfig(config: Partial): E2EConfig | never { + let isConfigValid = true; + + for (const key of Object.keys(config)) { + const value = config[key as keyof E2EConfig] ?? undefined; + if (value === undefined) { + isConfigValid = false; + // eslint-disable-next-line no-console + console.error(`\u26A0\uFE0F Config option '${key}' is missing`); + } + } + + if (!isConfigValid) { + // eslint-disable-next-line no-console + console.log(); + process.exit(1); + } + + // Now, when we checked all the options, we can safely cast to `E2EConfig` + return config as E2EConfig; +} + +function loadOverrides(): object { + try { + const result = require('./config.local'); // eslint-disable-line global-require + if (typeof result === 'object' && result !== null) { + return result; + } + } catch (e) { + // ignore + } + return {}; +} + +export default validateConfig({ + host: process.env.E2E_HOST, + path: process.env.E2E_PATH, + token: process.env.E2E_ACCESS_TOKEN, + catalog: process.env.E2E_CATALOG, + schema: process.env.E2E_SCHEMA, + volume: process.env.E2E_VOLUME, + tableSuffix: process.env.E2E_TABLE_SUFFIX, + ...loadOverrides(), +}); diff --git a/tests/e2e/utils/logger.js b/tests/e2e/utils/logger.js deleted file mode 100644 index 1c77c16c..00000000 --- a/tests/e2e/utils/logger.js +++ /dev/null @@ -1,22 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -const getMessage = (message) => { - return new Date() + ' [INFO] ' + message + '\n'; -}; - -const logToFile = (message) => { - fs.appendFileSync(path.join(__dirname, '../../e2e.log'), getMessage(message)); -}; - -module.exports = (type = 'CONSOLE') => { - switch (type) { - case 'QUIET': - return () => {}; - case 'FILE': - return logToFile; - case 'CONSOLE': - default: - return (message) => console.log(message); - } -}; diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 00000000..7b375312 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./dist/" /* Redirect output structure to the directory. */, + "rootDir": "./lib/" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + }, + "exclude": ["./tests/**/*", "./dist/**/*"] +} diff --git a/tsconfig.json b/tsconfig.json index 030f8cfa..43e7eae2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,12 @@ { "compilerOptions": { - "target": "ES6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, - "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, + "target": "ES6", + "module": "commonjs", "declaration": true, "sourceMap": true, - "outDir": "./dist/" /* Redirect output structure to the directory. */, - "rootDir": "./lib/" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, - "strict": true /* Enable all strict type-checking options. */, - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ - } + "strict": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true + }, + "exclude": ["./dist/**/*"] }