test: raise coverage above 95 percent

This commit is contained in:
xixu-me committed 2026-03-20 13:03:50 +08:00
1 parent c9f1286216
commit 8e5550c709
7 files changed
+1087 -6

No files matched your search

+4 -5
View File
@@ -9,13 +9,12 @@ coverage:
status: status:
project: project:
default: default:
target: 70% target: auto
threshold: 1% threshold: 0.5%
patch: patch:
default: default:
target: auto target: 85%
threshold: 5% threshold: 2%
informational: true
ignore: ignore:
- "test/**" - "test/**"
+144
View File
@@ -0,0 +1,144 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import { CONFIG } from '../../src/config/index.js';
import {
fetchToken,
getScopeFromUrl,
handleDockerAuth,
normalizeRegistryApiPath,
parseAuthenticate,
readRegistryTokenResponse
} from '../../src/protocols/docker.js';
afterEach(() => {
vi.restoreAllMocks();
});
describe('Docker helper coverage', () => {
it('throws on malformed authenticate headers', () => {
expect(() => parseAuthenticate('Bearer service="registry.docker.io"')).toThrow(
/invalid WWW-Authenticate/
);
});
it('includes authorization when fetching registry tokens', async () => {
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response('{}', {
status: 200,
headers: { 'Content-Type': 'application/json' }
})
);
await fetchToken(
{ realm: 'https://auth.example.com/token', service: 'registry.example.com' },
'repository:demo/app:pull',
'Bearer registry-secret'
);
const upstreamHeaders = new Headers(fetchSpy.mock.calls[0][1]?.headers);
expect(String(fetchSpy.mock.calls[0][0])).toContain('scope=repository%3Ademo%2Fapp%3Apull');
expect(upstreamHeaders.get('Authorization')).toBe('Bearer registry-secret');
});
it('reads both token formats and rejects malformed token payloads', async () => {
await expect(
readRegistryTokenResponse(
new Response(JSON.stringify({ token: 'abc123' }), {
status: 200,
headers: { 'Content-Type': 'application/json' }
})
)
).resolves.toBe('abc123');
await expect(
readRegistryTokenResponse(
new Response(JSON.stringify({ access_token: 'def456' }), {
status: 200,
headers: { 'Content-Type': 'application/json' }
})
)
).resolves.toBe('def456');
await expect(
readRegistryTokenResponse(
new Response(JSON.stringify('invalid-shape'), {
status: 200,
headers: { 'Content-Type': 'application/json' }
})
)
).resolves.toBeNull();
await expect(
readRegistryTokenResponse(
new Response('{not-json', {
status: 200,
headers: { 'Content-Type': 'application/json' }
})
)
).resolves.toBeNull();
});
it('derives catalog and empty scopes from registry paths', () => {
const catalogUrl = new URL('https://example.com/cr/ghcr/v2/_catalog');
const unsupportedUrl = new URL('https://example.com/cr/ghcr/v2');
expect(getScopeFromUrl(catalogUrl, catalogUrl.pathname, 'cr-ghcr')).toBe('registry:catalog:*');
expect(getScopeFromUrl(unsupportedUrl, unsupportedUrl.pathname, 'cr-ghcr')).toBe('');
});
it('leaves normalized registry paths untouched when no library prefix is needed', () => {
expect(normalizeRegistryApiPath('cr-ghcr', '/v2/org/app/manifests/latest')).toBe(
'/v2/org/app/manifests/latest'
);
expect(normalizeRegistryApiPath('cr-docker', '/v2/library/nginx/manifests/latest')).toBe(
'/v2/library/nginx/manifests/latest'
);
expect(normalizeRegistryApiPath('cr-docker', '/v2/_catalog')).toBe('/v2/_catalog');
});
it('returns a generic error for unsupported Docker auth scopes', async () => {
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
const request = new Request(
'https://example.com/v2/auth?scope=repository:cr/unknown/private:pull'
);
const response = await handleDockerAuth(request, new URL(request.url), CONFIG);
expect(response.status).toBe(400);
expect(await response.text()).toBe('Invalid Docker authentication request');
expect(errorSpy).toHaveBeenCalledWith(
'Failed to resolve Docker auth target:',
expect.any(Error)
);
});
it('forwards upstream auth responses that are not challenges', async () => {
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response('already-authorized', {
status: 200,
headers: { 'Content-Type': 'text/plain' }
})
);
const request = new Request('https://example.com/cr/ghcr/v2/auth?service=Xget');
const response = await handleDockerAuth(request, new URL(request.url), CONFIG);
expect(response.status).toBe(200);
expect(await response.text()).toBe('already-authorized');
});
it('forwards 401 responses without authenticate headers from the upstream root probe', async () => {
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response('missing-authenticate', {
status: 401,
headers: { 'Content-Type': 'text/plain' }
})
);
const request = new Request('https://example.com/cr/ghcr/v2/auth?service=Xget');
const response = await handleDockerAuth(request, new URL(request.url), CONFIG);
expect(response.status).toBe(401);
expect(await response.text()).toBe('missing-authenticate');
});
});
+136
View File
@@ -0,0 +1,136 @@
import { describe, expect, it } from 'vitest';
import { configureAIHeaders } from '../../src/protocols/ai.js';
import { configureGitHeaders, isGitLFSRequest, isGitRequest } from '../../src/protocols/git.js';
import {
configureHuggingFaceHeaders,
isHuggingFaceAPIRequest
} from '../../src/protocols/huggingface.js';
describe('Protocol helper coverage', () => {
it('detects Git requests from service queries and content types', () => {
const serviceRequest = new Request('https://example.com/repo.git?service=git-receive-pack');
const contentTypeRequest = new Request('https://example.com/repo.git', {
method: 'POST',
headers: { 'Content-Type': 'application/x-git-upload-pack-request' }
});
expect(isGitRequest(serviceRequest, new URL(serviceRequest.url))).toBe(true);
expect(isGitRequest(contentTypeRequest, new URL(contentTypeRequest.url))).toBe(true);
});
it('detects Git LFS requests from object paths and headers', () => {
const infoRequest = new Request('https://example.com/repo.git/info/lfs');
const objectRequest = new Request(
'https://example.com/repo.git/objects/0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
);
const headerRequest = new Request('https://example.com/repo.git/download', {
headers: { Accept: 'application/vnd.git-lfs+json' }
});
expect(isGitLFSRequest(infoRequest, new URL(infoRequest.url))).toBe(true);
expect(isGitLFSRequest(objectRequest, new URL(objectRequest.url))).toBe(true);
expect(isGitLFSRequest(headerRequest, new URL(headerRequest.url))).toBe(true);
});
it('configures standard Git upload and receive pack headers', () => {
const uploadHeaders = new Headers();
const uploadRequest = new Request('https://example.com/repo.git/git-upload-pack', {
method: 'POST'
});
configureGitHeaders(uploadHeaders, uploadRequest, new URL(uploadRequest.url), false);
const receiveHeaders = new Headers();
const receiveRequest = new Request('https://example.com/repo.git/git-receive-pack', {
method: 'POST'
});
configureGitHeaders(receiveHeaders, receiveRequest, new URL(receiveRequest.url), false);
expect(uploadHeaders.get('User-Agent')).toBe('git/2.34.1');
expect(uploadHeaders.get('Content-Type')).toBe('application/x-git-upload-pack-request');
expect(receiveHeaders.get('User-Agent')).toBe('git/2.34.1');
expect(receiveHeaders.get('Content-Type')).toBe('application/x-git-receive-pack-request');
});
it('preserves existing Git headers when already provided', () => {
const headers = new Headers({
'Content-Type': 'application/custom',
'User-Agent': 'custom-git/9.9.9'
});
const request = new Request('https://example.com/repo.git/git-upload-pack', {
method: 'POST'
});
configureGitHeaders(headers, request, new URL(request.url), false);
expect(headers.get('User-Agent')).toBe('custom-git/9.9.9');
expect(headers.get('Content-Type')).toBe('application/custom');
});
it('configures Git LFS batch and object download headers', () => {
const batchHeaders = new Headers();
const batchRequest = new Request('https://example.com/repo.git/objects/batch', {
method: 'POST'
});
configureGitHeaders(batchHeaders, batchRequest, new URL(batchRequest.url), true);
const objectHeaders = new Headers();
const objectRequest = new Request(
'https://example.com/repo.git/objects/0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
);
configureGitHeaders(objectHeaders, objectRequest, new URL(objectRequest.url), true);
expect(batchHeaders.get('User-Agent')).toContain('git-lfs/');
expect(batchHeaders.get('Accept')).toBe('application/vnd.git-lfs+json');
expect(batchHeaders.get('Content-Type')).toBe('application/vnd.git-lfs+json');
expect(objectHeaders.get('Accept')).toBe('application/octet-stream');
});
it('detects Hugging Face API and token passthrough endpoints', () => {
const apiRequest = new Request('https://example.com/hf/api/models/demo');
const tokenRequest = new Request('https://example.com/hf/token');
const regularRequest = new Request(
'https://example.com/hf/meta-llama/model/resolve/main/config.json'
);
expect(isHuggingFaceAPIRequest(apiRequest, new URL(apiRequest.url))).toBe(true);
expect(isHuggingFaceAPIRequest(tokenRequest, new URL(tokenRequest.url))).toBe(true);
expect(isHuggingFaceAPIRequest(regularRequest, new URL(regularRequest.url))).toBe(false);
});
it('configures Hugging Face headers without overwriting explicit content types', () => {
const headers = new Headers();
const request = new Request('https://example.com/hf/api/models/demo', {
method: 'POST',
headers: { Authorization: 'Bearer secret-token' }
});
configureHuggingFaceHeaders(headers, request);
const preconfiguredHeaders = new Headers({ 'Content-Type': 'multipart/form-data' });
configureHuggingFaceHeaders(preconfiguredHeaders, request);
expect(headers.get('Authorization')).toBe('Bearer secret-token');
expect(headers.get('Content-Type')).toBe('application/json');
expect(preconfiguredHeaders.get('Content-Type')).toBe('multipart/form-data');
});
it('configures AI passthrough headers and preserves explicit values', () => {
const headers = new Headers();
const request = new Request('https://example.com/ip/openai/v1/chat/completions', {
method: 'POST'
});
configureAIHeaders(headers, request);
const preconfiguredHeaders = new Headers({
'Content-Type': 'application/x-ndjson',
'User-Agent': 'custom-ai-proxy/2.0'
});
configureAIHeaders(preconfiguredHeaders, request);
expect(headers.get('Content-Type')).toBe('application/json');
expect(headers.get('User-Agent')).toBe('Xget-AI-Proxy/1.0');
expect(preconfiguredHeaders.get('Content-Type')).toBe('application/x-ndjson');
expect(preconfiguredHeaders.get('User-Agent')).toBe('custom-ai-proxy/2.0');
});
});
+65
View File
@@ -0,0 +1,65 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import { PerformanceMonitor, addPerformanceHeaders } from '../../src/utils/performance.js';
import {
isFlatpakReferenceFilePath,
rewriteTextResponse,
shouldRewriteTextResponse
} from '../../src/utils/rewrite.js';
afterEach(() => {
vi.restoreAllMocks();
});
describe('Runtime helper coverage', () => {
it('serializes performance metrics and warns on duplicate marks', () => {
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
const monitor = new PerformanceMonitor();
monitor.mark('request-start');
monitor.mark('request-start');
monitor.mark('complete');
const response = addPerformanceHeaders(
new Response('ok', {
status: 200,
headers: { 'Content-Type': 'text/plain' }
}),
monitor
);
const metrics = JSON.parse(response.headers.get('X-Performance-Metrics') || '{}');
expect(warnSpy).toHaveBeenCalledWith('Mark with name request-start already exists.');
expect(metrics).toHaveProperty('request-start');
expect(metrics).toHaveProperty('complete');
expect(response.headers.get('X-Frame-Options')).toBe('DENY');
});
it('rewrites only supported upstream response types', () => {
expect(shouldRewriteTextResponse('pypi', '/pypi/simple/demo/', 'text/html')).toBe(true);
expect(shouldRewriteTextResponse('npm', '/npm/demo', 'application/json')).toBe(true);
expect(
shouldRewriteTextResponse(
'flathub',
'/flathub/repo/demo.flatpakrepo',
'application/octet-stream'
)
).toBe(true);
expect(shouldRewriteTextResponse('gh', '/gh/user/repo/file.txt', 'text/plain')).toBe(false);
expect(isFlatpakReferenceFilePath('/flathub/repo/demo.flatpakref')).toBe(true);
expect(isFlatpakReferenceFilePath('/flathub/repo/summary')).toBe(false);
expect(
rewriteTextResponse(
'flathub',
'/flathub/repo/demo.flatpakrepo',
'Url=https://dl.flathub.org/repo/',
'https://example.com'
)
).toContain('https://example.com/flathub/repo/');
expect(
rewriteTextResponse('gh', '/gh/user/repo/file.txt', 'unchanged', 'https://example.com')
).toBe('unchanged');
});
});
+154 -1
View File
@@ -3,11 +3,12 @@ import { describe, expect, it } from 'vitest';
import { createConfig } from '../../src/config/index.js'; import { createConfig } from '../../src/config/index.js';
import { isGitLFSRequest, isGitRequest } from '../../src/protocols/git.js'; import { isGitLFSRequest, isGitRequest } from '../../src/protocols/git.js';
import { import {
addCorsHeaders,
addSecurityHeaders, addSecurityHeaders,
createErrorResponse, createErrorResponse,
resolveAllowedOrigin resolveAllowedOrigin
} from '../../src/utils/security.js'; } from '../../src/utils/security.js';
import { getAllowedMethods, validateRequest } from '../../src/utils/validation.js'; import { getAllowedMethods, isDockerRequest, validateRequest } from '../../src/utils/validation.js';
describe('Utility Functions', () => { describe('Utility Functions', () => {
describe('isGitRequest', () => { describe('isGitRequest', () => {
@@ -91,6 +92,73 @@ describe('Utility Functions', () => {
expect(result.valid).toBe(false); expect(result.valid).toBe(false);
expect(result.status).toBe(400); expect(result.status).toBe(400);
}); });
it('should reject raw traversal sequences from the original request URL', () => {
const request = /** @type {Request} */ ({
headers: new Headers(),
method: 'GET',
url: 'https://example.com/gh/user/repo/../secret'
});
const url = new URL('https://example.com/gh/user/secret');
const result = validateRequest(request, url, createConfig());
expect(result.valid).toBe(false);
expect(result.status).toBe(400);
});
it('should reject paths containing ASCII control characters', () => {
const baseUrl = new URL('https://example.com/gh/user/repo/%00file');
const request = /** @type {Request} */ ({
headers: new Headers(),
method: 'GET',
url: 'https://example.com/gh/user/repo/%00file'
});
const url = /** @type {URL} */ ({
origin: 'https://example.com',
pathname: '/gh/user/repo/\u0000file',
searchParams: baseUrl.searchParams
});
const result = validateRequest(request, url, createConfig());
expect(result.valid).toBe(false);
expect(result.status).toBe(400);
});
it('should reject malformed percent-encoded paths', () => {
const baseUrl = new URL('https://example.com/gh/user/repo/%E0%A4%A');
const request = /** @type {Request} */ ({
headers: new Headers(),
method: 'GET',
url: 'https://example.com/gh/user/repo/%E0%A4%A'
});
const url = /** @type {URL} */ ({
origin: 'https://example.com',
pathname: '/gh/user/repo/%E0%A4%A',
searchParams: baseUrl.searchParams
});
const result = validateRequest(request, url, createConfig());
expect(result.valid).toBe(false);
expect(result.status).toBe(400);
});
it('should reject unsupported methods for regular requests', () => {
const request = new Request('https://example.com/gh/user/repo/file.txt', { method: 'PATCH' });
const url = new URL(request.url);
const result = validateRequest(request, url, createConfig());
expect(result.valid).toBe(false);
expect(result.status).toBe(405);
});
it('should reject paths longer than the configured maximum', () => {
const request = new Request(`https://example.com/gh/${'a'.repeat(200)}`);
const url = new URL(request.url);
const result = validateRequest(request, url, createConfig({ MAX_PATH_LENGTH: '32' }));
expect(result.valid).toBe(false);
expect(result.status).toBe(414);
});
}); });
describe('getAllowedMethods', () => { describe('getAllowedMethods', () => {
@@ -101,6 +169,52 @@ describe('Utility Functions', () => {
expect(getAllowedMethods(request, url, config)).toEqual(['GET', 'HEAD', 'POST']); expect(getAllowedMethods(request, url, config)).toEqual(['GET', 'HEAD', 'POST']);
}); });
it('should allow mutating methods for Hugging Face API endpoints', () => {
const request = new Request('https://example.com/hf/token', { method: 'DELETE' });
const url = new URL(request.url);
expect(getAllowedMethods(request, url)).toEqual([
'GET',
'HEAD',
'POST',
'PUT',
'PATCH',
'DELETE'
]);
});
});
describe('isDockerRequest', () => {
it('should identify canonical registry API paths', () => {
const request = new Request('https://example.com/cr/ghcr/v2/demo/manifests/latest');
const url = new URL(request.url);
expect(isDockerRequest(request, url)).toBe(true);
});
it('should identify Docker requests by user agent or manifest headers', () => {
const userAgentRequest = new Request('https://example.com/cr/docker/library/nginx', {
headers: { 'User-Agent': 'docker/27.0.0' }
});
const acceptRequest = new Request('https://example.com/cr/docker/library/nginx', {
headers: { Accept: 'application/vnd.oci.image.manifest.v1+json' }
});
const contentTypeRequest = new Request('https://example.com/cr/docker/library/nginx', {
headers: { 'Content-Type': 'application/vnd.docker.distribution.manifest.v2+json' }
});
expect(isDockerRequest(userAgentRequest, new URL(userAgentRequest.url))).toBe(true);
expect(isDockerRequest(acceptRequest, new URL(acceptRequest.url))).toBe(true);
expect(isDockerRequest(contentTypeRequest, new URL(contentTypeRequest.url))).toBe(true);
});
it('should not treat generic /cr/ requests as Docker traffic without registry hints', () => {
const request = new Request('https://example.com/cr/docker/library/nginx/readme');
const url = new URL(request.url);
expect(isDockerRequest(request, url)).toBe(false);
});
}); });
describe('addSecurityHeaders', () => { describe('addSecurityHeaders', () => {
@@ -142,6 +256,33 @@ describe('Utility Functions', () => {
expect(resolveAllowedOrigin(request, config)).toBeNull(); expect(resolveAllowedOrigin(request, config)).toBeNull();
}); });
it('should allow any origin when wildcard CORS is configured', () => {
const config = createConfig({ ALLOWED_ORIGINS: '*' });
const request = new Request('https://example.com/gh/test/repo', {
headers: { Origin: 'https://app.example.com' }
});
expect(resolveAllowedOrigin(request, config)).toBe('*');
});
});
describe('addCorsHeaders', () => {
it('should append allow headers and preserve existing Vary values', () => {
const config = createConfig({ ALLOWED_ORIGINS: '*' });
const request = new Request('https://example.com/gh/test/repo', {
headers: {
Origin: 'https://app.example.com',
'Access-Control-Request-Headers': 'X-Test-Header'
}
});
const headers = addCorsHeaders(new Headers({ Vary: 'Accept-Encoding' }), request, config);
expect(headers.get('Access-Control-Allow-Origin')).toBe('*');
expect(headers.get('Access-Control-Allow-Headers')).toBe('X-Test-Header');
expect(headers.get('Vary')).toBe('Accept-Encoding, Origin');
});
}); });
describe('createErrorResponse', () => { describe('createErrorResponse', () => {
@@ -153,5 +294,17 @@ describe('Utility Functions', () => {
expect(response.headers.get('X-Frame-Options')).toBe('DENY'); expect(response.headers.get('X-Frame-Options')).toBe('DENY');
expect(await response.text()).toBe('Bad Request'); expect(await response.text()).toBe('Bad Request');
}); });
it('should create detailed JSON error responses when requested', async () => {
const response = createErrorResponse('Unauthorized', 401, true);
const body = await response.json();
expect(response.headers.get('Content-Type')).toBe('application/json');
expect(body).toMatchObject({
error: 'Unauthorized',
status: 401
});
expect(body.timestamp).toBeTruthy();
});
}); });
}); });
+583
View File
@@ -85,6 +85,7 @@ describe('Worker regression coverage', () => {
expect(response.headers.get('Cache-Control')).toBe('no-store'); expect(response.headers.get('Cache-Control')).toBe('no-store');
expect(cacheDefault.put).not.toHaveBeenCalled(); expect(cacheDefault.put).not.toHaveBeenCalled();
}); });
it('forwards body and content type for configured non-protocol POST requests', async () => { it('forwards body and content type for configured non-protocol POST requests', async () => {
/** @type {{ url: string, method: string | undefined, body: string | null, contentType: string | null, cf: unknown }} */ /** @type {{ url: string, method: string | undefined, body: string | null, contentType: string | null, cf: unknown }} */
let observed = { let observed = {
@@ -132,4 +133,586 @@ describe('Worker regression coverage', () => {
expect(cacheDefault.match).not.toHaveBeenCalled(); expect(cacheDefault.match).not.toHaveBeenCalled();
expect(response.headers.get('Cache-Control')).toBe('no-store'); expect(response.headers.get('Cache-Control')).toBe('no-store');
}); });
it('returns Docker registry version metadata for /v2/ probes', async () => {
const response = await worker.fetch(
new Request('https://example.com/v2/'),
{},
executionContext
);
expect(response.status).toBe(200);
expect(response.headers.get('Docker-Distribution-Api-Version')).toBe('registry/2.0');
expect(response.headers.get('X-Performance-Metrics')).toBeNull();
expect(await response.text()).toBe('{}');
});
it('redirects unknown platforms and bare platform prefixes to the homepage', async () => {
const unknownPlatform = await worker.fetch(
new Request('https://example.com/not-a-platform/resource'),
{},
executionContext
);
const barePlatform = await worker.fetch(
new Request('https://example.com/gh/', { method: 'GET' }),
{},
executionContext
);
expect(unknownPlatform.status).toBe(302);
expect(unknownPlatform.headers.get('Location')).toBe('https://github.com/xixu-me/Xget');
expect(barePlatform.status).toBe(302);
expect(barePlatform.headers.get('Location')).toBe('https://github.com/xixu-me/Xget');
});
it('rejects Docker requests that do not use a /cr/ prefix', async () => {
const response = await worker.fetch(
new Request('https://example.com/v2/library/nginx/manifests/latest'),
{},
executionContext
);
expect(response.status).toBe(400);
expect(await response.text()).toContain('/cr/ prefix');
});
it('rejects disallowed CORS preflight methods before proxying upstream', async () => {
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo', {
method: 'OPTIONS',
headers: {
Origin: 'https://app.example.com',
'Access-Control-Request-Method': 'POST'
}
}),
{ ALLOWED_ORIGINS: 'https://app.example.com' },
executionContext
);
expect(response.status).toBe(405);
expect(await response.text()).toBe('Method not allowed');
});
it('serves cached responses without proxying upstream', async () => {
cacheDefault.match.mockResolvedValueOnce(
new Response('cached-body', {
status: 200,
headers: { 'Content-Type': 'text/plain' }
})
);
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response('should-not-run', {
status: 200,
headers: { 'Content-Type': 'text/plain' }
})
);
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo/file.txt'),
{},
executionContext
);
const metrics = JSON.parse(response.headers.get('X-Performance-Metrics') || '{}');
expect(response.status).toBe(200);
expect(await response.text()).toBe('cached-body');
expect(metrics).toHaveProperty('cache_hit');
expect(fetchSpy).not.toHaveBeenCalled();
});
it('reuses cached full content for range requests when a ranged entry is absent', async () => {
cacheDefault.match.mockResolvedValueOnce(null).mockResolvedValueOnce(
new Response('full-body', {
status: 200,
headers: { 'Content-Type': 'text/plain' }
})
);
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response('should-not-run', {
status: 200,
headers: { 'Content-Type': 'text/plain' }
})
);
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo/file.txt', {
headers: { Range: 'bytes=0-3' }
}),
{},
executionContext
);
const metrics = JSON.parse(response.headers.get('X-Performance-Metrics') || '{}');
expect(response.status).toBe(200);
expect(await response.text()).toBe('full-body');
expect(metrics).toHaveProperty('cache_hit_full_content');
expect(fetchSpy).not.toHaveBeenCalled();
});
it('falls back to upstream fetch when cache lookup throws', async () => {
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
cacheDefault.match.mockRejectedValueOnce(new Error('cache-down'));
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response('ok', {
status: 200,
headers: { 'Content-Type': 'text/plain' }
})
);
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo/file.txt'),
{},
executionContext
);
expect(response.status).toBe(200);
expect(fetchSpy).toHaveBeenCalledTimes(1);
expect(warnSpy).toHaveBeenCalledWith('Cache API unavailable:', expect.any(Error));
});
it('configures Git passthrough headers for upload-pack requests', async () => {
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response('', {
status: 200,
headers: { 'Content-Type': 'application/x-git-upload-pack-result' }
})
);
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo.git/git-upload-pack', {
method: 'POST'
}),
{},
executionContext
);
const upstreamHeaders = new Headers(fetchSpy.mock.calls[0][1]?.headers);
expect(response.status).toBe(200);
expect(upstreamHeaders.get('User-Agent')).toBe('git/2.34.1');
expect(upstreamHeaders.get('Content-Type')).toBe('application/x-git-upload-pack-request');
});
it('derives HEAD content length from a range probe when the upstream omits it', async () => {
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
if (init?.method === 'HEAD') {
return new Response(null, {
status: 200,
headers: { 'Content-Type': 'text/plain' }
});
}
expect(new Headers(init?.headers).get('Range')).toBe('bytes=0-0');
return new Response(null, {
status: 206,
headers: { 'Content-Range': 'bytes 0-0/123' }
});
});
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo/file.txt', { method: 'HEAD' }),
{},
executionContext
);
expect(response.status).toBe(200);
expect(response.headers.get('Content-Length')).toBe('123');
expect(fetchSpy).toHaveBeenCalledTimes(2);
});
it('uses a successful GET probe to recover missing HEAD content length', async () => {
vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
if (init?.method === 'HEAD') {
return new Response(null, {
status: 200,
headers: { 'Content-Type': 'text/plain' }
});
}
return new Response(null, {
status: 200,
headers: { 'Content-Length': '321' }
});
});
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo/file.txt', { method: 'HEAD' }),
{},
executionContext
);
expect(response.status).toBe(200);
expect(response.headers.get('Content-Length')).toBe('321');
});
it('wraps upstream client errors in detailed JSON responses', async () => {
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response('teapot', {
status: 418,
headers: { 'Content-Type': 'text/plain' }
})
);
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo/file.txt'),
{ MAX_RETRIES: '1', RETRY_DELAY_MS: '0' },
executionContext
);
const body = await response.json();
expect(response.status).toBe(418);
expect(body.error).toContain('Upstream server error (418): teapot');
});
it('retries upstream 5xx responses before succeeding', async () => {
const fetchSpy = vi
.spyOn(globalThis, 'fetch')
.mockResolvedValueOnce(
new Response('busy', {
status: 503,
headers: { 'Content-Type': 'text/plain' }
})
)
.mockResolvedValueOnce(
new Response('ok', {
status: 200,
headers: { 'Content-Type': 'text/plain' }
})
);
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo/file.txt'),
{ MAX_RETRIES: '2', RETRY_DELAY_MS: '0' },
executionContext
);
expect(response.status).toBe(200);
expect(fetchSpy).toHaveBeenCalledTimes(2);
});
it('retries rejected upstream fetches before succeeding', async () => {
const fetchSpy = vi
.spyOn(globalThis, 'fetch')
.mockRejectedValueOnce(new Error('temporary-network-failure'))
.mockResolvedValueOnce(
new Response('ok', {
status: 200,
headers: { 'Content-Type': 'text/plain' }
})
);
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo/file.txt'),
{ MAX_RETRIES: '2', RETRY_DELAY_MS: '0' },
executionContext
);
expect(response.status).toBe(200);
expect(fetchSpy).toHaveBeenCalledTimes(2);
});
it('times out requests when the abort timer fires', async () => {
const timeoutToken = { id: 'abort-timeout' };
const clearTimeoutSpy = vi.fn();
vi.stubGlobal(
'setTimeout',
vi.fn((callback, delay) => {
void delay;
callback();
return timeoutToken;
})
);
vi.stubGlobal('clearTimeout', clearTimeoutSpy);
vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
if (init?.signal?.aborted) {
const error = new Error(`Aborted before fetching ${String(input)}`);
error.name = 'AbortError';
throw error;
}
return new Response('unexpected-success', {
status: 200,
headers: { 'Content-Type': 'text/plain' }
});
});
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo/file.txt'),
{ MAX_RETRIES: '2', RETRY_DELAY_MS: '0', TIMEOUT_SECONDS: '1' },
executionContext
);
expect(response.status).toBe(408);
expect(await response.text()).toBe('Request timeout');
expect(clearTimeoutSpy).toHaveBeenCalledWith(timeoutToken);
});
it('returns a generic 500 when retry configuration prevents any upstream attempt', async () => {
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response('ok', {
status: 200,
headers: { 'Content-Type': 'text/plain' }
})
);
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo/file.txt'),
{ MAX_RETRIES: '-1' },
executionContext
);
expect(response.status).toBe(500);
expect(await response.text()).toBe('No response received after all retry attempts');
expect(fetchSpy).not.toHaveBeenCalled();
});
it('logs and recovers when request setup throws unexpectedly', async () => {
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
const redirectSpy = vi.spyOn(Response, 'redirect').mockImplementation(() => {
throw new Error('boom');
});
const response = await worker.fetch(new Request('https://example.com/'), {}, executionContext);
expect(response.status).toBe(500);
expect(await response.text()).toBe('Internal Server Error');
expect(errorSpy).toHaveBeenCalledWith('Error handling request:', expect.any(Error));
expect(redirectSpy).toHaveBeenCalled();
});
it('retries Docker requests with an anonymous token and follows redirects on success', async () => {
const fetchSpy = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
const url = String(input);
const headers = new Headers(init?.headers);
if (url === 'https://ghcr.io/v2/private/repo/manifests/latest') {
if (!headers.has('Authorization')) {
return new Response('', {
status: 401,
headers: {
'WWW-Authenticate': 'Bearer realm="https://ghcr.io/token",service="ghcr.io"'
}
});
}
expect(headers.get('Authorization')).toBe('Bearer token-123');
return new Response(null, {
status: 302,
headers: { Location: 'https://pkg.example.com/manifest' }
});
}
if (url.startsWith('https://ghcr.io/token')) {
return new Response(JSON.stringify({ token: 'token-123' }), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
}
if (url === 'https://pkg.example.com/manifest') {
expect(headers.get('Authorization')).toBeNull();
return new Response('', {
status: 200,
headers: { 'Content-Length': '0' }
});
}
throw new Error(`Unexpected fetch URL: ${url}`);
});
const response = await worker.fetch(
new Request('https://example.com/cr/ghcr/v2/private/repo/manifests/latest', {
headers: { Accept: 'application/vnd.docker.distribution.manifest.v2+json' }
}),
{},
executionContext
);
expect(response.status).toBe(200);
expect(fetchSpy).toHaveBeenCalledTimes(4);
});
it('warns and falls back to a Docker auth challenge when token negotiation fails', async () => {
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response('', {
status: 401,
headers: { 'WWW-Authenticate': 'Bearer realm="https://ghcr.io/token"' }
})
);
const response = await worker.fetch(
new Request('https://example.com/cr/ghcr/v2/private/repo/manifests/latest', {
headers: { Accept: 'application/vnd.docker.distribution.manifest.v2+json' }
}),
{},
executionContext
);
expect(response.status).toBe(401);
expect(response.headers.get('WWW-Authenticate')).toBe(
'Bearer realm="https://example.com/cr/ghcr/v2/auth",service="Xget"'
);
expect(warnSpy).toHaveBeenCalledWith('Token fetch failed:', expect.any(Error));
});
it('returns a ranged response after caching the full upstream body', async () => {
cacheDefault.match
.mockResolvedValueOnce(null)
.mockResolvedValueOnce(null)
.mockResolvedValueOnce(
new Response('xy', {
status: 206,
headers: { 'Content-Range': 'bytes 0-1/6' }
})
);
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response('xyz123', {
status: 200,
headers: { 'Content-Type': 'text/plain' }
})
);
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo/file.bin', {
headers: { Range: 'bytes=0-1' }
}),
{},
executionContext
);
const metrics = JSON.parse(response.headers.get('X-Performance-Metrics') || '{}');
expect(response.status).toBe(206);
expect(metrics).toHaveProperty('range_cache_hit_after_full_cache');
});
it('warns when cache writes fail without waitUntil support', async () => {
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
cacheDefault.put.mockRejectedValueOnce(new Error('cache-put-down'));
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response('cached', {
status: 200,
headers: { 'Content-Type': 'text/plain' }
})
);
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo/file.txt'),
{},
/** @type {ExecutionContext} */ ({})
);
await Promise.resolve();
expect(response.status).toBe(200);
expect(warnSpy).toHaveBeenCalledWith('Cache put failed:', expect.any(Error));
});
it('warns when post-store cache lookups fail for range requests', async () => {
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
cacheDefault.match
.mockResolvedValueOnce(null)
.mockResolvedValueOnce(null)
.mockRejectedValueOnce(new Error('range-cache-down'));
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response('abcdef', {
status: 200,
headers: { 'Content-Type': 'text/plain' }
})
);
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo/file.bin', {
headers: { Range: 'bytes=0-1' }
}),
{},
executionContext
);
expect(response.status).toBe(200);
expect(warnSpy).toHaveBeenCalledWith('Cache put/match failed:', expect.any(Error));
});
it('copies upstream content length from non-standard header objects when needed', async () => {
const upstreamHeaders = {
/**
* Reads an upstream header value.
* @param {string} name
*/
get(name) {
const header = name.toLowerCase();
if (header === 'content-type') {
return 'text/plain';
}
if (header === 'content-length') {
return '777';
}
return null;
},
*[Symbol.iterator]() {
yield ['Content-Type', 'text/plain'];
}
};
const fakeResponse = /** @type {Response} */ ({
body: null,
headers: upstreamHeaders,
ok: true,
status: 200,
statusText: 'OK',
text: async () => 'ok'
});
vi.spyOn(globalThis, 'fetch').mockResolvedValue(fakeResponse);
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo/file.txt'),
{},
executionContext
);
expect(response.status).toBe(200);
expect(response.headers.get('Content-Length')).toBe('777');
});
it('warns when upstream content length cannot be read during response finalization', async () => {
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
const upstreamHeaders = {
/**
* Reads an upstream header value.
* @param {string} name
*/
get(name) {
if (name.toLowerCase() === 'content-type') {
return 'text/plain';
}
throw new Error('content-length unavailable');
},
*[Symbol.iterator]() {
yield ['Content-Type', 'text/plain'];
}
};
const fakeResponse = /** @type {Response} */ ({
body: null,
headers: upstreamHeaders,
ok: true,
status: 200,
statusText: 'OK',
text: async () => 'ok'
});
vi.spyOn(globalThis, 'fetch').mockResolvedValue(fakeResponse);
const response = await worker.fetch(
new Request('https://example.com/gh/user/repo/file.txt'),
{},
executionContext
);
expect(response.status).toBe(200);
expect(warnSpy).toHaveBeenCalledWith('Could not set Content-Length header:', expect.any(Error));
});
}); });
+1
View File
@@ -5,6 +5,7 @@ export default defineConfig({
testTimeout: 60000, testTimeout: 60000,
hookTimeout: 30000, hookTimeout: 30000,
include: [ include: [
'test/features/auth.test.js',
'test/unit/**/*.test.js', 'test/unit/**/*.test.js',
'test/platforms/crates.test.js', 'test/platforms/crates.test.js',
'test/platforms/cran.test.js', 'test/platforms/cran.test.js',