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.
239 lines
8.3 KiB
JavaScript
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);
|
|
}
|
|
});
|
|
});
|
|
});
|