307 lines
11 KiB
JavaScript
307 lines
11 KiB
JavaScript
import { SELF } from 'cloudflare:test';
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
describe('Integration Tests', () => {
|
|
describe('End-to-End Platform Integration', () => {
|
|
it('should proxy GitHub file requests correctly', async () => {
|
|
const testUrl = 'https://example.com/gh/microsoft/vscode/blob/main/package.json';
|
|
const response = await SELF.fetch(testUrl, { method: 'HEAD' });
|
|
|
|
// Should attempt to proxy to GitHub
|
|
expect([200, 301, 302, 404]).toContain(response.status);
|
|
|
|
// Should include security headers
|
|
expect(response.headers.get('Strict-Transport-Security')).toBeTruthy();
|
|
expect(response.headers.get('X-Performance-Metrics')).toBeTruthy();
|
|
});
|
|
|
|
it('should handle GitHub raw file requests', async () => {
|
|
const testUrl = 'https://example.com/gh/microsoft/vscode/raw/main/README.md';
|
|
const response = await SELF.fetch(testUrl, { method: 'HEAD' });
|
|
|
|
expect([200, 301, 302, 404]).toContain(response.status);
|
|
});
|
|
|
|
it('should handle GitHub release downloads', async () => {
|
|
const testUrl = 'https://example.com/gh/microsoft/vscode/archive/refs/heads/main.zip';
|
|
const response = await SELF.fetch(testUrl, { method: 'HEAD' });
|
|
|
|
expect([200, 301, 302, 404, 408]).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';
|
|
const response = await SELF.fetch(testUrl, { method: 'HEAD' });
|
|
|
|
expect([200, 301, 302, 404]).toContain(response.status);
|
|
expect(response.headers.get('X-Performance-Metrics')).toBeTruthy();
|
|
});
|
|
|
|
it('should handle Hugging Face model files', async () => {
|
|
const testUrl = 'https://example.com/hf/microsoft/DialoGPT-medium/resolve/main/config.json';
|
|
const response = await SELF.fetch(testUrl, { method: 'HEAD' });
|
|
|
|
expect([200, 301, 302, 404, 429]).toContain(response.status);
|
|
}, 10000);
|
|
|
|
it('should handle npm package requests', async () => {
|
|
const testUrl = 'https://example.com/npm/react';
|
|
const response = await SELF.fetch(testUrl, { method: 'HEAD' });
|
|
|
|
expect([200, 301, 302, 404]).toContain(response.status);
|
|
});
|
|
|
|
it('should handle PyPI package requests', async () => {
|
|
const testUrl = 'https://example.com/pypi/simple/requests/';
|
|
const response = await SELF.fetch(testUrl, { method: 'HEAD' });
|
|
|
|
expect([200, 301, 302, 404]).toContain(response.status);
|
|
});
|
|
|
|
it('should handle conda package requests', async () => {
|
|
const testUrl = 'https://example.com/conda/pkgs/main/linux-64/repodata.json';
|
|
const response = await SELF.fetch(testUrl, { method: 'HEAD' });
|
|
|
|
expect([200, 301, 302, 404]).toContain(response.status);
|
|
});
|
|
});
|
|
|
|
describe('Caching Integration', () => {
|
|
it('should cache responses appropriately', async () => {
|
|
const testUrl = 'https://example.com/gh/test/repo/static-file.txt';
|
|
|
|
// First request
|
|
const response1 = await SELF.fetch(testUrl);
|
|
const metrics1 = response1.headers.get('X-Performance-Metrics');
|
|
|
|
// Second request (should potentially hit cache)
|
|
const response2 = await SELF.fetch(testUrl);
|
|
const metrics2 = response2.headers.get('X-Performance-Metrics');
|
|
|
|
expect(metrics1).toBeTruthy();
|
|
expect(metrics2).toBeTruthy();
|
|
|
|
// Both requests should succeed
|
|
expect(response1.status).toBe(response2.status);
|
|
});
|
|
|
|
it('should not cache Git protocol requests', async () => {
|
|
const testUrl = 'https://example.com/gh/test/repo.git/info/refs?service=git-upload-pack';
|
|
|
|
const response = await SELF.fetch(testUrl, {
|
|
headers: {
|
|
'User-Agent': 'git/2.34.1'
|
|
}
|
|
});
|
|
|
|
// Git requests should not be cached (no cache headers)
|
|
expect(response.headers.get('Cache-Control') || '').not.toContain('max-age=1800');
|
|
});
|
|
});
|
|
|
|
describe('Error Handling Integration', () => {
|
|
it('should handle upstream server errors gracefully', async () => {
|
|
const testUrl = 'https://example.com/gh/nonexistent/repo/file.txt';
|
|
const response = await SELF.fetch(testUrl);
|
|
|
|
// Should handle 404 from upstream gracefully
|
|
expect([404, 502, 503]).toContain(response.status);
|
|
expect(response.headers.get('X-Performance-Metrics')).toBeTruthy();
|
|
});
|
|
|
|
it('should handle network timeouts', async () => {
|
|
// This would test timeout handling, but is difficult to simulate
|
|
// in a unit test environment. In practice, this would be tested
|
|
// with a mock server that delays responses.
|
|
const testUrl = 'https://example.com/gh/test/repo/file.txt';
|
|
const response = await SELF.fetch(testUrl);
|
|
|
|
// Should complete within reasonable time
|
|
expect(response.status).toBeDefined();
|
|
});
|
|
|
|
it('should retry failed requests', async () => {
|
|
// Test retry mechanism by checking performance metrics
|
|
const testUrl = 'https://example.com/gh/test/unreliable-endpoint';
|
|
const response = await SELF.fetch(testUrl);
|
|
|
|
const metricsHeader = response.headers.get('X-Performance-Metrics');
|
|
if (metricsHeader) {
|
|
const metrics = JSON.parse(metricsHeader);
|
|
// If retries occurred, there should be timing data
|
|
expect(typeof metrics).toBe('object');
|
|
}
|
|
}, 20000);
|
|
});
|
|
|
|
describe('Performance Integration', () => {
|
|
it('should complete requests within reasonable time', async () => {
|
|
const startTime = Date.now();
|
|
const testUrl = 'https://example.com/gh/test/repo/small-file.txt';
|
|
|
|
const response = await SELF.fetch(testUrl);
|
|
const endTime = Date.now();
|
|
|
|
const duration = endTime - startTime;
|
|
|
|
// Should complete within 30 seconds (timeout limit)
|
|
expect(duration).toBeLessThan(30000);
|
|
expect(response.status).toBeDefined();
|
|
});
|
|
|
|
it('should include performance metrics in all responses', async () => {
|
|
const testUrls = [
|
|
'https://example.com/gh/test/repo/file.txt',
|
|
'https://example.com/npm/test-package',
|
|
'https://example.com/pypi/simple/test/'
|
|
];
|
|
|
|
const responses = await Promise.all(testUrls.map(url => SELF.fetch(url, { method: 'HEAD' })));
|
|
|
|
for (const response of responses) {
|
|
expect(response.headers.get('X-Performance-Metrics')).toBeTruthy();
|
|
}
|
|
}, 20000);
|
|
});
|
|
|
|
describe('Content Type Handling', () => {
|
|
it('should preserve content types from upstream', async () => {
|
|
const testCases = [
|
|
{ url: 'https://example.com/gh/test/repo/image.png', expectedType: 'image' },
|
|
{ url: 'https://example.com/gh/test/repo/data.json', expectedType: 'json' },
|
|
{ url: 'https://example.com/gh/test/repo/style.css', expectedType: 'css' },
|
|
{ url: 'https://example.com/gh/test/repo/script.js', expectedType: 'javascript' }
|
|
];
|
|
|
|
for (const testCase of testCases) {
|
|
const response = await SELF.fetch(testCase.url, { method: 'HEAD' });
|
|
|
|
if (response.status === 200) {
|
|
const contentType = response.headers.get('Content-Type');
|
|
if (contentType) {
|
|
expect(contentType.toLowerCase()).toContain(testCase.expectedType);
|
|
}
|
|
}
|
|
}
|
|
}, 30000);
|
|
});
|
|
|
|
describe('Range Request Support', () => {
|
|
it('should support partial content requests', async () => {
|
|
const testUrl = 'https://example.com/gh/test/repo/large-file.zip';
|
|
const response = await SELF.fetch(testUrl, {
|
|
headers: {
|
|
Range: 'bytes=0-1023'
|
|
}
|
|
});
|
|
|
|
// Should either support range requests (206) or return full content (200)
|
|
expect([200, 206, 404]).toContain(response.status);
|
|
|
|
if (response.status === 206) {
|
|
expect(response.headers.get('Content-Range')).toBeTruthy();
|
|
}
|
|
});
|
|
|
|
it('should handle range requests with proper caching strategy', async () => {
|
|
const testUrl = 'https://example.com/gh/test/repo/test-file.pdf';
|
|
|
|
// First, make a regular request to cache the full content
|
|
const fullResponse = await SELF.fetch(testUrl);
|
|
|
|
if (fullResponse.status === 200) {
|
|
// Verify the response has proper headers for Range support
|
|
expect(fullResponse.headers.get('Accept-Ranges')).toBe('bytes');
|
|
expect(fullResponse.headers.get('Cache-Control')).toContain('public');
|
|
|
|
// Now make a range request
|
|
const rangeResponse = await SELF.fetch(testUrl, {
|
|
headers: {
|
|
Range: 'bytes=0-1023'
|
|
}
|
|
});
|
|
|
|
// Should either get partial content or full content
|
|
expect([200, 206]).toContain(rangeResponse.status);
|
|
|
|
if (rangeResponse.status === 206) {
|
|
expect(rangeResponse.headers.get('Content-Range')).toBeTruthy();
|
|
expect(rangeResponse.headers.get('Content-Length')).toBe('1024');
|
|
}
|
|
}
|
|
});
|
|
|
|
it('should avoid compression for media files', async () => {
|
|
const mediaFiles = [
|
|
'https://example.com/gh/test/repo/video.mp4',
|
|
'https://example.com/gh/test/repo/audio.mp3',
|
|
'https://example.com/gh/test/repo/image.jpg',
|
|
'https://example.com/gh/test/repo/archive.zip'
|
|
];
|
|
|
|
for (const url of mediaFiles) {
|
|
const response = await SELF.fetch(url, { method: 'HEAD' });
|
|
|
|
if (response.status === 200) {
|
|
// Media files should have Accept-Ranges header for proper range support
|
|
expect(response.headers.get('Accept-Ranges')).toBe('bytes');
|
|
|
|
// Should not be compressed if it's a media file
|
|
const contentEncoding = response.headers.get('Content-Encoding');
|
|
if (contentEncoding) {
|
|
expect(['identity', null, undefined]).toContain(contentEncoding);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
it('should cache only 200 responses, not 206 responses', async () => {
|
|
const testUrl = 'https://example.com/gh/test/repo/large-document.pdf';
|
|
|
|
// Make a range request first
|
|
const rangeResponse = await SELF.fetch(testUrl, {
|
|
headers: {
|
|
Range: 'bytes=0-1023'
|
|
}
|
|
});
|
|
|
|
// Verify performance metrics don't show 206 caching attempts
|
|
if (rangeResponse.status === 206) {
|
|
const metrics = rangeResponse.headers.get('X-Performance-Metrics');
|
|
if (metrics) {
|
|
const parsedMetrics = JSON.parse(metrics);
|
|
// Should not have cache_put_206_error or similar
|
|
expect(parsedMetrics).not.toHaveProperty('cache_put_error');
|
|
}
|
|
}
|
|
|
|
// Follow up with a full request to ensure it gets cached properly
|
|
const fullResponse = await SELF.fetch(testUrl);
|
|
|
|
if (fullResponse.status === 200) {
|
|
expect(fullResponse.headers.get('Cache-Control')).toContain('public');
|
|
expect(fullResponse.headers.get('Accept-Ranges')).toBe('bytes');
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Cross-Platform Consistency', () => {
|
|
it('should handle similar requests consistently across platforms', async () => {
|
|
const testCases = [
|
|
'https://example.com/gh/test/repo/README.md',
|
|
'https://example.com/gl/test/repo/README.md'
|
|
];
|
|
|
|
const responses = await Promise.all(
|
|
testCases.map(url => SELF.fetch(url, { method: 'HEAD' }))
|
|
);
|
|
|
|
// All responses should have consistent security headers
|
|
responses.forEach((/** @type {Response} */ response) => {
|
|
expect(response.headers.get('Strict-Transport-Security')).toBeTruthy();
|
|
expect(response.headers.get('X-Performance-Metrics')).toBeTruthy();
|
|
});
|
|
});
|
|
});
|
|
});
|