CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
warningHandler.test.ts254 linesDownload Raw Back to utils
1/**2 * @license3 * Copyright 2025 Qwen4 * SPDX-License-Identifier: Apache-2.05 */6 7import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';8import {9  initializeWarningHandler,10  resetWarningHandlerForTests,11} from './warningHandler.js';12 13const ENV_KEYS = ['NODE_ENV', 'DEBUG', 'QWEN_DEBUG'] as const;14 15describe('initializeWarningHandler', () => {16  const originalEnv: Partial<Record<(typeof ENV_KEYS)[number], string>> = {};17  let originalListeners: NodeJS.WarningListener[] = [];18  // Mock prior listener — installed before initializeWarningHandler so it19  // becomes one of the captured "priorListeners" the handler fans out to.20  // This is the channel the real Node default printer travels on, so21  // asserting fan-out here is equivalent to asserting "the default printer22  // would have fired" without coupling tests to internal Node behavior.23  let priorListener: ReturnType<typeof vi.fn>;24 25  beforeEach(() => {26    for (const k of ENV_KEYS) originalEnv[k] = process.env[k];27    for (const k of ENV_KEYS) delete process.env[k];28    originalListeners = [...process.listeners('warning')];29    process.removeAllListeners('warning');30    resetWarningHandlerForTests();31    priorListener = vi.fn();32    process.on('warning', priorListener);33  });34 35  afterEach(() => {36    resetWarningHandlerForTests();37    process.removeAllListeners('warning');38    for (const l of originalListeners) process.on('warning', l);39    for (const k of ENV_KEYS) {40      if (originalEnv[k] === undefined) delete process.env[k];41      else process.env[k] = originalEnv[k];42    }43  });44 45  function makeWarning(name: string, message: string): Error {46    const err = new Error(message);47    err.name = name;48    return err;49  }50 51  function emit(warning: Error): void {52    // Drive the real listener chain (process.emit dispatches synchronously53    // to every registered 'warning' listener). After initializeWarningHandler,54    // the only listener is ours; it decides whether to fan out to the55    // captured priorListener mock.56    process.emit('warning', warning);57  }58 59  it('suppresses MaxListenersExceededWarning for AbortSignal in production', () => {60    initializeWarningHandler();61    emit(62      makeWarning(63        'MaxListenersExceededWarning',64        'Possible EventTarget memory leak detected. 1509 abort listeners added to [AbortSignal].',65      ),66    );67    expect(priorListener).not.toHaveBeenCalled();68  });69 70  it('does NOT suppress generic [EventTarget] warnings — only AbortSignal', () => {71    initializeWarningHandler();72    emit(73      makeWarning(74        'MaxListenersExceededWarning',75        'Possible EventTarget memory leak detected. 11 listeners added to [EventTarget].',76      ),77    );78    expect(priorListener).toHaveBeenCalledTimes(1);79  });80 81  it('suppresses AbortSignal warnings with class metadata, e.g. [AbortSignal{aborted: false}]', () => {82    initializeWarningHandler();83    emit(84      makeWarning(85        'MaxListenersExceededWarning',86        'Possible EventTarget memory leak detected. 11 abort listeners added to [AbortSignal{aborted: false}].',87      ),88    );89    expect(priorListener).not.toHaveBeenCalled();90  });91 92  it('fans out unrelated warnings to captured prior listeners (e.g. Node default printer)', () => {93    initializeWarningHandler();94    const warning = makeWarning('DeprecationWarning', 'Some legacy thing');95    emit(warning);96    expect(priorListener).toHaveBeenCalledTimes(1);97    expect(priorListener).toHaveBeenCalledWith(warning);98  });99 100  it('preserves third-party warning listeners — they still fire for non-suppressed warnings', () => {101    const telemetryHook = vi.fn();102    process.on('warning', telemetryHook);103    initializeWarningHandler();104    emit(makeWarning('DeprecationWarning', 'X'));105    expect(priorListener).toHaveBeenCalledTimes(1);106    expect(telemetryHook).toHaveBeenCalledTimes(1);107  });108 109  it('a buggy prior listener does not break the chain for the rest', () => {110    const buggy = vi.fn(() => {111      throw new Error('boom');112    });113    const downstream = vi.fn();114    process.on('warning', buggy);115    process.on('warning', downstream);116    initializeWarningHandler();117    emit(makeWarning('DeprecationWarning', 'X'));118    expect(buggy).toHaveBeenCalledTimes(1);119    expect(downstream).toHaveBeenCalledTimes(1);120  });121 122  it('keeps suppressed warnings visible when DEBUG is set', () => {123    process.env['DEBUG'] = '1';124    initializeWarningHandler();125    emit(126      makeWarning(127        'MaxListenersExceededWarning',128        'Possible EventTarget memory leak detected. 1500 abort listeners added to [AbortSignal].',129      ),130    );131    expect(priorListener).toHaveBeenCalledTimes(1);132  });133 134  it('treats DEBUG=0 and DEBUG=false as not set', () => {135    process.env['DEBUG'] = '0';136    initializeWarningHandler();137    emit(138      makeWarning(139        'MaxListenersExceededWarning',140        'Possible EventTarget memory leak detected. 1500 abort listeners added to [AbortSignal].',141      ),142    );143    expect(priorListener).not.toHaveBeenCalled();144  });145 146  it('keeps warnings visible when QWEN_DEBUG is set', () => {147    process.env['QWEN_DEBUG'] = '1';148    initializeWarningHandler();149    emit(150      makeWarning(151        'MaxListenersExceededWarning',152        'Possible EventTarget memory leak detected. 1500 abort listeners added to [AbortSignal].',153      ),154    );155    expect(priorListener).toHaveBeenCalledTimes(1);156  });157 158  it('keeps warnings visible when NODE_ENV=development', () => {159    process.env['NODE_ENV'] = 'development';160    initializeWarningHandler();161    emit(162      makeWarning(163        'MaxListenersExceededWarning',164        'Possible EventTarget memory leak detected. 1500 abort listeners added to [AbortSignal].',165      ),166    );167    expect(priorListener).toHaveBeenCalledTimes(1);168  });169 170  it('is idempotent — repeated calls install only one listener', () => {171    initializeWarningHandler();172    initializeWarningHandler();173    initializeWarningHandler();174    expect(process.listeners('warning').length).toBe(1);175  });176 177  it('honors runtime DEBUG toggles — debug check is evaluated per warning', () => {178    initializeWarningHandler();179    // Initially DEBUG unset → suppression active.180    emit(181      makeWarning(182        'MaxListenersExceededWarning',183        'Possible EventTarget memory leak detected. 1500 abort listeners added to [AbortSignal].',184      ),185    );186    expect(priorListener).not.toHaveBeenCalled();187 188    // Flip DEBUG at runtime → next suppressed-pattern warning passes through.189    process.env['DEBUG'] = '1';190    emit(191      makeWarning(192        'MaxListenersExceededWarning',193        'Possible EventTarget memory leak detected. 1500 abort listeners added to [AbortSignal].',194      ),195    );196    expect(priorListener).toHaveBeenCalledTimes(1);197  });198});199 200describe('initializeWarningHandler — end-to-end stderr behavior', () => {201  // Integration test: spawn a child Node process to verify the real202  // emitWarning → default printer path is actually suppressed. The unit203  // tests above can't catch this because the default printer lives inside204  // Node and writes to stderr via internal mechanisms, not via the same205  // process.stderr.write spy.206  it('a child process with the handler installed does not print suppressed AbortSignal warnings to stderr', async () => {207    const { spawn } = await import('node:child_process');208    const { fileURLToPath, pathToFileURL } = await import('node:url');209    const { dirname, join } = await import('node:path');210    const { writeFile, mkdtemp, rm } = await import('node:fs/promises');211    const { tmpdir } = await import('node:os');212 213    const here = dirname(fileURLToPath(import.meta.url));214    // Convert the absolute path to a `file://` URL — on Windows, Node's ESM215    // loader rejects raw absolute paths (it treats `D:` as a URL scheme),216    // so import specifiers MUST be file URLs.217    const helperImportSpecifier = pathToFileURL(218      join(here, 'warningHandler.ts'),219    ).href;220 221    const dir = await mkdtemp(join(tmpdir(), 'warning-handler-e2e-'));222    try {223      const script = `224        import { initializeWarningHandler } from ${JSON.stringify(helperImportSpecifier)};225        delete process.env.DEBUG; delete process.env.QWEN_DEBUG;226        process.env.NODE_ENV = 'production';227        initializeWarningHandler();228        process.emitWarning(229          'Possible EventTarget memory leak detected. 1509 abort listeners added to [AbortSignal].',230          'MaxListenersExceededWarning'231        );232        process.emitWarning('Plain deprecation', 'DeprecationWarning');233      `;234      const scriptPath = join(dir, 'run.mjs');235      await writeFile(scriptPath, script);236 237      const child = spawn(process.execPath, ['--import', 'tsx', scriptPath], {238        stdio: ['ignore', 'pipe', 'pipe'],239      });240      let stderr = '';241      child.stderr.on('data', (b) => {242        stderr += b.toString();243      });244      await new Promise<void>((resolve) => child.on('exit', () => resolve()));245 246      expect(stderr).not.toMatch(/abort listeners added to \[AbortSignal\]/);247      // Deprecation should still print via the fanned-out default printer.248      expect(stderr).toMatch(/DeprecationWarning.*Plain deprecation/);249    } finally {250      await rm(dir, { recursive: true, force: true });251    }252  }, 15_000);253});254 
basant307/AI_Governance_Project · CoolFace