or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

code-coverage.mdcommand-line-interface.mdindex.mdmemory-leak-detection.mdtest-execution.mdtest-organization.mdtypescript-integration.md
tile.json

memory-leak-detection.mddocs/

Memory Leak Detection

Global variable leak detection to ensure test isolation and prevent memory leaks. This system monitors the global scope for unexpected variable additions that could indicate memory leaks or improper test isolation.

Capabilities

Leak Detection

Detects global variable leaks by comparing the global scope before and after test execution.

/**
 * Detects global variable leaks during test execution
 * @param customGlobals - Array of global variable names to ignore
 * @param options - Leak detection configuration options
 * @returns Promise resolving to leak detection report
 */
leaks.detect(customGlobals?: string[], options?: LeakOptions): Promise<LeakReport>;

Usage Examples:

const Lab = require('@hapi/lab');

// Basic leak detection
const leakReport = await Lab.leaks.detect();

// Ignore specific globals
const report = await Lab.leaks.detect([
    'myGlobalVar',
    'SOME_CONSTANT',
    'Symbol(special)'
]);

// With options
const detailedReport = await Lab.leaks.detect(['myGlobal'], {
    timeout: 5000
});

Leak Detection Configuration

Leak Options

Configuration options for controlling leak detection behavior.

interface LeakOptions {
    /** Maximum time to wait for leak detection in milliseconds */
    timeout?: number;
    
    /** Whether to enable leak detection (default: true) */
    leaks?: boolean;
    
    /** Additional global variables to ignore during detection */
    globals?: string[];
}

Leak Detection Results

Leak Report

Detailed report of detected global variable leaks.

interface LeakReport {
    /** Whether any leaks were detected */
    hasLeaks: boolean;
    
    /** Array of detected leaked global variables */
    leaks: GlobalLeak[];
    
    /** Total number of detected leaks */
    count: number;
    
    /** Baseline global count before tests */
    baseline: number;
    
    /** Final global count after tests */
    final: number;
    
    /** Execution time for leak detection */
    duration: number;
}

interface GlobalLeak {
    /** Name of the leaked global variable */
    name: string;
    
    /** Type of the leaked variable */
    type: string;
    
    /** String representation of the variable value */
    value?: string;
    
    /** Whether this is a symbol */
    isSymbol: boolean;
    
    /** Stack trace of where the leak was introduced (if available) */
    stack?: string;
}

Integration with Test Execution

Automatic Leak Detection

Leak detection integrates automatically with the test execution system when enabled.

Usage Examples:

// Enable leak detection in test execution
const result = await Lab.report(script, {
    leaks: true,
    globals: ['myExpectedGlobal', 'CONFIGURATION']
});

if (result.leaks && result.leaks.hasLeaks) {
    console.log(`Detected ${result.leaks.count} global leaks:`);
    result.leaks.leaks.forEach(leak => {
        console.log(`- ${leak.name} (${leak.type}): ${leak.value}`);
    });
}

CLI Integration

Leak detection works seamlessly with the command-line interface.

Command Line Usage:

# Enable leak detection
lab --leaks

# Disable leak detection  
lab --no-leaks

# Ignore specific globals
lab --globals myGlobal,anotherGlobal

# Ignore symbols
lab --globals "Symbol(special),myVar"

Common Global Leaks

Typical Leak Sources

Common sources of global variable leaks in Node.js applications:

  1. Undeclared Variables: Variables assigned without
    var
    ,
    let
    , or
    const
  2. Polyfills: Libraries that add properties to global objects
  3. Testing Libraries: Test utilities that modify global scope
  4. Node.js Modules: Modules that attach to global object
  5. Timers: Uncleared
    setTimeout
    or
    setInterval
    references
  6. Process Listeners: Event listeners attached to
    process
    object

Leak Prevention

Best practices for preventing global leaks in tests:

// ❌ Bad: Creates global leak
test('bad example', () => {
    undeclaredVar = 'this creates a global';
    expect(undeclaredVar).to.equal('this creates a global');
});

// ✅ Good: Properly scoped variable
test('good example', () => {
    const localVar = 'this is properly scoped';
    expect(localVar).to.equal('this is properly scoped');
});

// ✅ Good: Clean up global modifications
test('global modification with cleanup', (flags) => {
    global.temporaryValue = 'test data';
    
    flags.onCleanup = () => {
        delete global.temporaryValue;
    };
    
    expect(global.temporaryValue).to.equal('test data');
});

Advanced Leak Detection

Custom Global Whitelist

For applications that legitimately add global variables, configure a whitelist:

// Test configuration with expected globals
const result = await Lab.report(script, {
    leaks: true,
    globals: [
        // Application globals
        'APP_CONFIG',
        'DEBUG_MODE',
        
        // Third-party library globals
        '_',           // lodash
        '$',           // jQuery (if in browser-like environment)
        'Buffer',      // Node.js Buffer (if not standard)
        
        // Symbols
        'Symbol(react.element)',
        'Symbol(react.portal)'
    ]
});

Programmatic Leak Detection

Use leak detection programmatically for custom test flows:

const Lab = require('@hapi/lab');

const customTestRunner = async () => {
    // Take baseline measurement
    const baselineGlobals = Object.keys(global).length;
    
    // Run your tests
    await runMyTests();
    
    // Check for leaks
    const leakReport = await Lab.leaks.detect([
        'expectedGlobal1',
        'expectedGlobal2'
    ]);
    
    if (leakReport.hasLeaks) {
        console.error('Memory leaks detected:');
        leakReport.leaks.forEach(leak => {
            console.error(`  ${leak.name}: ${leak.value}`);
        });
        throw new Error(`${leakReport.count} memory leaks detected`);
    }
    
    console.log('No memory leaks detected');
};

Symbol Leak Detection

Special handling for Symbol leaks, which can be harder to track:

// Symbols must be represented as strings in the globals array
const result = await Lab.report(script, {
    leaks: true,
    globals: [
        'Symbol(react.element)',
        'Symbol(my.custom.symbol)',
        'Symbol(iterator)'
    ]
});

Debugging Leaks

Leak Investigation

When leaks are detected, the report provides detailed information for debugging:

const leakReport = await Lab.leaks.detect();

if (leakReport.hasLeaks) {
    leakReport.leaks.forEach(leak => {
        console.log(`Leak: ${leak.name}`);
        console.log(`Type: ${leak.type}`);
        console.log(`Value: ${leak.value}`);
        
        if (leak.stack) {
            console.log(`Stack trace:\n${leak.stack}`);
        }
        
        if (leak.isSymbol) {
            console.log('This is a Symbol leak');
        }
    });
}

Performance Impact

Leak detection adds minimal overhead to test execution:

  • Baseline Measurement: ~1-2ms per test run
  • Leak Scanning: ~1ms per 1000 global properties
  • Symbol Detection: ~0.5ms additional overhead

The system is optimized for production test environments and can handle large test suites efficiently.

CI/CD Integration

Automated Leak Detection

Example for continuous integration with fail-on-leak behavior:

const runTestsWithLeakDetection = async () => {
    const result = await Lab.report(script, {
        leaks: true,
        globals: process.env.ALLOWED_GLOBALS?.split(',') || []
    });
    
    if (result.leaks && result.leaks.hasLeaks) {
        console.error(`❌ ${result.leaks.count} memory leaks detected`);
        result.leaks.leaks.forEach(leak => {
            console.error(`   ${leak.name} (${leak.type}): ${leak.value}`);
        });
        process.exit(1);
    }
    
    console.log('✅ No memory leaks detected');
    return result;
};