diff --git a/.github/workflows/ci-coverage.yml b/.github/workflows/ci-coverage.yml index c05b9d260f..95536c417b 100644 --- a/.github/workflows/ci-coverage.yml +++ b/.github/workflows/ci-coverage.yml @@ -58,7 +58,7 @@ jobs: run: node tools/wait-up.js - name: Run tests - run: FILTER=${{matrix.filter}} MYSQL_USE_TLS=${{ matrix.use-tls }} MYSQL_USE_COMPRESSION=${{ matrix.use-compression }} npm run coverage-test + run: FILTER=${{matrix.filter}} MYSQL_USE_TLS=${{ matrix.use-tls }} MYSQL_USE_COMPRESSION=${{ matrix.use-compression }} npm run test:coverage - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4 diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 29e52400bd..4b215661b0 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -116,64 +116,6 @@ jobs: run: bun run test:bun timeout-minutes: 10 - tests-linux-deno-v1: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - deno-version: [v1.x] - mysql-version: ['mysql:8.3'] - use-compression: [0, 1] - static-parser: [0, 1] - # TODO: investigate error when using SSL (1) - # - # errno: -4094 - # code: "UNKNOWN" - # syscall: "read" - use-tls: [0] - - env: - STATIC_PARSER: ${{ matrix.static-parser }} - - name: Deno ${{ matrix.deno-version }} - DB ${{ matrix.mysql-version }} - SSL=${{matrix.use-tls}} Compression=${{matrix.use-compression}} Static Parser=${{matrix.static-parser}} - - steps: - - uses: actions/checkout@v4 - - name: Set up MySQL - run: docker run -d -e MYSQL_ALLOW_EMPTY_PASSWORD=1 -e MYSQL_DATABASE=${{ env.MYSQL_DATABASE }} -v $PWD/mysqldata:/var/lib/mysql/ -v $PWD/test/fixtures/custom-conf:/etc/mysql/conf.d -v $PWD/test/fixtures/ssl/certs:/certs -p ${{ env.MYSQL_PORT }}:3306 ${{ matrix.mysql-version }} - - - name: Set up Deno ${{ matrix.deno-version }} - uses: denoland/setup-deno@v1 - with: - deno-version: ${{ matrix.deno-version }} - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - - name: Cache dependencies - uses: actions/cache@v4 - with: - path: ~/.npm - key: npm-linux-${{ hashFiles('package-lock.json') }} - restore-keys: npm-linux- - - - name: Install npm dependencies - run: npm ci - - - name: Wait mysql server is ready - run: node tools/wait-up.js - - - name: run tests - env: - MYSQL_USER: ${{ env.MYSQL_USER }} - MYSQL_DATABASE: ${{ env.MYSQL_DATABASE }} - MYSQL_PORT: ${{ env.MYSQL_PORT }} - MYSQL_USE_COMPRESSION: ${{ matrix.use-compression }} - MYSQL_USE_TLS: ${{ matrix.use-tls }} - run: deno task test:deno -- --denoCjs='.js,.cjs' - timeout-minutes: 10 - tests-linux-deno-v2: runs-on: ubuntu-latest strategy: diff --git a/.github/workflows/ci-tsc-build.yml b/.github/workflows/ci-tsc-build.yml index b1f0924309..a057cfcabe 100644 --- a/.github/workflows/ci-tsc-build.yml +++ b/.github/workflows/ci-tsc-build.yml @@ -35,4 +35,4 @@ jobs: run: npm ci - name: Testing TypeScript build - run: npm run test:tsc-build + run: npm run typecheck diff --git a/Contributing.md b/Contributing.md index f0be064853..0caf797078 100644 --- a/Contributing.md +++ b/Contributing.md @@ -133,7 +133,7 @@ FILTER='timeout' npm test For testing **coverage**: ```bash -npm run coverage-test +npm run test:coverage ``` #### Docker diff --git a/deno.json b/deno.json new file mode 100644 index 0000000000..0411b3c41a --- /dev/null +++ b/deno.json @@ -0,0 +1,3 @@ +{ + "unstable": ["sloppy-imports"] +} diff --git a/eslint.config.mjs b/eslint.config.mjs index fc5dd2be2d..8f3bed2b70 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -83,6 +83,33 @@ export default [ ], }, }, + ...compat.extends('plugin:@typescript-eslint/recommended').map((config) => ({ + ...config, + files: ['**/*.mts'], + })), + { + files: ['**/*.mts'], + plugins: { + '@typescript-eslint': typescriptEslint, + }, + languageOptions: { + parser: tsParser, + }, + rules: { + '@typescript-eslint/no-empty-interface': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-empty-object-type': 'off', + strict: 'off', + 'no-restricted-syntax': [ + 'error', + { + selector: + 'ImportDeclaration[source.value=/^\\./][source.value!=/\\.(m?js)$/]', + message: 'Local imports must have the explicit .mjs or .js extension', + }, + ], + }, + }, { files: ['**/*.md'], processor: 'markdown/markdown', @@ -111,14 +138,14 @@ export default [ }, }, { - files: ['**/**/*.test.ts'], + files: ['**/**/*.test.ts', '**/**/*.test.mts'], rules: { '@typescript-eslint/no-unused-expressions': 'off', 'arrow-parens': ['error', 'always'], }, }, { - files: ['**/*.mjs'], + files: ['**/*.mjs', '**/*.mts'], languageOptions: { ecmaVersion: 'latest', sourceType: 'module', diff --git a/package-lock.json b/package-lock.json index 4364584c1d..23ef10b047 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,7 @@ "poku": "^3.0.2", "portfinder": "^1.0.38", "prettier": "^3.8.0", + "tsx": "^4.21.0", "typescript": "^5.9.3" }, "engines": { @@ -54,6 +55,448 @@ "node": ">=18" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -1199,6 +1642,48 @@ "stackframe": "^1.3.4" } }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1703,6 +2188,21 @@ "node": ">=0.4.x" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/generate-function": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", @@ -1722,6 +2222,19 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/github-slugger": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", @@ -1733,6 +2246,7 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { @@ -3371,6 +3885,16 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -3670,6 +4194,26 @@ "typescript": ">=4.8.4" } }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index df70c47010..45a9cb5039 100644 --- a/package.json +++ b/package.json @@ -11,14 +11,14 @@ "test": "poku -d -r=verbose --sequential test/esm test/unit test/integration", "test:bun": "bun poku -d --sequential test/esm test/unit test/integration", "test:deno": "deno run --allow-read --allow-env --allow-run npm:poku -d --sequential --denoAllow=\"read,env,net,sys\" test/esm test/unit test/integration", - "test:tsc-build": "cd \"test/tsc-build\" && npx tsc -p \"tsconfig.json\"", "test:docker:up": "docker compose -f test/docker-compose.yml up --abort-on-container-exit --remove-orphans", "test:docker:down": "docker compose -f test/docker-compose.yml down", "test:docker:node": "npm run test:docker:up -- node && npm run test:docker:down", "test:docker:bun": "npm run test:docker:up -- bun && npm run test:docker:down", "test:docker:deno": "npm run test:docker:up -- deno && npm run test:docker:down", "test:docker:coverage": "npm run test:docker:up -- coverage && npm run test:docker:down", - "coverage-test": "c8 npm run test", + "test:coverage": "c8 npm test", + "typecheck": "cd \"test/tsc-build\" && tsc -p \"tsconfig.json\" && cd ../esm && tsc -p \"tsconfig.json\" --noEmit", "benchmark": "node ./benchmarks/benchmark.js", "wait-port": "wait-on" }, @@ -81,6 +81,7 @@ "poku": "^3.0.2", "portfinder": "^1.0.38", "prettier": "^3.8.0", + "tsx": "^4.21.0", "typescript": "^5.9.3" } } diff --git a/test/common.test.cjs b/test/common.test.cjs index 9600939276..f1bb3a50cd 100644 --- a/test/common.test.cjs +++ b/test/common.test.cjs @@ -5,7 +5,6 @@ const path = require('node:path'); const process = require('node:process'); const disableEval = process.env.STATIC_PARSER === '1'; -exports.disableEval = disableEval; const config = { host: process.env.MYSQL_HOST || 'localhost', @@ -27,6 +26,8 @@ if (process.env.MYSQL_USE_TLS === '1') { }; } +exports.config = config; + const encUser = encodeURIComponent(config.user ?? ''); const encPass = encodeURIComponent(config.password ?? ''); const host = config.host; @@ -36,7 +37,6 @@ const db = config.database; const configURI = `mysql://${encUser}:${encPass}@${host}:${port}/${db}`; exports.SqlString = require('sql-escaper'); -exports.config = config; exports.waitDatabaseReady = function (callback) { const start = Date.now(); diff --git a/test/docker-compose.yml b/test/docker-compose.yml index d29ba7ecbf..42744f8f66 100644 --- a/test/docker-compose.yml +++ b/test/docker-compose.yml @@ -26,8 +26,9 @@ services: FILTER: '' volumes: - ../:/usr/app:ro + - /usr/app/node_modules working_dir: /usr/app - command: npm test + command: sh -c "npm ci && npm test" depends_on: mysql: condition: service_healthy @@ -83,8 +84,9 @@ services: volumes: - ../:/usr/app:ro - ../coverage:/usr/app/coverage:rw + - /usr/app/node_modules working_dir: /usr/app - command: npm run coverage-test + command: sh -c "npm ci && npm run test:coverage" depends_on: mysql: condition: service_healthy diff --git a/test/esm/common.test.mjs b/test/esm/common.test.mts similarity index 67% rename from test/esm/common.test.mjs rename to test/esm/common.test.mts index 1e9cc602f7..a8e6560465 100644 --- a/test/esm/common.test.mjs +++ b/test/esm/common.test.mts @@ -1,3 +1,10 @@ +import type { Connection as PromiseConnection } from '../../promise.js'; +import type { + ConnectionOptions, + PoolOptions, + PoolClusterOptions, + Connection, +} from '../../index.js'; import { createRequire } from 'node:module'; import fs from 'node:fs'; import path from 'node:path'; @@ -5,31 +12,34 @@ import process from 'node:process'; import { fileURLToPath } from 'node:url'; export * as SqlString from 'sql-escaper'; import portfinder from 'portfinder'; +import ClientFlags from '../../lib/constants/client.js'; +import * as driver from '../../index.js'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); export const require = createRequire(import.meta.url); -const ClientFlags = require('../../lib/constants/client.js'); - -export const driver = require('../../index.js'); -export const promiseDriver = require('../../promise.js'); -export const packets = require('../../lib/packets/index.js'); -export const PrepareCommand = require('../../lib/commands/prepare.js'); -export const getBinaryParser = require('../../lib/parsers/binary_parser.js'); -export const TextRowParser = require('../../lib/parsers/text_parser.js'); -export const { _keyFromFields } = require('../../lib/parsers/parser_cache.js'); -export const { privateObjectProps } = require('../../lib/helpers.js'); - -export const disableEval = process.env.STATIC_PARSER === '1'; - -const config = { +const disableEval: boolean = process.env.STATIC_PARSER === '1'; + +const config: { + host: string; + user: string; + password: string; + database: string; + compress: boolean; + port: number; + disableEval: boolean; + ssl?: { + rejectUnauthorized: boolean; + ca: string; + }; +} = { host: process.env.MYSQL_HOST || 'localhost', user: process.env.MYSQL_USER || 'root', password: (process.env.CI ? process.env.MYSQL_PASSWORD : '') || '', database: process.env.MYSQL_DATABASE || 'test', compress: process.env.MYSQL_USE_COMPRESSION === '1', - port: process.env.MYSQL_PORT || 3306, + port: Number(process.env.MYSQL_PORT) || 3306, disableEval, }; @@ -45,16 +55,15 @@ if (process.env.MYSQL_USE_TLS === '1') { export { config }; -const encUser = encodeURIComponent(config.user ?? ''); -const encPass = encodeURIComponent(config.password ?? ''); -const host = config.host; -const port = config.port; -const db = config.database; +const encUser: string = encodeURIComponent(config.user ?? ''); +const encPass: string = encodeURIComponent(config.password ?? ''); +const host: string = config.host; +const port: number = config.port; +const db: string = config.database; -const configURI = `mysql://${encUser}:${encPass}@${host}:${port}/${db}`; +const configURI: string = `mysql://${encUser}:${encPass}@${host}:${port}/${db}`; -export const createConnection = function (args) { - const driver = require('../../index.js'); +export const createConnection = function (args?: ConnectionOptions) { if (!args?.port && process.env.MYSQL_CONNECTION_URL) { return driver.createConnection({ ...args, @@ -97,9 +106,9 @@ export const createConnection = function (args) { return conn; }; -export const waitDatabaseReady = function (callback) { - const start = Date.now(); - const timeout = 300000; // 5 minutes in milliseconds +export const waitDatabaseReady = function (callback: () => void) { + const start: number = Date.now(); + const timeout: number = 300000; // 5 minutes in milliseconds const tryConnect = function () { if (Date.now() - start > timeout) { @@ -112,7 +121,7 @@ export const waitDatabaseReady = function (callback) { password: process.env.MYSQL_PASSWORD, }); - conn.once('error', (err) => { + conn.once('error', (err: Error & { code?: string }) => { if ( err.code !== 'PROTOCOL_CONNECTION_LOST' && err.code !== 'ETIMEDOUT' && @@ -123,7 +132,7 @@ export const waitDatabaseReady = function (callback) { } try { - conn.close(); + conn.end(); } catch (err) { console.log(err); } @@ -134,7 +143,7 @@ export const waitDatabaseReady = function (callback) { conn.once('connect', () => { console.log(`ready after ${Date.now() - start}ms!`); - conn.close(); + conn.end(); callback(); }); }; @@ -142,7 +151,7 @@ export const waitDatabaseReady = function (callback) { tryConnect(); }; -export const getConfig = function (input) { +export const getConfig = function (input?: ConnectionOptions) { const args = input || {}; const params = { host: args.host || config.host, @@ -173,9 +182,7 @@ export const getConfig = function (input) { return params; }; -export const createPool = function (args) { - let driver = require('../../index.js'); - +export const createPool = function (args?: PoolOptions) { if (!args?.port && process.env.MYSQL_CONNECTION_URL) { return driver.createPool({ ...args, @@ -188,18 +195,18 @@ export const createPool = function (args) { } if (process.env.BENCHMARK_MYSQL1) { - driver = require('mysql'); + const mysql1 = require('mysql'); + return mysql1.createPool(getConfig(args)); } return driver.createPool(getConfig(args)); }; -export const createPoolCluster = function (args = {}) { - const driver = require('../../index.js'); - - if (!args?.port && process.env.MYSQL_CONNECTION_URL) { +export const createPoolCluster = function (args: PoolClusterOptions = {}) { + if (!('port' in args) && process.env.MYSQL_CONNECTION_URL) { return driver.createPoolCluster({ ...args, + // @ts-expect-error: TODO: implement typings uri: process.env.MYSQL_CONNECTION_URL, }); } @@ -208,8 +215,6 @@ export const createPoolCluster = function (args = {}) { }; export const createConnectionWithURI = function () { - const driver = require('../../index.js'); - return driver.createConnection({ uri: configURI }); }; @@ -220,15 +225,20 @@ export const createTemplate = function () { return jade.compile(template); }; -export const createServer = function (onListening, handler) { - const server = require('../../index.js').createServer(); - server.on('connection', (conn) => { +export const createServer = function ( + onListening: () => void, + handler?: (conn: Connection) => void +) { + // @ts-expect-error: TODO: implement typings + const server = driver.createServer(); + server.on('connection', (conn: Connection) => { conn.on('error', () => { // server side of the connection // ignore disconnects }); + // remove ssl bit from the flags - let flags = 0xffffff; + let flags: number = 0xffffff; flags = flags ^ (ClientFlags.COMPRESS | ClientFlags.SSL); conn.serverHandshake({ @@ -239,13 +249,17 @@ export const createServer = function (onListening, handler) { characterSet: 8, capabilityFlags: flags, }); + if (handler) { handler(conn); } }); - portfinder.getPort((err, port) => { + + portfinder.getPort((_: Error | null, port: number) => { + // @ts-expect-error: TODO: implement typings server.listen(port, onListening); }); + return server; }; @@ -253,13 +267,14 @@ export const useTestDb = function () { // no-op in my setup, need it for compatibility with node-mysql tests }; -export const version = Number(process.version.match(/v(\d+)\./)?.[1]); - -export const getMysqlVersion = async function (connection) { - const conn = connection.promise ? connection.promise() : connection; +export const version: number = Number(process.version.match(/v(\d+)\./)?.[1]); +export const getMysqlVersion = async function ( + connection: Connection | PromiseConnection +) { + const conn = 'promise' in connection ? connection.promise() : connection; const [rows] = await conn.query('SELECT VERSION() AS `version`'); - const serverVersion = rows[0].version; + const serverVersion: string = rows[0].version; const [major, minor, patch] = serverVersion .split('.') @@ -272,16 +287,17 @@ export const getMysqlVersion = async function (connection) { }; }; -const pad = (number, length = 2) => String(number).padStart(length, '0'); - -export const localDate = (date) => { - const year = pad(date.getFullYear(), 4); - const month = pad(date.getMonth() + 1); - const day = pad(date.getDate()); - const hour = pad(date.getHours()); - const minute = pad(date.getMinutes()); - const second = pad(date.getSeconds()); - const millisecond = pad(date.getMilliseconds(), 3); +const pad = (number: number, length: number = 2): string => + String(number).padStart(length, '0'); + +export const localDate = (date: Date): string => { + const year: string = pad(date.getFullYear(), 4); + const month: string = pad(date.getMonth() + 1); + const day: string = pad(date.getDate()); + const hour: string = pad(date.getHours()); + const minute: string = pad(date.getMinutes()); + const second: string = pad(date.getSeconds()); + const millisecond: string = pad(date.getMilliseconds(), 3); return `${year}-${month}-${day} ${hour}:${minute}:${second}.${millisecond}`; }; diff --git a/test/esm/integration/connection/test-column-inspect.test.mjs b/test/esm/integration/connection/test-column-inspect.test.mts similarity index 92% rename from test/esm/integration/connection/test-column-inspect.test.mjs rename to test/esm/integration/connection/test-column-inspect.test.mts index a20d53ac42..308ceba24c 100644 --- a/test/esm/integration/connection/test-column-inspect.test.mjs +++ b/test/esm/integration/connection/test-column-inspect.test.mts @@ -1,11 +1,12 @@ import { assert, describe, afterEach, beforeEach, it } from 'poku'; import util from 'node:util'; +import type { Connection as PromiseConnection } from '../../../../promise.js'; import { config, createConnection, version } from '../../common.test.mjs'; const { database: currentDatabase } = config; await describe('Custom inspect for column definition', async () => { - let connection; + let connection: PromiseConnection; beforeEach(async () => { connection = createConnection().promise(); @@ -47,8 +48,8 @@ await describe('Custom inspect for column definition', async () => { ); const [, columns] = await connection.query('select * from test_fields'); - const inspectResults = util.inspect(columns); - const schemaArray = schema + const inspectResults: string = util.inspect(columns); + const schemaArray: string[] = schema .split('\n') .map((line) => line.trim()) .filter((line) => line.length > 0) @@ -58,7 +59,7 @@ await describe('Custom inspect for column definition', async () => { return [name, ...words.slice(1)].join(' '); }); - const normalizedInspectResults = inspectResults + const normalizedInspectResults: string[] = inspectResults .split('\n') .slice(1, -2) // remove "[" and "]" lines and also last dummy field .map((line) => line.trim()) diff --git a/test/esm/integration/connection/test-execute-1.test.mjs b/test/esm/integration/connection/test-execute-1.test.mts similarity index 94% rename from test/esm/integration/connection/test-execute-1.test.mjs rename to test/esm/integration/connection/test-execute-1.test.mts index 9c79f03e28..813b8f4fed 100644 --- a/test/esm/integration/connection/test-execute-1.test.mjs +++ b/test/esm/integration/connection/test-execute-1.test.mts @@ -1,3 +1,4 @@ +import type { RowDataPacket } from '../../../../promise.js'; import { it, assert, describe } from 'poku'; import { createConnection } from '../../common.test.mjs'; @@ -50,7 +51,9 @@ await describe(async () => { 'проводить собрания, митинги и демонстрации, шествия и пикетирование', ]); - const [_rows] = await connection.execute('SELECT * FROM announcements'); + const [_rows] = await connection.execute( + 'SELECT * FROM announcements' + ); assert.equal(_rows.length, 2, 'rows length needs to be 2'); assert.equal( diff --git a/test/esm/integration/connection/test-vector.test.mjs b/test/esm/integration/connection/test-vector.test.mts similarity index 79% rename from test/esm/integration/connection/test-vector.test.mjs rename to test/esm/integration/connection/test-vector.test.mts index dea48f8a62..94f6a00852 100644 --- a/test/esm/integration/connection/test-vector.test.mjs +++ b/test/esm/integration/connection/test-vector.test.mts @@ -2,15 +2,16 @@ import { it, assert, describe } from 'poku'; import { createConnection, getMysqlVersion } from '../../common.test.mjs'; const sql = `SELECT TO_VECTOR("[1.05, -17.8, 32, 123.456]") as test`; -const expectedArray = [1.05, -17.8, 32, 123.456]; -const epsilon = 1e-6; +const expectedArray: number[] = [1.05, -17.8, 32, 123.456]; +const epsilon: number = 1e-6; -const compareFloat = (a, b) => Math.abs((a - b) / a) < epsilon; -const compareFLoatsArray = (a, b) => a.every((v, i) => compareFloat(v, b[i])); +const compareFloat = (a: number, b: number): boolean => + Math.abs((a - b) / a) < epsilon; +const compareFLoatsArray = (a: number[], b: number[]): boolean => + a.every((v, i) => compareFloat(v, b[i])); await describe(async () => { const connection = createConnection().promise(); - const mySqlVersion = await getMysqlVersion(connection); if (mySqlVersion.major < 9) { diff --git a/test/esm/integration/named-placeholders.test.mjs b/test/esm/integration/named-placeholders.test.mts similarity index 100% rename from test/esm/integration/named-placeholders.test.mjs rename to test/esm/integration/named-placeholders.test.mts diff --git a/test/esm/integration/parsers/execute-results-creation.test.mjs b/test/esm/integration/parsers/execute-results-creation.test.mts similarity index 94% rename from test/esm/integration/parsers/execute-results-creation.test.mjs rename to test/esm/integration/parsers/execute-results-creation.test.mts index 7f7448a204..824d743ff0 100644 --- a/test/esm/integration/parsers/execute-results-creation.test.mjs +++ b/test/esm/integration/parsers/execute-results-creation.test.mts @@ -12,7 +12,7 @@ await describe('Execute: Results Creation', async () => { ]; const emptyObject = {}; const proto = Object.getPrototypeOf(emptyObject); - const privateObjectProps = Object.getOwnPropertyNames(proto); + const privateObjectProps: string[] = Object.getOwnPropertyNames(proto); const [results] = await connection.execute('SELECT 1+1 AS `test`'); diff --git a/test/esm/integration/parsers/json-parse.test.mjs b/test/esm/integration/parsers/json-parse.test.mts similarity index 100% rename from test/esm/integration/parsers/json-parse.test.mjs rename to test/esm/integration/parsers/json-parse.test.mts diff --git a/test/esm/integration/parsers/json-string.test.mjs b/test/esm/integration/parsers/json-string.test.mts similarity index 100% rename from test/esm/integration/parsers/json-string.test.mjs rename to test/esm/integration/parsers/json-string.test.mts diff --git a/test/esm/integration/parsers/query-results-creation.test.mjs b/test/esm/integration/parsers/query-results-creation.test.mts similarity index 94% rename from test/esm/integration/parsers/query-results-creation.test.mjs rename to test/esm/integration/parsers/query-results-creation.test.mts index 476fe3f649..2c238dfaff 100644 --- a/test/esm/integration/parsers/query-results-creation.test.mjs +++ b/test/esm/integration/parsers/query-results-creation.test.mts @@ -12,7 +12,7 @@ await describe('Query: Results Creation', async () => { ]; const emptyObject = {}; const proto = Object.getPrototypeOf(emptyObject); - const privateObjectProps = Object.getOwnPropertyNames(proto); + const privateObjectProps: string[] = Object.getOwnPropertyNames(proto); const [results] = await connection.query('SELECT 1+1 AS `test`'); diff --git a/test/esm/integration/parsers/typecast-field-datetime.test.mjs b/test/esm/integration/parsers/typecast-field-datetime.test.mts similarity index 79% rename from test/esm/integration/parsers/typecast-field-datetime.test.mjs rename to test/esm/integration/parsers/typecast-field-datetime.test.mts index 18401241d3..77cf6c1796 100644 --- a/test/esm/integration/parsers/typecast-field-datetime.test.mjs +++ b/test/esm/integration/parsers/typecast-field-datetime.test.mts @@ -1,13 +1,16 @@ import { describe, it, assert } from 'poku'; +import type { TypeCastField } from '../../../../index.js'; import { createConnection } from '../../common.test.mjs'; await describe('typeCast field.datetime', async () => { const conn = createConnection({ - typeCast: (field) => field.string(), + typeCast: (field: TypeCastField) => field.string(), }).promise(); - const query = {}; - const execute = {}; + const query = {} as { + date: string | null; + }; + const execute = {} as typeof query; await conn.query('CREATE TEMPORARY TABLE `tmp_date` (`datetime` DATETIME)'); diff --git a/test/esm/integration/parsers/typecast-field-string.test.mjs b/test/esm/integration/parsers/typecast-field-string.test.mts similarity index 88% rename from test/esm/integration/parsers/typecast-field-string.test.mjs rename to test/esm/integration/parsers/typecast-field-string.test.mts index df2987d588..25bd28752f 100644 --- a/test/esm/integration/parsers/typecast-field-string.test.mjs +++ b/test/esm/integration/parsers/typecast-field-string.test.mts @@ -1,13 +1,20 @@ import { describe, it, assert } from 'poku'; +import type { TypeCastField } from '../../../../index.js'; import { createConnection } from '../../common.test.mjs'; await describe('typeCast field.string', async () => { const conn = createConnection({ - typeCast: (field) => field.string(), + typeCast: (field: TypeCastField) => field.string(), }).promise(); - const query = {}; - const execute = {}; + const query = {} as { + date: string | null; + time: string | null; + datetime: string | null; + timestamp: string | null; + tiny: { signed: string | null; unsigned: string | null }; + }; + const execute = {} as typeof query; await conn.query( 'CREATE TEMPORARY TABLE `tmp_tiny` (`signed` TINYINT, `unsigned` TINYINT UNSIGNED)' diff --git a/test/esm/integration/pool-cluster/test-promise-wrapper.test.mjs b/test/esm/integration/pool-cluster/test-promise-wrapper.test.mts similarity index 84% rename from test/esm/integration/pool-cluster/test-promise-wrapper.test.mjs rename to test/esm/integration/pool-cluster/test-promise-wrapper.test.mts index fb6301b76b..8a640a64e6 100644 --- a/test/esm/integration/pool-cluster/test-promise-wrapper.test.mjs +++ b/test/esm/integration/pool-cluster/test-promise-wrapper.test.mts @@ -1,5 +1,7 @@ import { it, assert, describe } from 'poku'; -import { config, promiseDriver } from '../../common.test.mjs'; +import type { QueryError } from '../../../../index.js'; +import promiseDriver from '../../../../promise.js'; +import { config } from '../../common.test.mjs'; const { createPoolCluster } = promiseDriver; @@ -19,6 +21,7 @@ await describe('Test pool cluster', async () => { }); }); + // @ts-expect-error: TODO: implement typings poolCluster.poolCluster.emit('warn', new Error()); }); @@ -37,6 +40,7 @@ await describe('Test pool cluster', async () => { }); }); + // @ts-expect-error: TODO: implement typings poolCluster.poolCluster.emit('remove'); }); @@ -55,6 +59,7 @@ await describe('Test pool cluster', async () => { }); }); + // @ts-expect-error: TODO: implement typings poolCluster.poolCluster.emit('offline'); }); @@ -73,6 +78,7 @@ await describe('Test pool cluster', async () => { }); }); + // @ts-expect-error: TODO: implement typings poolCluster.poolCluster.emit('online'); }); @@ -83,7 +89,9 @@ await describe('Test pool cluster', async () => { const poolNamespace = poolCluster.of('MASTER'); assert.equal( + // @ts-expect-error: TODO: implement typings poolNamespace.poolNamespace, + // @ts-expect-error: TODO: implement typings poolCluster.poolCluster.of('MASTER') ); @@ -114,9 +122,9 @@ await describe('Test pool cluster', async () => { try { await poolCluster.getConnection('SLAVE1'); assert.fail('An error was expected'); - } catch (error) { + } catch (error: unknown) { assert.equal( - error.code, + (error as QueryError).code, 'POOL_NOEXIST', 'should throw when PoolNamespace does not exist' ); @@ -130,8 +138,10 @@ await describe('Test pool cluster', async () => { poolCluster.add('SLAVE1', config); try { + // @ts-expect-error: TODO: implement typings const connection = await poolCluster.getConnection(/SLAVE[12]/); assert.equal( + // @ts-expect-error: TODO: implement typings connection.connection._clusterId, 'SLAVE1', 'should match regex pattern' diff --git a/test/esm/integration/test-pool.test.mjs b/test/esm/integration/test-pool.test.mts similarity index 88% rename from test/esm/integration/test-pool.test.mjs rename to test/esm/integration/test-pool.test.mts index ebd61b086c..a175ddc95a 100644 --- a/test/esm/integration/test-pool.test.mjs +++ b/test/esm/integration/test-pool.test.mts @@ -1,14 +1,16 @@ import { assert, it, describe } from 'poku'; -import { driver as mysql } from '../common.test.mjs'; +import mysql from '../../../index.js'; const poolConfig = {}; // config: { connectionConfig: {} }; const pool = mysql.createPool(poolConfig); describe('Pool methods tests', () => { + // @ts-expect-error: TODO: implement typings assert.equal(pool.escape(123), '123', 'escape method works correctly'); assert.equal( + // @ts-expect-error: TODO: implement typings pool.escapeId('table name'), '`table name`', 'escapeId method works correctly' @@ -17,6 +19,7 @@ describe('Pool methods tests', () => { it(() => { const params = ['table name', 'thing']; assert.equal( + // @ts-expect-error: TODO: implement typings pool.format('SELECT a FROM ?? WHERE b = ?', params), "SELECT a FROM `table name` WHERE b = 'thing'", 'format method works correctly' @@ -49,6 +52,7 @@ describe('Pool.promise() methods tests', () => { }); }); +// @ts-expect-error: TODO: implement typings const promisePool = mysql.createPoolPromise(poolConfig); describe('PromisePool methods tests', () => { diff --git a/test/esm/regressions/2052.test.mjs b/test/esm/regressions/2052.test.mts similarity index 70% rename from test/esm/regressions/2052.test.mjs rename to test/esm/regressions/2052.test.mts index d766df76a1..6871350998 100644 --- a/test/esm/regressions/2052.test.mjs +++ b/test/esm/regressions/2052.test.mts @@ -1,11 +1,9 @@ import { assert, describe, it } from 'poku'; import { Buffer } from 'node:buffer'; -import { - createConnection, - getMysqlVersion, - packets, - PrepareCommand, -} from '../common.test.mjs'; +import packets from '../../../lib/packets/index.js'; +import PrepareCommand from '../../../lib/commands/prepare.js'; +import type { QueryError, PrepareStatementInfo } from '../../../index.js'; +import { createConnection, getMysqlVersion } from '../common.test.mjs'; await describe(async () => { await it('Unit Test - Prepare result with number of parameters incorrectly reported by the server', async () => { @@ -22,7 +20,7 @@ await describe(async () => { config: { charsetNumber: 33, }, - writePacket: (packet) => { + writePacket: (packet: any) => { // client -> server COM_PREPARE packet.writeHeader(1); assert.equal( @@ -36,12 +34,15 @@ await describe(async () => { await new Promise((resolve, reject) => { const prepareCommand = new PrepareCommand( { sql: 'select * from users order by ?' }, - (err, result) => { + (err: QueryError | null, result: PrepareStatementInfo) => { try { assert.equal(err, null, 'expect no error'); + // @ts-expect-error: TODO: implement typings assert.equal(result.parameters.length, 0, 'parameters'); + // @ts-expect-error: TODO: implement typings assert.equal(result.columns.length, 51, 'columns'); + // @ts-expect-error: TODO: implement typings assert.equal(result.id, 1, 'id'); resolve(null); @@ -103,31 +104,35 @@ await describe(async () => { await it('E2E Prepare result with number of parameters incorrectly reported by the server', async () => new Promise((resolve, reject) => { - connection.prepare('select * from user order by ?', async (err, stmt) => { - if (err) { - connection.end(); - reject(err); + connection.prepare( + 'select * from user order by ?', + async (err: QueryError | null, stmt: PrepareStatementInfo) => { + if (err) { + connection.end(); + reject(err); + + return; + } - return; - } + if (hasIncorrectPrepareParameter) { + assert.equal( + // @ts-expect-error: TODO: implement typings + stmt.parameters.length, + 0, + 'should report 0 actual parameters when 1 placeholder is used in ORDER BY ?' + ); + } else { + assert.equal( + // @ts-expect-error: TODO: implement typings + stmt.parameters.length, + 1, + 'parameters length needs to be 1' + ); + } - if (hasIncorrectPrepareParameter) { - assert.equal( - stmt.parameters.length, - 0, - 'parameters length needs to be 0', - 'should report 0 actual parameters when 1 placeholder is used in ORDER BY ?' - ); - } else { - assert.equal( - stmt.parameters.length, - 1, - 'parameters length needs to be 1' - ); + resolve(null); } - - resolve(null); - }); + ); })); await it( @@ -135,7 +140,7 @@ await describe(async () => { new Promise((resolve, reject) => { connection.prepare( 'select * from user where user.User like ? order by ?', - async (err, stmt) => { + async (err: QueryError | null, stmt: PrepareStatementInfo) => { if (err) { connection.end(); reject(err); @@ -145,13 +150,14 @@ await describe(async () => { if (hasIncorrectPrepareParameter) { assert.equal( + // @ts-expect-error: TODO: implement typings stmt.parameters.length, 1, - 'parameters length needs to be 1', 'should report 1 actual parameters when 2 placeholders used in ORDER BY?' ); } else { assert.equal( + // @ts-expect-error: TODO: implement typings stmt.parameters.length, 2, 'parameters length needs to be 2' @@ -164,7 +170,7 @@ await describe(async () => { }) ); - connection.end((err) => { + connection.end((err: QueryError | null) => { assert.ifError(err); }); }); diff --git a/test/esm/tsconfig.json b/test/esm/tsconfig.json new file mode 100644 index 0000000000..d6b5d6b384 --- /dev/null +++ b/test/esm/tsconfig.json @@ -0,0 +1,23 @@ +{ + "include": ["**/*.mts"], + "compilerOptions": { + "target": "ESNext", + "lib": ["ESNext"], + "module": "NodeNext", + "moduleResolution": "NodeNext", + "noEmit": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "allowJs": true, + "strict": true, + "alwaysStrict": true, + "strictFunctionTypes": false, + "strictNullChecks": true, + "noImplicitAny": false, + "noImplicitThis": false, + "noUnusedLocals": true, + "noUnusedParameters": true, + "allowUnreachableCode": false, + "allowUnusedLabels": false + } +} diff --git a/test/esm/unit/check-extensions.test.mjs b/test/esm/unit/check-extensions.test.mts similarity index 80% rename from test/esm/unit/check-extensions.test.mjs rename to test/esm/unit/check-extensions.test.mts index 832b323fd8..1ab3760af0 100644 --- a/test/esm/unit/check-extensions.test.mjs +++ b/test/esm/unit/check-extensions.test.mts @@ -2,13 +2,13 @@ import { EOL } from 'node:os'; import { listFiles, it, assert, describe } from 'poku'; await describe('Check for invalid file types found in restricted directories', async () => { - const invalidFiles = []; - const message = []; + const invalidFiles: string[] = []; + const message: string[] = []; const checkExtensions = async ( - dirs, - allowedExtensions, - ignoreList = /\.DS_Store/ + dirs: string[], + allowedExtensions: RegExp, + ignoreList: RegExp = /\.DS_Store|\.json/ ) => { for (const dir of dirs) { const files = await listFiles(dir, { filter: /\./ }); @@ -24,7 +24,7 @@ await describe('Check for invalid file types found in restricted directories', a }; await checkExtensions(['test/unit', 'test/integration'], /\.test\.cjs$/); - await checkExtensions(['test/esm'], /\.test\.mjs$/); + await checkExtensions(['test/esm'], /\.test\.mts$/); await checkExtensions(['test/tsc-build'], /(\.test\.ts|tsconfig\.json)$/); it(() => { diff --git a/test/esm/unit/parsers/big-numbers-strings-binary-sanitization.test.mjs b/test/esm/unit/parsers/big-numbers-strings-binary-sanitization.test.mts similarity index 84% rename from test/esm/unit/parsers/big-numbers-strings-binary-sanitization.test.mjs rename to test/esm/unit/parsers/big-numbers-strings-binary-sanitization.test.mts index 55c0dee5c3..30f5186718 100644 --- a/test/esm/unit/parsers/big-numbers-strings-binary-sanitization.test.mjs +++ b/test/esm/unit/parsers/big-numbers-strings-binary-sanitization.test.mts @@ -6,7 +6,7 @@ await describe('Binary Parser: bigNumberStrings Sanitization', async () => { const sql = 'SELECT 9007199254740991+100 AS `total`'; - const cases = [ + const cases: [Record, string, string][] = [ [ { supportBigNumbers: true, bigNumberStrings: true }, 'string', @@ -18,11 +18,13 @@ await describe('Binary Parser: bigNumberStrings Sanitization', async () => { 'Valid bigNumberStrings disabled', ], [ + // @ts-expect-error: testing sanitization with invalid string value { supportBigNumbers: 'text', bigNumberStrings: 'text' }, 'string', 'bigNumberStrings as a random string should be enabled', ], [ + // @ts-expect-error: testing sanitization with invalid string value { supportBigNumbers: '', bigNumberStrings: '' }, 'number', 'bigNumberStrings as an empty string should be disabled', diff --git a/test/esm/unit/parsers/big-numbers-strings-text-sanitization.test.mjs b/test/esm/unit/parsers/big-numbers-strings-text-sanitization.test.mts similarity index 84% rename from test/esm/unit/parsers/big-numbers-strings-text-sanitization.test.mjs rename to test/esm/unit/parsers/big-numbers-strings-text-sanitization.test.mts index 2f22f6565b..5ad83d615f 100644 --- a/test/esm/unit/parsers/big-numbers-strings-text-sanitization.test.mjs +++ b/test/esm/unit/parsers/big-numbers-strings-text-sanitization.test.mts @@ -6,7 +6,7 @@ await describe('Text Parser: bigNumberStrings Sanitization', async () => { const sql = 'SELECT 9007199254740991+100 AS `total`'; - const cases = [ + const cases: [Record, string, string][] = [ [ { supportBigNumbers: true, bigNumberStrings: true }, 'string', @@ -18,11 +18,13 @@ await describe('Text Parser: bigNumberStrings Sanitization', async () => { 'Valid bigNumberStrings disabled', ], [ + // @ts-expect-error: testing sanitization with invalid string value { supportBigNumbers: 'text', bigNumberStrings: 'text' }, 'string', 'bigNumberStrings as a random string should be enabled', ], [ + // @ts-expect-error: testing sanitization with invalid string value { supportBigNumbers: '', bigNumberStrings: '' }, 'number', 'bigNumberStrings as an empty string should be disabled', diff --git a/test/esm/unit/parsers/cache-key-serialization.test.mjs b/test/esm/unit/parsers/cache-key-serialization.test.mts similarity index 95% rename from test/esm/unit/parsers/cache-key-serialization.test.mjs rename to test/esm/unit/parsers/cache-key-serialization.test.mts index dac1bd4770..ba51dda6ef 100644 --- a/test/esm/unit/parsers/cache-key-serialization.test.mjs +++ b/test/esm/unit/parsers/cache-key-serialization.test.mts @@ -1,5 +1,6 @@ import { describe, it, assert } from 'poku'; -import { _keyFromFields } from '../../common.test.mjs'; +import type { TypeCastField, TypeCastNext } from '../../../../index.js'; +import { _keyFromFields } from '../../../../lib/parsers/parser_cache.js'; describe('Cache Key Serialization', () => { // Invalid @@ -274,7 +275,7 @@ describe('Cache Key Serialization', () => { nestTables: true, rowsAsArray: 2, supportBigNumbers: 'yes', - bigNumberStrings: [], + bigNumberStrings: [] as any[], typeCast: true, timezone: 'local', decimalNumbers: { @@ -308,13 +309,13 @@ describe('Cache Key Serialization', () => { rowsAsArray: false, supportBigNumbers: false, // Expected: true - bigNumberStrings: (_, next) => next(), + bigNumberStrings: (_: any, next: any) => next(), // Expected: "function" - typeCast: (_, next) => next(), + typeCast: (_: TypeCastField, next: TypeCastNext) => next(), timezone: 'local', decimalNumbers: false, // Expected: null - dateStrings: (_, next) => next(), + dateStrings: (_: any, next: any) => next(), }, config: { supportBigNumbers: undefined, @@ -363,7 +364,8 @@ describe('Cache Key Serialization', () => { }, }; - const keyFrom = (t) => _keyFromFields(t.type, t.fields, t.options, t.config); + const keyFrom = (t: any): string => + _keyFromFields(t.type, t.fields, t.options, t.config); it(() => { const result1 = keyFrom(test1); @@ -489,7 +491,7 @@ describe('Cache Key Serialization', () => { const stringify = JSON.stringify; // Overwriting the native `JSON.stringify` - JSON.stringify = (value, replacer, space = 8) => + JSON.stringify = (value: any, replacer?: any, space: any = 8) => stringify(value, replacer, space); const result1 = keyFrom(test1); diff --git a/test/esm/unit/parsers/ensure-safe-binary-fields.test.mjs b/test/esm/unit/parsers/ensure-safe-binary-fields.test.mts similarity index 75% rename from test/esm/unit/parsers/ensure-safe-binary-fields.test.mjs rename to test/esm/unit/parsers/ensure-safe-binary-fields.test.mts index 1efc397831..54c1726ef4 100644 --- a/test/esm/unit/parsers/ensure-safe-binary-fields.test.mjs +++ b/test/esm/unit/parsers/ensure-safe-binary-fields.test.mts @@ -1,19 +1,20 @@ import { describe, it, assert } from 'poku'; -import { getBinaryParser, privateObjectProps } from '../../common.test.mjs'; +import getBinaryParser from '../../../../lib/parsers/binary_parser.js'; +import { privateObjectProps } from '../../../../lib/helpers.js'; describe('Binary Parser: Block Native Object Props', () => { - const blockedFields = Array.from(privateObjectProps).map((prop) => [ - { name: prop, table: '' }, - ]); + const blockedFields: { name: string; table: string }[][] = Array.from( + privateObjectProps + ).map((prop) => [{ name: prop, table: '' }]); it(() => { blockedFields.forEach((fields) => { try { getBinaryParser(fields, {}, {}); assert.fail('An error was expected'); - } catch (error) { + } catch (error: unknown) { assert.strictEqual( - error.message, + (error as Error).message, `The field name (${fields[0].name}) can't be the same as an object's private property.`, `Ensure safe ${fields[0].name}` ); @@ -30,9 +31,9 @@ describe('Binary Parser: Block Native Object Props', () => { try { getBinaryParser(fields, { nestTables: '_' }, {}); assert.fail('An error was expected'); - } catch (error) { + } catch (error: unknown) { assert.strictEqual( - error.message, + (error as Error).message, `The field name (_${fields[0].name}) can't be the same as an object's private property.`, `Ensure safe _${fields[0].name} for nestTables as string` ); @@ -49,9 +50,9 @@ describe('Binary Parser: Block Native Object Props', () => { try { getBinaryParser(fields, { nestTables: true }, {}); assert.fail('An error was expected'); - } catch (error) { + } catch (error: unknown) { assert.strictEqual( - error.message, + (error as Error).message, `The field name (${fields[0].table}) can't be the same as an object's private property.`, `Ensure safe ${fields[0].table} for nestTables as true` ); diff --git a/test/esm/unit/parsers/ensure-safe-text-fields.test.mjs b/test/esm/unit/parsers/ensure-safe-text-fields.test.mts similarity index 75% rename from test/esm/unit/parsers/ensure-safe-text-fields.test.mjs rename to test/esm/unit/parsers/ensure-safe-text-fields.test.mts index 7ab19bd037..02c742fb5a 100644 --- a/test/esm/unit/parsers/ensure-safe-text-fields.test.mjs +++ b/test/esm/unit/parsers/ensure-safe-text-fields.test.mts @@ -1,19 +1,20 @@ import { describe, it, assert } from 'poku'; -import { TextRowParser, privateObjectProps } from '../../common.test.mjs'; +import TextRowParser from '../../../../lib/parsers/text_parser.js'; +import { privateObjectProps } from '../../../../lib/helpers.js'; describe('Text Parser: Block Native Object Props', () => { - const blockedFields = Array.from(privateObjectProps).map((prop) => [ - { name: prop, table: '' }, - ]); + const blockedFields: { name: string; table: string }[][] = Array.from( + privateObjectProps + ).map((prop) => [{ name: prop, table: '' }]); it(() => { blockedFields.forEach((fields) => { try { TextRowParser(fields, {}, {}); assert.fail('An error was expected'); - } catch (error) { + } catch (error: unknown) { assert.strictEqual( - error.message, + (error as Error).message, `The field name (${fields[0].name}) can't be the same as an object's private property.`, `Ensure safe ${fields[0].name}` ); @@ -30,9 +31,9 @@ describe('Text Parser: Block Native Object Props', () => { try { TextRowParser(fields, { nestTables: '_' }, {}); assert.fail('An error was expected'); - } catch (error) { + } catch (error: unknown) { assert.strictEqual( - error.message, + (error as Error).message, `The field name (_${fields[0].name}) can't be the same as an object's private property.`, `Ensure safe _${fields[0].name} for nestTables as string` ); @@ -49,9 +50,9 @@ describe('Text Parser: Block Native Object Props', () => { try { TextRowParser(fields, { nestTables: true }, {}); assert.fail('An error was expected'); - } catch (error) { + } catch (error: unknown) { assert.strictEqual( - error.message, + (error as Error).message, `The field name (${fields[0].table}) can't be the same as an object's private property.`, `Ensure safe ${fields[0].table} for nestTables as true` ); diff --git a/test/esm/unit/parsers/stringify-objects-as-false.test.mjs b/test/esm/unit/parsers/stringify-objects-as-false.test.mts similarity index 78% rename from test/esm/unit/parsers/stringify-objects-as-false.test.mjs rename to test/esm/unit/parsers/stringify-objects-as-false.test.mts index bad7e9e55d..39e0d10a8c 100644 --- a/test/esm/unit/parsers/stringify-objects-as-false.test.mjs +++ b/test/esm/unit/parsers/stringify-objects-as-false.test.mts @@ -1,5 +1,7 @@ +import type { format as Format } from 'sql-escaper'; import { assert, describe, it } from 'poku'; -import { config, localDate, driver } from '../../common.test.mjs'; +import driver from '../../../../index.js'; +import { config, localDate } from '../../common.test.mjs'; await describe('stringifyObjects: false', async () => { const connection = driver @@ -9,13 +11,15 @@ await describe('stringifyObjects: false', async () => { }) .promise(); - const format = (sql, values) => connection.connection.format(sql, values); + const format: typeof Format = (sql, values): string => + // @ts-expect-error: TODO: implement typings + connection.connection.format(sql, values); await connection.end(); describe('SELECT without values', () => { it('should return the query unchanged', () => { - const query = format('SELECT * FROM users', []); + const query: string = format('SELECT * FROM users', []); assert.strictEqual(query, 'SELECT * FROM users'); }); @@ -23,7 +27,7 @@ await describe('stringifyObjects: false', async () => { describe('SELECT with object parameter', () => { it('should generate a safe query for a legitimate string', () => { - const query = format('SELECT * FROM users WHERE email = ?', [ + const query: string = format('SELECT * FROM users WHERE email = ?', [ 'admin@example.com', ]); @@ -34,7 +38,7 @@ await describe('stringifyObjects: false', async () => { }); it('should not generate a SQL fragment for object { email: 1 }', () => { - const query = format('SELECT * FROM users WHERE email = ?', [ + const query: string = format('SELECT * FROM users WHERE email = ?', [ { email: 1 }, ]); @@ -47,7 +51,7 @@ await describe('stringifyObjects: false', async () => { describe('SELECT with multiple parameters', () => { it('should generate a safe query for a wrong password', () => { - const query = format( + const query: string = format( 'SELECT * FROM users WHERE email = ? AND password = ?', ['admin@example.com', 'wrong_password'] ); @@ -59,7 +63,7 @@ await describe('stringifyObjects: false', async () => { }); it('should not alter the query structure for object { email: 1 }', () => { - const query = format( + const query: string = format( 'SELECT * FROM users WHERE email = ? AND password = ?', [{ email: 1 }, 'user1_pass'] ); @@ -73,13 +77,15 @@ await describe('stringifyObjects: false', async () => { describe('DELETE with object parameter', () => { it('should generate a safe query for a legitimate id', () => { - const query = format('DELETE FROM users WHERE id = ?', [1]); + const query: string = format('DELETE FROM users WHERE id = ?', [1]); assert.strictEqual(query, 'DELETE FROM users WHERE id = 1'); }); it('should not generate a SQL fragment for object { id: true }', () => { - const query = format('DELETE FROM users WHERE id = ?', [{ id: true }]); + const query: string = format('DELETE FROM users WHERE id = ?', [ + { id: true }, + ]); assert.strictEqual( query, @@ -90,7 +96,7 @@ await describe('stringifyObjects: false', async () => { describe('SET with object parameter', () => { it('should convert object to key-value pairs for UPDATE SET clause', () => { - const query = format('UPDATE users SET ?', [ + const query: string = format('UPDATE users SET ?', [ { name: 'foo', email: 'bar@test.com' }, ]); @@ -101,7 +107,7 @@ await describe('stringifyObjects: false', async () => { }); it('should convert object to key-value pairs for INSERT SET clause', () => { - const query = format('INSERT INTO users SET ?', [ + const query: string = format('INSERT INTO users SET ?', [ { name: 'foo', email: 'bar@test.com' }, ]); @@ -112,7 +118,7 @@ await describe('stringifyObjects: false', async () => { }); it('should convert object to key-value pairs for ON DUPLICATE KEY UPDATE clause', () => { - const query = format( + const query: string = format( 'INSERT INTO users (name, email) VALUES (?, ?) ON DUPLICATE KEY UPDATE ?', ['foo', 'bar@test.com', { name: 'foo', email: 'bar@test.com' }] ); @@ -127,7 +133,10 @@ await describe('stringifyObjects: false', async () => { describe('SELECT and INSERT with Date parameter', () => { it('should format Date as a valid datetime string, not as [object Object]', () => { const date = new Date('2026-01-01T10:30:00.000Z'); - const query = format('SELECT * FROM events WHERE created_at = ?', [date]); + const query: string = format( + 'SELECT * FROM events WHERE created_at = ?', + [date] + ); assert.strictEqual( query, @@ -137,7 +146,7 @@ await describe('stringifyObjects: false', async () => { it('should format Date in INSERT statements', () => { const date = new Date('2026-01-01T00:00:00.000Z'); - const query = format( + const query: string = format( 'INSERT INTO logs (message, created_at) VALUES (?, ?)', ['test', date] ); diff --git a/test/esm/unit/parsers/stringify-objects-as-true.test.mjs b/test/esm/unit/parsers/stringify-objects-as-true.test.mts similarity index 77% rename from test/esm/unit/parsers/stringify-objects-as-true.test.mjs rename to test/esm/unit/parsers/stringify-objects-as-true.test.mts index 653cc3f072..b3816dce72 100644 --- a/test/esm/unit/parsers/stringify-objects-as-true.test.mjs +++ b/test/esm/unit/parsers/stringify-objects-as-true.test.mts @@ -1,5 +1,7 @@ +import type { format as Format } from 'sql-escaper'; import { assert, describe, it } from 'poku'; -import { config, localDate, driver } from '../../common.test.mjs'; +import driver from '../../../../index.js'; +import { config, localDate } from '../../common.test.mjs'; await describe('stringifyObjects: true', async () => { const connection = driver @@ -9,13 +11,15 @@ await describe('stringifyObjects: true', async () => { }) .promise(); - const format = (sql, values) => connection.connection.format(sql, values); + const format: typeof Format = (sql, values): string => + // @ts-expect-error: TODO: implement typings + connection.connection.format(sql, values); await connection.end(); describe('SELECT without values', () => { it('should return the query unchanged', () => { - const query = format('SELECT * FROM users', []); + const query: string = format('SELECT * FROM users', []); assert.strictEqual(query, 'SELECT * FROM users'); }); @@ -23,7 +27,7 @@ await describe('stringifyObjects: true', async () => { describe('SELECT with object parameter', () => { it('should generate a safe query for a legitimate string', () => { - const query = format('SELECT * FROM users WHERE email = ?', [ + const query: string = format('SELECT * FROM users WHERE email = ?', [ 'admin@example.com', ]); @@ -34,7 +38,7 @@ await describe('stringifyObjects: true', async () => { }); it('should not generate a SQL fragment for object { email: 1 }', () => { - const query = format('SELECT * FROM users WHERE email = ?', [ + const query: string = format('SELECT * FROM users WHERE email = ?', [ { email: 1 }, ]); @@ -47,7 +51,7 @@ await describe('stringifyObjects: true', async () => { describe('SELECT with multiple parameters', () => { it('should generate a safe query for a wrong password', () => { - const query = format( + const query: string = format( 'SELECT * FROM users WHERE email = ? AND password = ?', ['admin@example.com', 'wrong_password'] ); @@ -59,7 +63,7 @@ await describe('stringifyObjects: true', async () => { }); it('should not alter the query structure for object { email: 1 }', () => { - const query = format( + const query: string = format( 'SELECT * FROM users WHERE email = ? AND password = ?', [{ email: 1 }, 'user1_pass'] ); @@ -73,13 +77,15 @@ await describe('stringifyObjects: true', async () => { describe('DELETE with object parameter', () => { it('should generate a safe query for a legitimate id', () => { - const query = format('DELETE FROM users WHERE id = ?', [1]); + const query: string = format('DELETE FROM users WHERE id = ?', [1]); assert.strictEqual(query, 'DELETE FROM users WHERE id = 1'); }); it('should not generate a SQL fragment for object { id: true }', () => { - const query = format('DELETE FROM users WHERE id = ?', [{ id: true }]); + const query: string = format('DELETE FROM users WHERE id = ?', [ + { id: true }, + ]); assert.strictEqual( query, @@ -90,7 +96,7 @@ await describe('stringifyObjects: true', async () => { describe('SET with object parameter', () => { it('should stringify object instead of expanding for UPDATE SET clause', () => { - const query = format('UPDATE users SET ?', [ + const query: string = format('UPDATE users SET ?', [ { name: 'foo', email: 'bar@test.com' }, ]); @@ -98,7 +104,7 @@ await describe('stringifyObjects: true', async () => { }); it('should stringify object instead of expanding for INSERT SET clause', () => { - const query = format('INSERT INTO users SET ?', [ + const query: string = format('INSERT INTO users SET ?', [ { name: 'foo', email: 'bar@test.com' }, ]); @@ -106,7 +112,7 @@ await describe('stringifyObjects: true', async () => { }); it('should stringify object instead of expanding for ON DUPLICATE KEY UPDATE clause', () => { - const query = format( + const query: string = format( 'INSERT INTO users (name, email) VALUES (?, ?) ON DUPLICATE KEY UPDATE ?', ['foo', 'bar@test.com', { name: 'foo', email: 'bar@test.com' }] ); @@ -121,7 +127,10 @@ await describe('stringifyObjects: true', async () => { describe('SELECT and INSERT with Date parameter', () => { it('should format Date as a valid datetime string, not as [object Object]', () => { const date = new Date('2026-01-01T10:30:00.000Z'); - const query = format('SELECT * FROM events WHERE created_at = ?', [date]); + const query: string = format( + 'SELECT * FROM events WHERE created_at = ?', + [date] + ); assert.strictEqual( query, @@ -131,7 +140,7 @@ await describe('stringifyObjects: true', async () => { it('should format Date in INSERT statements', () => { const date = new Date('2026-01-01T00:00:00.000Z'); - const query = format( + const query: string = format( 'INSERT INTO logs (message, created_at) VALUES (?, ?)', ['test', date] ); diff --git a/test/esm/unit/parsers/support-big-numbers-binary-sanitization.test.mjs b/test/esm/unit/parsers/support-big-numbers-binary-sanitization.test.mts similarity index 70% rename from test/esm/unit/parsers/support-big-numbers-binary-sanitization.test.mjs rename to test/esm/unit/parsers/support-big-numbers-binary-sanitization.test.mts index db161847d9..30dd062122 100644 --- a/test/esm/unit/parsers/support-big-numbers-binary-sanitization.test.mjs +++ b/test/esm/unit/parsers/support-big-numbers-binary-sanitization.test.mts @@ -5,19 +5,26 @@ await describe('Binary Parser: supportBigNumbers Sanitization', async () => { const connection = createConnection().promise(); const sql = 'SELECT 9007199254740991+100 AS `total`'; - const cases = [ + const cases: [boolean, string, string][] = [ [true, 'string', 'Valid supportBigNumbers enabled'], [false, 'number', 'Valid supportBigNumbers disabled'], [ + // @ts-expect-error: testing sanitization with invalid string value 'text', 'string', 'supportBigNumbers as a random string should be enabled', ], - ['', 'number', 'supportBigNumbers as an empty string should be disabled'], + [ + // @ts-expect-error: testing sanitization with invalid string value + '', + 'number', + 'supportBigNumbers as an empty string should be disabled', + ], ]; for (const [supportBigNumbers, expectedType, label] of cases) { await it(label, async () => { + // @ts-expect-error: TODO: implement typings const [results] = await connection.execute({ sql, supportBigNumbers, diff --git a/test/esm/unit/parsers/support-big-numbers-text-sanitization.test.mjs b/test/esm/unit/parsers/support-big-numbers-text-sanitization.test.mts similarity index 70% rename from test/esm/unit/parsers/support-big-numbers-text-sanitization.test.mjs rename to test/esm/unit/parsers/support-big-numbers-text-sanitization.test.mts index 04850e142b..b46185f369 100644 --- a/test/esm/unit/parsers/support-big-numbers-text-sanitization.test.mjs +++ b/test/esm/unit/parsers/support-big-numbers-text-sanitization.test.mts @@ -5,19 +5,26 @@ await describe('Text Parser: supportBigNumbers Sanitization', async () => { const connection = createConnection().promise(); const sql = 'SELECT 9007199254740991+100 AS `total`'; - const cases = [ + const cases: [boolean, string, string][] = [ [true, 'string', 'Valid supportBigNumbers enabled'], [false, 'number', 'Valid supportBigNumbers disabled'], [ + // @ts-expect-error: testing sanitization with invalid string value 'text', 'string', 'supportBigNumbers as a random string should be enabled', ], - ['', 'number', 'supportBigNumbers as an empty string should be disabled'], + [ + // @ts-expect-error: testing sanitization with invalid string value + '', + 'number', + 'supportBigNumbers as an empty string should be disabled', + ], ]; for (const [supportBigNumbers, expectedType, label] of cases) { await it(label, async () => { + // @ts-expect-error: TODO: implement typings const [results] = await connection.query({ sql, supportBigNumbers, diff --git a/test/esm/unit/parsers/timezone-binary-sanitization.test.mjs b/test/esm/unit/parsers/timezone-binary-sanitization.test.mts similarity index 92% rename from test/esm/unit/parsers/timezone-binary-sanitization.test.mjs rename to test/esm/unit/parsers/timezone-binary-sanitization.test.mts index 80f5b9c2a3..b03ed86dba 100644 --- a/test/esm/unit/parsers/timezone-binary-sanitization.test.mjs +++ b/test/esm/unit/parsers/timezone-binary-sanitization.test.mts @@ -8,6 +8,7 @@ await describe('Binary Parser: timezone Sanitization', async () => { await it(async () => { process.env.TEST_ENV_VALUE = 'secure'; + // @ts-expect-error: TODO: implement typings await connection.execute({ sql: 'SELECT NOW()', timezone: `'); process.env.TEST_ENV_VALUE = "not so much"; //`, diff --git a/test/esm/unit/parsers/timezone-text-sanitization.test.mjs b/test/esm/unit/parsers/timezone-text-sanitization.test.mts similarity index 92% rename from test/esm/unit/parsers/timezone-text-sanitization.test.mjs rename to test/esm/unit/parsers/timezone-text-sanitization.test.mts index 0f1d48cc7d..6e515b9356 100644 --- a/test/esm/unit/parsers/timezone-text-sanitization.test.mjs +++ b/test/esm/unit/parsers/timezone-text-sanitization.test.mts @@ -8,6 +8,7 @@ await describe('Text Parser: timezone Sanitization', async () => { await it(async () => { process.env.TEST_ENV_VALUE = 'secure'; + // @ts-expect-error: TODO: implement typings await connection.query({ sql: 'SELECT NOW()', timezone: `'); process.env.TEST_ENV_VALUE = "not so much"; //`, diff --git a/test/esm/unit/protocol/SqlString.test.mjs b/test/esm/unit/protocol/SqlString.test.mts similarity index 89% rename from test/esm/unit/protocol/SqlString.test.mjs rename to test/esm/unit/protocol/SqlString.test.mts index 203fa5cb90..9a7bef5fb2 100644 --- a/test/esm/unit/protocol/SqlString.test.mjs +++ b/test/esm/unit/protocol/SqlString.test.mts @@ -181,7 +181,7 @@ describe('SqlString.escape tests', () => { it(() => { const expected = '2012-05-07 11:42:03.002'; const date = new Date(2012, 4, 7, 11, 42, 3, 2); - const string = SqlString.escape(date); + const string: string = SqlString.escape(date); assert.strictEqual( string, `'${expected}'`, @@ -191,7 +191,7 @@ describe('SqlString.escape tests', () => { it(() => { const buffer = Buffer.from([0, 1, 254, 255]); - const string = SqlString.escape(buffer); + const string: string = SqlString.escape(buffer); assert.strictEqual(string, "X'0001feff'", 'buffers are converted to hex'); }); @@ -210,7 +210,7 @@ describe('SqlString.escape tests', () => { describe('SqlString.format tests', () => { it(() => { - const sql = SqlString.format('? and ?', ['a', 'b']); + const sql: string = SqlString.format('? and ?', ['a', 'b']); assert.equal( sql, "'a' and 'b'", @@ -219,17 +219,17 @@ describe('SqlString.format tests', () => { }); it(() => { - const sql = SqlString.format('? and ?', ['a']); + const sql: string = SqlString.format('? and ?', ['a']); assert.equal(sql, "'a' and ?", 'extra question marks are left untouched'); }); it(() => { - const sql = SqlString.format('? and ?', ['a', 'b', 'c']); + const sql: string = SqlString.format('? and ?', ['a', 'b', 'c']); assert.equal(sql, "'a' and 'b'", 'extra arguments are not used'); }); it(() => { - const sql = SqlString.format('? and ?', ['hello?', 'b']); + const sql: string = SqlString.format('? and ?', ['hello?', 'b']); assert.equal( sql, "'hello?' and 'b'", @@ -238,12 +238,12 @@ describe('SqlString.format tests', () => { }); it(() => { - const sql = SqlString.format('?', undefined); + const sql: string = SqlString.format('?', undefined); assert.equal(sql, '?', 'undefined is ignored'); }); it(() => { - const sql = SqlString.format('SET ?', { hello: 'world' }); + const sql: string = SqlString.format('SET ?', { hello: 'world' }); assert.equal( sql, "SET `hello` = 'world'", @@ -252,14 +252,14 @@ describe('SqlString.format tests', () => { }); it(() => { - const sql = SqlString.format('?', { hello: 'world' }, true); + const sql: string = SqlString.format('?', { hello: 'world' }, true); assert.equal( sql, "'[object Object]'", 'objects is not converted to values when flag is true' ); - const sql2 = SqlString.format( + const sql2: string = SqlString.format( '?', { toString: function () { diff --git a/test/tsc-build/index.test.ts b/test/tsc-build/index.test.ts index 0ae091ed9a..f5ef28dcb0 100644 --- a/test/tsc-build/index.test.ts +++ b/test/tsc-build/index.test.ts @@ -1,7 +1,7 @@ /** * This tests doesn't execute the scripts or connect in any database. * It only compiles all typings in the project and ensures that the compilation will be successful. - * To test it, run: npm run test:tsc-build + * To test it, run: npm run typecheck * * The purpose of this test is to prevent changes that break the typings in new PRs *