From c700b6acee9277f28523447eb9fb675af42b9fff Mon Sep 17 00:00:00 2001 From: Xi Xu Date: Sat, 31 Jan 2026 22:22:59 +0800 Subject: [PATCH] fix: increase test timeouts and fix lint error - Add global testTimeout (60s) and hookTimeout (30s) in vitest.config.js - Increase timeouts for network-dependent tests in security, integration, and container-registry test files - Use HEAD requests where appropriate to reduce network overhead - Fix lint error: change 'let scope' to 'const scope' in src/index.js --- src/index.js | 76 +++++++++++++---------- test/features/security.test.js | 20 +++--- test/index.test.js | 12 ++-- test/integration.test.js | 6 +- test/platforms/container-registry.test.js | 4 +- vitest.config.js | 2 + 6 files changed, 69 insertions(+), 51 deletions(-) diff --git a/src/index.js b/src/index.js index 8dec3e1..4594432 100644 --- a/src/index.js +++ b/src/index.js @@ -13,11 +13,11 @@ import { SORTED_PLATFORMS, transformPath } from './config/platforms.js'; import { configureAIHeaders, isAIInferenceRequest } from './protocols/ai.js'; import { configureHuggingFaceHeaders, isHuggingFaceAPIRequest } from './protocols/huggingface.js'; import { - fetchToken, - getScopeFromUrl, - handleDockerAuth, - parseAuthenticate, - responseUnauthorized + fetchToken, + getScopeFromUrl, + handleDockerAuth, + parseAuthenticate, + responseUnauthorized } from './protocols/docker.js'; import { configureGitHeaders, isGitLFSRequest, isGitRequest } from './protocols/git.js'; import { PerformanceMonitor, addPerformanceHeaders } from './utils/performance.js'; @@ -341,21 +341,26 @@ async function handleRequest(request, env, ctx) { clearTimeout(timeoutId); // Handle manual redirect for Docker - if (isDocker && (response.status === 301 || response.status === 302 || response.status === 307)) { - const location = response.headers.get('Location'); - if (location) { - // Fetch the new location without Authorization header - // Cloudflare Workers fetch should follow this automatically if we used 'follow', - // but we used 'manual' to strip headers. - const redirectHeaders = new Headers(finalFetchOptions.headers); - redirectHeaders.delete('Authorization'); - - response = await fetch(location, { - ...finalFetchOptions, - headers: redirectHeaders, - redirect: 'follow' // Follow subsequent redirects normally - }); - } + if ( + isDocker && + (response.status === 301 || + response.status === 302 || + response.status === 307) + ) { + const location = response.headers.get('Location'); + if (location) { + // Fetch the new location without Authorization header + // Cloudflare Workers fetch should follow this automatically if we used 'follow', + // but we used 'manual' to strip headers. + const redirectHeaders = new Headers(finalFetchOptions.headers); + redirectHeaders.delete('Authorization'); + + response = await fetch(location, { + ...finalFetchOptions, + headers: redirectHeaders, + redirect: 'follow' // Follow subsequent redirects normally + }); + } } if (response.ok || response.status === 206) { @@ -368,9 +373,9 @@ async function handleRequest(request, env, ctx) { monitor.mark('docker_auth_challenge'); const authenticateStr = response.headers.get('WWW-Authenticate'); - + // Calculate scope for upstream token fetch - let scope = getScopeFromUrl(url, effectivePath, platform); + const scope = getScopeFromUrl(url, effectivePath, platform); if (authenticateStr) { try { @@ -388,12 +393,12 @@ async function handleRequest(request, env, ctx) { if (tokenData.token) { const retryHeaders = new Headers(requestHeaders); retryHeaders.set('Authorization', `Bearer ${tokenData.token}`); - + const retryOptions = { ...finalFetchOptions, headers: retryHeaders }; - + // Also use manual redirect for retry if (isDocker) { retryOptions.redirect = 'manual'; @@ -402,17 +407,22 @@ async function handleRequest(request, env, ctx) { let retryResponse = await fetch(targetUrl, retryOptions); // Handle manual redirect for retry - if (isDocker && (retryResponse.status === 301 || retryResponse.status === 302 || retryResponse.status === 307)) { + if ( + isDocker && + (retryResponse.status === 301 || + retryResponse.status === 302 || + retryResponse.status === 307) + ) { const location = retryResponse.headers.get('Location'); if (location) { - const redirectHeaders = new Headers(retryOptions.headers); - redirectHeaders.delete('Authorization'); - - retryResponse = await fetch(location, { - ...retryOptions, - headers: redirectHeaders, - redirect: 'follow' - }); + const redirectHeaders = new Headers(retryOptions.headers); + redirectHeaders.delete('Authorization'); + + retryResponse = await fetch(location, { + ...retryOptions, + headers: redirectHeaders, + redirect: 'follow' + }); } } diff --git a/test/features/security.test.js b/test/features/security.test.js index 2f4b1d6..2c4c8c6 100644 --- a/test/features/security.test.js +++ b/test/features/security.test.js @@ -93,12 +93,13 @@ describe('Security Features', () => { for (const path of maliciousPaths) { const response = await SELF.fetch(`https://example.com${path}`, { + method: 'HEAD', redirect: 'manual' // Don't follow redirects }); // Should either reject with 400, redirect (302/301), or safely handle the path expect([400, 404, 500, 302, 301]).toContain(response.status); } - }); + }, 30000); it('should reject extremely long paths', async () => { const longPath = `/gh/${'a'.repeat(3000)}`; @@ -115,11 +116,11 @@ describe('Security Features', () => { ]; for (const path of encodedPaths) { - const response = await SELF.fetch(`https://example.com${path}`); + const response = await SELF.fetch(`https://example.com${path}`, { method: 'HEAD' }); // Should handle encoded paths without security issues expect(response.status).not.toBe(500); } - }); + }, 30000); }); describe('Input Sanitization', () => { @@ -132,11 +133,11 @@ describe('Security Features', () => { ]; for (const path of specialPaths) { - const response = await SELF.fetch(`https://example.com${path}`); + const response = await SELF.fetch(`https://example.com${path}`, { method: 'HEAD' }); // Should safely handle special characters expect(response.status).not.toBe(500); } - }); + }, 30000); it('should handle Unicode characters safely', async () => { const unicodePaths = [ @@ -146,11 +147,11 @@ describe('Security Features', () => { ]; for (const path of unicodePaths) { - const response = await SELF.fetch(`https://example.com${path}`); + const response = await SELF.fetch(`https://example.com${path}`, { method: 'HEAD' }); // Should handle Unicode without issues expect(response.status).not.toBe(500); } - }); + }, 20000); }); describe('Request Header Validation', () => { @@ -162,6 +163,7 @@ describe('Security Features', () => { for (const userAgent of maliciousUserAgents) { const response = await SELF.fetch('https://example.com/gh/test/repo', { + method: 'HEAD', headers: { 'User-Agent': userAgent } @@ -170,7 +172,7 @@ describe('Security Features', () => { // Should handle malicious user agents safely expect(response.status).not.toBe(500); } - }); + }, 20000); it('should handle header injection attempts', async () => { // Headers with CRLF injection should be rejected by the runtime @@ -216,7 +218,7 @@ describe('Security Features', () => { const elapsed = Date.now() - startTime; expect(elapsed).toBeLessThan(40000); // 40 seconds max } - }); + }, 45000); }); describe('Error Information Disclosure', () => { diff --git a/test/index.test.js b/test/index.test.js index fdbad2d..6d36b72 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -178,7 +178,7 @@ describe('Xget Core Functionality', () => { it('should accept normal length paths', async () => { const normalPath = '/gh/microsoft/vscode/archive/refs/heads/main.zip'; - const response = await SELF.fetch(`https://example.com${normalPath}`); + const response = await SELF.fetch(`https://example.com${normalPath}`, { method: 'HEAD' }); expect(response.status).not.toBe(414); }); @@ -186,13 +186,17 @@ describe('Xget Core Functionality', () => { describe('Performance Headers', () => { it('should include performance metrics in response headers', async () => { - const response = await SELF.fetch('https://example.com/gh/test/repo/file.txt'); + const response = await SELF.fetch('https://example.com/gh/test/repo/file.txt', { + method: 'HEAD' + }); expect(response.headers.get('X-Performance-Metrics')).toBeTruthy(); }); it('should include valid JSON in performance metrics', async () => { - const response = await SELF.fetch('https://example.com/gh/test/repo/file.txt'); + const response = await SELF.fetch('https://example.com/gh/test/repo/file.txt', { + method: 'HEAD' + }); const metricsHeader = response.headers.get('X-Performance-Metrics'); expect(metricsHeader).toBeTruthy(); @@ -204,7 +208,7 @@ describe('Xget Core Functionality', () => { it('should rewrite npm registry URLs in JSON responses', async () => { // Mock npm package metadata request const testUrl = 'https://example.com/npm/lodash'; - const response = await SELF.fetch(testUrl); + const response = await SELF.fetch(testUrl, { method: 'HEAD' }); // This test would need actual npm registry response mocking // For now, just verify the request doesn't fail diff --git a/test/integration.test.js b/test/integration.test.js index 863bb50..426172c 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -27,7 +27,7 @@ describe('Integration Tests', () => { const response = await SELF.fetch(testUrl, { method: 'HEAD' }); expect([200, 301, 302, 404]).toContain(response.status); - }); + }, 60000); it('should proxy GitLab file requests correctly', async () => { const testUrl = 'https://example.com/gl/gitlab-org/gitlab/-/raw/master/package.json'; @@ -131,7 +131,7 @@ describe('Integration Tests', () => { // If retries occurred, there should be timing data expect(typeof metrics).toBe('object'); } - }); + }, 20000); }); describe('Performance Integration', () => { @@ -185,7 +185,7 @@ describe('Integration Tests', () => { } } } - }); + }, 30000); }); describe('Range Request Support', () => { diff --git a/test/platforms/container-registry.test.js b/test/platforms/container-registry.test.js index 1b9cb0a..67bc6bb 100644 --- a/test/platforms/container-registry.test.js +++ b/test/platforms/container-registry.test.js @@ -283,7 +283,7 @@ describe('Container Registry Support', () => { // Should attempt to proxy to Docker Hub expect(response.status).not.toBe(400); - }); + }, 30000); it('should handle Docker Hub user images (namespace/image format)', async () => { // User images already have namespace prefix @@ -297,7 +297,7 @@ describe('Container Registry Support', () => { // Should attempt to proxy to Docker Hub expect(response.status).not.toBe(400); - }); + }, 30000); it('should allow GET for Docker Hub manifest requests', async () => { const response = await SELF.fetch('https://example.com/cr/docker/v2/nginx/manifests/latest', { diff --git a/vitest.config.js b/vitest.config.js index 688213c..428e02e 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -2,6 +2,8 @@ import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config'; export default defineWorkersConfig({ test: { + testTimeout: 60000, + hookTimeout: 30000, poolOptions: { workers: { wrangler: { configPath: './wrangler.toml' }