Files
xget/test/features/range-cache.test.js
xixu-me 3832d565e2 Refactor integration tests and add feature-specific test suites
Moves Git, Git LFS, and authentication header forwarding tests from integration.test.js into dedicated feature test files under test/features/. Updates range-cache, security, and container-registry tests for consistency and minor improvements. Refactors src/index.js for code style and readability, especially around cache logic and error handling.
2025-12-10 16:23:44 +08:00

239 lines
8.3 KiB
JavaScript

import { SELF } from 'cloudflare:test';
import { describe, expect, it } from 'vitest';
/**
* Tests for Range Request Caching Strategy
*
* This test suite validates the new caching strategy that:
* 1. Only caches 200 responses (not 206)
* 2. Handles Range requests by caching full content first
* 3. Lets Cloudflare edge serve 206 responses from cached 200 content
* 4. Avoids compression for media files to ensure proper Range support
*/
describe('Range Request Caching Strategy', () => {
describe('Cache Behavior for Range Requests', () => {
it('should not attempt to cache 206 responses', async () => {
const testUrl = 'https://example.com/gh/test/repo/sample.pdf';
// Make a range request that might return 206
const response = await SELF.fetch(testUrl, {
headers: {
Range: 'bytes=0-1023'
}
});
// The response should either be 200 (full content) or 206 (partial)
// But we should never get a cache error from trying to cache 206
expect([200, 206, 404]).toContain(response.status);
// Check performance metrics for any cache-related errors
const metrics = response.headers.get('X-Performance-Metrics');
if (metrics) {
const parsedMetrics = JSON.parse(metrics);
// Should not contain any cache put errors
const errorKeys = Object.keys(parsedMetrics).filter(
key => (key.includes('error') || key.includes('fail')) && key !== 'client_error'
);
expect(errorKeys).toHaveLength(0);
}
});
it('should cache full content when receiving 200 response', async () => {
const testUrl = 'https://example.com/gh/test/repo/document.pdf';
// First request - should cache the full content
const firstResponse = await SELF.fetch(testUrl);
if (firstResponse.status === 200) {
// Verify caching headers are set correctly
expect(firstResponse.headers.get('Cache-Control')).toContain('public');
expect(firstResponse.headers.get('Accept-Ranges')).toBe('bytes');
// Second request should hit cache
const secondResponse = await SELF.fetch(testUrl);
expect(secondResponse.status).toBe(200);
// Performance metrics should show cache hit
const metrics = secondResponse.headers.get('X-Performance-Metrics');
if (metrics) {
const parsedMetrics = JSON.parse(metrics);
expect(parsedMetrics).toHaveProperty('cache_hit');
}
}
});
it('should handle range requests after caching full content', async () => {
const testUrl = 'https://example.com/gh/test/repo/large-file.bin';
// First, cache the full content
const fullResponse = await SELF.fetch(testUrl);
if (fullResponse.status === 200) {
// Now make a range request - should leverage cached content
const rangeResponse = await SELF.fetch(testUrl, {
headers: {
Range: 'bytes=100-199'
}
});
// Should either return the requested range 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('100');
}
}
});
});
describe('Media File Handling', () => {
it('should avoid compression for media files', async () => {
const mediaTestCases = [
{ url: 'https://example.com/gh/test/repo/video.mp4', type: 'video' },
{ url: 'https://example.com/gh/test/repo/audio.mp3', type: 'audio' },
{ url: 'https://example.com/gh/test/repo/image.png', type: 'image' },
{ url: 'https://example.com/gh/test/repo/archive.zip', type: 'archive' }
];
for (const testCase of mediaTestCases) {
const response = await SELF.fetch(testCase.url, { method: 'HEAD' });
if (response.status === 200) {
// Media files should have proper range support headers
expect(response.headers.get('Accept-Ranges')).toBe('bytes');
// Should not be compressed to ensure proper byte-range handling
const contentEncoding = response.headers.get('Content-Encoding');
if (contentEncoding) {
expect(['identity', null]).toContain(contentEncoding);
}
}
}
});
it('should send identity encoding for range requests on media files', async () => {
const testUrl = 'https://example.com/gh/test/repo/large-video.mp4';
const response = await SELF.fetch(testUrl, {
headers: {
Range: 'bytes=0-1023'
}
});
// For media files with range requests, should not use compression
if ([200, 206].includes(response.status)) {
const contentEncoding = response.headers.get('Content-Encoding');
if (contentEncoding) {
expect(['identity', null]).toContain(contentEncoding);
}
}
});
});
describe('Cache Key Management', () => {
it('should use correct cache keys for range vs full requests', async () => {
const testUrl = 'https://example.com/gh/test/repo/test-document.pdf';
// Make a range request first
const rangeResponse1 = await SELF.fetch(testUrl, {
headers: {
Range: 'bytes=0-512'
}
});
// Make a full request
const fullResponse = await SELF.fetch(testUrl);
// Make another range request
const rangeResponse2 = await SELF.fetch(testUrl, {
headers: {
Range: 'bytes=512-1023'
}
});
// All requests should succeed
[rangeResponse1, fullResponse, rangeResponse2].forEach(response => {
expect([200, 206, 404]).toContain(response.status);
});
// Full response should have caching headers
if (fullResponse.status === 200) {
expect(fullResponse.headers.get('Cache-Control')).toContain('public');
expect(fullResponse.headers.get('Accept-Ranges')).toBe('bytes');
}
});
it('should handle Content-Length header properly', async () => {
const testUrl = 'https://example.com/gh/test/repo/sized-file.bin';
const response = await SELF.fetch(testUrl);
if (response.status === 200) {
// Should have Content-Length for proper range support
const contentLength = response.headers.get('Content-Length');
if (contentLength) {
expect(parseInt(contentLength, 10)).toBeGreaterThan(0);
}
// Should have Accept-Ranges header
expect(response.headers.get('Accept-Ranges')).toBe('bytes');
}
});
});
describe('Performance Metrics', () => {
it('should track cache performance for range requests', async () => {
const testUrl = 'https://example.com/gh/test/repo/metrics-test.dat';
// First request
const response1 = await SELF.fetch(testUrl);
const metrics1 = response1.headers.get('X-Performance-Metrics');
if (response1.status === 200 && metrics1) {
const parsed1 = JSON.parse(metrics1);
expect(parsed1).toHaveProperty('start');
expect(parsed1).toHaveProperty('complete');
}
// Second request (should hit cache)
const response2 = await SELF.fetch(testUrl);
const metrics2 = response2.headers.get('X-Performance-Metrics');
if (response2.status === 200 && metrics2) {
const parsed2 = JSON.parse(metrics2);
expect(parsed2).toHaveProperty('cache_hit');
}
});
it('should track range-specific cache behavior', async () => {
const testUrl = 'https://example.com/gh/test/repo/range-metrics.bin';
// Cache full content first
await SELF.fetch(testUrl);
// Now make a range request
const rangeResponse = await SELF.fetch(testUrl, {
headers: {
Range: 'bytes=0-1023'
}
});
const metrics = rangeResponse.headers.get('X-Performance-Metrics');
if (metrics && [200, 206].includes(rangeResponse.status)) {
const parsed = JSON.parse(metrics);
// Should have timing information
expect(parsed).toHaveProperty('start');
// May have cache-related metrics
const cacheKeys = Object.keys(parsed).filter(key => key.includes('cache'));
// At least one cache-related metric should be present
expect(cacheKeys.length).toBeGreaterThanOrEqual(0);
}
});
});
});