Skip to content

Commit

Permalink
Convert E2E tests to Typescript (#257)
Browse files Browse the repository at this point in the history
* Convert E2E tests to typescript

Signed-off-by: Levko Kravets <[email protected]>

* Adjust Typescript configs for building and linting scenarios

Signed-off-by: Levko Kravets <[email protected]>

* Update tests

Signed-off-by: Levko Kravets <[email protected]>

---------

Signed-off-by: Levko Kravets <[email protected]>
  • Loading branch information
kravets-levko authored May 14, 2024
1 parent fb817b5 commit 3eed509
Show file tree
Hide file tree
Showing 18 changed files with 413 additions and 272 deletions.
7 changes: 7 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@
}
]
}
},
{
"files": ["*.test.js", "*.test.ts"],
"rules": {
"no-unused-expressions": "off",
"@typescript-eslint/no-unused-expressions": "off"
}
}
]
}
243 changes: 156 additions & 87 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand All @@ -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",
Expand All @@ -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"
},
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/.mocharc.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

const allSpecs = 'tests/e2e/**/*.test.js';
const allSpecs = 'tests/e2e/**/*.test.ts';

const argvSpecs = process.argv.slice(4);

Expand Down
47 changes: 29 additions & 18 deletions tests/e2e/arrow.test.js → tests/e2e/arrow.test.ts
Original file line number Diff line number Diff line change
@@ -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<ClientConfig> = {}) {
const client = new DBSQLClient();

const clientConfig = client.getConfig();
Expand All @@ -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);
Expand All @@ -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<void>,
customConfig: Partial<ClientConfig> = {},
) {
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();
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand Down
42 changes: 26 additions & 16 deletions tests/e2e/batched_fetch.test.js → tests/e2e/batched_fetch.test.ts
Original file line number Diff line number Diff line change
@@ -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<ClientConfig> = {}) {
const client = new DBSQLClient();

const clientConfig = client.getConfig();
Expand All @@ -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,
});
}

Expand All @@ -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();
Expand All @@ -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;
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand Down
34 changes: 21 additions & 13 deletions tests/e2e/cloudfetch.test.js → tests/e2e/cloudfetch.test.ts
Original file line number Diff line number Diff line change
@@ -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<ClientConfig> = {}) {
const client = new DBSQLClient();

const clientConfig = client.getConfig();
Expand All @@ -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,
});
}

Expand Down Expand Up @@ -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);
Expand All @@ -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;

Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 3eed509

Please sign in to comment.