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
This commit is contained in:
xixu-me committed 2026-01-31 22:22:59 +08:00
1 parent 98fa5e9d30
commit c700b6acee
6 files changed
+69 -51

No files matched your search

+43 -33
View File
@@ -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'
});
}
}
+11 -9
View File
@@ -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', () => {
+8 -4
View File
@@ -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
+3 -3
View File
@@ -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', () => {
+2 -2
View File
@@ -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', {
+2
View File
@@ -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' }