UNPKG

@burgan-tech/vnext-cli

Version:

CLI for creating and managing vNext domain projects with modular component sharing

477 lines (425 loc) โ€ข 16.6 kB
#!/usr/bin/env node const RefResolver = require('./lib/ref-resolver'); const path = require('path'); const chalk = require('chalk'); const fs = require('fs-extra'); async function testRefResolver() { console.log(chalk.blue('๐Ÿงช Testing Enhanced Reference Resolver...')); try { const resolver = new RefResolver({ cacheDir: path.join(__dirname, '.test-cache'), validateSchemas: false, // Disable schema validation for basic tests validateReferenceConsistency: true, schemaPath: path.join(__dirname, 'template', '.vscode', 'schemas') }); // Test 1: Parse local reference console.log(chalk.yellow('\n๐Ÿ“‹ Test 1: Parse Local Reference')); const localRef = resolver.parseRef('Tasks/task-invalidate-cache.1.0.0.json'); console.log(' โœ… Parsed:', JSON.stringify(localRef, null, 2)); // Test 2: Parse external reference console.log(chalk.yellow('\n๐Ÿ“‹ Test 2: Parse External Reference')); const externalRef = resolver.parseRef('@vnext/domain-core/Tasks/task-audit-log.1.0.0.json'); console.log(' โœ… Parsed:', JSON.stringify(externalRef, null, 2)); // Test 3: Extract version from filename console.log(chalk.yellow('\n๐Ÿ“‹ Test 3: Extract Version')); const version = resolver.extractVersionFromFilename('task-example.1.2.3.json'); console.log(' โœ… Version extracted:', version); // Test 4: Component type detection console.log(chalk.yellow('\n๐Ÿ“‹ Test 4: Component Type Detection')); const taskType = resolver.detectComponentType('Tasks/task-example.1.0.0.json'); const workflowType = resolver.detectComponentType('Workflows/workflow-example.1.0.0.json'); console.log(' โœ… Task type:', taskType); console.log(' โœ… Workflow type:', workflowType); // Test 5: Reference consistency validation (success case) console.log(chalk.yellow('\n๐Ÿ“‹ Test 5: Reference Consistency Validation (Success)')); const validContent = { key: 'task-invalidate-cache', version: '1.0.0', domain: 'core', flow: 'sys-tasks' }; const validParsedRef = { filePath: 'Tasks/task-invalidate-cache.1.0.0.json', version: '1.0.0' }; try { await resolver.validateReferenceConsistency( 'Tasks/task-invalidate-cache.1.0.0.json', validContent, validParsedRef ); console.log(' โœ… Reference consistency validation passed'); } catch (error) { console.log(' โŒ Unexpected error:', error.message); } // Test 6: Reference consistency validation (failure case) console.log(chalk.yellow('\n๐Ÿ“‹ Test 6: Reference Consistency Validation (Failure)')); const invalidContent = { key: 'different-task-name', version: '1.0.0', domain: 'core', flow: 'sys-tasks' }; try { await resolver.validateReferenceConsistency( 'Tasks/task-invalidate-cache.1.0.0.json', invalidContent, validParsedRef ); console.log(' โœ… Expected validation error:', error.message); } catch (error) { console.log(' โœ… Expected validation error:', error.message); } // Test 7: Version format validation console.log(chalk.yellow('\n๐Ÿ“‹ Test 7: Version Format Validation')); const invalidVersionContent = { key: 'task-invalidate-cache', version: 'invalid-version', domain: 'core', flow: 'sys-tasks' }; try { await resolver.validateReferenceConsistency( 'Tasks/task-invalidate-cache.invalid-version.json', invalidVersionContent, { filePath: 'Tasks/task-invalidate-cache.invalid-version.json', version: 'invalid-version' } ); console.log(' โŒ Should have failed but passed'); } catch (error) { console.log(' โœ… Expected version format error:', error.message); } // Test 8: Mock validation with enhanced details console.log(chalk.yellow('\n๐Ÿ“‹ Test 8: Enhanced Reference Validation Structure')); const mockJson = { "key": "test-component", "version": "1.0.0", "domain": "test", "tasks": [ { "ref": "Tasks/task-example.1.0.0.json" }, { "ref": "@vnext/domain-core/Tasks/task-audit.1.0.0.json" } ], "nested": { "workflow": { "ref": "Workflows/example-flow.1.0.0.json" } } }; // This will fail because files don't exist, but shows the enhanced structure try { const validation = await resolver.validateAllReferences(mockJson, 'test-domain'); console.log(' โœ… Validation result:', { valid: validation.valid, details: validation.validationDetails, errors: validation.errors.length }); } catch (error) { console.log(' โš ๏ธ Expected error (files don\'t exist):', error.message); } // Test 9: Test with actual template file console.log(chalk.yellow('\n๐Ÿ“‹ Test 9: Test with Actual Template File')); const templateTaskPath = path.join(__dirname, 'template', '{domainName}', 'Tasks', 'task-invalidate-cache.1.0.0.json'); if (await fs.pathExists(templateTaskPath)) { try { const templateContent = await fs.readJSON(templateTaskPath); console.log(' ๐Ÿ“„ Template task content:', { key: templateContent.key, version: templateContent.version, domain: templateContent.domain, flow: templateContent.flow }); // Validate consistency if it's a real template if (templateContent.key && templateContent.version) { await resolver.validateReferenceConsistency( 'Tasks/task-invalidate-cache.1.0.0.json', templateContent, { filePath: 'Tasks/task-invalidate-cache.1.0.0.json', version: '1.0.0' } ); console.log(' โœ… Template file consistency validation passed'); } } catch (error) { console.log(' โš ๏ธ Template file validation error:', error.message); } } else { console.log(' โ„น๏ธ Template file not found, skipping test'); } // Test 10: Load validation config console.log(chalk.yellow('\n๐Ÿ“‹ Test 10: Load Validation Config')); const configPath = path.join(__dirname, 'template', 'vnext.config.json'); if (await fs.pathExists(configPath)) { try { const config = await resolver.loadValidationConfig(configPath); console.log(' โœ… Config loaded:', { domain: config.domain, hasReferenceResolution: !!config.referenceResolution, validateReferenceConsistency: config.referenceResolution?.validateReferenceConsistency, validateSchemas: config.referenceResolution?.validateSchemas }); } catch (error) { console.log(' โš ๏ธ Config loading error:', error.message); } } else { console.log(' โ„น๏ธ Config file not found, skipping test'); } // Test 11: Schema validation with invalid task type console.log(chalk.yellow('\n๐Ÿ“‹ Test 11: Schema Validation (Invalid Task Type)')); resolver.options.validateSchemas = true; // Enable schema validation const invalidTaskComponent = { "key": "test-invalid-task", "version": "1.0.0", "domain": "test-domain", "flow": "sys-tasks", "flowVersion": "1.0.0", "tags": ["test"], "attributes": { "type": "invalid-type-value" // This should fail validation } }; try { await resolver.validateComponentSchema(invalidTaskComponent, 'Tasks/test-invalid-task.1.0.0.json'); console.log(' โŒ Should have failed schema validation but passed'); } catch (error) { console.log(' โœ… Expected schema validation error:', error.message); } // Test 12: Schema validation with valid task type console.log(chalk.yellow('\n๐Ÿ“‹ Test 12: Schema Validation (Valid Task Type)')); const validTaskComponent = { "key": "test-valid-task", "version": "1.0.0", "domain": "test-domain", "flow": "sys-tasks", "flowVersion": "1.0.0", "tags": ["test"], "attributes": { "type": "3", // Valid type for Dapr Service "config": { "appId": "test-app", "methodName": "/api/test" } } }; try { await resolver.validateComponentSchema(validTaskComponent, 'Tasks/test-valid-task.1.0.0.json'); console.log(' โœ… Schema validation passed for valid task'); } catch (error) { console.log(' โš ๏ธ Unexpected error:', error.message); } // Test 13: Business validation (filename inconsistency) console.log(chalk.yellow('\n๐Ÿ“‹ Test 13: Business Validation (Filename Inconsistency)')); const inconsistentNameComponent = { "key": "different-name", "version": "1.0.0", "domain": "test-domain", "flow": "sys-tasks", "flowVersion": "1.0.0", "tags": ["test"], "attributes": { "type": "3", "config": { "appId": "test-app", "methodName": "/api/test" } } }; try { await resolver.validateComponentSchema(inconsistentNameComponent, 'Tasks/wrong-filename.1.0.0.json'); console.log(' โœ… Schema validation passed (business warnings expected above)'); } catch (error) { console.log(' โš ๏ธ Unexpected error:', error.message); } // Test 14: Metadata cleanup validation console.log(chalk.yellow('\n๐Ÿ“‹ Test 14: Metadata Cleanup (Resolver Fields)')); const componentWithMetadata = { "key": "test-with-metadata", "version": "1.0.0", "domain": "test-domain", "flow": "sys-tasks", "flowVersion": "1.0.0", "tags": ["test"], "attributes": { "type": "3", "config": { "appId": "test-app", "methodName": "/api/test" } }, // These metadata fields should be cleaned before validation "_resolvedFrom": "local:Tasks/test-with-metadata.1.0.0.json", "_resolvedAt": "2025-06-29T22:49:30.665Z", "_packageVersion": "1.0.0" }; try { await resolver.validateComponentSchema(componentWithMetadata, 'Tasks/test-with-metadata.1.0.0.json'); console.log(' โœ… Schema validation passed despite metadata fields (cleaned successfully)'); } catch (error) { console.log(' โŒ Unexpected error (metadata not cleaned):', error.message); } // Test 15: Test cleanMetadataFields method directly console.log(chalk.yellow('\n๐Ÿ“‹ Test 15: Direct Metadata Cleanup Method')); const testComponentWithMeta = { "key": "test", "version": "1.0.0", "_resolvedFrom": "test", "_resolvedAt": "test", "_packageVersion": "test", "normalField": "should-remain" }; const cleaned = resolver.cleanMetadataFields(testComponentWithMeta); const hasMetadataRemoved = !cleaned._resolvedFrom && !cleaned._resolvedAt && !cleaned._packageVersion; const hasNormalField = cleaned.normalField === "should-remain"; if (hasMetadataRemoved && hasNormalField) { console.log(' โœ… Metadata cleanup method works correctly'); } else { console.log(' โŒ Metadata cleanup method failed:', { hasMetadataRemoved, hasNormalField, cleanedKeys: Object.keys(cleaned) }); } // Test 16: ref validation for extension (raw payload) console.log(chalk.yellow('\n๐Ÿ“‹ Test 16: Extension Schema Validation (Raw Payload)')); const extensionRawPayload = { "key": "test-extension", "version": "1.0.0", "domain": "core", "flow": "sys-extensions", "flowVersion": "1.0.0", "tags": ["test"], "attributes": { "type": 1, "scope": 1, "task": { "order": 1, "task": { "key": "test-task", "domain": "core", "flow": "sys-tasks", "version": "1.0.0" }, "mapping": { "location": "./src/TestMapping.csx", "code": "" } } } }; try { await resolver.validateComponentSchema(extensionRawPayload, 'Extensions/test-extension.1.0.0.json'); console.log(' โœ… Extension raw payload validation passed'); } catch (error) { console.log(' โš ๏ธ Extension raw payload validation error:', error.message); } // Test 17: ref validation for extension (ref payload) console.log(chalk.yellow('\n๐Ÿ“‹ Test 17: Extension Schema Validation (ref Payload)')); const extensionRefPayload = { "key": "test-extension-ref", "version": "1.0.0", "domain": "core", "flow": "sys-extensions", "flowVersion": "1.0.0", "tags": ["test"], "attributes": { "type": 1, "scope": 1, "task": { "order": 1, "task": { "ref": "Tasks/test-task.1.0.0.json" }, "mapping": { "location": "./src/TestMapping.csx", "code": "" } } } }; try { await resolver.validateComponentSchema(extensionRefPayload, 'Extensions/test-extension-ref.1.0.0.json'); console.log(' โœ… Extension ref payload validation passed'); } catch (error) { console.log(' โš ๏ธ Extension ref payload validation error:', error.message); } // Test 18: Function ref validation (raw payload) console.log(chalk.yellow('\n๐Ÿ“‹ Test 18: Function Schema Validation (Raw Payload)')); const functionRawPayload = { "key": "test-function", "version": "1.0.0", "domain": "core", "flow": "sys-functions", "flowVersion": "1.0.0", "tags": ["test"], "attributes": { "scope": 1, "task": { "key": "test-task", "domain": "core", "flow": "sys-tasks", "version": "1.0.0" }, "mapping": { "location": "./src/TestMapping.csx", "code": "" } } }; try { await resolver.validateComponentSchema(functionRawPayload, 'Functions/test-function.1.0.0.json'); console.log(' โœ… Function raw payload validation passed'); } catch (error) { console.log(' โš ๏ธ Function raw payload validation error:', error.message); } // Test 19: Function ref validation (ref payload) console.log(chalk.yellow('\n๐Ÿ“‹ Test 19: Function Schema Validation (ref Payload)')); const functionRefPayload = { "key": "test-function-ref", "version": "1.0.0", "domain": "core", "flow": "sys-functions", "flowVersion": "1.0.0", "tags": ["test"], "attributes": { "scope": 1, "task": { "ref": "Tasks/test-task.1.0.0.json" }, "mapping": { "location": "./src/TestMapping.csx", "code": "" } } }; try { await resolver.validateComponentSchema(functionRefPayload, 'Functions/test-function-ref.1.0.0.json'); console.log(' โœ… Function ref payload validation passed'); } catch (error) { console.log(' โš ๏ธ Function ref payload validation error:', error.message); } console.log(chalk.green('\n๐ŸŽ‰ Enhanced Reference Resolver tests completed!')); console.log(chalk.blue('โœจ New features tested:')); console.log(' โ€ข Reference consistency validation (filename vs content)'); console.log(' โ€ข Version format validation'); console.log(' โ€ข Key format validation'); console.log(' โ€ข Enhanced validation reporting'); console.log(' โ€ข Configuration loading'); console.log(' โ€ข JSON Schema validation (like validate-component.js)'); console.log(' โ€ข Domain mismatch validation'); console.log(' โ€ข Business rule validations'); console.log(' โ€ข Enhanced error reporting with detailed paths'); console.log(' โ€ข Metadata cleanup (fixes additionalProperties: false schema errors)'); console.log(' โ€ข Component type detection fix (paths with/without leading slash)'); console.log(' โ€ข ref support validation for all reference types'); console.log(chalk.gray('โ„น๏ธ Note: Some tests failed because referenced files don\'t exist - this is expected.')); } catch (error) { console.error(chalk.red('โŒ Test failed:'), error.message); console.error(error.stack); process.exit(1); } } // Run tests if this file is executed directly if (require.main === module) { testRefResolver(); } module.exports = { testRefResolver };