UNPKG

@dbs-portal/tool-mock

Version:

API mocking toolkit using MSW for DBS Portal development workflows

264 lines 7.63 kB
/** * Network and performance simulation utilities */ import { getRandom } from '../factories/base'; import { applyDelay } from './delay'; /** * Simulate network conditions */ export async function simulateNetworkConditions(options) { const { connection, packetLoss, latency, bandwidth } = options; // Simulate packet loss if (packetLoss && getRandom() < packetLoss) { throw new Error('Packet loss simulated'); } // Apply connection-based latency switch (connection) { case 'fast': await applyDelay(latency || [10, 50]); break; case 'slow': await applyDelay(latency || [1000, 3000]); break; case 'unstable': await applyDelay(latency || [100, 2000]); break; case 'offline': throw new Error('Network offline'); default: await applyDelay(latency || [100, 300]); } // Simulate bandwidth limitations (simplified) if (bandwidth) { const dataSize = 1024; // Assume 1KB response const transferTime = (dataSize / bandwidth) * 1000; // Convert to ms await applyDelay(transferTime); } } /** * Simulate server load */ export async function simulateServerLoad(loadFactor = 0.5, baseDelay = 100) { // Higher load = longer delays const loadDelay = baseDelay * (1 + loadFactor * 2); await applyDelay([loadDelay * 0.8, loadDelay * 1.2]); } /** * Simulate database query time */ export async function simulateDatabaseQuery(complexity = 'simple', recordCount = 100) { let baseTime; switch (complexity) { case 'simple': baseTime = 10; break; case 'medium': baseTime = 50; break; case 'complex': baseTime = 200; break; default: baseTime = 50; } // Scale with record count (logarithmically) const scaledTime = baseTime * Math.log10(recordCount + 1); await applyDelay([scaledTime * 0.5, scaledTime * 1.5]); } /** * Simulate file processing time */ export async function simulateFileProcessing(fileSize, // in bytes processingType = 'upload') { const sizeInMB = fileSize / (1024 * 1024); let timePerMB; switch (processingType) { case 'upload': timePerMB = 500; // 500ms per MB break; case 'download': timePerMB = 200; // 200ms per MB break; case 'transform': timePerMB = 1000; // 1s per MB break; default: timePerMB = 500; } const processingTime = sizeInMB * timePerMB; await applyDelay([processingTime * 0.8, processingTime * 1.2]); } /** * Simulate cache behavior */ export function simulateCache(key, factory, ttl = 60000 // 1 minute default ) { const cache = getCacheInstance(); const cached = cache.get(key); if (cached && Date.now() - cached.timestamp < ttl) { return cached.value; } const value = factory(); cache.set(key, { value, timestamp: Date.now() }); return value; } /** * Simple in-memory cache */ const cacheStore = new Map(); function getCacheInstance() { return { get: (key) => cacheStore.get(key), set: (key, value) => { cacheStore.set(key, value); }, clear: () => cacheStore.clear(), delete: (key) => cacheStore.delete(key), }; } /** * Simulate authentication check delay */ export async function simulateAuthCheck(tokenComplexity = 'jwt') { switch (tokenComplexity) { case 'simple': await applyDelay([5, 15]); break; case 'jwt': await applyDelay([20, 50]); break; case 'oauth': await applyDelay([100, 300]); break; default: await applyDelay([20, 50]); } } /** * Simulate rate limiting */ export class RateLimiter { limit; windowMs; requests = new Map(); constructor(limit = 100, windowMs = 60000 // 1 minute ) { this.limit = limit; this.windowMs = windowMs; } isAllowed(identifier) { const now = Date.now(); const windowStart = now - this.windowMs; // Get existing requests for this identifier const requests = this.requests.get(identifier) || []; // Filter out old requests const recentRequests = requests.filter(time => time > windowStart); // Check if limit exceeded if (recentRequests.length >= this.limit) { return false; } // Add current request recentRequests.push(now); this.requests.set(identifier, recentRequests); return true; } getRemainingRequests(identifier) { const now = Date.now(); const windowStart = now - this.windowMs; const requests = this.requests.get(identifier) || []; const recentRequests = requests.filter(time => time > windowStart); return Math.max(0, this.limit - recentRequests.length); } getResetTime(identifier) { const requests = this.requests.get(identifier) || []; if (requests.length === 0) { return Date.now(); } return Math.min(...requests) + this.windowMs; } } /** * Simulate circuit breaker pattern */ export class CircuitBreaker { failureThreshold; recoveryTimeMs; failures = 0; lastFailureTime = 0; state = 'closed'; constructor(failureThreshold = 5, recoveryTimeMs = 60000 // 1 minute ) { this.failureThreshold = failureThreshold; this.recoveryTimeMs = recoveryTimeMs; } async execute(operation) { if (this.state === 'open') { if (Date.now() - this.lastFailureTime > this.recoveryTimeMs) { this.state = 'half-open'; } else { throw new Error('Circuit breaker is open'); } } try { const result = await operation(); this.onSuccess(); return result; } catch (error) { this.onFailure(); throw error; } } onSuccess() { this.failures = 0; this.state = 'closed'; } onFailure() { this.failures++; this.lastFailureTime = Date.now(); if (this.failures >= this.failureThreshold) { this.state = 'open'; } } getState() { return this.state; } } /** * Simulate progressive backoff */ export async function simulateBackoff(attempt, baseDelay = 1000, maxDelay = 30000, factor = 2) { const delay = Math.min(baseDelay * Math.pow(factor, attempt - 1), maxDelay); const jitter = delay * 0.1 * getRandom(); // Add 10% jitter await applyDelay(delay + jitter); } /** * Simulate health check */ export function simulateHealthCheck() { const checks = { database: getRandom() > 0.05, // 95% healthy cache: getRandom() > 0.02, // 98% healthy externalApi: getRandom() > 0.1, // 90% healthy storage: getRandom() > 0.01, // 99% healthy }; const healthyCount = Object.values(checks).filter(Boolean).length; const totalChecks = Object.keys(checks).length; let status; if (healthyCount === totalChecks) { status = 'healthy'; } else if (healthyCount >= totalChecks * 0.7) { status = 'degraded'; } else { status = 'unhealthy'; } return { status, checks, timestamp: new Date().toISOString(), }; } //# sourceMappingURL=simulation.js.map