mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
config: packages,json,shared (5 files)
This commit is contained in:
107
packages/json_script_example/tests/expressions.cases.json
Normal file
107
packages/json_script_example/tests/expressions.cases.json
Normal file
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"name": "Expression Tests",
|
||||
"description": "Test all expression types in JSON script",
|
||||
"tests": [
|
||||
{
|
||||
"name": "Binary expressions - addition",
|
||||
"function": "all_expressions",
|
||||
"args": [10, 5],
|
||||
"expected": {
|
||||
"sum": 15,
|
||||
"diff": 5,
|
||||
"product": 50,
|
||||
"max": 10,
|
||||
"bothPositive": true,
|
||||
"message": "Sum: 15, Product: 50, Max: 10"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Binary expressions - negative numbers",
|
||||
"function": "all_expressions",
|
||||
"args": [-10, 5],
|
||||
"expected": {
|
||||
"sum": -5,
|
||||
"diff": -15,
|
||||
"product": -50,
|
||||
"max": 5,
|
||||
"bothPositive": false,
|
||||
"message": "Sum: -5, Product: -50, Max: 5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Binary expressions - equal values",
|
||||
"function": "all_expressions",
|
||||
"args": [7, 7],
|
||||
"expected": {
|
||||
"sum": 14,
|
||||
"diff": 0,
|
||||
"product": 49,
|
||||
"max": 7,
|
||||
"bothPositive": true,
|
||||
"message": "Sum: 14, Product: 49, Max: 7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "All operators - basic arithmetic",
|
||||
"function": "all_operators",
|
||||
"args": [10, 3],
|
||||
"expected": {
|
||||
"arithmetic": {
|
||||
"add": 13,
|
||||
"subtract": 7,
|
||||
"multiply": 30,
|
||||
"divide": 3.3333333333333335,
|
||||
"modulo": 1
|
||||
},
|
||||
"comparison": {
|
||||
"equal": false,
|
||||
"notEqual": true,
|
||||
"lessThan": false,
|
||||
"greaterThan": true,
|
||||
"lessOrEqual": false,
|
||||
"greaterOrEqual": true
|
||||
},
|
||||
"logical": {
|
||||
"and": 3,
|
||||
"or": 10,
|
||||
"not": false
|
||||
},
|
||||
"unary": {
|
||||
"negate": -10,
|
||||
"plus": 10
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "All operators - equal values",
|
||||
"function": "all_operators",
|
||||
"args": [5, 5],
|
||||
"expected": {
|
||||
"arithmetic": {
|
||||
"add": 10,
|
||||
"subtract": 0,
|
||||
"multiply": 25,
|
||||
"divide": 1,
|
||||
"modulo": 0
|
||||
},
|
||||
"comparison": {
|
||||
"equal": true,
|
||||
"notEqual": false,
|
||||
"lessThan": false,
|
||||
"greaterThan": false,
|
||||
"lessOrEqual": true,
|
||||
"greaterOrEqual": true
|
||||
},
|
||||
"logical": {
|
||||
"and": 5,
|
||||
"or": 5,
|
||||
"not": false
|
||||
},
|
||||
"unary": {
|
||||
"negate": -5,
|
||||
"plus": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
104
packages/json_script_example/tests/statements.cases.json
Normal file
104
packages/json_script_example/tests/statements.cases.json
Normal file
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"name": "Statement Tests",
|
||||
"description": "Test all statement types in JSON script",
|
||||
"tests": [
|
||||
{
|
||||
"name": "For-each loop with array",
|
||||
"function": "all_statements",
|
||||
"args": [[10, 20, 30, 40, 50]],
|
||||
"expected": {
|
||||
"count": 5,
|
||||
"sum": 150,
|
||||
"average": 30,
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "For-each loop with empty array",
|
||||
"function": "all_statements",
|
||||
"args": [[]],
|
||||
"expected": {
|
||||
"count": 0,
|
||||
"sum": 0,
|
||||
"average": 0,
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "For-each loop with single item",
|
||||
"function": "all_statements",
|
||||
"args": [[100]],
|
||||
"expected": {
|
||||
"count": 1,
|
||||
"sum": 100,
|
||||
"average": 100,
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Control flow - negative",
|
||||
"function": "control_flow",
|
||||
"args": [-5],
|
||||
"expected": "negative"
|
||||
},
|
||||
{
|
||||
"name": "Control flow - zero",
|
||||
"function": "control_flow",
|
||||
"args": [0],
|
||||
"expected": "zero"
|
||||
},
|
||||
{
|
||||
"name": "Control flow - small",
|
||||
"function": "control_flow",
|
||||
"args": [5],
|
||||
"expected": "small"
|
||||
},
|
||||
{
|
||||
"name": "Control flow - medium",
|
||||
"function": "control_flow",
|
||||
"args": [50],
|
||||
"expected": "medium"
|
||||
},
|
||||
{
|
||||
"name": "Control flow - large",
|
||||
"function": "control_flow",
|
||||
"args": [150],
|
||||
"expected": "large"
|
||||
},
|
||||
{
|
||||
"name": "Control flow - boundary (10)",
|
||||
"function": "control_flow",
|
||||
"args": [10],
|
||||
"expected": "medium"
|
||||
},
|
||||
{
|
||||
"name": "Control flow - boundary (100)",
|
||||
"function": "control_flow",
|
||||
"args": [100],
|
||||
"expected": "large"
|
||||
},
|
||||
{
|
||||
"name": "Data structures - no parameters",
|
||||
"function": "data_structures",
|
||||
"args": [],
|
||||
"expected": {
|
||||
"numbers": [1, 2, 3, 4, 5],
|
||||
"person": {
|
||||
"name": "John Doe",
|
||||
"age": 30,
|
||||
"email": "john@example.com",
|
||||
"active": true
|
||||
},
|
||||
"config": {
|
||||
"server": {
|
||||
"host": "localhost",
|
||||
"port": 3000,
|
||||
"protocol": "http"
|
||||
},
|
||||
"features": ["auth", "api", "dashboard"]
|
||||
},
|
||||
"extractedName": "John Doe"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
241
packages/shared/seed/scripts/runtime/script_executor.d.ts
vendored
Normal file
241
packages/shared/seed/scripts/runtime/script_executor.d.ts
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* TypeScript type definitions for JSON Script Runtime Executor
|
||||
*/
|
||||
|
||||
// Expression Types
|
||||
export type Expression =
|
||||
| StringLiteral
|
||||
| NumberLiteral
|
||||
| BooleanLiteral
|
||||
| NullLiteral
|
||||
| TemplateLiteral
|
||||
| BinaryExpression
|
||||
| LogicalExpression
|
||||
| UnaryExpression
|
||||
| ConditionalExpression
|
||||
| MemberAccess
|
||||
| CallExpression
|
||||
| ObjectLiteral
|
||||
| ArrayLiteral
|
||||
| Reference;
|
||||
|
||||
export interface StringLiteral {
|
||||
type: 'string_literal';
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface NumberLiteral {
|
||||
type: 'number_literal';
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface BooleanLiteral {
|
||||
type: 'boolean_literal';
|
||||
value: boolean;
|
||||
}
|
||||
|
||||
export interface NullLiteral {
|
||||
type: 'null_literal';
|
||||
}
|
||||
|
||||
export interface TemplateLiteral {
|
||||
type: 'template_literal';
|
||||
template: string;
|
||||
}
|
||||
|
||||
export interface BinaryExpression {
|
||||
type: 'binary_expression';
|
||||
left: Expression | any;
|
||||
operator: '+' | '-' | '*' | '/' | '%' | '==' | '===' | '!=' | '!==' | '<' | '>' | '<=' | '>=';
|
||||
right: Expression | any;
|
||||
}
|
||||
|
||||
export interface LogicalExpression {
|
||||
type: 'logical_expression';
|
||||
left: Expression | any;
|
||||
operator: '&&' | '||' | '??' | 'and' | 'or';
|
||||
right: Expression | any;
|
||||
}
|
||||
|
||||
export interface UnaryExpression {
|
||||
type: 'unary_expression';
|
||||
operator: '!' | '-' | '+' | '~' | 'not' | 'typeof';
|
||||
argument: Expression | any;
|
||||
}
|
||||
|
||||
export interface ConditionalExpression {
|
||||
type: 'conditional_expression';
|
||||
test: Expression | any;
|
||||
consequent: Expression | any;
|
||||
alternate: Expression | any;
|
||||
}
|
||||
|
||||
export interface MemberAccess {
|
||||
type: 'member_access';
|
||||
object: Expression | any;
|
||||
property: string | Expression | any;
|
||||
computed?: boolean;
|
||||
optional?: boolean;
|
||||
}
|
||||
|
||||
export interface CallExpression {
|
||||
type: 'call_expression';
|
||||
callee: string | Expression;
|
||||
args?: Array<Expression | any>;
|
||||
}
|
||||
|
||||
export interface ObjectLiteral {
|
||||
type: 'object_literal';
|
||||
properties: Record<string, Expression | any>;
|
||||
}
|
||||
|
||||
export interface ArrayLiteral {
|
||||
type: 'array_literal';
|
||||
elements: Array<Expression | any>;
|
||||
}
|
||||
|
||||
export type Reference = string; // $ref:path.to.value
|
||||
|
||||
// Statement Types
|
||||
export type Statement =
|
||||
| ConstDeclaration
|
||||
| LetDeclaration
|
||||
| Assignment
|
||||
| IfStatement
|
||||
| ReturnStatement
|
||||
| TryCatch
|
||||
| CallExpressionStatement
|
||||
| ForEachLoop
|
||||
| Comment;
|
||||
|
||||
export interface ConstDeclaration {
|
||||
type: 'const_declaration';
|
||||
name: string;
|
||||
value: Expression | any;
|
||||
}
|
||||
|
||||
export interface LetDeclaration {
|
||||
type: 'let_declaration';
|
||||
name: string;
|
||||
value: Expression | any;
|
||||
}
|
||||
|
||||
export interface Assignment {
|
||||
type: 'assignment';
|
||||
target: string | Reference;
|
||||
value: Expression | any;
|
||||
}
|
||||
|
||||
export interface IfStatement {
|
||||
type: 'if_statement';
|
||||
condition: Expression | any;
|
||||
then?: Statement[];
|
||||
else?: Statement[];
|
||||
}
|
||||
|
||||
export interface ReturnStatement {
|
||||
type: 'return';
|
||||
value: Expression | any;
|
||||
}
|
||||
|
||||
export interface TryCatch {
|
||||
type: 'try_catch';
|
||||
try: Statement[];
|
||||
catch?: {
|
||||
param?: string;
|
||||
body: Statement[];
|
||||
};
|
||||
finally?: Statement[];
|
||||
}
|
||||
|
||||
export interface CallExpressionStatement {
|
||||
type: 'call_expression';
|
||||
callee: string | Expression;
|
||||
args?: Array<Expression | any>;
|
||||
}
|
||||
|
||||
export interface ForEachLoop {
|
||||
type: 'for_each_loop';
|
||||
iterator: string;
|
||||
iterable: Expression | any;
|
||||
body?: Statement[];
|
||||
}
|
||||
|
||||
export interface Comment {
|
||||
type: 'comment';
|
||||
text: string;
|
||||
}
|
||||
|
||||
// Function Definition
|
||||
export interface FunctionParameter {
|
||||
name: string;
|
||||
type: string;
|
||||
description?: string;
|
||||
optional?: boolean;
|
||||
default?: any;
|
||||
}
|
||||
|
||||
export interface FunctionDefinition {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
async?: boolean;
|
||||
exported?: boolean;
|
||||
params?: FunctionParameter[];
|
||||
returnType?: string;
|
||||
body: Statement[];
|
||||
}
|
||||
|
||||
// Constant Definition
|
||||
export interface ConstantDefinition {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
value: any;
|
||||
description?: string;
|
||||
exported?: boolean;
|
||||
}
|
||||
|
||||
// Script JSON Structure
|
||||
export interface ScriptJSON {
|
||||
schema_version: string;
|
||||
package: string;
|
||||
description?: string;
|
||||
constants?: ConstantDefinition[];
|
||||
functions: FunctionDefinition[];
|
||||
}
|
||||
|
||||
// Execution Context
|
||||
export interface ExecutionContext {
|
||||
params: Record<string, any>;
|
||||
local_vars: Record<string, any>;
|
||||
constants: Record<string, any>;
|
||||
imports: Record<string, any>;
|
||||
functions: Record<string, any>;
|
||||
catch?: Record<string, any>;
|
||||
}
|
||||
|
||||
// Return Value
|
||||
export interface ReturnValue {
|
||||
type: 'return';
|
||||
value: any;
|
||||
}
|
||||
|
||||
// Main API
|
||||
export function resolveRef(ref: any, context: ExecutionContext): any;
|
||||
export function evalExpression(expr: Expression | any, context: ExecutionContext): any;
|
||||
export function executeStatement(stmt: Statement, context: ExecutionContext): ReturnValue | null;
|
||||
export function executeFunction(scriptJson: ScriptJSON, functionName: string, args?: any[]): any;
|
||||
export function loadAndExecute(jsonPath: string, functionName: string, args?: any[]): Promise<any>;
|
||||
export function loadAndExecuteSync(jsonPath: string, functionName: string, args?: any[]): any;
|
||||
|
||||
declare const _default: {
|
||||
executeFunction: typeof executeFunction;
|
||||
loadAndExecute: typeof loadAndExecute;
|
||||
loadAndExecuteSync: typeof loadAndExecuteSync;
|
||||
resolveRef: typeof resolveRef;
|
||||
evalExpression: typeof evalExpression;
|
||||
executeStatement: typeof executeStatement;
|
||||
};
|
||||
|
||||
export default _default;
|
||||
43
packages/shared/seed/scripts/runtime/test_cli.js
Normal file
43
packages/shared/seed/scripts/runtime/test_cli.js
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* CLI Test Runner for JSON Script Tests
|
||||
* Usage: node test_cli.js <script.json> <test-suite.json>
|
||||
*/
|
||||
|
||||
const { runTestSuiteSync, formatResults } = require('./test_runner.js');
|
||||
const path = require('path');
|
||||
|
||||
function main() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length < 2) {
|
||||
console.error('Usage: node test_cli.js <script.json> <test-suite.json>');
|
||||
console.error('');
|
||||
console.error('Example:');
|
||||
console.error(' node test_cli.js ../../script.json ../../../json_script_example/tests/expressions.cases.json');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const scriptPath = path.resolve(args[0]);
|
||||
const testPath = path.resolve(args[1]);
|
||||
|
||||
console.log('Running tests...');
|
||||
console.log(`Script: ${scriptPath}`);
|
||||
console.log(`Test Suite: ${testPath}`);
|
||||
|
||||
try {
|
||||
const results = runTestSuiteSync(scriptPath, testPath);
|
||||
const output = formatResults(results);
|
||||
console.log(output);
|
||||
|
||||
// Exit with error code if tests failed
|
||||
process.exit(results.failed > 0 ? 1 : 0);
|
||||
} catch (error) {
|
||||
console.error('\n❌ Test execution failed:');
|
||||
console.error(error.message);
|
||||
console.error(error.stack);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
219
packages/shared/seed/scripts/runtime/test_runner.js
Normal file
219
packages/shared/seed/scripts/runtime/test_runner.js
Normal file
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* JSON-based Test Runner for Script Executor
|
||||
* Executes test cases defined in JSON format
|
||||
*/
|
||||
|
||||
import { executeFunction } from './script_executor.js';
|
||||
|
||||
/**
|
||||
* Deep equality comparison
|
||||
*/
|
||||
function deepEqual(a, b) {
|
||||
if (a === b) return true;
|
||||
if (a === null || b === null) return false;
|
||||
if (typeof a !== typeof b) return false;
|
||||
if (typeof a !== 'object') return false;
|
||||
|
||||
const keysA = Object.keys(a);
|
||||
const keysB = Object.keys(b);
|
||||
|
||||
if (keysA.length !== keysB.length) return false;
|
||||
|
||||
for (const key of keysA) {
|
||||
if (!keysB.includes(key)) return false;
|
||||
if (!deepEqual(a[key], b[key])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a single test case
|
||||
* @param {Object} testCase - Test case definition
|
||||
* @param {Object} scriptJson - Script JSON to test against
|
||||
* @returns {Object} Test result
|
||||
*/
|
||||
function executeTestCase(testCase, scriptJson) {
|
||||
const startTime = performance.now();
|
||||
let passed = false;
|
||||
let actualResult = null;
|
||||
let error = null;
|
||||
|
||||
try {
|
||||
actualResult = executeFunction(scriptJson, testCase.function, testCase.args || []);
|
||||
|
||||
// Check result
|
||||
if (testCase.expected !== undefined) {
|
||||
passed = deepEqual(actualResult, testCase.expected);
|
||||
} else if (testCase.expectedError) {
|
||||
passed = false; // Should have thrown
|
||||
} else {
|
||||
passed = true; // No assertion
|
||||
}
|
||||
} catch (err) {
|
||||
error = err;
|
||||
if (testCase.expectedError) {
|
||||
// Check if error matches expected
|
||||
passed = err.message.includes(testCase.expectedError);
|
||||
} else {
|
||||
passed = false;
|
||||
}
|
||||
}
|
||||
|
||||
const duration = performance.now() - startTime;
|
||||
|
||||
return {
|
||||
name: testCase.name,
|
||||
function: testCase.function,
|
||||
passed,
|
||||
duration,
|
||||
actualResult,
|
||||
expectedResult: testCase.expected,
|
||||
error: error ? error.message : null
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute test suite from JSON
|
||||
* @param {Object} testSuite - Test suite definition
|
||||
* @param {Object} scriptJson - Script JSON to test
|
||||
* @returns {Object} Test results
|
||||
*/
|
||||
function executeTestSuite(testSuite, scriptJson) {
|
||||
const results = {
|
||||
suite: testSuite.name || 'Unnamed Suite',
|
||||
description: testSuite.description,
|
||||
total: 0,
|
||||
passed: 0,
|
||||
failed: 0,
|
||||
duration: 0,
|
||||
tests: []
|
||||
};
|
||||
|
||||
const startTime = performance.now();
|
||||
|
||||
for (const testCase of (testSuite.tests || [])) {
|
||||
const result = executeTestCase(testCase, scriptJson);
|
||||
results.tests.push(result);
|
||||
results.total++;
|
||||
if (result.passed) {
|
||||
results.passed++;
|
||||
} else {
|
||||
results.failed++;
|
||||
}
|
||||
}
|
||||
|
||||
results.duration = performance.now() - startTime;
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format test results as human-readable string
|
||||
* @param {Object} results - Test results
|
||||
* @returns {string} Formatted output
|
||||
*/
|
||||
function formatResults(results) {
|
||||
const lines = [];
|
||||
lines.push(`\n${'='.repeat(60)}`);
|
||||
lines.push(`Test Suite: ${results.suite}`);
|
||||
if (results.description) {
|
||||
lines.push(`Description: ${results.description}`);
|
||||
}
|
||||
lines.push(`${'='.repeat(60)}\n`);
|
||||
|
||||
for (const test of results.tests) {
|
||||
const icon = test.passed ? '✅' : '❌';
|
||||
lines.push(`${icon} ${test.name}`);
|
||||
lines.push(` Function: ${test.function}`);
|
||||
lines.push(` Duration: ${test.duration.toFixed(2)}ms`);
|
||||
|
||||
if (!test.passed) {
|
||||
lines.push(` Expected: ${JSON.stringify(test.expectedResult)}`);
|
||||
lines.push(` Actual: ${JSON.stringify(test.actualResult)}`);
|
||||
if (test.error) {
|
||||
lines.push(` Error: ${test.error}`);
|
||||
}
|
||||
}
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
lines.push(`${'='.repeat(60)}`);
|
||||
lines.push(`Results: ${results.passed}/${results.total} passed (${results.failed} failed)`);
|
||||
lines.push(`Total Duration: ${results.duration.toFixed(2)}ms`);
|
||||
lines.push(`${'='.repeat(60)}\n`);
|
||||
|
||||
if (results.failed === 0) {
|
||||
lines.push('🎉 All tests passed!');
|
||||
} else {
|
||||
lines.push(`❌ ${results.failed} test(s) failed.`);
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and run test suite from files
|
||||
* @param {string} scriptPath - Path to script.json
|
||||
* @param {string} testPath - Path to test suite JSON
|
||||
* @returns {Promise<Object>} Test results
|
||||
*/
|
||||
async function runTestSuite(scriptPath, testPath) {
|
||||
const fs = await import('fs/promises');
|
||||
|
||||
const scriptContent = await fs.readFile(scriptPath, 'utf-8');
|
||||
const scriptJson = JSON.parse(scriptContent);
|
||||
|
||||
const testContent = await fs.readFile(testPath, 'utf-8');
|
||||
const testSuite = JSON.parse(testContent);
|
||||
|
||||
return executeTestSuite(testSuite, scriptJson);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronous version for Node.js
|
||||
*/
|
||||
function runTestSuiteSync(scriptPath, testPath) {
|
||||
if (typeof require !== 'undefined') {
|
||||
const fs = require('fs');
|
||||
|
||||
const scriptContent = fs.readFileSync(scriptPath, 'utf-8');
|
||||
const scriptJson = JSON.parse(scriptContent);
|
||||
|
||||
const testContent = fs.readFileSync(testPath, 'utf-8');
|
||||
const testSuite = JSON.parse(testContent);
|
||||
|
||||
return executeTestSuite(testSuite, scriptJson);
|
||||
}
|
||||
throw new Error('Synchronous file loading not available in browser environment');
|
||||
}
|
||||
|
||||
// Export for different module systems
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = {
|
||||
executeTestCase,
|
||||
executeTestSuite,
|
||||
formatResults,
|
||||
runTestSuite,
|
||||
runTestSuiteSync,
|
||||
deepEqual
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
executeTestCase,
|
||||
executeTestSuite,
|
||||
formatResults,
|
||||
runTestSuite,
|
||||
runTestSuiteSync,
|
||||
deepEqual
|
||||
};
|
||||
|
||||
export default {
|
||||
executeTestCase,
|
||||
executeTestSuite,
|
||||
formatResults,
|
||||
runTestSuite,
|
||||
runTestSuiteSync,
|
||||
deepEqual
|
||||
};
|
||||
Reference in New Issue
Block a user