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:
1 parent
98fa5e9d30
commit
c700b6acee
6 files changed
+69
-51
No files matched your search
+43
-33
@@ -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'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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,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' }
|
||||
|
||||
Reference in new issue
Block a user