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.
This commit is contained in:
1 parent
44b54580ad
commit
ce93ea9861
8 files changed
+275
-282
No files matched your search
+58
-64
@@ -17,17 +17,10 @@ import {
|
||||
parseAuthenticate,
|
||||
responseUnauthorized
|
||||
} from './protocols/docker.js';
|
||||
import {
|
||||
configureGitHeaders,
|
||||
isGitLFSRequest,
|
||||
isGitRequest
|
||||
} from './protocols/git.js';
|
||||
import { configureGitHeaders, isGitLFSRequest, isGitRequest } from './protocols/git.js';
|
||||
import { PerformanceMonitor, addPerformanceHeaders } from './utils/performance.js';
|
||||
import { addSecurityHeaders, createErrorResponse } from './utils/security.js';
|
||||
import {
|
||||
isDockerRequest,
|
||||
validateRequest
|
||||
} from './utils/validation.js';
|
||||
import { isDockerRequest, validateRequest } from './utils/validation.js';
|
||||
|
||||
/**
|
||||
* Main request handler with comprehensive caching, retry logic, and security measures.
|
||||
@@ -80,10 +73,7 @@ async function handleRequest(request, env, ctx) {
|
||||
!url.pathname.startsWith('/v2/cr/') &&
|
||||
url.pathname !== '/v2/auth'
|
||||
) {
|
||||
response = createErrorResponse(
|
||||
'container registry requests must use /cr/ prefix',
|
||||
400
|
||||
);
|
||||
response = createErrorResponse('container registry requests must use /cr/ prefix', 400);
|
||||
} else {
|
||||
// Remove /v2 from the path for container registry API consistency if present
|
||||
effectivePath = url.pathname.replace(/^\/v2/, '');
|
||||
@@ -140,7 +130,10 @@ async function handleRequest(request, env, ctx) {
|
||||
/** @type {Cache | null} */
|
||||
/** @type {Cache | null} */
|
||||
// @ts-ignore - Cloudflare Workers cache API
|
||||
const cache = typeof caches !== 'undefined' && /** @type {any} */ (caches).default ? /** @type {any} */ (caches).default : null;
|
||||
const cache =
|
||||
typeof caches !== 'undefined' && /** @type {any} */ (caches).default
|
||||
? /** @type {any} */ (caches).default
|
||||
: null;
|
||||
|
||||
if (cache && !isGit && !isGitLFS && !isDocker && !isAI) {
|
||||
try {
|
||||
@@ -301,8 +294,7 @@ async function handleRequest(request, env, ctx) {
|
||||
contentLength = rangeResponse.headers.get('Content-Length');
|
||||
if (!contentLength) {
|
||||
const sizeLimit = 50 * 1024 * 1024;
|
||||
const contentLengthHint =
|
||||
rangeResponse.headers.get('Content-Length');
|
||||
const contentLengthHint = rangeResponse.headers.get('Content-Length');
|
||||
if (
|
||||
!contentLengthHint ||
|
||||
parseInt(contentLengthHint, 10) < sizeLimit
|
||||
@@ -503,20 +495,20 @@ async function handleRequest(request, env, ctx) {
|
||||
(await response.clone().text()).includes('UNAUTHORIZED');
|
||||
|
||||
if (!isCustomError) {
|
||||
const errorText = await response.text().catch(() => '');
|
||||
response = createErrorResponse(
|
||||
const errorText = await response.text().catch(() => '');
|
||||
response = createErrorResponse(
|
||||
`Authentication required for this container registry resource. This may be a private repository. Original error: ${errorText}`,
|
||||
401,
|
||||
true
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const errorText = await response.text().catch(() => 'Unknown error');
|
||||
response = createErrorResponse(
|
||||
`Upstream server error (${response.status}): ${errorText}`,
|
||||
response.status,
|
||||
true
|
||||
);
|
||||
const errorText = await response.text().catch(() => 'Unknown error');
|
||||
response = createErrorResponse(
|
||||
`Upstream server error (${response.status}): ${errorText}`,
|
||||
response.status,
|
||||
true
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Success case processing (rewriting URLs etc)
|
||||
@@ -584,50 +576,52 @@ async function handleRequest(request, env, ctx) {
|
||||
|
||||
// Cache success logic
|
||||
if (
|
||||
cache &&
|
||||
!isGit &&
|
||||
!isGitLFS &&
|
||||
!isDocker &&
|
||||
!isAI &&
|
||||
request.method === 'GET' &&
|
||||
response.ok &&
|
||||
response.status === 200
|
||||
) {
|
||||
const rangeHeader = request.headers.get('Range');
|
||||
const cacheKey = rangeHeader
|
||||
? new Request(targetUrl, {
|
||||
method: 'GET',
|
||||
headers: new Headers(
|
||||
[...request.headers.entries()].filter(([k]) => k.toLowerCase() !== 'range')
|
||||
cache &&
|
||||
!isGit &&
|
||||
!isGitLFS &&
|
||||
!isDocker &&
|
||||
!isAI &&
|
||||
request.method === 'GET' &&
|
||||
response.ok &&
|
||||
response.status === 200
|
||||
) {
|
||||
const rangeHeader = request.headers.get('Range');
|
||||
const cacheKey = rangeHeader
|
||||
? new Request(targetUrl, {
|
||||
method: 'GET',
|
||||
headers: new Headers(
|
||||
[...request.headers.entries()].filter(
|
||||
([k]) => k.toLowerCase() !== 'range'
|
||||
)
|
||||
})
|
||||
: new Request(targetUrl, { method: 'GET' });
|
||||
)
|
||||
})
|
||||
: new Request(targetUrl, { method: 'GET' });
|
||||
|
||||
try {
|
||||
if (ctx && typeof ctx.waitUntil === 'function') {
|
||||
ctx.waitUntil(cache.put(cacheKey, response.clone()));
|
||||
} else {
|
||||
cache.put(cacheKey, response.clone()).catch(error => {
|
||||
console.warn('Cache put failed:', error);
|
||||
});
|
||||
}
|
||||
|
||||
if (rangeHeader && response.status === 200) {
|
||||
const rangedResponse = await cache.match(
|
||||
new Request(targetUrl, {
|
||||
method: 'GET',
|
||||
headers: request.headers
|
||||
})
|
||||
);
|
||||
if (rangedResponse) {
|
||||
monitor.mark('range_cache_hit_after_full_cache');
|
||||
response = rangedResponse;
|
||||
}
|
||||
}
|
||||
} catch (cacheError) {
|
||||
console.warn('Cache put/match failed:', cacheError);
|
||||
try {
|
||||
if (ctx && typeof ctx.waitUntil === 'function') {
|
||||
ctx.waitUntil(cache.put(cacheKey, response.clone()));
|
||||
} else {
|
||||
cache.put(cacheKey, response.clone()).catch(error => {
|
||||
console.warn('Cache put failed:', error);
|
||||
});
|
||||
}
|
||||
|
||||
if (rangeHeader && response.status === 200) {
|
||||
const rangedResponse = await cache.match(
|
||||
new Request(targetUrl, {
|
||||
method: 'GET',
|
||||
headers: request.headers
|
||||
})
|
||||
);
|
||||
if (rangedResponse) {
|
||||
monitor.mark('range_cache_hit_after_full_cache');
|
||||
response = rangedResponse;
|
||||
}
|
||||
}
|
||||
} catch (cacheError) {
|
||||
console.warn('Cache put/match failed:', cacheError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { SELF } from 'cloudflare:test';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
describe('Authentication Header Forwarding', () => {
|
||||
it('should forward Authorization header for Hugging Face requests', async () => {
|
||||
// Test with a Hugging Face dataset file that would require authentication
|
||||
const testUrl = 'https://example.com/hf/datasets/test/private-dataset/resolve/main/data.csv';
|
||||
const authToken = 'Bearer hf_test_token_12345';
|
||||
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
method: 'HEAD',
|
||||
headers: {
|
||||
Authorization: authToken
|
||||
}
|
||||
});
|
||||
|
||||
// Should accept the request (not 400 bad request)
|
||||
expect(response.status).not.toBe(400);
|
||||
// Should attempt to proxy to HF with auth (status depends on actual HF response)
|
||||
expect([200, 401, 403, 404]).toContain(response.status);
|
||||
});
|
||||
|
||||
it('should forward Authorization header for GitHub API requests', async () => {
|
||||
const testUrl = 'https://example.com/gh/test/private-repo/README.md';
|
||||
const authToken = 'Bearer ghp_test_token_12345';
|
||||
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
method: 'HEAD',
|
||||
headers: {
|
||||
Authorization: authToken
|
||||
}
|
||||
});
|
||||
|
||||
// Should accept the request and forward the auth header
|
||||
expect(response.status).not.toBe(400);
|
||||
expect([200, 401, 403, 404]).toContain(response.status);
|
||||
});
|
||||
|
||||
it('should forward Authorization header for PyPI authenticated requests', async () => {
|
||||
const testUrl = 'https://example.com/pypi/simple/private-package/';
|
||||
const authToken = 'Basic dGVzdDp0ZXN0MTIzNDU=';
|
||||
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
method: 'HEAD',
|
||||
headers: {
|
||||
Authorization: authToken
|
||||
}
|
||||
});
|
||||
|
||||
// Should accept the request
|
||||
expect(response.status).not.toBe(400);
|
||||
expect([200, 401, 403, 404]).toContain(response.status);
|
||||
});
|
||||
|
||||
it('should work with gated Hugging Face models', async () => {
|
||||
// Simulate a request to a gated model that requires authentication
|
||||
const testUrl = 'https://example.com/hf/meta-llama/Llama-2-7b/resolve/main/config.json';
|
||||
const authToken = 'Bearer hf_authenticated_token';
|
||||
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
headers: {
|
||||
Authorization: authToken
|
||||
}
|
||||
});
|
||||
|
||||
// Should attempt to proxy with authentication
|
||||
// The actual status depends on whether the token is valid and the model exists
|
||||
expect(response.status).not.toBe(400);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,95 @@
|
||||
import { SELF } from 'cloudflare:test';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
describe('Git LFS Protocol Integration', () => {
|
||||
it('should handle LFS info/lfs requests', async () => {
|
||||
const testUrl = 'https://example.com/gh/microsoft/vscode.git/info/lfs';
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'git-lfs/3.0.0 (GitHub; darwin amd64; go 1.17.2)'
|
||||
}
|
||||
});
|
||||
|
||||
expect([200, 301, 302, 404]).toContain(response.status);
|
||||
});
|
||||
|
||||
it('should handle LFS batch API requests', async () => {
|
||||
const testUrl = 'https://example.com/gh/microsoft/vscode.git/objects/batch';
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/vnd.git-lfs+json',
|
||||
Accept: 'application/vnd.git-lfs+json',
|
||||
'User-Agent': 'git-lfs/3.0.0'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
operation: 'download',
|
||||
objects: [
|
||||
{
|
||||
oid: 'a1b2c3d4e5f6789012345678901234567890123456789012345678901234abcd',
|
||||
size: 1024
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
expect([200, 301, 302, 400, 403, 404]).toContain(response.status);
|
||||
});
|
||||
|
||||
it('should handle LFS object download requests', async () => {
|
||||
const testUrl =
|
||||
'https://example.com/gh/microsoft/vscode.git/objects/a1b2c3d4e5f6789012345678901234567890123456789012345678901234abcd';
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'git-lfs/3.0.0',
|
||||
Accept: 'application/octet-stream'
|
||||
}
|
||||
});
|
||||
|
||||
expect([200, 301, 302, 403, 404]).toContain(response.status);
|
||||
});
|
||||
|
||||
it('should preserve LFS-specific headers', async () => {
|
||||
const testUrl = 'https://example.com/gh/test/repo.git/objects/batch';
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'User-Agent': 'git-lfs/3.0.0',
|
||||
Accept: 'application/vnd.git-lfs+json',
|
||||
'Content-Type': 'application/vnd.git-lfs+json'
|
||||
},
|
||||
body: '{}'
|
||||
});
|
||||
|
||||
// Should not reject LFS-specific headers
|
||||
expect(response.status).not.toBe(400);
|
||||
});
|
||||
|
||||
it('should skip caching for LFS requests', async () => {
|
||||
const testUrl = 'https://example.com/gh/test/repo.git/info/lfs';
|
||||
|
||||
// First request
|
||||
const response1 = await SELF.fetch(testUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'git-lfs/3.0.0'
|
||||
}
|
||||
});
|
||||
|
||||
// Second request - should not be cached
|
||||
const response2 = await SELF.fetch(testUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'git-lfs/3.0.0'
|
||||
}
|
||||
});
|
||||
|
||||
// Both requests should go to origin (no cache hit)
|
||||
const metrics1 = response1.headers.get('X-Performance-Metrics');
|
||||
const metrics2 = response2.headers.get('X-Performance-Metrics');
|
||||
|
||||
// Verify that neither indicates a cache hit
|
||||
if (metrics1 && metrics2) {
|
||||
expect(metrics1).not.toContain('cache_hit');
|
||||
expect(metrics2).not.toContain('cache_hit');
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import { SELF } from 'cloudflare:test';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
describe('Git Protocol Integration', () => {
|
||||
it('should handle Git info/refs requests', async () => {
|
||||
const testUrl = 'https://example.com/gh/microsoft/vscode.git/info/refs?service=git-upload-pack';
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'git/2.34.1'
|
||||
}
|
||||
});
|
||||
|
||||
expect([200, 301, 302, 404]).toContain(response.status);
|
||||
});
|
||||
|
||||
it('should handle Git upload-pack requests', async () => {
|
||||
const testUrl = 'https://example.com/gh/microsoft/vscode.git/git-upload-pack';
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-git-upload-pack-request',
|
||||
'User-Agent': 'git/2.34.1'
|
||||
},
|
||||
body: '0000' // Minimal Git protocol data
|
||||
});
|
||||
|
||||
expect([200, 301, 302, 400, 404]).toContain(response.status);
|
||||
});
|
||||
|
||||
it('should preserve Git-specific headers', async () => {
|
||||
const testUrl = 'https://example.com/gh/test/repo.git/info/refs';
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'git/2.34.1',
|
||||
'Git-Protocol': 'version=2'
|
||||
}
|
||||
});
|
||||
|
||||
// Should not reject Git-specific headers
|
||||
expect(response.status).not.toBe(400);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import { beforeAll, describe, expect, it } from 'vitest';
|
||||
import { SELF } from 'cloudflare:test';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
/**
|
||||
* Tests for Range Request Caching Strategy
|
||||
@@ -11,17 +12,6 @@ import { beforeAll, describe, expect, it } from 'vitest';
|
||||
*/
|
||||
|
||||
describe('Range Request Caching Strategy', () => {
|
||||
/** @type {any} */
|
||||
let SELF;
|
||||
|
||||
beforeAll(async () => {
|
||||
const { unstable_dev: unstableDev } = await import('wrangler');
|
||||
const worker = await unstableDev('src/index.js', {
|
||||
experimental: { disableExperimentalWarning: true }
|
||||
});
|
||||
SELF = worker;
|
||||
});
|
||||
|
||||
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';
|
||||
@@ -44,7 +34,7 @@ describe('Range Request Caching Strategy', () => {
|
||||
|
||||
// Should not contain any cache put errors
|
||||
const errorKeys = Object.keys(parsedMetrics).filter(
|
||||
key => key.includes('error') || key.includes('fail')
|
||||
key => (key.includes('error') || key.includes('fail')) && key !== 'client_error'
|
||||
);
|
||||
expect(errorKeys).toHaveLength(0);
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ describe('Security Features', () => {
|
||||
// If it doesn't throw, it should not be a server error
|
||||
} catch (error) {
|
||||
// Expected to throw TypeError for invalid header value
|
||||
expect((/** @type {Error} */ (error)).message).toMatch(/[Ii]nvalid|[Hh]eader/);
|
||||
expect(/** @type {Error} */ (error).message).toMatch(/[Ii]nvalid|[Hh]eader/);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -200,7 +200,7 @@ describe('Security Features', () => {
|
||||
responses.forEach((/** @type {Response} */ response) => {
|
||||
expect(response.status).not.toBe(500);
|
||||
});
|
||||
});
|
||||
}, 30000);
|
||||
|
||||
it('should timeout long-running requests', async () => {
|
||||
// This test would need to be implemented based on actual timeout behavior
|
||||
|
||||
@@ -66,140 +66,6 @@ describe('Integration Tests', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Git Protocol Integration', () => {
|
||||
it('should handle Git info/refs requests', async () => {
|
||||
const testUrl =
|
||||
'https://example.com/gh/microsoft/vscode.git/info/refs?service=git-upload-pack';
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'git/2.34.1'
|
||||
}
|
||||
});
|
||||
|
||||
expect([200, 301, 302, 404]).toContain(response.status);
|
||||
});
|
||||
|
||||
it('should handle Git upload-pack requests', async () => {
|
||||
const testUrl = 'https://example.com/gh/microsoft/vscode.git/git-upload-pack';
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-git-upload-pack-request',
|
||||
'User-Agent': 'git/2.34.1'
|
||||
},
|
||||
body: '0000' // Minimal Git protocol data
|
||||
});
|
||||
|
||||
expect([200, 301, 302, 400, 404]).toContain(response.status);
|
||||
});
|
||||
|
||||
it('should preserve Git-specific headers', async () => {
|
||||
const testUrl = 'https://example.com/gh/test/repo.git/info/refs';
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'git/2.34.1',
|
||||
'Git-Protocol': 'version=2'
|
||||
}
|
||||
});
|
||||
|
||||
// Should not reject Git-specific headers
|
||||
expect(response.status).not.toBe(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Git LFS Protocol Integration', () => {
|
||||
it('should handle LFS info/lfs requests', async () => {
|
||||
const testUrl = 'https://example.com/gh/microsoft/vscode.git/info/lfs';
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'git-lfs/3.0.0 (GitHub; darwin amd64; go 1.17.2)'
|
||||
}
|
||||
});
|
||||
|
||||
expect([200, 301, 302, 404]).toContain(response.status);
|
||||
});
|
||||
|
||||
it('should handle LFS batch API requests', async () => {
|
||||
const testUrl = 'https://example.com/gh/microsoft/vscode.git/objects/batch';
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/vnd.git-lfs+json',
|
||||
Accept: 'application/vnd.git-lfs+json',
|
||||
'User-Agent': 'git-lfs/3.0.0'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
operation: 'download',
|
||||
objects: [
|
||||
{
|
||||
oid: 'a1b2c3d4e5f6789012345678901234567890123456789012345678901234abcd',
|
||||
size: 1024
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
expect([200, 301, 302, 400, 403, 404]).toContain(response.status);
|
||||
});
|
||||
|
||||
it('should handle LFS object download requests', async () => {
|
||||
const testUrl =
|
||||
'https://example.com/gh/microsoft/vscode.git/objects/a1b2c3d4e5f6789012345678901234567890123456789012345678901234abcd';
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'git-lfs/3.0.0',
|
||||
Accept: 'application/octet-stream'
|
||||
}
|
||||
});
|
||||
|
||||
expect([200, 301, 302, 403, 404]).toContain(response.status);
|
||||
});
|
||||
|
||||
it('should preserve LFS-specific headers', async () => {
|
||||
const testUrl = 'https://example.com/gh/test/repo.git/objects/batch';
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'User-Agent': 'git-lfs/3.0.0',
|
||||
Accept: 'application/vnd.git-lfs+json',
|
||||
'Content-Type': 'application/vnd.git-lfs+json'
|
||||
},
|
||||
body: '{}'
|
||||
});
|
||||
|
||||
// Should not reject LFS-specific headers
|
||||
expect(response.status).not.toBe(400);
|
||||
});
|
||||
|
||||
it('should skip caching for LFS requests', async () => {
|
||||
const testUrl = 'https://example.com/gh/test/repo.git/info/lfs';
|
||||
|
||||
// First request
|
||||
const response1 = await SELF.fetch(testUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'git-lfs/3.0.0'
|
||||
}
|
||||
});
|
||||
|
||||
// Second request - should not be cached
|
||||
const response2 = await SELF.fetch(testUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'git-lfs/3.0.0'
|
||||
}
|
||||
});
|
||||
|
||||
// Both requests should go to origin (no cache hit)
|
||||
const metrics1 = response1.headers.get('X-Performance-Metrics');
|
||||
const metrics2 = response2.headers.get('X-Performance-Metrics');
|
||||
|
||||
// Verify that neither indicates a cache hit
|
||||
if (metrics1 && metrics2) {
|
||||
expect(metrics1).not.toContain('cache_hit');
|
||||
expect(metrics2).not.toContain('cache_hit');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Caching Integration', () => {
|
||||
it('should cache responses appropriately', async () => {
|
||||
const testUrl = 'https://example.com/gh/test/repo/static-file.txt';
|
||||
@@ -439,72 +305,4 @@ describe('Integration Tests', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Authentication Header Forwarding', () => {
|
||||
it('should forward Authorization header for Hugging Face requests', async () => {
|
||||
// Test with a Hugging Face dataset file that would require authentication
|
||||
const testUrl = 'https://example.com/hf/datasets/test/private-dataset/resolve/main/data.csv';
|
||||
const authToken = 'Bearer hf_test_token_12345';
|
||||
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
method: 'HEAD',
|
||||
headers: {
|
||||
Authorization: authToken
|
||||
}
|
||||
});
|
||||
|
||||
// Should accept the request (not 400 bad request)
|
||||
expect(response.status).not.toBe(400);
|
||||
// Should attempt to proxy to HF with auth (status depends on actual HF response)
|
||||
expect([200, 401, 403, 404]).toContain(response.status);
|
||||
});
|
||||
|
||||
it('should forward Authorization header for GitHub API requests', async () => {
|
||||
const testUrl = 'https://example.com/gh/test/private-repo/README.md';
|
||||
const authToken = 'Bearer ghp_test_token_12345';
|
||||
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
method: 'HEAD',
|
||||
headers: {
|
||||
Authorization: authToken
|
||||
}
|
||||
});
|
||||
|
||||
// Should accept the request and forward the auth header
|
||||
expect(response.status).not.toBe(400);
|
||||
expect([200, 401, 403, 404]).toContain(response.status);
|
||||
});
|
||||
|
||||
it('should forward Authorization header for PyPI authenticated requests', async () => {
|
||||
const testUrl = 'https://example.com/pypi/simple/private-package/';
|
||||
const authToken = 'Basic dGVzdDp0ZXN0MTIzNDU=';
|
||||
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
method: 'HEAD',
|
||||
headers: {
|
||||
Authorization: authToken
|
||||
}
|
||||
});
|
||||
|
||||
// Should accept the request
|
||||
expect(response.status).not.toBe(400);
|
||||
expect([200, 401, 403, 404]).toContain(response.status);
|
||||
});
|
||||
|
||||
it('should work with gated Hugging Face models', async () => {
|
||||
// Simulate a request to a gated model that requires authentication
|
||||
const testUrl = 'https://example.com/hf/meta-llama/Llama-2-7b/resolve/main/config.json';
|
||||
const authToken = 'Bearer hf_authenticated_token';
|
||||
|
||||
const response = await SELF.fetch(testUrl, {
|
||||
headers: {
|
||||
Authorization: authToken
|
||||
}
|
||||
});
|
||||
|
||||
// Should attempt to proxy with authentication
|
||||
// The actual status depends on whether the token is valid and the model exists
|
||||
expect(response.status).not.toBe(400);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -254,7 +254,11 @@ describe('Container Registry Support', () => {
|
||||
prefix: 'cr/ghcr',
|
||||
expectedStatus: [200, 301, 302, 401, 404, 429]
|
||||
},
|
||||
{ name: 'Amazon ECR Public', prefix: 'cr/ecr', expectedStatus: [200, 301, 302, 401, 404, 429] }
|
||||
{
|
||||
name: 'Amazon ECR Public',
|
||||
prefix: 'cr/ecr',
|
||||
expectedStatus: [200, 301, 302, 401, 404, 429]
|
||||
}
|
||||
];
|
||||
|
||||
containerRegistries.forEach(({ name, prefix, expectedStatus }) => {
|
||||
|
||||
Reference in new issue
Block a user