Skip to content

Commit 4f81793

Browse files
committed
test: update mock fixtures
1 parent 6c10982 commit 4f81793

File tree

3 files changed

+174
-32
lines changed

3 files changed

+174
-32
lines changed

modules/runtime/server/cache.ts

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,67 @@ async function processSingleFastNpmMeta(
281281
return result
282282
}
283283

284+
/**
285+
* Process a single package for the /versions/ endpoint.
286+
* Returns PackageVersionsInfo shape: { name, distTags, versions, specifier, time, lastSynced }
287+
*/
288+
async function processSingleVersionsMeta(
289+
packageQuery: string,
290+
storage: ReturnType<typeof useStorage>,
291+
metadata: boolean,
292+
): Promise<Record<string, unknown>> {
293+
let packageName = packageQuery
294+
let specifier = '*'
295+
296+
if (packageName.startsWith('@')) {
297+
const atIndex = packageName.indexOf('@', 1)
298+
if (atIndex !== -1) {
299+
specifier = packageName.slice(atIndex + 1)
300+
packageName = packageName.slice(0, atIndex)
301+
}
302+
} else {
303+
const atIndex = packageName.indexOf('@')
304+
if (atIndex !== -1) {
305+
specifier = packageName.slice(atIndex + 1)
306+
packageName = packageName.slice(0, atIndex)
307+
}
308+
}
309+
310+
if (packageName.includes('does-not-exist') || packageName.includes('nonexistent')) {
311+
return { name: packageName, error: 'not_found' }
312+
}
313+
314+
const fixturePath = getFixturePath('packument', packageName)
315+
const packument = await storage.getItem<any>(fixturePath)
316+
317+
if (!packument) {
318+
return { name: packageName, error: 'not_found' }
319+
}
320+
321+
const result: Record<string, unknown> = {
322+
name: packageName,
323+
specifier,
324+
distTags: packument['dist-tags'] || {},
325+
versions: Object.keys(packument.versions || {}),
326+
time: packument.time || {},
327+
lastSynced: Date.now(),
328+
}
329+
330+
if (metadata) {
331+
const versionsMeta: Record<string, Record<string, unknown>> = {}
332+
for (const [ver, data] of Object.entries(packument.versions || {})) {
333+
const meta: Record<string, unknown> = { version: ver }
334+
const vData = data as Record<string, unknown>
335+
if (vData.deprecated) meta.deprecated = vData.deprecated
336+
if (packument.time?.[ver]) meta.time = packument.time[ver]
337+
versionsMeta[ver] = meta
338+
}
339+
result.versionsMeta = versionsMeta
340+
}
341+
342+
return result
343+
}
344+
284345
async function handleFastNpmMeta(
285346
url: string,
286347
storage: ReturnType<typeof useStorage>,
@@ -296,22 +357,27 @@ async function handleFastNpmMeta(
296357

297358
if (host !== 'npm.antfu.dev') return null
298359

299-
const pathPart = decodeURIComponent(pathname.slice(1))
300-
if (!pathPart) return null
360+
const rawPath = decodeURIComponent(pathname.slice(1))
361+
if (!rawPath) return null
301362

302363
const metadata = searchParams.get('metadata') === 'true'
303364

365+
// Determine if this is a /versions/ request
366+
const isVersions = rawPath.startsWith('versions/')
367+
const pathPart = isVersions ? rawPath.slice('versions/'.length) : rawPath
368+
const processFn = isVersions
369+
? (pkg: string) => processSingleVersionsMeta(pkg, storage, metadata)
370+
: (pkg: string) => processSingleFastNpmMeta(pkg, storage, metadata)
371+
304372
// Handle batch requests (package1+package2+...)
305373
if (pathPart.includes('+')) {
306374
const packages = pathPart.split('+')
307-
const results = await Promise.all(
308-
packages.map(pkg => processSingleFastNpmMeta(pkg, storage, metadata)),
309-
)
375+
const results = await Promise.all(packages.map(processFn))
310376
return { data: results }
311377
}
312378

313379
// Handle single package request
314-
const result = await processSingleFastNpmMeta(pathPart, storage, metadata)
380+
const result = await processFn(pathPart)
315381
if ('error' in result) {
316382
return { data: null }
317383
}

server/api/registry/package-meta/[...pkg].get.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ export default defineCachedEventHandler(
2424
throw createError({ statusCode: 404, message: 'Package name is required' })
2525
}
2626

27-
const encodedName = encodePackageName(pkgParam)
27+
const packageName = decodeURIComponent(pkgParam)
28+
const encodedName = encodePackageName(packageName)
2829

2930
try {
3031
const [packument, downloads] = await Promise.all([
31-
fetchNpmPackage(pkgParam),
32+
fetchNpmPackage(packageName),
3233
$fetch<NpmDownloadCount>(`${NPM_API}/downloads/point/last-week/${encodedName}`).catch(
3334
() => null,
3435
),

test/fixtures/mock-routes.cjs

Lines changed: 99 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -204,36 +204,54 @@ function matchOsvApi(urlString) {
204204
}
205205

206206
/**
207-
* @param {string} urlString
208-
* @returns {MockResponse | null}
207+
* Parse a package query string into name and specifier.
208+
* Handles scoped packages: "@scope/name@specifier" and "name@specifier".
209+
*
210+
* @param {string} query
211+
* @param {string} defaultSpecifier
212+
* @returns {{ name: string; specifier: string }}
209213
*/
210-
function matchFastNpmMeta(urlString) {
211-
const url = new URL(urlString)
212-
let packageName = decodeURIComponent(url.pathname.slice(1))
213-
214-
if (!packageName) return null
215-
216-
let specifier = 'latest'
217-
if (packageName.startsWith('@')) {
218-
const atIndex = packageName.indexOf('@', 1)
214+
function parsePackageQuery(query, defaultSpecifier) {
215+
let name = query
216+
let specifier = defaultSpecifier
217+
if (name.startsWith('@')) {
218+
const atIndex = name.indexOf('@', 1)
219219
if (atIndex !== -1) {
220-
specifier = packageName.slice(atIndex + 1)
221-
packageName = packageName.slice(0, atIndex)
220+
specifier = name.slice(atIndex + 1)
221+
name = name.slice(0, atIndex)
222222
}
223223
} else {
224-
const atIndex = packageName.indexOf('@')
224+
const atIndex = name.indexOf('@')
225225
if (atIndex !== -1) {
226-
specifier = packageName.slice(atIndex + 1)
227-
packageName = packageName.slice(0, atIndex)
226+
specifier = name.slice(atIndex + 1)
227+
name = name.slice(0, atIndex)
228228
}
229229
}
230+
return { name, specifier }
231+
}
232+
233+
/**
234+
* Build a latest-version response for a single package (GET /:pkg endpoint).
235+
*
236+
* @param {string} query
237+
* @returns {object}
238+
*/
239+
function resolveSingleLatest(query) {
240+
const { name, specifier } = parsePackageQuery(query, 'latest')
241+
const packument = readFixture(packageToFixturePath(name))
230242

231-
const packument = readFixture(packageToFixturePath(packageName))
232-
if (!packument) return null
243+
if (!packument) {
244+
return {
245+
name,
246+
specifier,
247+
version: '0.0.0',
248+
publishedAt: new Date().toISOString(),
249+
lastSynced: Date.now(),
250+
}
251+
}
233252

234253
const distTags = packument['dist-tags']
235254
const versions = packument.versions
236-
const time = packument.time
237255

238256
let version
239257
if (specifier === 'latest' || !specifier) {
@@ -246,15 +264,72 @@ function matchFastNpmMeta(urlString) {
246264
version = distTags && distTags.latest
247265
}
248266

249-
if (!version) return null
267+
if (!version) {
268+
return {
269+
name,
270+
specifier,
271+
version: '0.0.0',
272+
publishedAt: new Date().toISOString(),
273+
lastSynced: Date.now(),
274+
}
275+
}
250276

251-
return json({
252-
name: packageName,
277+
return {
278+
name,
253279
specifier,
254280
version,
255-
publishedAt: (time && time[version]) || new Date().toISOString(),
281+
publishedAt: (packument.time && packument.time[version]) || new Date().toISOString(),
256282
lastSynced: Date.now(),
257-
})
283+
}
284+
}
285+
286+
/**
287+
* Build a versions response for a single package (GET /versions/:pkg endpoint).
288+
*
289+
* @param {string} query
290+
* @returns {object}
291+
*/
292+
function resolveSingleVersions(query) {
293+
const { name, specifier } = parsePackageQuery(query, '*')
294+
const packument = readFixture(packageToFixturePath(name))
295+
296+
if (!packument) {
297+
return { name, error: `"https://registry.npmjs.org/${name}": 404 Not Found` }
298+
}
299+
300+
return {
301+
name,
302+
specifier,
303+
distTags: packument['dist-tags'] || {},
304+
versions: Object.keys(packument.versions || {}),
305+
time: packument.time || {},
306+
lastSynced: Date.now(),
307+
}
308+
}
309+
310+
/**
311+
* @param {string} urlString
312+
* @returns {MockResponse | null}
313+
*/
314+
function matchFastNpmMeta(urlString) {
315+
const url = new URL(urlString)
316+
let pathPart = decodeURIComponent(url.pathname.slice(1))
317+
318+
if (!pathPart) return null
319+
320+
// /versions/ endpoint returns version lists (used by getVersionsBatch)
321+
const isVersions = pathPart.startsWith('versions/')
322+
if (isVersions) pathPart = pathPart.slice('versions/'.length)
323+
324+
const resolveFn = isVersions ? resolveSingleVersions : resolveSingleLatest
325+
326+
// Batch requests: package1+package2+...
327+
if (pathPart.includes('+')) {
328+
const results = pathPart.split('+').map(resolveFn)
329+
return json(results)
330+
}
331+
332+
return json(resolveFn(pathPart))
258333
}
259334

260335
/**

0 commit comments

Comments
 (0)