@vltpkg/vsr 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +6 -2
- package/test/README.md +0 -91
- package/test/access.test.js +0 -760
- package/test/cloudflare-waituntil.test.js +0 -141
- package/test/db.test.js +0 -447
- package/test/dist-tag.test.js +0 -415
- package/test/e2e.test.js +0 -904
- package/test/hono-context.test.js +0 -250
- package/test/integrity-validation.test.js +0 -183
- package/test/json-response.test.js +0 -76
- package/test/manifest-slimming.test.js +0 -449
- package/test/packument-consistency.test.js +0 -351
- package/test/packument-version-range.test.js +0 -144
- package/test/performance.test.js +0 -162
- package/test/route-with-waituntil.test.js +0 -298
- package/test/run-tests.js +0 -151
- package/test/setup-cache-tests.js +0 -190
- package/test/setup.js +0 -64
- package/test/stale-while-revalidate.test.js +0 -273
- package/test/static-assets.test.js +0 -85
- package/test/upstream-routing.test.js +0 -86
- package/test/utils/test-helpers.js +0 -84
- package/test/waituntil-correct.test.js +0 -208
- package/test/waituntil-demo.test.js +0 -138
- package/test/waituntil-readme.md +0 -113
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
-
|
|
3
|
-
// Mock config
|
|
4
|
-
const CONFIG = {
|
|
5
|
-
DOMAIN: 'registry.npmjs.org',
|
|
6
|
-
PROXY_PACKAGES: ['fastify', 'hono']
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* This test demonstrates testing an actual route handler that uses
|
|
11
|
-
* the Hono context and waitUntil, with proper mocking of both.
|
|
12
|
-
*/
|
|
13
|
-
describe('Route Handler with waitUntil', () => {
|
|
14
|
-
/**
|
|
15
|
-
* Creates a mock Hono context with necessary properties
|
|
16
|
-
*/
|
|
17
|
-
function createMockContext(options = {}) {
|
|
18
|
-
// Track background task functions
|
|
19
|
-
const backgroundTaskFns = [];
|
|
20
|
-
let responseData = null;
|
|
21
|
-
let responseStatus = 200;
|
|
22
|
-
let responseHeaders = {};
|
|
23
|
-
let wasJsonCalled = false;
|
|
24
|
-
|
|
25
|
-
// Default options
|
|
26
|
-
const defaults = {
|
|
27
|
-
params: { packageName: 'test-package' },
|
|
28
|
-
fresh: false,
|
|
29
|
-
cacheData: null,
|
|
30
|
-
upstreamData: { name: 'test-package', version: '1.0.0' },
|
|
31
|
-
upstreamError: null
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const opts = { ...defaults, ...options };
|
|
35
|
-
|
|
36
|
-
// Mock fetch function
|
|
37
|
-
const mockFetch = vi.fn().mockImplementation(async (url) => {
|
|
38
|
-
if (opts.upstreamError) {
|
|
39
|
-
throw opts.upstreamError;
|
|
40
|
-
}
|
|
41
|
-
return {
|
|
42
|
-
json: async () => opts.upstreamData,
|
|
43
|
-
status: 200,
|
|
44
|
-
ok: true
|
|
45
|
-
};
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// Mock DB
|
|
49
|
-
const mockDb = {
|
|
50
|
-
get: vi.fn().mockImplementation(async (key) => {
|
|
51
|
-
if (!opts.cacheData) return null;
|
|
52
|
-
|
|
53
|
-
return {
|
|
54
|
-
value: opts.cacheData,
|
|
55
|
-
timestamp: opts.fresh
|
|
56
|
-
? Date.now() - 60000 // 1 minute old (fresh)
|
|
57
|
-
: Date.now() - 3600000 // 1 hour old (stale)
|
|
58
|
-
};
|
|
59
|
-
}),
|
|
60
|
-
set: vi.fn()
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
// Create context
|
|
64
|
-
return {
|
|
65
|
-
// Request info
|
|
66
|
-
req: {
|
|
67
|
-
url: `https://registry.npmjs.org/${opts.params.packageName}`,
|
|
68
|
-
fresh: opts.fresh
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
// Route parameters
|
|
72
|
-
params: opts.params,
|
|
73
|
-
|
|
74
|
-
// Database methods
|
|
75
|
-
db: mockDb,
|
|
76
|
-
|
|
77
|
-
// Response methods
|
|
78
|
-
status: vi.fn((code) => {
|
|
79
|
-
responseStatus = code;
|
|
80
|
-
return context;
|
|
81
|
-
}),
|
|
82
|
-
|
|
83
|
-
header: vi.fn((name, value) => {
|
|
84
|
-
responseHeaders[name] = value;
|
|
85
|
-
return context;
|
|
86
|
-
}),
|
|
87
|
-
|
|
88
|
-
json: vi.fn((data) => {
|
|
89
|
-
responseData = data;
|
|
90
|
-
wasJsonCalled = true;
|
|
91
|
-
return context;
|
|
92
|
-
}),
|
|
93
|
-
|
|
94
|
-
// Execution context for background tasks
|
|
95
|
-
executionCtx: {
|
|
96
|
-
waitUntil: vi.fn((promise) => {
|
|
97
|
-
// Instead of accepting the promise directly, we'll store a function
|
|
98
|
-
// that will return the promise when called
|
|
99
|
-
const promiseCreator = () => promise;
|
|
100
|
-
backgroundTaskFns.push(promiseCreator);
|
|
101
|
-
})
|
|
102
|
-
},
|
|
103
|
-
|
|
104
|
-
// Utility methods for testing
|
|
105
|
-
runBackgroundTasks: async () => {
|
|
106
|
-
const tasks = [...backgroundTaskFns];
|
|
107
|
-
backgroundTaskFns.length = 0;
|
|
108
|
-
|
|
109
|
-
for (const taskFn of tasks) {
|
|
110
|
-
try {
|
|
111
|
-
await taskFn();
|
|
112
|
-
} catch (error) {
|
|
113
|
-
console.error('Background task error:', error);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
|
|
118
|
-
getResponse: () => ({
|
|
119
|
-
data: responseData,
|
|
120
|
-
status: responseStatus,
|
|
121
|
-
headers: responseHeaders,
|
|
122
|
-
wasJsonCalled
|
|
123
|
-
}),
|
|
124
|
-
|
|
125
|
-
// Expose methods for testing
|
|
126
|
-
fetch: mockFetch,
|
|
127
|
-
backgroundTaskFns
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// The context needs to be accessible in tests
|
|
132
|
-
let context;
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* This is a simplified version of getPackageMetadata that demonstrates
|
|
136
|
-
* the stale-while-revalidate caching pattern
|
|
137
|
-
*/
|
|
138
|
-
async function getPackageMetadata(c) {
|
|
139
|
-
const { packageName } = c.params;
|
|
140
|
-
|
|
141
|
-
try {
|
|
142
|
-
// Try to get from cache first
|
|
143
|
-
const cachedData = await c.db.get(`package:${packageName}`);
|
|
144
|
-
|
|
145
|
-
if (cachedData) {
|
|
146
|
-
const isStale = Date.now() - cachedData.timestamp > 5 * 60 * 1000;
|
|
147
|
-
|
|
148
|
-
if (!isStale) {
|
|
149
|
-
// Return fresh cached data
|
|
150
|
-
return c.json(cachedData.value);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Return stale data, but refresh in background
|
|
154
|
-
c.executionCtx.waitUntil(new Promise(async (resolve) => {
|
|
155
|
-
// In a real implementation this executes immediately
|
|
156
|
-
// In our test, we'll capture the function and run it later
|
|
157
|
-
const bgRefresh = async () => {
|
|
158
|
-
try {
|
|
159
|
-
const response = await c.fetch(`https://${CONFIG.DOMAIN}/${packageName}`);
|
|
160
|
-
const freshData = await response.json();
|
|
161
|
-
|
|
162
|
-
await c.db.set(`package:${packageName}`, {
|
|
163
|
-
value: freshData,
|
|
164
|
-
timestamp: Date.now()
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
resolve();
|
|
168
|
-
} catch (error) {
|
|
169
|
-
console.error(`Error refreshing ${packageName}:`, error);
|
|
170
|
-
resolve();
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
// Don't call bgRefresh() directly, we'll run it during runBackgroundTasks
|
|
175
|
-
setTimeout(bgRefresh, 0);
|
|
176
|
-
}));
|
|
177
|
-
|
|
178
|
-
// Return stale data immediately
|
|
179
|
-
return c.json(cachedData.value);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Not in cache, fetch directly
|
|
183
|
-
const response = await c.fetch(`https://${CONFIG.DOMAIN}/${packageName}`);
|
|
184
|
-
const packageData = await response.json();
|
|
185
|
-
|
|
186
|
-
// Store in cache
|
|
187
|
-
await c.db.set(`package:${packageName}`, {
|
|
188
|
-
value: packageData,
|
|
189
|
-
timestamp: Date.now()
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
return c.json(packageData);
|
|
193
|
-
} catch (error) {
|
|
194
|
-
// Handle upstream errors gracefully
|
|
195
|
-
console.error(`Error fetching ${packageName}:`, error);
|
|
196
|
-
return c.status(500).json({
|
|
197
|
-
error: `Failed to fetch package: ${packageName}`,
|
|
198
|
-
message: error.message
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
describe('getPackageMetadata', () => {
|
|
204
|
-
beforeEach(() => {
|
|
205
|
-
vi.clearAllMocks();
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it('should return cached data if fresh', async () => {
|
|
209
|
-
// Create mock context with fresh cached data
|
|
210
|
-
context = createMockContext({
|
|
211
|
-
fresh: true,
|
|
212
|
-
cacheData: { name: 'test-package', version: '1.0.0' }
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
// Execute the handler
|
|
216
|
-
await getPackageMetadata(context);
|
|
217
|
-
|
|
218
|
-
const { data } = context.getResponse();
|
|
219
|
-
|
|
220
|
-
// Verify
|
|
221
|
-
expect(context.db.get).toHaveBeenCalledWith('package:test-package');
|
|
222
|
-
expect(context.fetch).not.toHaveBeenCalled();
|
|
223
|
-
expect(data).toEqual({ name: 'test-package', version: '1.0.0' });
|
|
224
|
-
expect(context.backgroundTaskFns.length).toBe(0);
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
it('should return stale data and refresh in background', async () => {
|
|
228
|
-
// Setup context with stale data
|
|
229
|
-
context = createMockContext({
|
|
230
|
-
fresh: false,
|
|
231
|
-
cacheData: { name: 'test-package', version: '1.0.0' },
|
|
232
|
-
upstreamData: { name: 'test-package', version: '1.0.1' }
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
// Execute the handler
|
|
236
|
-
await getPackageMetadata(context);
|
|
237
|
-
|
|
238
|
-
const { data } = context.getResponse();
|
|
239
|
-
|
|
240
|
-
// Verify initial response
|
|
241
|
-
expect(context.db.get).toHaveBeenCalledWith('package:test-package');
|
|
242
|
-
expect(context.fetch).not.toHaveBeenCalled(); // Not yet called
|
|
243
|
-
expect(data).toEqual({ name: 'test-package', version: '1.0.0' });
|
|
244
|
-
expect(context.backgroundTaskFns.length).toBe(1);
|
|
245
|
-
|
|
246
|
-
// Now run background tasks
|
|
247
|
-
await context.runBackgroundTasks();
|
|
248
|
-
|
|
249
|
-
// Verify background refresh
|
|
250
|
-
expect(context.fetch).toHaveBeenCalledWith('https://registry.npmjs.org/test-package');
|
|
251
|
-
expect(context.db.set).toHaveBeenCalledWith('package:test-package', {
|
|
252
|
-
value: { name: 'test-package', version: '1.0.1' },
|
|
253
|
-
timestamp: expect.any(Number)
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it('should fetch directly if not in cache', async () => {
|
|
258
|
-
// Setup context with no cached data
|
|
259
|
-
context = createMockContext({
|
|
260
|
-
cacheData: null,
|
|
261
|
-
upstreamData: { name: 'test-package', version: '1.0.1' }
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
// Execute the handler
|
|
265
|
-
await getPackageMetadata(context);
|
|
266
|
-
|
|
267
|
-
const { data } = context.getResponse();
|
|
268
|
-
|
|
269
|
-
// Verify
|
|
270
|
-
expect(context.db.get).toHaveBeenCalledWith('package:test-package');
|
|
271
|
-
expect(context.fetch).toHaveBeenCalledWith('https://registry.npmjs.org/test-package');
|
|
272
|
-
expect(data).toEqual({ name: 'test-package', version: '1.0.1' });
|
|
273
|
-
expect(context.backgroundTaskFns.length).toBe(0);
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
it('should handle upstream errors gracefully', async () => {
|
|
277
|
-
// Setup context with error
|
|
278
|
-
context = createMockContext({
|
|
279
|
-
cacheData: null,
|
|
280
|
-
upstreamError: new Error('Network error')
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
// Execute the handler
|
|
284
|
-
await getPackageMetadata(context);
|
|
285
|
-
|
|
286
|
-
const { data, status } = context.getResponse();
|
|
287
|
-
|
|
288
|
-
// Verify
|
|
289
|
-
expect(context.db.get).toHaveBeenCalledWith('package:test-package');
|
|
290
|
-
expect(context.fetch).toThrow;
|
|
291
|
-
expect(status).toBe(500);
|
|
292
|
-
expect(data).toEqual({
|
|
293
|
-
error: 'Failed to fetch package: test-package',
|
|
294
|
-
message: 'Network error'
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
});
|
package/test/run-tests.js
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
const { spawn } = require('child_process')
|
|
2
|
-
const { execSync } = require('child_process')
|
|
3
|
-
const { setTimeout } = require('timers')
|
|
4
|
-
|
|
5
|
-
function sleep(ms) {
|
|
6
|
-
return new Promise(resolve => {
|
|
7
|
-
setTimeout(resolve, ms)
|
|
8
|
-
})
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
async function waitForServer(url, maxAttempts = 5) {
|
|
12
|
-
for (let i = 0; i < maxAttempts; i++) {
|
|
13
|
-
try {
|
|
14
|
-
const response = await fetch(url, {
|
|
15
|
-
headers: {
|
|
16
|
-
'User-Agent': 'vlt-serverless-registry-test'
|
|
17
|
-
}
|
|
18
|
-
})
|
|
19
|
-
if (response.ok) {
|
|
20
|
-
console.log('Server is ready!')
|
|
21
|
-
return true
|
|
22
|
-
}
|
|
23
|
-
} catch (err) {
|
|
24
|
-
console.log(`Waiting for server to start... (attempt ${i + 1}/${maxAttempts})`)
|
|
25
|
-
await new Promise(resolve => setTimeout(resolve, 2000)) // Wait 2 seconds between attempts
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
throw new Error('Server failed to start')
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async function runTests() {
|
|
32
|
-
let wrangler = null
|
|
33
|
-
let vitest = null
|
|
34
|
-
let testTimeout = null
|
|
35
|
-
|
|
36
|
-
// Function to clean up processes
|
|
37
|
-
const cleanup = async () => {
|
|
38
|
-
console.log('Cleaning up processes...')
|
|
39
|
-
if (testTimeout) {
|
|
40
|
-
clearTimeout(testTimeout)
|
|
41
|
-
testTimeout = null
|
|
42
|
-
}
|
|
43
|
-
if (wrangler) {
|
|
44
|
-
wrangler.kill()
|
|
45
|
-
wrangler = null
|
|
46
|
-
}
|
|
47
|
-
if (vitest) {
|
|
48
|
-
vitest.kill()
|
|
49
|
-
vitest = null
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Handle process termination
|
|
54
|
-
const handleTermination = async () => {
|
|
55
|
-
console.log('Received termination signal, cleaning up...')
|
|
56
|
-
await cleanup()
|
|
57
|
-
process.exit(0)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
process.on('SIGINT', handleTermination)
|
|
61
|
-
process.on('SIGTERM', handleTermination)
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
// Kill any existing wrangler processes
|
|
65
|
-
try {
|
|
66
|
-
execSync('pkill -f wrangler', { stdio: 'ignore' })
|
|
67
|
-
} catch (err) {
|
|
68
|
-
// Ignore errors if no process was found
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Setup test database
|
|
72
|
-
console.log('Setting up test database...')
|
|
73
|
-
execSync('npm run test:setup', { stdio: 'inherit' })
|
|
74
|
-
|
|
75
|
-
// Start wrangler dev server
|
|
76
|
-
console.log('Starting wrangler dev server...')
|
|
77
|
-
wrangler = spawn('wrangler', ['dev', '--local', '--persist-to', 'local-store', '--experimental-json-config'], {
|
|
78
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
let serverStarted = false
|
|
82
|
-
let serverError = null
|
|
83
|
-
|
|
84
|
-
// Handle server output
|
|
85
|
-
wrangler.stdout.on('data', (data) => {
|
|
86
|
-
const output = data.toString().trim()
|
|
87
|
-
console.log(`[Wrangler] ${output}`)
|
|
88
|
-
|
|
89
|
-
// Check for server ready message
|
|
90
|
-
if (output.includes('Ready in') || output.includes('Listening on')) {
|
|
91
|
-
serverStarted = true
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
wrangler.stderr.on('data', (data) => {
|
|
96
|
-
const output = data.toString().trim()
|
|
97
|
-
console.error(`[Wrangler Error] ${output}`)
|
|
98
|
-
|
|
99
|
-
// Check for common errors
|
|
100
|
-
if (output.includes('Error') || output.includes('Failed')) {
|
|
101
|
-
serverError = output
|
|
102
|
-
}
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
// Wait for server to start
|
|
106
|
-
try {
|
|
107
|
-
await waitForServer('http://localhost:1337')
|
|
108
|
-
} catch (err) {
|
|
109
|
-
console.error('Failed to start server:', err)
|
|
110
|
-
if (serverError) {
|
|
111
|
-
console.error('Server error:', serverError)
|
|
112
|
-
}
|
|
113
|
-
await cleanup()
|
|
114
|
-
process.exit(1)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Run vitest
|
|
118
|
-
console.log('Running tests...')
|
|
119
|
-
vitest = spawn('vitest', ['run', '--no-watch'], {
|
|
120
|
-
stdio: 'inherit',
|
|
121
|
-
env: {
|
|
122
|
-
...process.env,
|
|
123
|
-
NODE_ENV: 'test',
|
|
124
|
-
VITEST_MAX_TIMEOUT: '120000' // 2 minutes
|
|
125
|
-
}
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
// Set a timeout for the entire test process
|
|
129
|
-
testTimeout = setTimeout(() => {
|
|
130
|
-
console.error('Tests timed out after 120 seconds')
|
|
131
|
-
cleanup().then(() => process.exit(1))
|
|
132
|
-
}, 120000)
|
|
133
|
-
|
|
134
|
-
// Wait for vitest to complete
|
|
135
|
-
vitest.on('close', async (code) => {
|
|
136
|
-
console.log('Tests completed')
|
|
137
|
-
await cleanup()
|
|
138
|
-
process.exit(code)
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
} catch (err) {
|
|
142
|
-
console.error('Test runner failed:', err)
|
|
143
|
-
await cleanup()
|
|
144
|
-
process.exit(1)
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
runTests().catch((err) => {
|
|
149
|
-
console.error('Test runner failed:', err)
|
|
150
|
-
process.exit(1)
|
|
151
|
-
})
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import { vi, afterAll } from 'vitest';
|
|
2
|
-
|
|
3
|
-
// Setup mock global objects for testing
|
|
4
|
-
// This runs before the tests to provide necessary mocks
|
|
5
|
-
|
|
6
|
-
// Mock global web standard objects that are available in Cloudflare Workers
|
|
7
|
-
// but not in Node.js test environment
|
|
8
|
-
|
|
9
|
-
// Mock Request class if not available
|
|
10
|
-
if (typeof Request === 'undefined') {
|
|
11
|
-
global.Request = class Request {
|
|
12
|
-
constructor(input, init) {
|
|
13
|
-
this.url = input;
|
|
14
|
-
this.method = (init?.method || 'GET').toUpperCase();
|
|
15
|
-
this.headers = new Headers(init?.headers);
|
|
16
|
-
this.body = init?.body;
|
|
17
|
-
this.json = async () => {
|
|
18
|
-
if (typeof this.body === 'string') {
|
|
19
|
-
return JSON.parse(this.body);
|
|
20
|
-
} else if (this.body && typeof this.body.json === 'function') {
|
|
21
|
-
return this.body.json();
|
|
22
|
-
}
|
|
23
|
-
return undefined;
|
|
24
|
-
};
|
|
25
|
-
this.text = async () => {
|
|
26
|
-
if (typeof this.body === 'string') {
|
|
27
|
-
return this.body;
|
|
28
|
-
} else if (this.body && typeof this.body.text === 'function') {
|
|
29
|
-
return this.body.text();
|
|
30
|
-
}
|
|
31
|
-
return '';
|
|
32
|
-
};
|
|
33
|
-
this.arrayBuffer = async () => new ArrayBuffer(0);
|
|
34
|
-
this.formData = async () => new FormData();
|
|
35
|
-
this.blob = async () => new Blob();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
get path() {
|
|
39
|
-
try {
|
|
40
|
-
return new URL(this.url).pathname;
|
|
41
|
-
} catch (e) {
|
|
42
|
-
return this.url;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Mock Response class if not available
|
|
49
|
-
if (typeof Response === 'undefined') {
|
|
50
|
-
global.Response = class Response {
|
|
51
|
-
constructor(body, init) {
|
|
52
|
-
this.body = body;
|
|
53
|
-
this.status = init?.status || 200;
|
|
54
|
-
this.statusText = init?.statusText || '';
|
|
55
|
-
this.headers = new Headers(init?.headers);
|
|
56
|
-
this.ok = this.status >= 200 && this.status < 300;
|
|
57
|
-
this.type = 'default';
|
|
58
|
-
this.redirected = false;
|
|
59
|
-
this.url = '';
|
|
60
|
-
this._bodyUsed = false;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
get bodyUsed() {
|
|
64
|
-
return this._bodyUsed;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async arrayBuffer() {
|
|
68
|
-
this._bodyUsed = true;
|
|
69
|
-
if (this.body instanceof ArrayBuffer) return this.body;
|
|
70
|
-
if (typeof this.body === 'string') {
|
|
71
|
-
const encoder = new TextEncoder();
|
|
72
|
-
return encoder.encode(this.body).buffer;
|
|
73
|
-
}
|
|
74
|
-
return new ArrayBuffer(0);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async text() {
|
|
78
|
-
this._bodyUsed = true;
|
|
79
|
-
if (typeof this.body === 'string') return this.body;
|
|
80
|
-
if (this.body && typeof this.body.text === 'function') return this.body.text();
|
|
81
|
-
if (this.body && typeof this.body === 'object') return JSON.stringify(this.body);
|
|
82
|
-
return '';
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async json() {
|
|
86
|
-
this._bodyUsed = true;
|
|
87
|
-
if (typeof this.body === 'string') return JSON.parse(this.body);
|
|
88
|
-
if (this.body && typeof this.body.json === 'function') return this.body.json();
|
|
89
|
-
if (this.body && typeof this.body === 'object') return this.body;
|
|
90
|
-
return {};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
clone() {
|
|
94
|
-
return new Response(this.body, {
|
|
95
|
-
status: this.status,
|
|
96
|
-
statusText: this.statusText,
|
|
97
|
-
headers: new Headers(this.headers)
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Mock Headers class if not available
|
|
104
|
-
if (typeof Headers === 'undefined') {
|
|
105
|
-
global.Headers = class Headers {
|
|
106
|
-
constructor(init) {
|
|
107
|
-
this._headers = new Map();
|
|
108
|
-
if (init) {
|
|
109
|
-
if (init instanceof Headers) {
|
|
110
|
-
init.forEach((value, key) => {
|
|
111
|
-
this.append(key, value);
|
|
112
|
-
});
|
|
113
|
-
} else if (Array.isArray(init)) {
|
|
114
|
-
init.forEach(([key, value]) => {
|
|
115
|
-
this.append(key, value);
|
|
116
|
-
});
|
|
117
|
-
} else if (typeof init === 'object') {
|
|
118
|
-
Object.entries(init).forEach(([key, value]) => {
|
|
119
|
-
this.append(key, value);
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
append(name, value) {
|
|
126
|
-
name = name.toLowerCase();
|
|
127
|
-
if (this._headers.has(name)) {
|
|
128
|
-
this._headers.set(name, `${this._headers.get(name)}, ${value}`);
|
|
129
|
-
} else {
|
|
130
|
-
this._headers.set(name, value);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
delete(name) {
|
|
135
|
-
this._headers.delete(name.toLowerCase());
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
get(name) {
|
|
139
|
-
return this._headers.get(name.toLowerCase()) || null;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
has(name) {
|
|
143
|
-
return this._headers.has(name.toLowerCase());
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
set(name, value) {
|
|
147
|
-
this._headers.set(name.toLowerCase(), value);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
forEach(callback) {
|
|
151
|
-
this._headers.forEach((value, key) => {
|
|
152
|
-
callback(value, key, this);
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Mock TransformStream if not available
|
|
159
|
-
if (typeof TransformStream === 'undefined') {
|
|
160
|
-
global.TransformStream = class TransformStream {
|
|
161
|
-
constructor() {
|
|
162
|
-
this.readable = {
|
|
163
|
-
getReader: () => ({
|
|
164
|
-
read: async () => ({ done: true, value: undefined }),
|
|
165
|
-
cancel: async () => {}
|
|
166
|
-
})
|
|
167
|
-
};
|
|
168
|
-
this.writable = {
|
|
169
|
-
getWriter: () => ({
|
|
170
|
-
write: async () => {},
|
|
171
|
-
close: async () => {},
|
|
172
|
-
abort: async () => {}
|
|
173
|
-
})
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Mock console to avoid noise in tests
|
|
180
|
-
const originalConsole = { ...console };
|
|
181
|
-
console.debug = vi.fn();
|
|
182
|
-
console.log = vi.fn();
|
|
183
|
-
console.info = vi.fn();
|
|
184
|
-
// Keep error and warn for debugging
|
|
185
|
-
|
|
186
|
-
// Clean up all mocks after tests are done
|
|
187
|
-
afterAll(() => {
|
|
188
|
-
vi.restoreAllMocks();
|
|
189
|
-
Object.assign(console, originalConsole);
|
|
190
|
-
});
|
package/test/setup.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { beforeAll, afterAll } from 'vitest'
|
|
2
|
-
import { execSync } from 'child_process'
|
|
3
|
-
|
|
4
|
-
// Mock environment variables needed for testing
|
|
5
|
-
beforeAll(() => {
|
|
6
|
-
process.env.DOMAIN = 'http://localhost:0'
|
|
7
|
-
process.env.PROXY = 'true'
|
|
8
|
-
process.env.PROXY_URL = 'https://registry.npmjs.org'
|
|
9
|
-
|
|
10
|
-
// Setup test database
|
|
11
|
-
try {
|
|
12
|
-
execSync('npm run test:setup', { stdio: 'inherit' })
|
|
13
|
-
} catch (err) {
|
|
14
|
-
console.error('Failed to setup test database:', err)
|
|
15
|
-
process.exit(1)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Mock the Cloudflare Workers environment
|
|
19
|
-
globalThis.env = {
|
|
20
|
-
DB: {
|
|
21
|
-
prepare: async (query) => ({
|
|
22
|
-
bind: async (...args) => ({
|
|
23
|
-
run: async () => {
|
|
24
|
-
// For token validation
|
|
25
|
-
if (query.includes('SELECT * FROM tokens')) {
|
|
26
|
-
return {
|
|
27
|
-
results: [{
|
|
28
|
-
token: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
|
29
|
-
uuid: 'admin',
|
|
30
|
-
scope: JSON.stringify([
|
|
31
|
-
{
|
|
32
|
-
values: ['*'],
|
|
33
|
-
types: {
|
|
34
|
-
pkg: { read: true, write: true },
|
|
35
|
-
user: { read: true, write: true }
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
])
|
|
39
|
-
}]
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return { results: [] }
|
|
43
|
-
}
|
|
44
|
-
})
|
|
45
|
-
})
|
|
46
|
-
},
|
|
47
|
-
BUCKET: {
|
|
48
|
-
get: async () => null,
|
|
49
|
-
put: async () => {}
|
|
50
|
-
},
|
|
51
|
-
executionCtx: {
|
|
52
|
-
waitUntil: (promise) => promise
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
afterAll(() => {
|
|
58
|
-
// Cleanup test database
|
|
59
|
-
try {
|
|
60
|
-
execSync('npm run test:cleanup', { stdio: 'inherit' })
|
|
61
|
-
} catch (err) {
|
|
62
|
-
console.error('Failed to cleanup test database:', err)
|
|
63
|
-
}
|
|
64
|
-
})
|