@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,141 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* This test demonstrates the waitUntil behavior in Cloudflare Workers
|
|
5
|
-
* in the most simplified form possible, avoiding the issues with immediate execution.
|
|
6
|
-
*/
|
|
7
|
-
describe('Cloudflare waitUntil Behavior', () => {
|
|
8
|
-
// Simple test that shows the waitUntil pattern
|
|
9
|
-
it('should demonstrate the waitUntil pattern correctly', async () => {
|
|
10
|
-
let mainComplete = false;
|
|
11
|
-
let bgTaskExecuted = false;
|
|
12
|
-
|
|
13
|
-
// This mimics how Cloudflare Workers actually handles waitUntil
|
|
14
|
-
// It stores the promises but doesn't block on them
|
|
15
|
-
function simulateCloudflareWorker(handler) {
|
|
16
|
-
const bgTasks = [];
|
|
17
|
-
const executionCtx = {
|
|
18
|
-
waitUntil: (promise) => {
|
|
19
|
-
bgTasks.push(promise);
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
return async () => {
|
|
24
|
-
// Run the main handler first to completion
|
|
25
|
-
const response = await handler(executionCtx);
|
|
26
|
-
|
|
27
|
-
// Mark main function as complete
|
|
28
|
-
mainComplete = true;
|
|
29
|
-
|
|
30
|
-
// Then run background tasks
|
|
31
|
-
for (const task of bgTasks) {
|
|
32
|
-
await task;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return response;
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Sample worker code using waitUntil
|
|
40
|
-
async function workerHandler(ctx) {
|
|
41
|
-
// Return response immediately
|
|
42
|
-
const response = { message: "Fast response!" };
|
|
43
|
-
|
|
44
|
-
// Queue background work
|
|
45
|
-
ctx.waitUntil((async () => {
|
|
46
|
-
// Simulate slow background work
|
|
47
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
48
|
-
bgTaskExecuted = true;
|
|
49
|
-
})());
|
|
50
|
-
|
|
51
|
-
return response;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Wrap the handler to simulate worker runtime
|
|
55
|
-
const simulatedWorker = simulateCloudflareWorker(workerHandler);
|
|
56
|
-
|
|
57
|
-
// Execute the simulated worker
|
|
58
|
-
const response = await simulatedWorker();
|
|
59
|
-
|
|
60
|
-
// Verify the response returned before background work completed
|
|
61
|
-
expect(response).toEqual({ message: "Fast response!" });
|
|
62
|
-
|
|
63
|
-
// Verify main function completed before background task
|
|
64
|
-
expect(mainComplete).toBe(true);
|
|
65
|
-
|
|
66
|
-
// Verify background task eventually completed
|
|
67
|
-
expect(bgTaskExecuted).toBe(true);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
// Test with stale cache pattern
|
|
71
|
-
it('should demonstrate the stale-while-revalidate pattern', async () => {
|
|
72
|
-
const cache = {
|
|
73
|
-
data: { key: 'test', value: 'stale-data', timestamp: Date.now() - 3600000 }, // 1 hour old
|
|
74
|
-
get: vi.fn(function(key) { return this.data; }),
|
|
75
|
-
set: vi.fn(function(key, value) { this.data = value; })
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const fetchMock = vi.fn().mockResolvedValue('fresh-data');
|
|
79
|
-
|
|
80
|
-
let cacheRefreshed = false;
|
|
81
|
-
|
|
82
|
-
// Simplified runtime simulation
|
|
83
|
-
function simulateCloudflareWorker(handler) {
|
|
84
|
-
const bgTasks = [];
|
|
85
|
-
const executionCtx = {
|
|
86
|
-
waitUntil: (promise) => {
|
|
87
|
-
bgTasks.push(promise);
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
return async () => {
|
|
92
|
-
// Run the main handler to completion first
|
|
93
|
-
const response = await handler(executionCtx);
|
|
94
|
-
|
|
95
|
-
// Then execute background tasks
|
|
96
|
-
for (const task of bgTasks) {
|
|
97
|
-
await task;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return response;
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Worker handler using stale-while-revalidate pattern
|
|
105
|
-
async function cacheHandler(ctx) {
|
|
106
|
-
// Get from cache
|
|
107
|
-
const cached = cache.get('test-key');
|
|
108
|
-
|
|
109
|
-
// Check if stale
|
|
110
|
-
const isStale = Date.now() - cached.timestamp > 5 * 60 * 1000; // 5 minutes
|
|
111
|
-
|
|
112
|
-
if (isStale) {
|
|
113
|
-
// Background refresh
|
|
114
|
-
ctx.waitUntil((async () => {
|
|
115
|
-
const fresh = await fetchMock('test-key');
|
|
116
|
-
cache.set('test-key', {
|
|
117
|
-
key: 'test',
|
|
118
|
-
value: fresh,
|
|
119
|
-
timestamp: Date.now()
|
|
120
|
-
});
|
|
121
|
-
cacheRefreshed = true;
|
|
122
|
-
})());
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Return cached data immediately
|
|
126
|
-
return cached.value;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Wrap the handler
|
|
130
|
-
const simulatedWorker = simulateCloudflareWorker(cacheHandler);
|
|
131
|
-
|
|
132
|
-
// Execute - we should get stale data first, then background refresh
|
|
133
|
-
const result = await simulatedWorker();
|
|
134
|
-
|
|
135
|
-
// Verify
|
|
136
|
-
expect(result).toBe('stale-data'); // Got stale data immediately
|
|
137
|
-
expect(fetchMock).toHaveBeenCalledWith('test-key'); // Fetch happened in background
|
|
138
|
-
expect(cacheRefreshed).toBe(true); // Cache was eventually refreshed
|
|
139
|
-
expect(cache.set).toHaveBeenCalled(); // Cache was updated with fresh data
|
|
140
|
-
});
|
|
141
|
-
});
|
package/test/db.test.js
DELETED
|
@@ -1,447 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
|
|
2
|
-
import { drizzle } from 'drizzle-orm/d1';
|
|
3
|
-
import { createDatabaseOperations } from '../src/db/client';
|
|
4
|
-
|
|
5
|
-
describe('Database Operations', () => {
|
|
6
|
-
let dbOps;
|
|
7
|
-
let mockData;
|
|
8
|
-
|
|
9
|
-
beforeAll(() => {
|
|
10
|
-
// Create a mock D1 database that implements the required interface
|
|
11
|
-
const testDb = {
|
|
12
|
-
prepare: () => ({
|
|
13
|
-
bind: () => ({
|
|
14
|
-
first: () => Promise.resolve(null),
|
|
15
|
-
all: () => Promise.resolve([]),
|
|
16
|
-
raw: () => Promise.resolve([]),
|
|
17
|
-
run: () => Promise.resolve({ success: true }),
|
|
18
|
-
values: () => Promise.resolve([]),
|
|
19
|
-
}),
|
|
20
|
-
}),
|
|
21
|
-
batch: (statements) => Promise.resolve(statements.map(() => ({ success: true }))),
|
|
22
|
-
exec: () => Promise.resolve({ success: true }),
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// Create database operations with the mock
|
|
26
|
-
dbOps = createDatabaseOperations(drizzle(testDb));
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
beforeEach(() => {
|
|
30
|
-
// Reset mock data before each test
|
|
31
|
-
mockData = {
|
|
32
|
-
packages: new Map(),
|
|
33
|
-
tokens: new Map(),
|
|
34
|
-
versions: new Map(),
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// Override database operations with mock implementations
|
|
38
|
-
dbOps.getPackage = async (name) => {
|
|
39
|
-
return mockData.packages.get(name) || null;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
dbOps.upsertPackage = async (name, tags) => {
|
|
43
|
-
const pkg = { name, tags };
|
|
44
|
-
mockData.packages.set(name, pkg);
|
|
45
|
-
return pkg;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
dbOps.getToken = async (token) => {
|
|
49
|
-
return mockData.tokens.get(token) || null;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
// Implement validation for upsertToken
|
|
53
|
-
dbOps.upsertToken = async (token, uuid, scope, authToken) => {
|
|
54
|
-
// Validate UUID doesn't start with special characters
|
|
55
|
-
const specialChars = ['~', '!', '*', '^', '&'];
|
|
56
|
-
if (uuid && specialChars.some(char => uuid.startsWith(char))) {
|
|
57
|
-
throw new Error('Invalid uuid - uuids can not start with special characters (ex. - ~ ! * ^ &)');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// If authToken is provided, validate access permissions
|
|
61
|
-
if (authToken) {
|
|
62
|
-
const authUser = mockData.tokens.get(authToken);
|
|
63
|
-
if (!authUser) {
|
|
64
|
-
throw new Error('Unauthorized');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Allow users to modify their own tokens
|
|
68
|
-
if (authUser.uuid !== uuid) {
|
|
69
|
-
// Check if has global user permissions
|
|
70
|
-
let hasPermission = false;
|
|
71
|
-
if (Array.isArray(authUser.scope)) {
|
|
72
|
-
for (const s of authUser.scope) {
|
|
73
|
-
if (s.types?.user &&
|
|
74
|
-
s.values.includes('*') &&
|
|
75
|
-
s.types.user.write) {
|
|
76
|
-
hasPermission = true;
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (!hasPermission) {
|
|
83
|
-
throw new Error('Unauthorized');
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const tokenData = { token, uuid, scope };
|
|
89
|
-
mockData.tokens.set(token, tokenData);
|
|
90
|
-
return tokenData;
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
dbOps.deleteToken = async (token, authToken) => {
|
|
94
|
-
if (authToken) {
|
|
95
|
-
const tokenData = mockData.tokens.get(token);
|
|
96
|
-
const authUser = mockData.tokens.get(authToken);
|
|
97
|
-
|
|
98
|
-
if (!authUser) {
|
|
99
|
-
throw new Error('Unauthorized');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (tokenData && authUser.uuid !== tokenData.uuid) {
|
|
103
|
-
// Check if has global user permissions
|
|
104
|
-
let hasPermission = false;
|
|
105
|
-
if (Array.isArray(authUser.scope)) {
|
|
106
|
-
for (const s of authUser.scope) {
|
|
107
|
-
if (s.types?.user &&
|
|
108
|
-
s.values.includes('*') &&
|
|
109
|
-
s.types.user.write) {
|
|
110
|
-
hasPermission = true;
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (!hasPermission) {
|
|
117
|
-
throw new Error('Unauthorized');
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
mockData.tokens.delete(token);
|
|
123
|
-
return true;
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
dbOps.getVersion = async (spec) => {
|
|
127
|
-
return mockData.versions.get(spec) || null;
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
dbOps.upsertVersion = async (spec, manifest, published_at) => {
|
|
131
|
-
const version = { spec, manifest, published_at };
|
|
132
|
-
mockData.versions.set(spec, version);
|
|
133
|
-
return version;
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
dbOps.searchPackages = async (query, scope) => {
|
|
137
|
-
const results = Array.from(mockData.packages.values()).filter(pkg => {
|
|
138
|
-
if (scope) {
|
|
139
|
-
return pkg.name.startsWith(scope + '/') && pkg.name.toLowerCase().includes(query.toLowerCase());
|
|
140
|
-
}
|
|
141
|
-
return pkg.name.toLowerCase().includes(query.toLowerCase());
|
|
142
|
-
});
|
|
143
|
-
return results;
|
|
144
|
-
};
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
describe('Packages', () => {
|
|
148
|
-
const testPackage = {
|
|
149
|
-
name: 'test-package',
|
|
150
|
-
tags: { latest: '1.0.0' },
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
it('should insert and retrieve a package', async () => {
|
|
154
|
-
await dbOps.upsertPackage(testPackage.name, testPackage.tags);
|
|
155
|
-
const retrieved = await dbOps.getPackage(testPackage.name);
|
|
156
|
-
expect(retrieved).toEqual({
|
|
157
|
-
name: testPackage.name,
|
|
158
|
-
tags: testPackage.tags,
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('should update an existing package', async () => {
|
|
163
|
-
const updatedTags = { latest: '2.0.0' };
|
|
164
|
-
await dbOps.upsertPackage(testPackage.name, updatedTags);
|
|
165
|
-
const retrieved = await dbOps.getPackage(testPackage.name);
|
|
166
|
-
expect(retrieved).toEqual({
|
|
167
|
-
name: testPackage.name,
|
|
168
|
-
tags: updatedTags,
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
describe('Tokens', () => {
|
|
174
|
-
const testToken = {
|
|
175
|
-
token: 'test-token',
|
|
176
|
-
uuid: 'test-uuid',
|
|
177
|
-
scope: [
|
|
178
|
-
{
|
|
179
|
-
values: ['*'],
|
|
180
|
-
types: {
|
|
181
|
-
pkg: { read: true, write: true },
|
|
182
|
-
},
|
|
183
|
-
},
|
|
184
|
-
],
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
it('should insert and retrieve a token', async () => {
|
|
188
|
-
await dbOps.upsertToken(testToken.token, testToken.uuid, testToken.scope);
|
|
189
|
-
const retrieved = await dbOps.getToken(testToken.token);
|
|
190
|
-
expect(retrieved).toEqual({
|
|
191
|
-
token: testToken.token,
|
|
192
|
-
uuid: testToken.uuid,
|
|
193
|
-
scope: testToken.scope,
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it('should update an existing token', async () => {
|
|
198
|
-
const updatedScope = [
|
|
199
|
-
{
|
|
200
|
-
values: ['@test'],
|
|
201
|
-
types: {
|
|
202
|
-
pkg: { read: true, write: false },
|
|
203
|
-
},
|
|
204
|
-
},
|
|
205
|
-
];
|
|
206
|
-
await dbOps.upsertToken(testToken.token, testToken.uuid, updatedScope);
|
|
207
|
-
const retrieved = await dbOps.getToken(testToken.token);
|
|
208
|
-
expect(retrieved).toEqual({
|
|
209
|
-
token: testToken.token,
|
|
210
|
-
uuid: testToken.uuid,
|
|
211
|
-
scope: updatedScope,
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it('should reject UUIDs with special characters', async () => {
|
|
216
|
-
const invalidUuid = '~invalidUuid';
|
|
217
|
-
|
|
218
|
-
await expect(
|
|
219
|
-
dbOps.upsertToken('invalid-token', invalidUuid, [])
|
|
220
|
-
).rejects.toThrow('Invalid uuid');
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it('should validate user access permissions', async () => {
|
|
224
|
-
// Setup test tokens
|
|
225
|
-
const adminToken = 'admin-token';
|
|
226
|
-
const userAToken = 'user-a-token';
|
|
227
|
-
|
|
228
|
-
// Setup mock tokens
|
|
229
|
-
await dbOps.upsertToken(adminToken, 'admin', [
|
|
230
|
-
{
|
|
231
|
-
values: ['*'],
|
|
232
|
-
types: { user: { read: true, write: true } }
|
|
233
|
-
}
|
|
234
|
-
]);
|
|
235
|
-
|
|
236
|
-
await dbOps.upsertToken(userAToken, 'user-a', [
|
|
237
|
-
{
|
|
238
|
-
values: ['~user-a'],
|
|
239
|
-
types: { user: { read: true, write: true } }
|
|
240
|
-
}
|
|
241
|
-
]);
|
|
242
|
-
|
|
243
|
-
// Case 1: User can modify their own token
|
|
244
|
-
await expect(
|
|
245
|
-
dbOps.upsertToken('new-token', 'user-a', [], userAToken)
|
|
246
|
-
).resolves.not.toThrow();
|
|
247
|
-
|
|
248
|
-
// Case 2: Admin can modify any user's token
|
|
249
|
-
await expect(
|
|
250
|
-
dbOps.upsertToken('other-token', 'user-b', [], adminToken)
|
|
251
|
-
).resolves.not.toThrow();
|
|
252
|
-
|
|
253
|
-
// Case 3: User cannot modify another user's token
|
|
254
|
-
await expect(
|
|
255
|
-
dbOps.upsertToken('other-token', 'user-b', [], userAToken)
|
|
256
|
-
).rejects.toThrow('Unauthorized');
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
it('should validate user access permissions for token operations', async () => {
|
|
260
|
-
// Import token functions
|
|
261
|
-
const { postToken, putToken } = await import('../src/routes/tokens.js');
|
|
262
|
-
|
|
263
|
-
// Test case 1: User trying to modify their own token (authorized)
|
|
264
|
-
const selfContext = {
|
|
265
|
-
req: {
|
|
266
|
-
json: async () => ({ token: 'own-token', uuid: 'user-a', scope: [] }),
|
|
267
|
-
param: () => 'own-token',
|
|
268
|
-
header: () => 'Bearer auth-token'
|
|
269
|
-
},
|
|
270
|
-
json: (response, statusCode) => ({ response, statusCode }),
|
|
271
|
-
db: {
|
|
272
|
-
...dbOps,
|
|
273
|
-
getToken: async (token) => {
|
|
274
|
-
if (token === 'auth-token') {
|
|
275
|
-
return {
|
|
276
|
-
token: 'auth-token',
|
|
277
|
-
uuid: 'user-a',
|
|
278
|
-
scope: []
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
return null;
|
|
282
|
-
},
|
|
283
|
-
upsertToken: async (token, uuid, scope, authToken) => {
|
|
284
|
-
if (authToken) {
|
|
285
|
-
const authUser = await dbOps.getToken(authToken);
|
|
286
|
-
if (authUser && authUser.uuid !== uuid) {
|
|
287
|
-
throw new Error('Unauthorized');
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
return true;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
// User modifying their own token should be authorized
|
|
296
|
-
const selfPostResult = await postToken(selfContext);
|
|
297
|
-
expect(selfPostResult.statusCode).not.toBe(401);
|
|
298
|
-
|
|
299
|
-
// Test case 2: User trying to modify another user's token without permission
|
|
300
|
-
const unauthorizedContext = {
|
|
301
|
-
req: {
|
|
302
|
-
json: async () => ({ token: 'other-token', uuid: 'user-b', scope: [] }),
|
|
303
|
-
param: () => 'other-token',
|
|
304
|
-
header: () => 'Bearer restricted-token'
|
|
305
|
-
},
|
|
306
|
-
json: (response, statusCode) => ({ response, statusCode }),
|
|
307
|
-
db: {
|
|
308
|
-
...dbOps,
|
|
309
|
-
getToken: async (token) => {
|
|
310
|
-
if (token === 'restricted-token') {
|
|
311
|
-
return {
|
|
312
|
-
token: 'restricted-token',
|
|
313
|
-
uuid: 'user-a',
|
|
314
|
-
scope: [
|
|
315
|
-
{
|
|
316
|
-
values: ['~user-a'],
|
|
317
|
-
types: {
|
|
318
|
-
user: { read: true, write: true }
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
]
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
return null;
|
|
325
|
-
},
|
|
326
|
-
upsertToken: async (token, uuid, scope, authToken) => {
|
|
327
|
-
if (authToken === 'restricted-token' && uuid === 'user-b') {
|
|
328
|
-
throw new Error('Unauthorized');
|
|
329
|
-
}
|
|
330
|
-
return true;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
};
|
|
334
|
-
|
|
335
|
-
// User without permission to modify another user's token should be unauthorized
|
|
336
|
-
const unauthorizedPostResult = await postToken(unauthorizedContext);
|
|
337
|
-
expect(unauthorizedPostResult.statusCode).toBe(401);
|
|
338
|
-
expect(unauthorizedPostResult.response.error).toBe('Unauthorized');
|
|
339
|
-
|
|
340
|
-
// Test case 3: Admin user with global permissions modifying another user's token
|
|
341
|
-
const adminContext = {
|
|
342
|
-
req: {
|
|
343
|
-
json: async () => ({ token: 'other-token', uuid: 'user-b', scope: [] }),
|
|
344
|
-
param: () => 'other-token',
|
|
345
|
-
header: () => 'Bearer admin-token'
|
|
346
|
-
},
|
|
347
|
-
json: (response, statusCode) => ({ response, statusCode }),
|
|
348
|
-
db: {
|
|
349
|
-
...dbOps,
|
|
350
|
-
getToken: async (token) => {
|
|
351
|
-
if (token === 'admin-token') {
|
|
352
|
-
return {
|
|
353
|
-
token: 'admin-token',
|
|
354
|
-
uuid: 'admin',
|
|
355
|
-
scope: [
|
|
356
|
-
{
|
|
357
|
-
values: ['*'],
|
|
358
|
-
types: {
|
|
359
|
-
user: { read: true, write: true }
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
]
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
return null;
|
|
366
|
-
},
|
|
367
|
-
upsertToken: async () => true
|
|
368
|
-
}
|
|
369
|
-
};
|
|
370
|
-
|
|
371
|
-
// Admin with proper permissions should be authorized
|
|
372
|
-
const adminPostResult = await postToken(adminContext);
|
|
373
|
-
expect(adminPostResult.statusCode).not.toBe(401);
|
|
374
|
-
});
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
describe('Versions', () => {
|
|
378
|
-
const testVersion = {
|
|
379
|
-
spec: 'test-package@1.0.0',
|
|
380
|
-
manifest: {
|
|
381
|
-
name: 'test-package',
|
|
382
|
-
version: '1.0.0',
|
|
383
|
-
description: 'Test package',
|
|
384
|
-
},
|
|
385
|
-
published_at: '2025-03-25T15:33:21.971Z',
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
it('should insert and retrieve a version', async () => {
|
|
389
|
-
await dbOps.upsertVersion(testVersion.spec, testVersion.manifest, testVersion.published_at);
|
|
390
|
-
const retrieved = await dbOps.getVersion(testVersion.spec);
|
|
391
|
-
expect(retrieved).toEqual({
|
|
392
|
-
spec: testVersion.spec,
|
|
393
|
-
manifest: testVersion.manifest,
|
|
394
|
-
published_at: testVersion.published_at,
|
|
395
|
-
});
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
it('should update an existing version', async () => {
|
|
399
|
-
const updatedManifest = {
|
|
400
|
-
name: 'test-package',
|
|
401
|
-
version: '1.0.0',
|
|
402
|
-
description: 'Updated test package',
|
|
403
|
-
};
|
|
404
|
-
await dbOps.upsertVersion(testVersion.spec, updatedManifest, testVersion.published_at);
|
|
405
|
-
const retrieved = await dbOps.getVersion(testVersion.spec);
|
|
406
|
-
expect(retrieved).toEqual({
|
|
407
|
-
spec: testVersion.spec,
|
|
408
|
-
manifest: updatedManifest,
|
|
409
|
-
published_at: testVersion.published_at,
|
|
410
|
-
});
|
|
411
|
-
});
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
describe('Search Operations', () => {
|
|
415
|
-
it('should search packages by name', async () => {
|
|
416
|
-
const testPackages = [
|
|
417
|
-
{ name: 'test-package-1', tags: { latest: '1.0.0' } },
|
|
418
|
-
{ name: 'test-package-2', tags: { latest: '1.0.0' } },
|
|
419
|
-
{ name: 'other-package', tags: { latest: '1.0.0' } },
|
|
420
|
-
];
|
|
421
|
-
|
|
422
|
-
for (const pkg of testPackages) {
|
|
423
|
-
await dbOps.upsertPackage(pkg.name, pkg.tags);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
const results = await dbOps.searchPackages('test');
|
|
427
|
-
expect(results).toHaveLength(2);
|
|
428
|
-
expect(results.map(p => p.name)).toEqual(['test-package-1', 'test-package-2']);
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
it('should search packages by scope', async () => {
|
|
432
|
-
const testPackages = [
|
|
433
|
-
{ name: '@test/package-1', tags: { latest: '1.0.0' } },
|
|
434
|
-
{ name: '@test/package-2', tags: { latest: '1.0.0' } },
|
|
435
|
-
{ name: '@other/package', tags: { latest: '1.0.0' } },
|
|
436
|
-
];
|
|
437
|
-
|
|
438
|
-
for (const pkg of testPackages) {
|
|
439
|
-
await dbOps.upsertPackage(pkg.name, pkg.tags);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
const results = await dbOps.searchPackages('package', '@test');
|
|
443
|
-
expect(results).toHaveLength(2);
|
|
444
|
-
expect(results.map(p => p.name)).toEqual(['@test/package-1', '@test/package-2']);
|
|
445
|
-
});
|
|
446
|
-
});
|
|
447
|
-
});
|