UNPKG

hana-cli

Version:
632 lines (516 loc) 23 kB
// @ts-nocheck /** * Unit tests for utils/dbInspect.js * Testing database inspection and metadata retrieval utilities */ import { expect } from 'chai' import sinon from 'sinon' import * as dbInspect from '../../utils/dbInspect.js' describe('dbInspect.js - Database Inspection Functions', () => { /** * @type {object} */ let mockDb beforeEach(() => { // Create a comprehensive mock database connection mockDb = { preparePromisified: sinon.stub(), statementExecPromisified: sinon.stub(), loadProcedurePromisified: sinon.stub(), callProcedurePromisified: sinon.stub() } dbInspect.resetHANAVersionCache() }) afterEach(() => { sinon.restore() }) describe('getHANAVersion()', () => { it('should return HANA version information', async () => { const mockStatement = {} const mockVersion = [{ VERSION: '2.00.065.00.1649859137', USAGE: 'DEVELOPMENT' }] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.resolves(mockVersion) const result = await dbInspect.getHANAVersion(mockDb) expect(result).to.be.an('object') expect(result.VERSION).to.equal('2.00.065.00.1649859137') expect(result.versionMajor).to.equal('2') expect(mockDb.preparePromisified.calledOnce).to.be.true }) it('should extract major version from VERSION string', async () => { const mockStatement = {} const mockVersion = [{ VERSION: '4.00.000.00.1234567890' }] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.resolves(mockVersion) const result = await dbInspect.getHANAVersion(mockDb) expect(result.versionMajor).to.equal('4') }) it('should throw error if no database version found', async () => { const mockStatement = {} mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.resolves([]) try { await dbInspect.getHANAVersion(mockDb) expect.fail('Should have thrown error') } catch (error) { expect(error).to.be.an('error') } }) it('should handle version with single digit major', async () => { const mockStatement = {} const mockVersion = [{ VERSION: '1.00.122.00.1234567890' }] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.resolves(mockVersion) const result = await dbInspect.getHANAVersion(mockDb) expect(result.versionMajor).to.equal('1') }) }) describe('isCalculationView()', () => { it('should return false for HANA version < 2', async () => { const mockStatement = {} const mockVersion = [{ VERSION: '1.00.122.00.1234567890' }] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.resolves(mockVersion) const result = await dbInspect.isCalculationView(mockDb, 'MYSCHEMA', 'MY_VIEW', { forceRefresh: true }) expect(result).to.be.false }) it('should return true when calculation view found by qualified name', async () => { const mockStatement = {} const mockVersion = [{ VERSION: '2.00.065.00.1649859137' }] const mockCalcView = [{ CUBE_ID: 'CV123', SCHEMA_NAME: 'MYSCHEMA', QUALIFIED_NAME: 'MY_CALC_VIEW', VIEW_NAME: 'MY_CALC_VIEW' }] mockDb.preparePromisified.onFirstCall().resolves(mockStatement) mockDb.statementExecPromisified.onFirstCall().resolves(mockVersion) mockDb.preparePromisified.onSecondCall().resolves(mockStatement) mockDb.statementExecPromisified.onSecondCall().resolves(mockCalcView) const result = await dbInspect.isCalculationView(mockDb, 'MYSCHEMA', 'MY_CALC_VIEW', { forceRefresh: true }) expect(result).to.be.true }) it('should return true when calculation view found by view name', async () => { const mockStatement = {} const mockVersion = [{ VERSION: '2.00.065.00.1649859137' }] mockDb.preparePromisified.onFirstCall().resolves(mockStatement) mockDb.statementExecPromisified.onFirstCall().resolves(mockVersion) mockDb.preparePromisified.onSecondCall().resolves(mockStatement) mockDb.statementExecPromisified.onSecondCall().resolves([]) mockDb.preparePromisified.onThirdCall().resolves(mockStatement) mockDb.statementExecPromisified.onThirdCall().resolves([{ CUBE_ID: 'CV123', VIEW_NAME: 'MY_VIEW' }]) const result = await dbInspect.isCalculationView(mockDb, 'MYSCHEMA', 'MY_VIEW', { forceRefresh: true }) expect(result).to.be.true }) it('should return false when calculation view not found', async () => { const mockStatement = {} const mockVersion = [{ VERSION: '2.00.065.00.1649859137' }] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.onFirstCall().resolves(mockVersion) mockDb.statementExecPromisified.onSecondCall().resolves([]) mockDb.statementExecPromisified.onThirdCall().resolves([]) const result = await dbInspect.isCalculationView(mockDb, 'MYSCHEMA', 'NOT_A_CALC_VIEW', { forceRefresh: true }) expect(result).to.be.false }) }) describe('getView()', () => { it('should return view details for HANA 2+', async () => { const mockStatement = {} const mockVersion = [{ VERSION: '2.00.065.00.1649859137' }] const mockView = [{ SCHEMA_NAME: 'MYSCHEMA', VIEW_NAME: 'MY_VIEW', VIEW_OID: '12345', COMMENTS: 'Test view', IS_COLUMN_VIEW: 'TRUE', VIEW_TYPE: 'CALC', HAS_STRUCTURED_PRIVILEGE_CHECK: 'FALSE', HAS_PARAMETERS: 'TRUE', HAS_CACHE: 'FALSE', CREATE_TIME: '2023-01-01 12:00:00' }] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.onFirstCall().resolves(mockVersion) mockDb.statementExecPromisified.onSecondCall().resolves(mockView) const result = await dbInspect.getView(mockDb, 'MYSCHEMA', 'MY_VIEW') expect(result).to.be.an('array') expect(result[0].VIEW_NAME).to.equal('MY_VIEW') expect(result[0].SCHEMA_NAME).to.equal('MYSCHEMA') }) it('should return view details for HANA 1', async () => { const mockStatement = {} const mockVersion = [{ VERSION: '1.00.122.00.1234567890' }] const mockView = [{ SCHEMA_NAME: 'MYSCHEMA', VIEW_NAME: 'MY_VIEW', VIEW_OID: '12345', IS_COLUMN_VIEW: 'TRUE' }] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.onFirstCall().resolves(mockVersion) mockDb.statementExecPromisified.onSecondCall().resolves(mockView) const result = await dbInspect.getView(mockDb, 'MYSCHEMA', 'MY_VIEW') expect(result).to.be.an('array') expect(result[0].VIEW_NAME).to.equal('MY_VIEW') }) it('should throw error when view not found', async () => { const mockStatement = {} const mockVersion = [{ VERSION: '2.00.065.00.1649859137' }] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.onFirstCall().resolves(mockVersion) mockDb.statementExecPromisified.onSecondCall().resolves([]) try { await dbInspect.getView(mockDb, 'MYSCHEMA', 'NONEXISTENT') expect.fail('Should have thrown error') } catch (error) { expect(error).to.be.an('error') } }) }) describe('getDef()', () => { it('should return object definition', async () => { const mockProcedure = {} const mockDefinition = { results: [{ OBJECT_CREATION_STATEMENT: 'CREATE TABLE "MYSCHEMA"."MYTABLE" (ID INTEGER, NAME VARCHAR(100))' }] } mockDb.loadProcedurePromisified.resolves(mockProcedure) mockDb.callProcedurePromisified.resolves(mockDefinition) const result = await dbInspect.getDef(mockDb, 'MYSCHEMA', 'MYTABLE') expect(result).to.be.a('string') expect(result).to.include('CREATE TABLE') expect(mockDb.loadProcedurePromisified.calledWith('SYS', 'GET_OBJECT_DEFINITION')).to.be.true }) it('should format definition with line breaks', async () => { const mockProcedure = {} const mockDefinition = { results: [{ OBJECT_CREATION_STATEMENT: 'CREATE TABLE "S"."T" (ID INT,NAME VARCHAR(100),AGE INT)' }] } mockDb.loadProcedurePromisified.resolves(mockProcedure) mockDb.callProcedurePromisified.resolves(mockDefinition) const result = await dbInspect.getDef(mockDb, 'S', 'T') expect(result).to.include(',\n') }) it('should throw error when object not found', async () => { const mockProcedure = {} const mockDefinition = { results: [] } mockDb.loadProcedurePromisified.resolves(mockProcedure) mockDb.callProcedurePromisified.resolves(mockDefinition) try { await dbInspect.getDef(mockDb, 'MYSCHEMA', 'NONEXISTENT') expect.fail('Should have thrown error') } catch (error) { expect(error).to.be.an('error') } }) }) describe('getTable()', () => { it('should return table details for HANA 2+', async () => { const mockStatement = {} const mockVersion = [{ VERSION: '2.00.065.00.1649859137' }] const mockTable = [{ SCHEMA_NAME: 'MYSCHEMA', TABLE_NAME: 'MY_TABLE', TABLE_OID: '12345', TABLE_TYPE: 'COLUMN', HAS_PRIMARY_KEY: 'TRUE', UNLOAD_PRIORITY: 5, IS_PRELOAD: 'TRUE', CREATE_TIME: '2023-01-01 12:00:00' }] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.onFirstCall().resolves(mockVersion) mockDb.statementExecPromisified.onSecondCall().resolves(mockTable) const result = await dbInspect.getTable(mockDb, 'MYSCHEMA', 'MY_TABLE') expect(result).to.be.an('array') expect(result[0].TABLE_NAME).to.equal('MY_TABLE') expect(result[0].TABLE_TYPE).to.equal('COLUMN') }) it('should return table details for HANA 1', async () => { const mockStatement = {} const mockVersion = [{ VERSION: '1.00.122.00.1234567890' }] const mockTable = [{ SCHEMA_NAME: 'MYSCHEMA', TABLE_NAME: 'MY_TABLE', TABLE_OID: '12345', TABLE_TYPE: 'ROW' }] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.onFirstCall().resolves(mockVersion) mockDb.statementExecPromisified.onSecondCall().resolves(mockTable) const result = await dbInspect.getTable(mockDb, 'MYSCHEMA', 'MY_TABLE') expect(result).to.be.an('array') expect(result[0].TABLE_NAME).to.equal('MY_TABLE') }) it('should throw error when table not found', async () => { const mockStatement = {} const mockVersion = [{ VERSION: '2.00.065.00.1649859137' }] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.onFirstCall().resolves(mockVersion) mockDb.statementExecPromisified.onSecondCall().resolves([]) try { await dbInspect.getTable(mockDb, 'MYSCHEMA', 'NONEXISTENT') expect.fail('Should have thrown error') } catch (error) { expect(error).to.be.an('error') } }) }) describe('getTableFields()', () => { it('should return table fields for HANA 2+', async () => { const mockStatement = {} const mockVersion = [{ VERSION: '2.00.065.00.1649859137' }] const mockFields = [ { COLUMN_NAME: 'ID', POSITION: 1, DATA_TYPE_NAME: 'INTEGER', LENGTH: 10, IS_NULLABLE: 'FALSE' }, { COLUMN_NAME: 'NAME', POSITION: 2, DATA_TYPE_NAME: 'VARCHAR', LENGTH: 100, IS_NULLABLE: 'TRUE' } ] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.onFirstCall().resolves(mockVersion) mockDb.statementExecPromisified.onSecondCall().resolves(mockFields) const result = await dbInspect.getTableFields(mockDb, '12345') expect(result).to.be.an('array') expect(result.length).to.equal(2) expect(result[0].COLUMN_NAME).to.equal('ID') expect(result[1].COLUMN_NAME).to.equal('NAME') }) it('should return empty array when no fields found', async () => { const mockStatement = {} const mockVersion = [{ VERSION: '2.00.065.00.1649859137' }] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.onFirstCall().resolves(mockVersion) mockDb.statementExecPromisified.onSecondCall().resolves([]) const result = await dbInspect.getTableFields(mockDb, '12345') expect(result).to.be.an('array') expect(result.length).to.equal(0) }) }) describe('getViewFields()', () => { it('should return view fields', async () => { const mockStatement = {} const mockFields = [ { SCHEMA_NAME: 'MYSCHEMA', VIEW_NAME: 'MY_VIEW', VIEW_OID: '12345', COLUMN_NAME: 'ID', POSITION: 1, DATA_TYPE_NAME: 'INTEGER' } ] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.resolves(mockFields) const result = await dbInspect.getViewFields(mockDb, '12345') expect(result).to.be.an('array') expect(result[0].COLUMN_NAME).to.equal('ID') }) it('should return empty array when no fields found', async () => { const mockStatement = {} mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.resolves([]) const result = await dbInspect.getViewFields(mockDb, '12345') expect(result).to.be.an('array') expect(result.length).to.equal(0) }) }) describe('getCalcViewFields()', () => { it('should return calculation view fields', async () => { const mockStatement = {} const mockFields = [ { SCHEMA_NAME: 'MYSCHEMA', QUALIFIED_NAME: 'MY_CALC_VIEW', COLUMN_NAME: 'PRODUCT_ID', POSITION: 1, DATA_TYPE_NAME: 'NVARCHAR', SCALE: 0, IS_NULLABLE: 'FALSE' } ] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.onFirstCall().resolves(mockFields) mockDb.statementExecPromisified.onSecondCall().resolves([{ OFFSET: 0, LENGTH: 50, DEFAULT_VALUE: null, DATA_TYPE_NAME: 'NVARCHAR' }]) const result = await dbInspect.getCalcViewFields(mockDb, 'MYSCHEMA', 'MY_CALC_VIEW', '12345') expect(result).to.be.an('array') expect(result[0].COLUMN_NAME).to.equal('PRODUCT_ID') }) }) describe('getCalcViewParameters()', () => { it('should return calculation view parameters', async () => { const mockStatement = {} const mockParams = [ { SCHEMA_NAME: 'MYSCHEMA', QUALIFIED_NAME: 'MY_CALC_VIEW', VARIABLE_NAME: 'P_YEAR', COLUMN_TYPE: 'INPUT', DATA_TYPE_NAME: 'INTEGER', COLUMN_SQL_TYPE: 'INTEGER(10)', MANDATORY: 'TRUE' } ] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.resolves(mockParams) const result = await dbInspect.getCalcViewParameters(mockDb, 'MYSCHEMA', 'MY_CALC_VIEW', '12345') expect(result).to.be.an('array') expect(result[0].VARIABLE_NAME).to.equal('P_YEAR') expect(result[0].LENGTH).to.equal('10') }) it('should handle VARCHAR type with length', async () => { const mockStatement = {} const mockParams = [ { VARIABLE_NAME: 'P_NAME', COLUMN_SQL_TYPE: 'VARCHAR(255)', } ] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.resolves(mockParams) const result = await dbInspect.getCalcViewParameters(mockDb, 'MYSCHEMA', 'MY_VIEW', '12345') expect(result[0].LENGTH).to.equal('255') }) }) describe('getViewParameters()', () => { it('should return view parameters', async () => { const mockStatement = {} const mockParams = [ { SCHEMA_NAME: 'MYSCHEMA', VIEW_NAME: 'MY_VIEW', VIEW_OID: '12345', PARAMETER_NAME: 'P_ID', DATA_TYPE_NAME: 'INTEGER' } ] mockDb.preparePromisified.resolves(mockStatement) mockDb.statementExecPromisified.resolves(mockParams) const result = await dbInspect.getViewParameters(mockDb, '12345') expect(result).to.be.an('array') expect(result[0].PARAMETER_NAME).to.equal('P_ID') }) }) }) describe('dbInspect.js - Module Exports', () => { it('should export getHANAVersion function', () => { expect(dbInspect.getHANAVersion).to.be.a('function') }) it('should export isCalculationView function', () => { expect(dbInspect.isCalculationView).to.be.a('function') }) it('should export getView function', () => { expect(dbInspect.getView).to.be.a('function') }) it('should export getDef function', () => { expect(dbInspect.getDef).to.be.a('function') }) it('should export getTable function', () => { expect(dbInspect.getTable).to.be.a('function') }) it('should export getTableFields function', () => { expect(dbInspect.getTableFields).to.be.a('function') }) it('should export getViewFields function', () => { expect(dbInspect.getViewFields).to.be.a('function') }) it('should export getCalcViewFields function', () => { expect(dbInspect.getCalcViewFields).to.be.a('function') }) it('should export getCalcViewParameters function', () => { expect(dbInspect.getCalcViewParameters).to.be.a('function') }) it('should export getViewParameters function', () => { expect(dbInspect.getViewParameters).to.be.a('function') }) }) describe('dbInspect.js - CDS Type Mapping Regression', () => { beforeEach(() => { dbInspect.options.useHanaTypes = true dbInspect.options.useExists = false dbInspect.options.useQuoted = false dbInspect.options.noColons = false dbInspect.options.keepPath = false }) afterEach(() => { dbInspect.options.useHanaTypes = false dbInspect.options.useExists = true }) it('should map common types when useHanaTypes is true', async () => { global.__xRef = [] const object = [{ SCHEMA_NAME: 'MYSCHEMA', TABLE_NAME: 'STAR_WARS_FILM', HAS_PRIMARY_KEY: 'TRUE' }] const fields = [ { COLUMN_NAME: 'ID', DATA_TYPE_NAME: 'NVARCHAR', LENGTH: 36, SCALE: 0, IS_NULLABLE: 'FALSE', DEFAULT_VALUE: null, COMMENTS: null }, { COLUMN_NAME: 'CREATEDAT', DATA_TYPE_NAME: 'TIMESTAMP', LENGTH: 0, SCALE: 0, IS_NULLABLE: 'TRUE', DEFAULT_VALUE: null, COMMENTS: null }, { COLUMN_NAME: 'EPISODE_ID', DATA_TYPE_NAME: 'INTEGER', LENGTH: 10, SCALE: 0, IS_NULLABLE: 'TRUE', DEFAULT_VALUE: null, COMMENTS: null }, { COLUMN_NAME: 'RELEASE_DATE', DATA_TYPE_NAME: 'DATE', LENGTH: 0, SCALE: 0, IS_NULLABLE: 'TRUE', DEFAULT_VALUE: null, COMMENTS: null } ] const constraints = [{ COLUMN_NAME: 'ID' }] const mockDb = { preparePromisified: sinon.stub(), statementExecPromisified: sinon.stub() } const cds = await dbInspect.formatCDS(mockDb, object, fields, constraints, 'hdbtable', 'MYSCHEMA', null) expect(cds).to.include('ID: String(36)') expect(cds).to.include('CREATEDAT: Timestamp') expect(cds).to.include('EPISODE_ID: Integer') expect(cds).to.include('RELEASE_DATE: Date') expect(cds).to.not.include('**UNSUPPORTED TYPE') }) })