UNPKG

nerdamer-prime

Version:
400 lines (347 loc) 18.3 kB
import type nerdamer from '../index'; import tsMorph from 'ts-morph'; // Type alias for dynamic property access in investigation tests type Indexable = Record<string, any>; describe('Arity Mismatch Investigation', () => { let nerdamerRuntime: typeof nerdamer & Indexable; let project: tsMorph.Project; let sourceFile: tsMorph.SourceFile; let nerdamerPrimeNamespace: tsMorph.ModuleDeclaration | undefined; beforeAll(async () => { // Load complete nerdamer with all modules const module = await import('../all.js'); nerdamerRuntime = module.default || module; project = new tsMorph.Project({ tsConfigFilePath: 'tsconfig.json', }); sourceFile = project.getSourceFileOrThrow('index.d.ts'); nerdamerPrimeNamespace = sourceFile .getDescendantsOfKind(tsMorph.SyntaxKind.ModuleDeclaration) .find((ns: tsMorph.ModuleDeclaration) => ns.getName() === 'nerdamerPrime'); }); it('should analyze specific arity mismatch functions in detail', () => { // Select a few representative functions with arity mismatches const testFunctions = ['atan2', 'solve', 'matget', 'cross', 'diff', 'integrate', 'defint']; console.log('\n=== Detailed Arity Mismatch Analysis ==='); for (const funcName of testFunctions) { const runtimeFunction = nerdamerRuntime[funcName]; if (typeof runtimeFunction !== 'function') { console.log(`❌ ${funcName}: Not found in runtime`); continue; } // Get TypeScript function declaration if (!nerdamerPrimeNamespace) { console.log(`❌ ${funcName}: nerdamerPrime namespace not found`); continue; } const functionDeclarations = nerdamerPrimeNamespace .getDescendantsOfKind(tsMorph.SyntaxKind.FunctionDeclaration) .filter(f => f.getName() === funcName); if (functionDeclarations.length === 0) { console.log(`❌ ${funcName}: No TS declaration found`); continue; } const funcDecl = functionDeclarations[0]; if (!funcDecl) { console.log(`❌ ${funcName}: Function declaration is undefined`); continue; } const tsParams = funcDecl.getParameters(); const requiredParams = tsParams.filter(p => !p.hasQuestionToken() && !p.isRestParameter()).length; const runtimeArity = runtimeFunction.length; console.log(`\n📋 ${funcName}:`); console.log(` TS required params: ${requiredParams}`); console.log(` Runtime arity: ${runtimeArity}`); const signatureText = funcDecl.getText(); const signature = signatureText.split('{')[0]?.trim() || signatureText; console.log(` TS signature: ${signature}`); // Analyze function source code const functionSource = runtimeFunction.toString(); const usesArguments = functionSource.includes('arguments'); const argMatches = functionSource.match(/arguments\[(?:\d+)\]/gu) || []; const maxArgIndex = argMatches.length > 0 ? Math.max(...argMatches.map((m: string) => parseInt(/\d+/u.exec(m)![0], 10))) + 1 : 0; console.log(` Uses arguments object: ${usesArguments}`); if (usesArguments) { console.log(` Max argument index used: ${maxArgIndex}`); console.log(` Arguments pattern matches: ${argMatches.length}`); } // Test if function actually works with expected parameters let functionalTest = 'Not tested'; try { switch (funcName) { case 'atan2': { const result1 = runtimeFunction(1, 2); functionalTest = `✅ Works: atan2(1,2) = ${result1?.toString?.() || result1}`; break; } case 'solve': { const result2 = runtimeFunction('x^2-4', 'x'); functionalTest = `✅ Works: solve('x^2-4','x') returns ${typeof result2}`; break; } case 'matget': { const testMatrix = nerdamerRuntime.matrix([ [1, 2], [3, 4], ]); const result3 = runtimeFunction(testMatrix, 0, 1); functionalTest = `✅ Works: matget(matrix,0,1) = ${result3?.toString?.() || result3}`; break; } case 'cross': { const v1 = nerdamerRuntime.vector([1, 0, 0]); const v2 = nerdamerRuntime.vector([0, 1, 0]); const result4 = runtimeFunction(v1, v2); functionalTest = `✅ Works: cross(v1,v2) returns ${typeof result4}`; break; } case 'diff': { const result5 = runtimeFunction('x^2', 'x'); functionalTest = `✅ Works: diff('x^2','x') = ${result5?.toString?.() || result5}`; break; } case 'integrate': { const result6 = runtimeFunction('x^2', 'x'); functionalTest = `✅ Works: integrate('x^2','x') = ${result6?.toString?.() || result6}`; break; } case 'defint': { const result7 = runtimeFunction('x', 0, 1, 'x'); functionalTest = `✅ Works: defint('x',0,1,'x') = ${result7?.toString?.() || result7}`; break; } } } catch (error) { functionalTest = `❌ Error: ${error instanceof Error ? error.message : String(error)}`; } console.log(` Functional test: ${functionalTest}`); // Check if this is a wrapper function pattern const isWrapper = functionSource.includes('return') && functionSource.length < 200; if (isWrapper) { console.log(` 🔄 Appears to be a wrapper function`); } } }); it('should analyze the overall pattern of arity mismatches', () => { console.log('\n=== Overall Arity Pattern Analysis ==='); const allFunctions = Object.keys(nerdamerRuntime).filter(key => typeof nerdamerRuntime[key] === 'function'); const arityStats = { zeroArity: 0, nonZeroArity: 0, usesArguments: 0, doesNotUseArguments: 0, wrapperFunctions: 0, }; const sampleArityZeroFunctions: string[] = []; for (const funcName of allFunctions) { const func = nerdamerRuntime[funcName]; const arity = func.length; const source = func.toString(); if (arity === 0) { arityStats.zeroArity++; if (sampleArityZeroFunctions.length < 10) { sampleArityZeroFunctions.push(funcName); } } else { arityStats.nonZeroArity++; } if (source.includes('arguments')) { arityStats.usesArguments++; } else { arityStats.doesNotUseArguments++; } // Simple heuristic for wrapper functions if (source.includes('return') && source.length < 200 && !source.includes('function')) { arityStats.wrapperFunctions++; } } console.log('Function arity distribution:'); console.log( ` Zero arity: ${arityStats.zeroArity} (${Math.round((arityStats.zeroArity / allFunctions.length) * 100)}%)` ); console.log( ` Non-zero arity: ${arityStats.nonZeroArity} (${Math.round((arityStats.nonZeroArity / allFunctions.length) * 100)}%)` ); console.log(` Uses arguments object: ${arityStats.usesArguments}`); console.log(` Does not use arguments: ${arityStats.doesNotUseArguments}`); console.log(` Apparent wrapper functions: ${arityStats.wrapperFunctions}`); console.log('\nSample zero-arity functions:', sampleArityZeroFunctions); // Test if zero-arity functions are typically module exports console.log('\n=== Zero-Arity Function Analysis ==='); for (const funcName of sampleArityZeroFunctions.slice(0, 5)) { const func = nerdamerRuntime[funcName]; const source = func.toString(); console.log(`\n${funcName}:`); console.log(` Source length: ${source.length} chars`); console.log(` Uses arguments: ${source.includes('arguments')}`); console.log(` First 100 chars: ${source.substring(0, 100)}...`); } }); it('should test common JavaScript argument patterns', () => { console.log('\n=== JavaScript Argument Pattern Testing ==='); // Test some specific functions that should work despite arity 0 const testCases = [ { name: 'Basic arithmetic with solve', test: () => nerdamerRuntime.solve('x^2 - 4', 'x'), description: 'solve() with 2 arguments', }, { name: 'Trigonometric with multiple args', test: () => nerdamerRuntime.atan2(1, 1), description: 'atan2() with 2 arguments', }, { name: 'Matrix operations', test: () => { const mat = nerdamerRuntime.matrix([ [1, 2], [3, 4], ]); return nerdamerRuntime.matget(mat, 0, 1); }, description: 'matget() with 3 arguments', }, { name: 'Variable argument functions', test: () => nerdamerRuntime.max(1, 2, 3, 4, 5), description: 'max() with variable arguments', }, { name: 'Integration with bounds', test: () => nerdamerRuntime.defint('x^2', 0, 2, 'x'), description: 'defint() with 4 arguments', }, ]; for (const testCase of testCases) { try { const result = testCase.test(); console.log(`✅ ${testCase.name}: ${testCase.description} -> ${result?.toString?.() || typeof result}`); } catch (error) { console.log( `❌ ${testCase.name}: ${testCase.description} -> ERROR: ${error instanceof Error ? error.message : String(error)}` ); } } console.log('\n🔍 Pattern Analysis Conclusion:'); console.log('Most zero-arity functions likely use the arguments object or are'); console.log('wrapper functions that delegate to internal implementations.'); console.log('This is a common JavaScript pattern for flexible function interfaces.'); }); it('should investigate the 6 remaining genuine issues from robust arity checking', () => { console.log('\n=== Investigation of 6 Remaining Genuine Issues ==='); const remainingIssues = [ 'getVars', // Return type mismatch 'supported', // Return type mismatch 'matset', // Complex function that may not handle 4+ parameters correctly 'sum', // Complex function that may not handle 4+ parameters correctly 'product', // Complex function that may not handle 4+ parameters correctly 'solveEquations', // Genuine arity mismatch (declares 5 but needs 1) ]; for (const funcName of remainingIssues) { const runtimeFunction = nerdamerRuntime[funcName]; if (typeof runtimeFunction !== 'function') { console.log(`❌ ${funcName}: Not found in runtime`); continue; } console.log(`\n🔍 Investigating ${funcName}:`); console.log(` Runtime arity: ${runtimeFunction.length}`); const functionSource = runtimeFunction.toString(); const usesArguments = functionSource.includes('arguments'); console.log(` Uses arguments object: ${usesArguments}`); console.log(` Function length: ${functionSource.length} chars`); // Show function signature preview const firstLine = functionSource.split('\n')[0].trim(); console.log(` Signature: ${firstLine}...`); // Test specific issue types switch (funcName) { case 'getVars': console.log(' 🧪 Testing return type:'); try { const result = runtimeFunction(); console.log(` - Returns: ${typeof result} (${JSON.stringify(result)})`); console.log(` - Expected: Record<string, NerdamerExpression | string>`); console.log(` - Issue: Returns empty object when no variables set`); } catch (error) { console.log(` - Error: ${error instanceof Error ? error.message : String(error)}`); } break; case 'supported': console.log(' 🧪 Testing return type:'); try { // Test with no arguments const result1 = runtimeFunction(); console.log(` - No args: ${typeof result1} (length: ${result1?.length || 'N/A'})`); // Test with string argument const result2 = runtimeFunction('cos'); console.log(` - With 'cos': ${typeof result2} (${result2})`); console.log(` - Expected: boolean`); console.log( ` - Issue: Returns array when called without arguments, boolean with arguments` ); } catch (error) { console.log(` - Error: ${error instanceof Error ? error.message : String(error)}`); } break; case 'matset': case 'sum': case 'product': console.log(' 🧪 Testing complex parameter handling:'); try { if (funcName === 'matset') { const testMatrix = nerdamerRuntime.matrix([ [1, 2], [3, 4], ]); const result = runtimeFunction(testMatrix, 0, 1, 'newValue'); console.log(` - 4 params test: Success (${typeof result})`); } else if (funcName === 'sum') { const result = runtimeFunction('x^2', 'x', 1, 5); console.log(` - 4 params test: Success (${result?.toString?.() || typeof result})`); } else if (funcName === 'product') { const result = runtimeFunction('x', 'x', 1, 3); console.log(` - 4 params test: Success (${result?.toString?.() || typeof result})`); } } catch (error) { console.log( ` - 4 params test: Failed - ${error instanceof Error ? error.message : String(error)}` ); } break; case 'solveEquations': console.log(' 🧪 Testing arity mismatch:'); console.log(` - Declared arity: ${runtimeFunction.length}`); console.log(` - TypeScript expects: 1 required param (equations array)`); try { // Test with array of equations const result1 = runtimeFunction(['x + y = 5', 'x - y = 1']); console.log(` - Array test: Success (${typeof result1})`); // Test with additional variables parameter const result2 = runtimeFunction(['x + y = 5', 'x - y = 1'], ['x', 'y']); console.log(` - Array + vars test: Success (${typeof result2})`); } catch (error) { console.log(` - Test failed: ${error instanceof Error ? error.message : String(error)}`); } break; } // Additional argument pattern analysis if (usesArguments) { const argMatches = functionSource.match(/arguments\[(?:\d+)\]/gu) || []; const maxArgIndex = argMatches.length > 0 ? Math.max(...argMatches.map((m: string) => parseInt(/\d+/u.exec(m)![0], 10))) + 1 : 0; console.log(` Arguments pattern: max index ${maxArgIndex}, ${argMatches.length} references`); } } console.log('\n📋 Summary of Remaining Issues:'); console.log('1. getVars() - Returns correct type but empty object when no vars'); console.log('2. supported() - Overloaded: array with no args, boolean with string arg'); console.log('3. matset/sum/product - Functions work but complex argument handling'); console.log('4. solveEquations - High arity (5) suggests internal implementation detail'); console.log('\n💡 These represent edge cases in API design rather than bugs.'); }); });