From 3da3e4a7918480ba5b746741d790b87ef052cf2a Mon Sep 17 00:00:00 2001 From: Levko Kravets Date: Sat, 7 Oct 2023 23:10:07 +0300 Subject: [PATCH] Convert Json/Arrow/CloudFetch result handlers to implement result provider interface Signed-off-by: Levko Kravets --- lib/DBSQLOperation/index.ts | 33 +++++++----------- .../{ArrowResult.ts => ArrowResultHandler.ts} | 23 ++++++++++--- ...chResult.ts => CloudFetchResultHandler.ts} | 16 +++++---- lib/result/IOperationResult.ts | 7 ---- .../{JsonResult.ts => JsonResultHandler.ts} | 18 +++++++--- tests/e2e/arrow.test.js | 12 +++---- tests/e2e/cloudfetch.test.js | 12 +++---- tests/unit/DBSQLOperation.test.js | 12 +++---- ...ult.test.js => ArrowResultHandler.test.js} | 22 +++++++----- ...est.js => CloudFetchResultHandler.test.js} | 26 ++++++++------ ...sult.test.js => JsonResultHandler.test.js} | 34 ++++++++++++------- tests/unit/result/compatibility.test.js | 15 +++++--- .../result/fixtures/RowSetProviderMock.js | 11 ++++++ 13 files changed, 145 insertions(+), 96 deletions(-) rename lib/result/{ArrowResult.ts => ArrowResultHandler.ts} (87%) rename lib/result/{CloudFetchResult.ts => CloudFetchResultHandler.ts} (78%) delete mode 100644 lib/result/IOperationResult.ts rename lib/result/{JsonResult.ts => JsonResultHandler.ts} (79%) rename tests/unit/result/{ArrowResult.test.js => ArrowResultHandler.test.js} (80%) rename tests/unit/result/{CloudFetchResult.test.js => CloudFetchResultHandler.test.js} (87%) rename tests/unit/result/{JsonResult.test.js => JsonResultHandler.test.js} (90%) create mode 100644 tests/unit/result/fixtures/RowSetProviderMock.js diff --git a/lib/DBSQLOperation/index.ts b/lib/DBSQLOperation/index.ts index 7d249962..08e32864 100644 --- a/lib/DBSQLOperation/index.ts +++ b/lib/DBSQLOperation/index.ts @@ -18,11 +18,11 @@ import { import Status from '../dto/Status'; import { LogLevel } from '../contracts/IDBSQLLogger'; import OperationStateError, { OperationStateErrorCode } from '../errors/OperationStateError'; -import IOperationResult from '../result/IOperationResult'; +import IResultsProvider from '../result/IResultsProvider'; import RowSetProvider from '../result/RowSetProvider'; -import JsonResult from '../result/JsonResult'; -import ArrowResult from '../result/ArrowResult'; -import CloudFetchResult from '../result/CloudFetchResult'; +import JsonResultHandler from '../result/JsonResultHandler'; +import ArrowResultHandler from '../result/ArrowResultHandler'; +import CloudFetchResultHandler from '../result/CloudFetchResultHandler'; import { definedOrError } from '../utils'; import HiveDriverError from '../errors/HiveDriverError'; import IClientContext from '../contracts/IClientContext'; @@ -68,7 +68,7 @@ export default class DBSQLOperation implements IOperation { private hasResultSet: boolean = false; - private resultHandler?: IOperationResult; + private resultHandler?: IResultsProvider>; constructor({ handle, directResults, context }: DBSQLOperationConstructorOptions) { this.operationHandle = handle; @@ -135,14 +135,12 @@ export default class DBSQLOperation implements IOperation { await this.waitUntilReady(options); - const [resultHandler, data] = await Promise.all([ - this.getResultHandler(), - this._data.fetchNext({ limit: options?.maxRows || defaultMaxRows }), - ]); + const resultHandler = await this.getResultHandler(); + await this.failIfClosed(); + const result = resultHandler.fetchNext({ limit: options?.maxRows || defaultMaxRows }); await this.failIfClosed(); - const result = await resultHandler.getValue(data ? [data] : []); this.context .getLogger() .log( @@ -234,14 +232,9 @@ export default class DBSQLOperation implements IOperation { return false; } - // Return early if there are still data available for fetching - if (this._data.hasMoreRows) { - return true; - } - // If we fetched all the data from server - check if there's anything buffered in result handler const resultHandler = await this.getResultHandler(); - return resultHandler.hasPendingData(); + return resultHandler.hasMore(); } public async getSchema(options?: GetSchemaOptions): Promise { @@ -342,20 +335,20 @@ export default class DBSQLOperation implements IOperation { return this.metadata; } - private async getResultHandler(): Promise { + private async getResultHandler(): Promise>> { const metadata = await this.fetchMetadata(); const resultFormat = definedOrError(metadata.resultFormat); if (!this.resultHandler) { switch (resultFormat) { case TSparkRowSetType.COLUMN_BASED_SET: - this.resultHandler = new JsonResult(this.context, metadata.schema); + this.resultHandler = new JsonResultHandler(this.context, this._data, metadata.schema); break; case TSparkRowSetType.ARROW_BASED_SET: - this.resultHandler = new ArrowResult(this.context, metadata.schema, metadata.arrowSchema); + this.resultHandler = new ArrowResultHandler(this.context, this._data, metadata.schema, metadata.arrowSchema); break; case TSparkRowSetType.URL_BASED_SET: - this.resultHandler = new CloudFetchResult(this.context, metadata.schema); + this.resultHandler = new CloudFetchResultHandler(this.context, this._data, metadata.schema); break; default: this.resultHandler = undefined; diff --git a/lib/result/ArrowResult.ts b/lib/result/ArrowResultHandler.ts similarity index 87% rename from lib/result/ArrowResult.ts rename to lib/result/ArrowResultHandler.ts index b44ae305..0e5058f7 100644 --- a/lib/result/ArrowResult.ts +++ b/lib/result/ArrowResultHandler.ts @@ -13,7 +13,7 @@ import { } from 'apache-arrow'; import { TRowSet, TTableSchema, TColumnDesc } from '../../thrift/TCLIService_types'; import IClientContext from '../contracts/IClientContext'; -import IOperationResult from './IOperationResult'; +import IResultsProvider, { ResultsProviderFetchNextOptions } from './IResultsProvider'; import { getSchemaColumns, convertThriftValue } from './utils'; const { isArrowBigNumSymbol, bigNumToBigInt } = arrowUtils; @@ -21,21 +21,34 @@ const { isArrowBigNumSymbol, bigNumToBigInt } = arrowUtils; type ArrowSchema = Schema; type ArrowSchemaField = Field>; -export default class ArrowResult implements IOperationResult { +export default class ArrowResultHandler implements IResultsProvider> { protected readonly context: IClientContext; + private readonly source: IResultsProvider; + private readonly schema: Array; private readonly arrowSchema?: Buffer; - constructor(context: IClientContext, schema?: TTableSchema, arrowSchema?: Buffer) { + constructor( + context: IClientContext, + source: IResultsProvider, + schema?: TTableSchema, + arrowSchema?: Buffer, + ) { this.context = context; + this.source = source; this.schema = getSchemaColumns(schema); this.arrowSchema = arrowSchema; } - async hasPendingData() { - return false; + async hasMore() { + return this.source.hasMore(); + } + + async fetchNext(options: ResultsProviderFetchNextOptions) { + const data = await this.source.fetchNext(options); + return this.getValue(data ? [data] : []); } async getValue(data?: Array) { diff --git a/lib/result/CloudFetchResult.ts b/lib/result/CloudFetchResultHandler.ts similarity index 78% rename from lib/result/CloudFetchResult.ts rename to lib/result/CloudFetchResultHandler.ts index 31fbd633..33c6eecd 100644 --- a/lib/result/CloudFetchResult.ts +++ b/lib/result/CloudFetchResultHandler.ts @@ -2,22 +2,26 @@ import { Buffer } from 'buffer'; import fetch, { RequestInfo, RequestInit } from 'node-fetch'; import { TRowSet, TSparkArrowResultLink, TTableSchema } from '../../thrift/TCLIService_types'; import IClientContext from '../contracts/IClientContext'; -import ArrowResult from './ArrowResult'; +import IResultsProvider from './IResultsProvider'; +import ArrowResultHandler from './ArrowResultHandler'; import globalConfig from '../globalConfig'; -export default class CloudFetchResult extends ArrowResult { +export default class CloudFetchResultHandler extends ArrowResultHandler { private pendingLinks: Array = []; private downloadedBatches: Array = []; - constructor(context: IClientContext, schema?: TTableSchema) { + constructor(context: IClientContext, source: IResultsProvider, schema?: TTableSchema) { // Arrow schema returned in metadata is not needed for CloudFetch results: // each batch already contains schema and could be decoded as is - super(context, schema, Buffer.alloc(0)); + super(context, source, schema, Buffer.alloc(0)); } - async hasPendingData() { - return this.pendingLinks.length > 0 || this.downloadedBatches.length > 0; + async hasMore() { + if (this.pendingLinks.length > 0 || this.downloadedBatches.length > 0) { + return true; + } + return super.hasMore(); } protected async getBatches(data: Array): Promise> { diff --git a/lib/result/IOperationResult.ts b/lib/result/IOperationResult.ts deleted file mode 100644 index 7b42a196..00000000 --- a/lib/result/IOperationResult.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { TRowSet } from '../../thrift/TCLIService_types'; - -export default interface IOperationResult { - getValue(data?: Array): Promise; - - hasPendingData(): Promise; -} diff --git a/lib/result/JsonResult.ts b/lib/result/JsonResultHandler.ts similarity index 79% rename from lib/result/JsonResult.ts rename to lib/result/JsonResultHandler.ts index 0c7daefa..692438d5 100644 --- a/lib/result/JsonResult.ts +++ b/lib/result/JsonResultHandler.ts @@ -1,21 +1,29 @@ import { ColumnCode } from '../hive/Types'; import { TRowSet, TTableSchema, TColumn, TColumnDesc } from '../../thrift/TCLIService_types'; import IClientContext from '../contracts/IClientContext'; -import IOperationResult from './IOperationResult'; +import IResultsProvider, { ResultsProviderFetchNextOptions } from './IResultsProvider'; import { getSchemaColumns, convertThriftValue } from './utils'; -export default class JsonResult implements IOperationResult { +export default class JsonResultHandler implements IResultsProvider> { private readonly context: IClientContext; + private readonly source: IResultsProvider; + private readonly schema: Array; - constructor(context: IClientContext, schema?: TTableSchema) { + constructor(context: IClientContext, source: IResultsProvider, schema?: TTableSchema) { this.context = context; + this.source = source; this.schema = getSchemaColumns(schema); } - async hasPendingData() { - return false; + async hasMore() { + return this.source.hasMore(); + } + + async fetchNext(options: ResultsProviderFetchNextOptions) { + const data = await this.source.fetchNext(options); + return this.getValue(data ? [data] : []); } async getValue(data?: Array): Promise> { diff --git a/tests/e2e/arrow.test.js b/tests/e2e/arrow.test.js index a75c3059..a2612c6b 100644 --- a/tests/e2e/arrow.test.js +++ b/tests/e2e/arrow.test.js @@ -2,7 +2,7 @@ const { expect } = require('chai'); const config = require('./utils/config'); const logger = require('./utils/logger')(config.logger); const { DBSQLClient } = require('../..'); -const ArrowResult = require('../../dist/result/ArrowResult').default; +const ArrowResultHandler = require('../../dist/result/ArrowResultHandler').default; const globalConfig = require('../../dist/globalConfig').default; const fixtures = require('../fixtures/compatibility'); @@ -76,7 +76,7 @@ describe('Arrow support', () => { expect(result).to.deep.equal(expectedColumn); const resultHandler = await operation.getResultHandler(); - expect(resultHandler).to.be.not.instanceof(ArrowResult); + expect(resultHandler).to.be.not.instanceof(ArrowResultHandler); await operation.close(); }), @@ -93,7 +93,7 @@ describe('Arrow support', () => { expect(fixArrowResult(result)).to.deep.equal(expectedArrow); const resultHandler = await operation.getResultHandler(); - expect(resultHandler).to.be.instanceof(ArrowResult); + expect(resultHandler).to.be.instanceof(ArrowResultHandler); await operation.close(); }), @@ -110,7 +110,7 @@ describe('Arrow support', () => { expect(fixArrowResult(result)).to.deep.equal(expectedArrowNativeTypes); const resultHandler = await operation.getResultHandler(); - expect(resultHandler).to.be.instanceof(ArrowResult); + expect(resultHandler).to.be.instanceof(ArrowResultHandler); await operation.close(); }), @@ -130,9 +130,9 @@ describe('Arrow support', () => { // We use some internals here to check that server returned response with multiple batches const resultHandler = await operation.getResultHandler(); - expect(resultHandler).to.be.instanceof(ArrowResult); + expect(resultHandler).to.be.instanceof(ArrowResultHandler); - const rawData = await operation._data.fetch(rowsCount); + const rawData = await operation._data.fetchNext({ limit: rowsCount }); // 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 expect(rawData.arrowBatches?.length).to.be.gt(1); diff --git a/tests/e2e/cloudfetch.test.js b/tests/e2e/cloudfetch.test.js index 3997f6af..03b2cb60 100644 --- a/tests/e2e/cloudfetch.test.js +++ b/tests/e2e/cloudfetch.test.js @@ -3,7 +3,7 @@ const sinon = require('sinon'); const config = require('./utils/config'); const logger = require('./utils/logger')(config.logger); const { DBSQLClient } = require('../..'); -const CloudFetchResult = require('../../dist/result/CloudFetchResult').default; +const CloudFetchResultHandler = require('../../dist/result/CloudFetchResultHandler').default; const globalConfig = require('../../dist/globalConfig').default; const openSession = async () => { @@ -57,24 +57,24 @@ describe('CloudFetch', () => { // Check if we're actually getting data via CloudFetch const resultHandler = await operation.getResultHandler(); - expect(resultHandler).to.be.instanceOf(CloudFetchResult); + expect(resultHandler).to.be.instanceOf(CloudFetchResultHandler); // Fetch first chunk and check if result handler behaves properly. // With the count of rows we queried, there should be at least one row set, // containing 8 result links. After fetching the first chunk, // result handler should download 5 of them and schedule the rest - expect(await resultHandler.hasPendingData()).to.be.false; + expect(await resultHandler.hasMore()).to.be.false; expect(resultHandler.pendingLinks.length).to.be.equal(0); expect(resultHandler.downloadedBatches.length).to.be.equal(0); - sinon.spy(operation._data, 'fetch'); + sinon.spy(operation._data, 'fetchNext'); const chunk = await operation.fetchChunk({ maxRows: 100000 }); // Count links returned from server - const resultSet = await operation._data.fetch.firstCall.returnValue; + const resultSet = await operation._data.fetchNext.firstCall.returnValue; const resultLinksCount = resultSet?.resultLinks?.length ?? 0; - expect(await resultHandler.hasPendingData()).to.be.true; + expect(await resultHandler.hasMore()).to.be.true; // expected batches minus first 5 already fetched expect(resultHandler.pendingLinks.length).to.be.equal( resultLinksCount - globalConfig.cloudFetchConcurrentDownloads, diff --git a/tests/unit/DBSQLOperation.test.js b/tests/unit/DBSQLOperation.test.js index 94834baf..ef9c89c9 100644 --- a/tests/unit/DBSQLOperation.test.js +++ b/tests/unit/DBSQLOperation.test.js @@ -6,9 +6,9 @@ const DBSQLOperation = require('../../dist/DBSQLOperation').default; const StatusError = require('../../dist/errors/StatusError').default; const OperationStateError = require('../../dist/errors/OperationStateError').default; const HiveDriverError = require('../../dist/errors/HiveDriverError').default; -const JsonResult = require('../../dist/result/JsonResult').default; -const ArrowResult = require('../../dist/result/ArrowResult').default; -const CloudFetchResult = require('../../dist/result/CloudFetchResult').default; +const JsonResultHandler = require('../../dist/result/JsonResultHandler').default; +const ArrowResultHandler = require('../../dist/result/ArrowResultHandler').default; +const CloudFetchResultHandler = require('../../dist/result/CloudFetchResultHandler').default; class OperationHandleMock { constructor(hasResultSet = true) { @@ -885,7 +885,7 @@ describe('DBSQLOperation', () => { const operation = new DBSQLOperation({ handle, context }); const resultHandler = await operation.getResultHandler(); expect(context.driver.getResultSetMetadata.called).to.be.true; - expect(resultHandler).to.be.instanceOf(JsonResult); + expect(resultHandler).to.be.instanceOf(JsonResultHandler); } arrowHandler: { @@ -895,7 +895,7 @@ describe('DBSQLOperation', () => { const operation = new DBSQLOperation({ handle, context }); const resultHandler = await operation.getResultHandler(); expect(context.driver.getResultSetMetadata.called).to.be.true; - expect(resultHandler).to.be.instanceOf(ArrowResult); + expect(resultHandler).to.be.instanceOf(ArrowResultHandler); } cloudFetchHandler: { @@ -905,7 +905,7 @@ describe('DBSQLOperation', () => { const operation = new DBSQLOperation({ handle, context }); const resultHandler = await operation.getResultHandler(); expect(context.driver.getResultSetMetadata.called).to.be.true; - expect(resultHandler).to.be.instanceOf(CloudFetchResult); + expect(resultHandler).to.be.instanceOf(CloudFetchResultHandler); } }); }); diff --git a/tests/unit/result/ArrowResult.test.js b/tests/unit/result/ArrowResultHandler.test.js similarity index 80% rename from tests/unit/result/ArrowResult.test.js rename to tests/unit/result/ArrowResultHandler.test.js index 27244190..5a59c3e6 100644 --- a/tests/unit/result/ArrowResult.test.js +++ b/tests/unit/result/ArrowResultHandler.test.js @@ -1,7 +1,8 @@ const { expect } = require('chai'); const fs = require('fs'); const path = require('path'); -const ArrowResult = require('../../../dist/result/ArrowResult').default; +const ArrowResultHandler = require('../../../dist/result/ArrowResultHandler').default; +const RowSetProviderMock = require('./fixtures/RowSetProviderMock'); const sampleThriftSchema = { columns: [ @@ -84,17 +85,19 @@ const rowSetAllNulls = { ], }; -describe('ArrowResult', () => { +describe('ArrowResultHandler', () => { it('should not buffer any data', async () => { const context = {}; - const result = new ArrowResult(context, sampleThriftSchema, sampleArrowSchema); + const rowSetProvider = new RowSetProviderMock(); + const result = new ArrowResultHandler(context, rowSetProvider, sampleThriftSchema, sampleArrowSchema); await result.getValue([sampleRowSet1]); - expect(await result.hasPendingData()).to.be.false; + expect(await result.hasMore()).to.be.false; }); it('should convert data', async () => { const context = {}; - const result = new ArrowResult(context, sampleThriftSchema, sampleArrowSchema); + const rowSetProvider = new RowSetProviderMock(); + const result = new ArrowResultHandler(context, rowSetProvider, sampleThriftSchema, sampleArrowSchema); expect(await result.getValue([sampleRowSet1])).to.be.deep.eq([]); expect(await result.getValue([sampleRowSet2])).to.be.deep.eq([]); expect(await result.getValue([sampleRowSet3])).to.be.deep.eq([]); @@ -103,20 +106,23 @@ describe('ArrowResult', () => { it('should return empty array if no data to process', async () => { const context = {}; - const result = new ArrowResult(context, sampleThriftSchema, sampleArrowSchema); + const rowSetProvider = new RowSetProviderMock(); + const result = new ArrowResultHandler(context, rowSetProvider, sampleThriftSchema, sampleArrowSchema); expect(await result.getValue()).to.be.deep.eq([]); expect(await result.getValue([])).to.be.deep.eq([]); }); it('should return empty array if no schema available', async () => { const context = {}; - const result = new ArrowResult(context); + const rowSetProvider = new RowSetProviderMock(); + const result = new ArrowResultHandler(context, rowSetProvider); expect(await result.getValue([sampleRowSet4])).to.be.deep.eq([]); }); it('should detect nulls', async () => { const context = {}; - const result = new ArrowResult(context, thriftSchemaAllNulls, arrowSchemaAllNulls); + const rowSetProvider = new RowSetProviderMock(); + const result = new ArrowResultHandler(context, rowSetProvider, thriftSchemaAllNulls, arrowSchemaAllNulls); expect(await result.getValue([rowSetAllNulls])).to.be.deep.eq([ { boolean_field: null, diff --git a/tests/unit/result/CloudFetchResult.test.js b/tests/unit/result/CloudFetchResultHandler.test.js similarity index 87% rename from tests/unit/result/CloudFetchResult.test.js rename to tests/unit/result/CloudFetchResultHandler.test.js index 20451093..aa517864 100644 --- a/tests/unit/result/CloudFetchResult.test.js +++ b/tests/unit/result/CloudFetchResultHandler.test.js @@ -1,8 +1,9 @@ const { expect, AssertionError } = require('chai'); const sinon = require('sinon'); const Int64 = require('node-int64'); -const CloudFetchResult = require('../../../dist/result/CloudFetchResult').default; +const CloudFetchResultHandler = require('../../../dist/result/CloudFetchResultHandler').default; const globalConfig = require('../../../dist/globalConfig').default; +const RowSetProviderMock = require('./fixtures/RowSetProviderMock'); const sampleThriftSchema = { columns: [ @@ -94,7 +95,7 @@ const sampleExpiredRowSet = { ], }; -describe('CloudFetchResult', () => { +describe('CloudFetchResultHandler', () => { let savedConcurrentDownloads; beforeEach(() => { @@ -107,24 +108,25 @@ describe('CloudFetchResult', () => { it('should report pending data if there are any', async () => { const context = {}; - const result = new CloudFetchResult({}, sampleThriftSchema, sampleArrowSchema); + const rowSetProvider = new RowSetProviderMock(); + const result = new CloudFetchResultHandler(context, rowSetProvider, sampleThriftSchema, sampleArrowSchema); case1: { result.pendingLinks = []; result.downloadedBatches = []; - expect(await result.hasPendingData()).to.be.false; + expect(await result.hasMore()).to.be.false; } case2: { result.pendingLinks = [{}]; // just anything here result.downloadedBatches = []; - expect(await result.hasPendingData()).to.be.true; + expect(await result.hasMore()).to.be.true; } case3: { result.pendingLinks = []; result.downloadedBatches = [{}]; // just anything here - expect(await result.hasPendingData()).to.be.true; + expect(await result.hasMore()).to.be.true; } }); @@ -132,8 +134,9 @@ describe('CloudFetchResult', () => { globalConfig.cloudFetchConcurrentDownloads = 0; // this will prevent it from downloading batches const context = {}; + const rowSetProvider = new RowSetProviderMock(); - const result = new CloudFetchResult({}, sampleThriftSchema, sampleArrowSchema); + const result = new CloudFetchResultHandler(context, rowSetProvider, sampleThriftSchema, sampleArrowSchema); sinon.stub(result, 'fetch').returns( Promise.resolve({ @@ -157,8 +160,9 @@ describe('CloudFetchResult', () => { globalConfig.cloudFetchConcurrentDownloads = 2; const context = {}; + const rowSetProvider = new RowSetProviderMock(); - const result = new CloudFetchResult({}, sampleThriftSchema, sampleArrowSchema); + const result = new CloudFetchResultHandler(context, rowSetProvider, sampleThriftSchema, sampleArrowSchema); sinon.stub(result, 'fetch').returns( Promise.resolve({ @@ -205,8 +209,9 @@ describe('CloudFetchResult', () => { globalConfig.cloudFetchConcurrentDownloads = 1; const context = {}; + const rowSetProvider = new RowSetProviderMock(); - const result = new CloudFetchResult({}, sampleThriftSchema, sampleArrowSchema); + const result = new CloudFetchResultHandler(context, rowSetProvider, sampleThriftSchema, sampleArrowSchema); sinon.stub(result, 'fetch').returns( Promise.resolve({ @@ -233,8 +238,9 @@ describe('CloudFetchResult', () => { it('should handle expired links', async () => { const context = {}; + const rowSetProvider = new RowSetProviderMock(); - const result = new CloudFetchResult(context, sampleThriftSchema, sampleArrowSchema); + const result = new CloudFetchResultHandler(context, rowSetProvider, sampleThriftSchema, sampleArrowSchema); sinon.stub(result, 'fetch').returns( Promise.resolve({ diff --git a/tests/unit/result/JsonResult.test.js b/tests/unit/result/JsonResultHandler.test.js similarity index 90% rename from tests/unit/result/JsonResult.test.js rename to tests/unit/result/JsonResultHandler.test.js index f7e90259..0d819a4f 100644 --- a/tests/unit/result/JsonResult.test.js +++ b/tests/unit/result/JsonResultHandler.test.js @@ -1,7 +1,8 @@ const { expect } = require('chai'); -const JsonResult = require('../../../dist/result/JsonResult').default; +const JsonResultHandler = require('../../../dist/result/JsonResultHandler').default; const { TCLIService_types } = require('../../../').thrift; const Int64 = require('node-int64'); +const RowSetProviderMock = require('./fixtures/RowSetProviderMock'); const getColumnSchema = (columnName, type, position) => { if (type === undefined) { @@ -27,7 +28,7 @@ const getColumnSchema = (columnName, type, position) => { }; }; -describe('JsonResult', () => { +describe('JsonResultHandler', () => { it('should not buffer any data', async () => { const schema = { columns: [getColumnSchema('table.id', TCLIService_types.TTypeId.STRING_TYPE, 1)], @@ -39,10 +40,11 @@ describe('JsonResult', () => { ]; const context = {}; + const rowSetProvider = new RowSetProviderMock(); - const result = new JsonResult(context, schema); + const result = new JsonResultHandler(context, rowSetProvider, schema); await result.getValue(data); - expect(await result.hasPendingData()).to.be.false; + expect(await result.hasMore()).to.be.false; }); it('should convert schema with primitive types to json', async () => { @@ -127,8 +129,9 @@ describe('JsonResult', () => { ]; const context = {}; + const rowSetProvider = new RowSetProviderMock(); - const result = new JsonResult(context, schema); + const result = new JsonResultHandler(context, rowSetProvider, schema); expect(await result.getValue(data)).to.be.deep.eq([ { @@ -199,8 +202,9 @@ describe('JsonResult', () => { ]; const context = {}; + const rowSetProvider = new RowSetProviderMock(); - const result = new JsonResult(context, schema); + const result = new JsonResultHandler(context, rowSetProvider, schema); expect(await result.getValue(data)).to.be.deep.eq([ { @@ -241,8 +245,9 @@ describe('JsonResult', () => { ]; const context = {}; + const rowSetProvider = new RowSetProviderMock(); - const result = new JsonResult(context, schema); + const result = new JsonResultHandler(context, rowSetProvider, schema); expect(await result.getValue(data)).to.be.deep.eq([ { 'table.id': '0' }, @@ -254,8 +259,9 @@ describe('JsonResult', () => { it('should detect nulls', () => { const context = {}; + const rowSetProvider = new RowSetProviderMock(); - const result = new JsonResult(context, null); + const result = new JsonResultHandler(context, rowSetProvider, null); const buf = Buffer.from([0x55, 0xaa, 0xc3]); [ @@ -368,8 +374,9 @@ describe('JsonResult', () => { ]; const context = {}; + const rowSetProvider = new RowSetProviderMock(); - const result = new JsonResult(context, schema); + const result = new JsonResultHandler(context, rowSetProvider, schema); expect(await result.getValue(data)).to.be.deep.eq([ { @@ -399,8 +406,9 @@ describe('JsonResult', () => { }; const context = {}; + const rowSetProvider = new RowSetProviderMock(); - const result = new JsonResult(context, schema); + const result = new JsonResultHandler(context, rowSetProvider, schema); expect(await result.getValue()).to.be.deep.eq([]); expect(await result.getValue([])).to.be.deep.eq([]); @@ -418,8 +426,9 @@ describe('JsonResult', () => { ]; const context = {}; + const rowSetProvider = new RowSetProviderMock(); - const result = new JsonResult(context); + const result = new JsonResultHandler(context, rowSetProvider); expect(await result.getValue(data)).to.be.deep.eq([]); }); @@ -453,8 +462,9 @@ describe('JsonResult', () => { ]; const context = {}; + const rowSetProvider = new RowSetProviderMock(); - const result = new JsonResult(context, schema); + const result = new JsonResultHandler(context, rowSetProvider, schema); expect(await result.getValue(data)).to.be.deep.eq([ { diff --git a/tests/unit/result/compatibility.test.js b/tests/unit/result/compatibility.test.js index 5b27d39e..b39efe12 100644 --- a/tests/unit/result/compatibility.test.js +++ b/tests/unit/result/compatibility.test.js @@ -1,30 +1,35 @@ const { expect } = require('chai'); -const ArrowResult = require('../../../dist/result/ArrowResult').default; -const JsonResult = require('../../../dist/result/JsonResult').default; +const ArrowResultHandler = require('../../../dist/result/ArrowResultHandler').default; +const JsonResultHandler = require('../../../dist/result/JsonResultHandler').default; const { fixArrowResult } = require('../../fixtures/compatibility'); const fixtureColumn = require('../../fixtures/compatibility/column'); const fixtureArrow = require('../../fixtures/compatibility/arrow'); const fixtureArrowNT = require('../../fixtures/compatibility/arrow_native_types'); +const RowSetProviderMock = require('./fixtures/RowSetProviderMock'); + describe('Result handlers compatibility tests', () => { it('colum-based data', async () => { const context = {}; - const result = new JsonResult(context, fixtureColumn.schema); + const rowSetProvider = new RowSetProviderMock(); + const result = new JsonResultHandler(context, rowSetProvider, fixtureColumn.schema); const rows = await result.getValue(fixtureColumn.rowSets); expect(rows).to.deep.equal(fixtureColumn.expected); }); it('arrow-based data without native types', async () => { const context = {}; - const result = new ArrowResult(context, fixtureArrow.schema, fixtureArrow.arrowSchema); + const rowSetProvider = new RowSetProviderMock(); + const result = new ArrowResultHandler(context, rowSetProvider, fixtureArrow.schema, fixtureArrow.arrowSchema); const rows = await result.getValue(fixtureArrow.rowSets); expect(fixArrowResult(rows)).to.deep.equal(fixtureArrow.expected); }); it('arrow-based data with native types', async () => { const context = {}; - const result = new ArrowResult(context, fixtureArrowNT.schema, fixtureArrowNT.arrowSchema); + const rowSetProvider = new RowSetProviderMock(); + const result = new ArrowResultHandler(context, rowSetProvider, fixtureArrowNT.schema, fixtureArrowNT.arrowSchema); const rows = await result.getValue(fixtureArrowNT.rowSets); expect(fixArrowResult(rows)).to.deep.equal(fixtureArrowNT.expected); }); diff --git a/tests/unit/result/fixtures/RowSetProviderMock.js b/tests/unit/result/fixtures/RowSetProviderMock.js new file mode 100644 index 00000000..96a54b5d --- /dev/null +++ b/tests/unit/result/fixtures/RowSetProviderMock.js @@ -0,0 +1,11 @@ +class RowSetProviderMock { + async hasMore() { + return false; + } + + async fetchNext() { + return undefined; + } +} + +module.exports = RowSetProviderMock;