@vltpkg/vsr 0.2.0 → 0.2.2
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/bin/vsr.ts +1 -1
- package/package.json +7 -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,273 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test suite for stale-while-revalidate cache strategy
|
|
3
|
-
*/
|
|
4
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
5
|
-
import { getCachedPackageWithRefresh, getCachedVersionWithRefresh } from '../src/utils/cache.ts'
|
|
6
|
-
|
|
7
|
-
describe('Stale-while-revalidate cache strategy', () => {
|
|
8
|
-
let mockContext
|
|
9
|
-
let mockFetchUpstream
|
|
10
|
-
let mockQueue
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
// Mock database operations
|
|
14
|
-
const mockDb = {
|
|
15
|
-
getCachedPackage: vi.fn(),
|
|
16
|
-
upsertCachedPackage: vi.fn(),
|
|
17
|
-
getCachedVersion: vi.fn(),
|
|
18
|
-
upsertCachedVersion: vi.fn()
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Mock Cloudflare Queue
|
|
22
|
-
mockQueue = {
|
|
23
|
-
send: vi.fn().mockResolvedValue(true)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Mock context
|
|
27
|
-
mockContext = {
|
|
28
|
-
db: mockDb,
|
|
29
|
-
env: {
|
|
30
|
-
CACHE_REFRESH_QUEUE: mockQueue
|
|
31
|
-
},
|
|
32
|
-
waitUntil: vi.fn()
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Mock upstream fetch function
|
|
36
|
-
mockFetchUpstream = vi.fn().mockResolvedValue({
|
|
37
|
-
'dist-tags': { latest: '1.0.0' },
|
|
38
|
-
versions: { '1.0.0': { name: 'test-package', version: '1.0.0' } },
|
|
39
|
-
time: { modified: new Date().toISOString() }
|
|
40
|
-
})
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
describe('getCachedPackageWithRefresh', () => {
|
|
44
|
-
it('should return fresh cache data immediately without queuing refresh', async () => {
|
|
45
|
-
// Mock fresh cache data (within TTL)
|
|
46
|
-
const freshCacheTime = new Date(Date.now() - 2 * 60 * 1000) // 2 minutes ago
|
|
47
|
-
mockContext.db.getCachedPackage.mockResolvedValue({
|
|
48
|
-
name: 'test-package',
|
|
49
|
-
tags: { latest: '1.0.0' },
|
|
50
|
-
origin: 'upstream',
|
|
51
|
-
cachedAt: freshCacheTime.toISOString()
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
const result = await getCachedPackageWithRefresh(
|
|
55
|
-
mockContext,
|
|
56
|
-
'test-package',
|
|
57
|
-
mockFetchUpstream,
|
|
58
|
-
{ packumentTtlMinutes: 5 }
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
expect(result.fromCache).toBe(true)
|
|
62
|
-
expect(result.stale).toBe(false)
|
|
63
|
-
expect(mockQueue.send).not.toHaveBeenCalled()
|
|
64
|
-
expect(mockFetchUpstream).not.toHaveBeenCalled()
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('should return stale cache data and queue background refresh', async () => {
|
|
68
|
-
// Mock stale cache data (beyond TTL but within stale window)
|
|
69
|
-
const staleCacheTime = new Date(Date.now() - 10 * 60 * 1000) // 10 minutes ago
|
|
70
|
-
mockContext.db.getCachedPackage.mockResolvedValue({
|
|
71
|
-
name: 'test-package',
|
|
72
|
-
tags: { latest: '1.0.0' },
|
|
73
|
-
origin: 'upstream',
|
|
74
|
-
cachedAt: staleCacheTime.toISOString()
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
const result = await getCachedPackageWithRefresh(
|
|
78
|
-
mockContext,
|
|
79
|
-
'test-package',
|
|
80
|
-
mockFetchUpstream,
|
|
81
|
-
{
|
|
82
|
-
packumentTtlMinutes: 5,
|
|
83
|
-
staleWhileRevalidateMinutes: 60
|
|
84
|
-
}
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
expect(result.fromCache).toBe(true)
|
|
88
|
-
expect(result.stale).toBe(true)
|
|
89
|
-
expect(mockQueue.send).toHaveBeenCalledWith({
|
|
90
|
-
type: 'package_refresh',
|
|
91
|
-
packageName: 'test-package',
|
|
92
|
-
upstream: 'npm',
|
|
93
|
-
timestamp: expect.any(Number),
|
|
94
|
-
options: {
|
|
95
|
-
packumentTtlMinutes: 5,
|
|
96
|
-
upstream: 'npm'
|
|
97
|
-
}
|
|
98
|
-
})
|
|
99
|
-
expect(mockFetchUpstream).not.toHaveBeenCalled()
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
it('should fetch upstream synchronously when no cache exists', async () => {
|
|
103
|
-
// Mock no cache data
|
|
104
|
-
mockContext.db.getCachedPackage.mockResolvedValue(null)
|
|
105
|
-
|
|
106
|
-
const result = await getCachedPackageWithRefresh(
|
|
107
|
-
mockContext,
|
|
108
|
-
'test-package',
|
|
109
|
-
mockFetchUpstream,
|
|
110
|
-
{ packumentTtlMinutes: 5 }
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
expect(result.fromCache).toBe(false)
|
|
114
|
-
expect(result.stale).toBe(false)
|
|
115
|
-
expect(mockFetchUpstream).toHaveBeenCalled()
|
|
116
|
-
expect(mockContext.waitUntil).toHaveBeenCalled()
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
it('should fallback to waitUntil when queue is not available', async () => {
|
|
120
|
-
// Remove queue from context
|
|
121
|
-
mockContext.env.CACHE_REFRESH_QUEUE = null
|
|
122
|
-
|
|
123
|
-
// Mock stale cache data
|
|
124
|
-
const staleCacheTime = new Date(Date.now() - 10 * 60 * 1000)
|
|
125
|
-
mockContext.db.getCachedPackage.mockResolvedValue({
|
|
126
|
-
name: 'test-package',
|
|
127
|
-
tags: { latest: '1.0.0' },
|
|
128
|
-
origin: 'upstream',
|
|
129
|
-
cachedAt: staleCacheTime.toISOString()
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
const result = await getCachedPackageWithRefresh(
|
|
133
|
-
mockContext,
|
|
134
|
-
'test-package',
|
|
135
|
-
mockFetchUpstream,
|
|
136
|
-
{
|
|
137
|
-
packumentTtlMinutes: 5,
|
|
138
|
-
staleWhileRevalidateMinutes: 60
|
|
139
|
-
}
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
expect(result.fromCache).toBe(true)
|
|
143
|
-
expect(result.stale).toBe(true)
|
|
144
|
-
expect(mockContext.waitUntil).toHaveBeenCalled()
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
it('should handle very stale cache data (beyond stale window)', async () => {
|
|
148
|
-
// Mock very stale cache data (beyond stale window)
|
|
149
|
-
const veryOldCacheTime = new Date(Date.now() - 2 * 60 * 60 * 1000) // 2 hours ago
|
|
150
|
-
mockContext.db.getCachedPackage.mockResolvedValue({
|
|
151
|
-
name: 'test-package',
|
|
152
|
-
tags: { latest: '1.0.0' },
|
|
153
|
-
origin: 'upstream',
|
|
154
|
-
cachedAt: veryOldCacheTime.toISOString()
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
const result = await getCachedPackageWithRefresh(
|
|
158
|
-
mockContext,
|
|
159
|
-
'test-package',
|
|
160
|
-
mockFetchUpstream,
|
|
161
|
-
{
|
|
162
|
-
packumentTtlMinutes: 5,
|
|
163
|
-
staleWhileRevalidateMinutes: 60 // 1 hour stale window
|
|
164
|
-
}
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
// Should fetch fresh data since cache is beyond stale window
|
|
168
|
-
expect(result.fromCache).toBe(false)
|
|
169
|
-
expect(mockFetchUpstream).toHaveBeenCalled()
|
|
170
|
-
})
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
describe('getCachedVersionWithRefresh', () => {
|
|
174
|
-
it('should use longer TTL and stale windows for manifests', async () => {
|
|
175
|
-
// Mock stale manifest data (1 year + 1 day old)
|
|
176
|
-
const staleManifestTime = new Date(Date.now() - (365 + 1) * 24 * 60 * 60 * 1000)
|
|
177
|
-
mockContext.db.getCachedVersion.mockResolvedValue({
|
|
178
|
-
spec: 'test-package@1.0.0',
|
|
179
|
-
manifest: { name: 'test-package', version: '1.0.0' },
|
|
180
|
-
cachedAt: staleManifestTime.toISOString()
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
const mockFetchManifest = vi.fn().mockResolvedValue({
|
|
184
|
-
manifest: { name: 'test-package', version: '1.0.0' },
|
|
185
|
-
publishedAt: new Date().toISOString()
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
const result = await getCachedVersionWithRefresh(
|
|
189
|
-
mockContext,
|
|
190
|
-
'test-package@1.0.0',
|
|
191
|
-
mockFetchManifest,
|
|
192
|
-
{
|
|
193
|
-
manifestTtlMinutes: 525600, // 1 year
|
|
194
|
-
staleWhileRevalidateMinutes: 1051200 // 2 years
|
|
195
|
-
}
|
|
196
|
-
)
|
|
197
|
-
|
|
198
|
-
expect(result.fromCache).toBe(true)
|
|
199
|
-
expect(result.stale).toBe(true)
|
|
200
|
-
expect(mockQueue.send).toHaveBeenCalledWith({
|
|
201
|
-
type: 'version_refresh',
|
|
202
|
-
spec: 'test-package@1.0.0',
|
|
203
|
-
upstream: 'npm',
|
|
204
|
-
timestamp: expect.any(Number),
|
|
205
|
-
options: {
|
|
206
|
-
manifestTtlMinutes: 525600,
|
|
207
|
-
upstream: 'npm'
|
|
208
|
-
}
|
|
209
|
-
})
|
|
210
|
-
})
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
describe('TTL and stale window calculations', () => {
|
|
214
|
-
it('should correctly calculate cache validity based on age', async () => {
|
|
215
|
-
const testCases = [
|
|
216
|
-
{
|
|
217
|
-
ageMinutes: 3,
|
|
218
|
-
ttlMinutes: 5,
|
|
219
|
-
staleMinutes: 60,
|
|
220
|
-
expectedValid: true,
|
|
221
|
-
expectedStale: false,
|
|
222
|
-
description: 'fresh cache'
|
|
223
|
-
},
|
|
224
|
-
{
|
|
225
|
-
ageMinutes: 10,
|
|
226
|
-
ttlMinutes: 5,
|
|
227
|
-
staleMinutes: 60,
|
|
228
|
-
expectedValid: false,
|
|
229
|
-
expectedStale: true,
|
|
230
|
-
description: 'stale but within stale window'
|
|
231
|
-
},
|
|
232
|
-
{
|
|
233
|
-
ageMinutes: 70,
|
|
234
|
-
ttlMinutes: 5,
|
|
235
|
-
staleMinutes: 60,
|
|
236
|
-
expectedValid: false,
|
|
237
|
-
expectedStale: false,
|
|
238
|
-
description: 'beyond stale window'
|
|
239
|
-
}
|
|
240
|
-
]
|
|
241
|
-
|
|
242
|
-
for (const testCase of testCases) {
|
|
243
|
-
const cacheTime = new Date(Date.now() - testCase.ageMinutes * 60 * 1000)
|
|
244
|
-
mockContext.db.getCachedPackage.mockResolvedValue({
|
|
245
|
-
name: 'test-package',
|
|
246
|
-
tags: { latest: '1.0.0' },
|
|
247
|
-
origin: 'upstream',
|
|
248
|
-
cachedAt: cacheTime.toISOString()
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
const result = await getCachedPackageWithRefresh(
|
|
252
|
-
mockContext,
|
|
253
|
-
'test-package',
|
|
254
|
-
mockFetchUpstream,
|
|
255
|
-
{
|
|
256
|
-
packumentTtlMinutes: testCase.ttlMinutes,
|
|
257
|
-
staleWhileRevalidateMinutes: testCase.staleMinutes
|
|
258
|
-
}
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
if (testCase.expectedValid) {
|
|
262
|
-
expect(result.fromCache, testCase.description).toBe(true)
|
|
263
|
-
expect(result.stale, testCase.description).toBe(false)
|
|
264
|
-
} else if (testCase.expectedStale) {
|
|
265
|
-
expect(result.fromCache, testCase.description).toBe(true)
|
|
266
|
-
expect(result.stale, testCase.description).toBe(true)
|
|
267
|
-
} else {
|
|
268
|
-
expect(result.fromCache, testCase.description).toBe(false)
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
})
|
|
272
|
-
})
|
|
273
|
-
})
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import app from '../src/index.ts'
|
|
3
|
-
|
|
4
|
-
describe('Static Asset Handling', () => {
|
|
5
|
-
const mockEnv = {
|
|
6
|
-
DB: {
|
|
7
|
-
prepare: () => ({
|
|
8
|
-
bind: () => ({
|
|
9
|
-
all: () => [],
|
|
10
|
-
get: () => null,
|
|
11
|
-
run: () => ({ success: true })
|
|
12
|
-
})
|
|
13
|
-
})
|
|
14
|
-
},
|
|
15
|
-
BUCKET: {
|
|
16
|
-
get: () => null,
|
|
17
|
-
put: () => Promise.resolve()
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
it('should handle favicon.ico requests gracefully', async () => {
|
|
22
|
-
const response = await app.request('/favicon.ico', {}, mockEnv)
|
|
23
|
-
expect(response.status).toBe(404)
|
|
24
|
-
|
|
25
|
-
const data = await response.json()
|
|
26
|
-
expect(data.error).toBe('Not found')
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('should handle robots.txt requests gracefully', async () => {
|
|
30
|
-
const response = await app.request('/robots.txt', {}, mockEnv)
|
|
31
|
-
expect(response.status).toBe(404)
|
|
32
|
-
|
|
33
|
-
const data = await response.json()
|
|
34
|
-
expect(data.error).toBe('Not found')
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it('should allow asset paths to pass through (handled by Wrangler in production)', async () => {
|
|
38
|
-
// Wrangler serves files from src/assets/ at root level, now under /public/ prefix
|
|
39
|
-
// In our test environment, these reach our package handler since Wrangler isn't serving them
|
|
40
|
-
// In production, Wrangler serves these before they reach Hono
|
|
41
|
-
const response = await app.request('/public/styles/styles.css', {}, mockEnv)
|
|
42
|
-
|
|
43
|
-
// In test environment, should return 404 from our handler (not blocked by static patterns)
|
|
44
|
-
// This proves our static asset detection is not interfering with asset paths
|
|
45
|
-
expect(response.status).toBe(404)
|
|
46
|
-
|
|
47
|
-
const data = await response.json()
|
|
48
|
-
expect(data.error).toBe('Not found')
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it('should allow image paths to pass through (handled by Wrangler in production)', async () => {
|
|
52
|
-
// Test another asset path that Wrangler would serve
|
|
53
|
-
const response = await app.request('/public/images/logo.png', {}, mockEnv)
|
|
54
|
-
|
|
55
|
-
// Should not be blocked by static asset pattern detection
|
|
56
|
-
expect(response.status).toBe(404)
|
|
57
|
-
|
|
58
|
-
const data = await response.json()
|
|
59
|
-
expect(data.error).toBe('Not found')
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('should reject static assets that slip through to package handler', async () => {
|
|
63
|
-
// Test a file extension that might slip through to the package handler
|
|
64
|
-
const response = await app.request('/some-file.png', {}, mockEnv)
|
|
65
|
-
expect(response.status).toBe(404)
|
|
66
|
-
|
|
67
|
-
const data = await response.json()
|
|
68
|
-
expect(data.error).toBe('Not found')
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
it('should reject manifest.json requests gracefully', async () => {
|
|
72
|
-
const response = await app.request('/manifest.json', {}, mockEnv)
|
|
73
|
-
expect(response.status).toBe(404)
|
|
74
|
-
|
|
75
|
-
const data = await response.json()
|
|
76
|
-
expect(data.error).toBe('Not found')
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
it('should still handle legitimate package requests', async () => {
|
|
80
|
-
// Make sure we didn't break legitimate package requests
|
|
81
|
-
const response = await app.request('/lodash', {}, mockEnv)
|
|
82
|
-
expect(response.status).toBe(302) // Should redirect to default upstream
|
|
83
|
-
expect(response.headers.get('location')).toBe('/local/lodash')
|
|
84
|
-
})
|
|
85
|
-
})
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
|
2
|
-
import app from '../src/index.ts'
|
|
3
|
-
|
|
4
|
-
describe('Upstream Routing', () => {
|
|
5
|
-
const mockEnv = {
|
|
6
|
-
DB: {
|
|
7
|
-
prepare: () => ({
|
|
8
|
-
bind: () => ({
|
|
9
|
-
all: () => [],
|
|
10
|
-
get: () => null,
|
|
11
|
-
run: () => ({ success: true })
|
|
12
|
-
})
|
|
13
|
-
})
|
|
14
|
-
},
|
|
15
|
-
BUCKET: {
|
|
16
|
-
get: () => null,
|
|
17
|
-
put: () => Promise.resolve()
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
// Reset any mocks
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
afterEach(() => {
|
|
26
|
-
// Clean up
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('should reject reserved upstream names for upstream routes only', async () => {
|
|
30
|
-
// Internal routes should work fine
|
|
31
|
-
const response1 = await app.request('/-/ping', {}, mockEnv)
|
|
32
|
-
expect(response1.status).toBe(200)
|
|
33
|
-
|
|
34
|
-
// Hash-based routes should work even though * is reserved
|
|
35
|
-
const response2 = await app.request('/*/abc123def456', {}, mockEnv)
|
|
36
|
-
expect(response2.status).toBe(501) // Not implemented, but not blocked by validation
|
|
37
|
-
const data2 = await response2.json()
|
|
38
|
-
expect(data2.error).toContain('Hash-based package lookup not yet implemented')
|
|
39
|
-
|
|
40
|
-
// Reserved upstream names should be rejected for upstream routes
|
|
41
|
-
const response3 = await app.request('/docs/some-package', {}, mockEnv)
|
|
42
|
-
expect(response3.status).toBe(400)
|
|
43
|
-
const data3 = await response3.json()
|
|
44
|
-
expect(data3.error).toContain('reserved')
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it('should redirect root-level packages to default upstream', async () => {
|
|
48
|
-
const response = await app.request('/lodash', {}, mockEnv)
|
|
49
|
-
expect(response.status).toBe(302)
|
|
50
|
-
expect(response.headers.get('location')).toBe('/local/lodash')
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('should redirect scoped packages to default upstream', async () => {
|
|
54
|
-
const response = await app.request('/@babel/core', {}, mockEnv)
|
|
55
|
-
expect(response.status).toBe(302)
|
|
56
|
-
expect(response.headers.get('location')).toBe('/local/@babel/core')
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('should handle upstream package requests', async () => {
|
|
60
|
-
const response = await app.request('/npm/lodash', {}, mockEnv)
|
|
61
|
-
// The npm upstream is configured and should work correctly
|
|
62
|
-
// It might succeed (200) if network is available, or fail with various error codes
|
|
63
|
-
expect([200, 400, 404, 501, 502, 503]).toContain(response.status)
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('should handle hash-based package requests', async () => {
|
|
67
|
-
const response = await app.request('/*/abc123def456', {}, mockEnv)
|
|
68
|
-
expect(response.status).toBe(501)
|
|
69
|
-
const data = await response.json()
|
|
70
|
-
expect(data.error).toContain('Hash-based package lookup not yet implemented')
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
it('should handle hash-based tarball requests', async () => {
|
|
74
|
-
const response = await app.request('/*/abc123def456/-/package-1.0.0.tgz', {}, mockEnv)
|
|
75
|
-
expect(response.status).toBe(501)
|
|
76
|
-
const data = await response.json()
|
|
77
|
-
expect(data.error).toContain('Hash-based tarball lookup not yet implemented')
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
it('should preserve existing internal routes', async () => {
|
|
81
|
-
const response = await app.request('/-/ping', {}, mockEnv)
|
|
82
|
-
expect(response.status).toBe(200)
|
|
83
|
-
const data = await response.json()
|
|
84
|
-
expect(data.ok).toBe(true)
|
|
85
|
-
})
|
|
86
|
-
})
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Creates a mock Hono context for testing
|
|
3
|
-
*
|
|
4
|
-
* @param {Object} options - Options for creating the context
|
|
5
|
-
* @param {Object} options.req - Request properties
|
|
6
|
-
* @param {Object} options.db - Mock database client
|
|
7
|
-
* @param {string} [options.pkg] - Optional package name (for packageSpec mock)
|
|
8
|
-
* @param {string} [options.username] - Optional username (for param mock)
|
|
9
|
-
* @returns {Object} Mock Hono context
|
|
10
|
-
*/
|
|
11
|
-
export function createContext(options = {}) {
|
|
12
|
-
const { req = {}, db = {}, pkg = null, username = null } = options;
|
|
13
|
-
|
|
14
|
-
// Create a mock response
|
|
15
|
-
let statusCode = 200;
|
|
16
|
-
let responseBody = null;
|
|
17
|
-
let responseHeaders = new Map();
|
|
18
|
-
|
|
19
|
-
// Create the context object
|
|
20
|
-
const context = {
|
|
21
|
-
// Request properties
|
|
22
|
-
req: {
|
|
23
|
-
method: req.method || 'GET',
|
|
24
|
-
header: (name) => req.headers?.[name] || null,
|
|
25
|
-
param: (name) => {
|
|
26
|
-
if (name === 'username' && username) {
|
|
27
|
-
return username;
|
|
28
|
-
}
|
|
29
|
-
if (req.param instanceof Map) {
|
|
30
|
-
return req.param.get(name);
|
|
31
|
-
}
|
|
32
|
-
if (typeof req.param === 'function') {
|
|
33
|
-
return req.param(name);
|
|
34
|
-
}
|
|
35
|
-
return req.param?.[name] || null;
|
|
36
|
-
},
|
|
37
|
-
query: (name) => {
|
|
38
|
-
if (req.query instanceof Map) {
|
|
39
|
-
return req.query.get(name);
|
|
40
|
-
}
|
|
41
|
-
if (typeof req.query === 'function') {
|
|
42
|
-
return req.query(name);
|
|
43
|
-
}
|
|
44
|
-
return req.query?.[name] || null;
|
|
45
|
-
},
|
|
46
|
-
json: req.json || (async () => (req.body || {})),
|
|
47
|
-
body: req.body || {},
|
|
48
|
-
...req
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
// Database client
|
|
52
|
-
db,
|
|
53
|
-
|
|
54
|
-
// Mock packageSpec if pkg is provided
|
|
55
|
-
pkg,
|
|
56
|
-
|
|
57
|
-
// Mock username for parameter access
|
|
58
|
-
username,
|
|
59
|
-
|
|
60
|
-
// Response methods
|
|
61
|
-
status: (code) => {
|
|
62
|
-
statusCode = code;
|
|
63
|
-
return context;
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
json: (body, status) => {
|
|
67
|
-
responseBody = body;
|
|
68
|
-
if (status) statusCode = status;
|
|
69
|
-
|
|
70
|
-
return {
|
|
71
|
-
status: statusCode,
|
|
72
|
-
headers: responseHeaders,
|
|
73
|
-
json: async () => responseBody
|
|
74
|
-
};
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
header: (name, value) => {
|
|
78
|
-
responseHeaders.set(name, value);
|
|
79
|
-
return context;
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
return context;
|
|
84
|
-
}
|