UNPKG

hana-cli

Version:
618 lines (513 loc) 24.2 kB
// @ts-nocheck /** * @module Base Utilities Tests - Unit tests for the base utility functions * * These tests validate the core functionality in utils/base.js that supports * all hana-cli commands, including: * - Debug flag handling (isDebug) * - Builder functions (getBuilder, getPromptSchema) * - Prompt handler functionality * - Connection and debug parameter injection */ import { assert } from '../base.js' import * as base from '../../utils/base.js' describe('Base Utility Functions', function () { describe('isDebug', function () { it('should return true when debug flag is set to true', function () { const prompts = { debug: true } const result = base.isDebug(prompts) assert.strictEqual(result, true) }) it('should return false when debug flag is set to false', function () { const prompts = { debug: false } const result = base.isDebug(prompts) assert.strictEqual(result, false) }) it('should return false when debug flag is not present', function () { const prompts = { other: 'value' } const result = base.isDebug(prompts) assert.strictEqual(result, false) }) it('should return false when prompts is null', function () { const result = base.isDebug(null) assert.strictEqual(result, false) }) it('should return false when prompts is undefined', function () { const result = base.isDebug(undefined) assert.strictEqual(result, false) }) it('should handle truthy values for debug flag', function () { const prompts = { debug: 'true' } const result = base.isDebug(prompts) // Should be truthy assert.ok(result) }) }) describe('isGui', function () { it('should return true when isGui flag is set to true', function () { const prompts = { isGui: true } const result = base.isGui(prompts) assert.strictEqual(result, true) }) it('should return false when isGui flag is set to false', function () { const prompts = { isGui: false } const result = base.isGui(prompts) assert.strictEqual(result, false) }) it('should return false when isGui flag is not present', function () { const prompts = { other: 'value' } const result = base.isGui(prompts) assert.strictEqual(result, false) }) }) describe('getBuilder', function () { it('should include debug options when iDebug is true', function () { const input = { myOption: { type: 'string' } } const builder = base.getBuilder(input, true, true) assert.ok(builder.debug, 'Builder should include debug option') assert.ok(builder.disableVerbose, 'Builder should include disableVerbose option') assert.strictEqual(builder.debug.type, 'boolean') assert.strictEqual(builder.debug.default, false) }) it('should not include debug options when iDebug is false', function () { const input = { myOption: { type: 'string' } } const builder = base.getBuilder(input, true, false) assert.ok(!builder.debug, 'Builder should not include debug option when iDebug is false') assert.ok(!builder.disableVerbose, 'Builder should not include disableVerbose option') }) it('should include connection options when iConn is true', function () { const input = { myOption: { type: 'string' } } const builder = base.getBuilder(input, true, true) assert.ok(builder.admin, 'Builder should include admin option') assert.ok(builder.conn, 'Builder should include conn option') assert.strictEqual(builder.admin.type, 'boolean') assert.strictEqual(builder.admin.default, false) }) it('should not include connection options when iConn is false', function () { const input = { myOption: { type: 'string' } } const builder = base.getBuilder(input, false, true) assert.ok(!builder.admin, 'Builder should not include admin option when iConn is false') assert.ok(!builder.conn, 'Builder should not include conn option') }) it('should preserve input options', function () { const input = { myOption: { type: 'string', desc: 'My test option' } } const builder = base.getBuilder(input, true, true) assert.ok(builder.myOption, 'Builder should preserve input options') assert.strictEqual(builder.myOption.type, 'string') assert.strictEqual(builder.myOption.desc, 'My test option') }) it('should have correct group assignments', function () { const input = { myOption: { type: 'string' } } const builder = base.getBuilder(input, true, true) // Debug options should be in debug group assert.ok(builder.debug.group) assert.ok(builder.disableVerbose.group) // Connection options should be in connection group assert.ok(builder.admin.group) assert.ok(builder.conn.group) }) it('should have correct aliases', function () { const input = { myOption: { type: 'string' } } const builder = base.getBuilder(input, true, true) // Check debug aliases assert.ok(builder.debug.alias.includes('d'), 'debug should have d alias') // Check admin aliases assert.ok(builder.admin.alias.includes('a'), 'admin should have a alias') // Check disableVerbose aliases assert.ok(builder.disableVerbose.alias.includes('quiet'), 'disableVerbose should have quiet alias') }) }) describe('getPromptSchema', function () { it('should include debug properties when iDebug is true', function () { const inputSchema = { myProp: { type: 'string' } } const schema = base.getPromptSchema(inputSchema, true, true) assert.ok(schema.properties.debug, 'Schema should include debug property') assert.ok(schema.properties.disableVerbose, 'Schema should include disableVerbose property') assert.strictEqual(schema.properties.debug.type, 'boolean') assert.strictEqual(schema.properties.debug.required, true) }) it('should not include debug properties when iDebug is false', function () { const inputSchema = { myProp: { type: 'string' } } const schema = base.getPromptSchema(inputSchema, true, false) assert.ok(!schema.properties.debug, 'Schema should not include debug when iDebug is false') assert.ok(!schema.properties.disableVerbose, 'Schema should not include disableVerbose') }) it('should include connection properties when iConn is true', function () { const inputSchema = { myProp: { type: 'string' } } const schema = base.getPromptSchema(inputSchema, true, true) assert.ok(schema.properties.admin, 'Schema should include admin property') assert.ok(schema.properties.conn, 'Schema should include conn property') assert.strictEqual(schema.properties.admin.type, 'boolean') }) it('should not include connection properties when iConn is false', function () { const inputSchema = { myProp: { type: 'string' } } const schema = base.getPromptSchema(inputSchema, false, true) assert.ok(!schema.properties.admin, 'Schema should not include admin when iConn is false') assert.ok(!schema.properties.conn, 'Schema should not include conn') }) it('should preserve input schema properties', function () { const inputSchema = { myProp: { type: 'string', description: 'Test property', required: true } } const schema = base.getPromptSchema(inputSchema, true, true) assert.ok(schema.properties.myProp, 'Schema should preserve input properties') assert.strictEqual(schema.properties.myProp.type, 'string') assert.strictEqual(schema.properties.myProp.description, 'Test property') assert.strictEqual(schema.properties.myProp.required, true) }) it('should set ask to askFalse for debug and connection properties', function () { const inputSchema = { myProp: { type: 'string' } } const schema = base.getPromptSchema(inputSchema, true, true) // These properties should not be prompted for assert.ok(schema.properties.debug.ask instanceof Function, 'debug.ask should be a function') assert.ok(schema.properties.disableVerbose.ask instanceof Function, 'disableVerbose.ask should be a function') assert.ok(schema.properties.admin.ask instanceof Function, 'admin.ask should be a function') assert.ok(schema.properties.conn.ask instanceof Function, 'conn.ask should be a function') // askFalse should return false assert.strictEqual(schema.properties.debug.ask(), false) assert.strictEqual(schema.properties.admin.ask(), false) }) }) describe('askFalse', function () { it('should always return false', function () { const result = base.askFalse() assert.strictEqual(result, false) }) }) describe('debug function', function () { it('should be callable without throwing', function () { // Debug function should not throw even if debug is not enabled assert.doesNotThrow(() => { base.debug('Test message') }) }) it('should accept string messages', function () { assert.doesNotThrow(() => { base.debug('Simple string message') }) }) it('should accept object messages', function () { assert.doesNotThrow(() => { base.debug({ key: 'value', nested: { data: 123 } }) }) }) }) describe('promptHandler critical path', function () { // This test verifies the fix for the --debug flag issue // It ensures that argv values are copied to result even when ask() returns false it('should copy debug flag from argv to result object', async function () { this.timeout(5000) let capturedResult = null // Mock processing function that captures the result const mockProcessing = async (result) => { capturedResult = result } // Mock argv with debug flag const mockArgv = { debug: true, schema: 'TEST_SCHEMA', limit: 10 } // Simple input schema const inputSchema = { schema: { description: 'Schema name', type: 'string', required: true, ask: () => false // Don't prompt }, limit: { description: 'Limit', type: 'number', required: true, ask: () => false // Don't prompt } } try { await base.promptHandler(mockArgv, mockProcessing, inputSchema, true, true) // Verify that debug flag was transferred to result assert.ok(capturedResult, 'Processing function should have been called') assert.strictEqual(capturedResult.debug, true, 'Debug flag should be copied from argv to result') assert.strictEqual(capturedResult.schema, 'TEST_SCHEMA', 'Schema should be copied from argv') assert.strictEqual(capturedResult.limit, 10, 'Limit should be copied from argv') } catch (err) { // Prompt handler might throw if user cancels, but we're testing the copy logic // If it throws, the test should still pass as long as we got to the assertion if (capturedResult) { assert.strictEqual(capturedResult.debug, true, 'Debug flag should be copied even if error occurs') } } }) it('should copy disableVerbose flag from argv', async function () { this.timeout(5000) let capturedResult = null const mockProcessing = async (result) => { capturedResult = result } const mockArgv = { disableVerbose: true, schema: 'TEST' } const inputSchema = { schema: { description: 'Schema', type: 'string', required: true, ask: () => false } } try { await base.promptHandler(mockArgv, mockProcessing, inputSchema, true, true) assert.ok(capturedResult, 'Processing function should have been called') assert.strictEqual(capturedResult.disableVerbose, true, 'disableVerbose should be copied from argv') } catch (err) { if (capturedResult) { assert.strictEqual(capturedResult.disableVerbose, true) } } }) it('should copy admin flag from argv', async function () { this.timeout(5000) let capturedResult = null const mockProcessing = async (result) => { capturedResult = result } const mockArgv = { admin: true, schema: 'TEST' } const inputSchema = { schema: { description: 'Schema', type: 'string', required: true, ask: () => false } } try { await base.promptHandler(mockArgv, mockProcessing, inputSchema, true, true) assert.ok(capturedResult, 'Processing function should have been called') assert.strictEqual(capturedResult.admin, true, 'admin flag should be copied from argv') } catch (err) { if (capturedResult) { assert.strictEqual(capturedResult.admin, true) } } }) it('should copy conn value from argv', async function () { this.timeout(5000) let capturedResult = null const mockProcessing = async (result) => { capturedResult = result } const mockArgv = { conn: 'default.env', schema: 'TEST' } const inputSchema = { schema: { description: 'Schema', type: 'string', required: true, ask: () => false } } try { await base.promptHandler(mockArgv, mockProcessing, inputSchema, true, true) assert.ok(capturedResult, 'Processing function should have been called') assert.strictEqual(capturedResult.conn, 'default.env', 'conn value should be copied from argv') } catch (err) { if (capturedResult) { assert.strictEqual(capturedResult.conn, 'default.env') } } }) }) describe('unknown options warning', function () { let consoleWarnOutput = [] let originalConsoleWarn beforeEach(function () { // Capture console.warn output consoleWarnOutput = [] originalConsoleWarn = console.warn console.warn = (msg) => { consoleWarnOutput.push(msg) } }) afterEach(function () { // Restore original console.warn console.warn = originalConsoleWarn }) it('should warn about unknown options', async function () { this.timeout(5000) const mockProcessing = async (result) => { // Do nothing } const mockArgv = { schema: 'TEST_SCHEMA', unknownOption: true, anotherBadOption: 'value' } const inputSchema = { schema: { description: 'Schema name', type: 'string', required: true, ask: () => false } } try { await base.promptHandler(mockArgv, mockProcessing, inputSchema, true, true) // Check that warnings were issued assert.ok(consoleWarnOutput.length > 0, 'Should have warning messages') const warningText = consoleWarnOutput.join(' ') assert.ok(warningText.includes('unknownOption') || warningText.includes('unknown-option'), 'Should warn about unknownOption') assert.ok(warningText.includes('anotherBadOption') || warningText.includes('another-bad-option'), 'Should warn about anotherBadOption') } catch (err) { // Prompt handler might throw, but warnings should still have been issued assert.ok(consoleWarnOutput.length > 0, 'Should have warnings even if prompt cancelled') } }) it('should not warn about valid options', async function () { this.timeout(5000) const mockProcessing = async (result) => { // Do nothing } const mockArgv = { schema: 'TEST_SCHEMA', limit: 100, debug: true, admin: false } const inputSchema = { schema: { description: 'Schema name', type: 'string', required: true, ask: () => false }, limit: { description: 'Limit', type: 'number', required: true, ask: () => false } } try { await base.promptHandler(mockArgv, mockProcessing, inputSchema, true, true) // Check that no warnings were issued for valid options const warningText = consoleWarnOutput.join(' ') assert.ok(!warningText.includes('schema'), 'Should not warn about schema') assert.ok(!warningText.includes('limit'), 'Should not warn about limit') assert.ok(!warningText.includes('debug'), 'Should not warn about debug') assert.ok(!warningText.includes('admin'), 'Should not warn about admin') } catch (err) { // Even if prompt throws, check warnings const warningText = consoleWarnOutput.join(' ') assert.ok(!warningText.includes('schema'), 'Should not warn about valid options') } }) it('should detect aliases as valid options', async function () { this.timeout(5000) const mockProcessing = async (result) => { // Do nothing } // When using -l 100, yargs creates both l:100 and limit:100 const mockArgv = { schema: 'TEST_SCHEMA', s: 'TEST_SCHEMA', // alias for schema limit: 100, l: 100 // alias for limit } const inputSchema = { schema: { description: 'Schema name', type: 'string', required: true, ask: () => false }, limit: { description: 'Limit', type: 'number', required: true, ask: () => false } } try { await base.promptHandler(mockArgv, mockProcessing, inputSchema, true, true) // Check that aliases are not flagged as unknown const warningText = consoleWarnOutput.join(' ') assert.ok(!warningText.includes("'s'") && !warningText.includes("'l'"), 'Should not warn about aliases that share values with known options') } catch (err) { // Check even if error const warningText = consoleWarnOutput.join(' ') assert.ok(!warningText.includes("'s'") && !warningText.includes("'l'"), 'Should not warn about valid aliases') } }) it('should not duplicate warnings for kebab-case and camelCase', async function () { this.timeout(5000) const mockProcessing = async (result) => { // Do nothing } // Yargs creates both when you use --unknown-option const mockArgv = { schema: 'TEST_SCHEMA', 'unknown-option': true, unknownOption: true } const inputSchema = { schema: { description: 'Schema name', type: 'string', required: true, ask: () => false } } try { await base.promptHandler(mockArgv, mockProcessing, inputSchema, true, true) // Should only warn once, not twice const unknownWarnings = consoleWarnOutput.filter(msg => msg.includes('unknown-option') || msg.includes('unknownOption') ) assert.ok(unknownWarnings.length <= 1, 'Should not duplicate warnings for kebab-case and camelCase variants') } catch (err) { // Check even if error const unknownWarnings = consoleWarnOutput.filter(msg => msg.includes('unknown-option') || msg.includes('unknownOption') ) assert.ok(unknownWarnings.length <= 1, 'Should not duplicate warnings') } }) }) describe('color utilities', function () { it('should have colors object available', function () { assert.ok(base.colors, 'colors should be available') assert.ok(typeof base.colors === 'object' || typeof base.colors === 'function') }) }) describe('bundle and localization', function () { it('should have bundle object available', function () { assert.ok(base.bundle, 'bundle should be available') }) it('should be able to get text from bundle', function () { assert.doesNotThrow(() => { const text = base.bundle.getText('usage') assert.ok(typeof text === 'string') }) }) }) })