UNPKG

@wgtechlabs/log-engine

Version:

A lightweight, security-first logging utility with automatic data redaction for Node.js applications - the first logging library with built-in PII protection.

276 lines 9.54 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.MockHttpHandler = void 0; exports.waitForFile = waitForFile; exports.waitForFiles = waitForFiles; exports.waitForFileContent = waitForFileContent; exports.waitForDirectoryEmpty = waitForDirectoryEmpty; exports.safeRemoveFile = safeRemoveFile; exports.safeCleanupDirectory = safeCleanupDirectory; exports.createTestTimeout = createTestTimeout; exports.withTimeout = withTimeout; /** * Test utilities for handling async file operations properly * Replaces arbitrary timeouts with proper Promise-based waiting * Optimized for CI environments with faster polling and shorter timeouts */ const fs = __importStar(require("fs")); const path = __importStar(require("path")); // Optimized defaults for CI environments const DEFAULT_TIMEOUT = 3000; // Reduced from 5000ms const POLL_INTERVAL = 5; // Reduced from 10ms for faster detection /** * Wait for a file to exist with optimized polling */ async function waitForFile(filePath, timeoutMs = DEFAULT_TIMEOUT) { const startTime = Date.now(); while (Date.now() - startTime < timeoutMs) { try { await fs.promises.access(filePath, fs.constants.F_OK); return; // File exists } catch (error) { // File doesn't exist yet, wait a bit await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL)); } } throw new Error(`File ${filePath} did not appear within ${timeoutMs}ms`); } /** * Wait for multiple files to exist with parallel checking */ async function waitForFiles(filePaths, timeoutMs = DEFAULT_TIMEOUT) { await Promise.all(filePaths.map(filePath => waitForFile(filePath, timeoutMs))); } /** * Wait for a file to have specific content with optimized polling */ async function waitForFileContent(filePath, expectedContent, timeoutMs = DEFAULT_TIMEOUT) { const startTime = Date.now(); while (Date.now() - startTime < timeoutMs) { try { const content = await fs.promises.readFile(filePath, 'utf8'); if (typeof expectedContent === 'string') { if (content.includes(expectedContent)) { return; } } else { if (expectedContent.test(content)) { return; } } } catch (error) { // File might not exist yet or be readable } await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL)); } throw new Error(`File ${filePath} did not contain expected content within ${timeoutMs}ms`); } /** * Wait for a directory to be empty with faster polling */ async function waitForDirectoryEmpty(dirPath, timeoutMs = DEFAULT_TIMEOUT) { const startTime = Date.now(); while (Date.now() - startTime < timeoutMs) { try { const files = await fs.promises.readdir(dirPath); if (files.length === 0) { return; } } catch (error) { // Directory might not exist, which is also "empty" return; } await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL)); } throw new Error(`Directory ${dirPath} was not empty within ${timeoutMs}ms`); } /** * Safely remove a file with optimized retry logic */ async function safeRemoveFile(filePath, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { await fs.promises.unlink(filePath); return; } catch (error) { if (i === maxRetries - 1) { // Only throw on last retry if it's not a "file not found" error if (error.code !== 'ENOENT') { throw error; } } else { // Wait a bit before retrying (reduced wait time) await new Promise(resolve => setTimeout(resolve, 10)); } } } } /** * Safely clean up a directory with retry logic */ async function safeCleanupDirectory(dirPath) { try { const files = await fs.promises.readdir(dirPath); // Remove all files with retry logic await Promise.all(files.map(file => safeRemoveFile(path.join(dirPath, file)))); // Remove the directory itself await fs.promises.rmdir(dirPath); } catch (error) { // Directory might not exist, which is fine if (error.code !== 'ENOENT') { // Try force removal as fallback try { await fs.promises.rm(dirPath, { recursive: true, force: true }); } catch (fallbackError) { // Ignore cleanup errors in tests } } } } /** * Enhanced mock HTTP handler with faster timeouts and better error handling */ class MockHttpHandler { constructor() { this.requests = []; this.pendingPromises = []; this.timeoutIds = new Set(); } addRequest(url, options) { let resolveRequest; const promise = new Promise(resolve => { resolveRequest = resolve; }); this.requests.push({ url, options, resolve: resolveRequest }); this.pendingPromises.push(promise); } getRequests() { return this.requests.map(({ url, options }) => ({ url, options })); } async waitForRequests(count = 1, timeoutMs = DEFAULT_TIMEOUT) { const startTime = Date.now(); // Set up a timeout that will be cleaned up let timeoutId; const timeoutPromise = new Promise((_, reject) => { timeoutId = setTimeout(() => { reject(new Error(`Expected ${count} requests but got ${this.requests.length} within ${timeoutMs}ms`)); }, timeoutMs); this.timeoutIds.add(timeoutId); }); try { while (this.requests.length < count && Date.now() - startTime < timeoutMs) { await new Promise(resolve => { const id = setTimeout(resolve, POLL_INTERVAL); this.timeoutIds.add(id); }); } if (this.requests.length < count) { throw new Error(`Expected ${count} requests but got ${this.requests.length} within ${timeoutMs}ms`); } // Mark all requests as processed this.requests.forEach(req => req.resolve()); // Wait for all pending promises to resolve await Promise.all(this.pendingPromises); } finally { // Clean up the timeout if (timeoutId) { clearTimeout(timeoutId); this.timeoutIds.delete(timeoutId); } } } clear() { // Clean up any remaining timeouts for (const timeoutId of this.timeoutIds) { clearTimeout(timeoutId); } this.timeoutIds.clear(); this.requests = []; this.pendingPromises = []; } } exports.MockHttpHandler = MockHttpHandler; /** * Create a test timeout that fails fast instead of hanging */ function createTestTimeout(timeoutMs = DEFAULT_TIMEOUT) { let timeoutId; const promise = new Promise((_, reject) => { timeoutId = setTimeout(() => { reject(new Error(`Test timed out after ${timeoutMs}ms`)); }, timeoutMs); }); const cancel = () => { if (timeoutId) { clearTimeout(timeoutId); } }; return { promise, cancel }; } /** * Race a promise against a timeout for fail-fast behavior */ async function withTimeout(promise, timeoutMs = DEFAULT_TIMEOUT) { const timeout = createTestTimeout(timeoutMs); try { const result = await Promise.race([ promise, timeout.promise ]); // Cancel the timeout since we got a result timeout.cancel(); return result; } catch (error) { // Cancel the timeout in case of error timeout.cancel(); throw error; } } //# sourceMappingURL=async-test-utils.js.map