Adding scrape files for testing

This commit is contained in:
Max committed 2026-11-03 11:20:03 -07:00
1 parent 7d255fe687
commit 8d60aae65c
40 files changed
+4423

No files matched your search

+127
View File
@@ -0,0 +1,127 @@
//
// Test harness for the outbound HTTP layer.
//
// Responses are built from the REAL Response/ReadableStream globals rather than
// hand-rolled stubs, so the streaming size cap, the charset decoder and the
// body-cancel paths in HttpFetch are genuinely exercised instead of being
// stubbed around.
//
let FetchMock = module.exports = {}
let originalFetch = null
let routes = []
let callLog = []
FetchMock.install = () => {
originalFetch = global.fetch
routes = []
callLog = []
global.fetch = jest.fn(async (url, options = {}) => {
const href = typeof url === 'string' ? url : url.href
callLog.push({ url: href, headers: options.headers || {}, options })
const route = routes.find(entry => entry.match(href))
if (!route) {
throw Object.assign(new Error(`No route for ${href}`), { code: 'ENOTFOUND' })
}
// A hanging route must still honour the abort signal, or the test would
// wait forever instead of proving the AbortController fires.
if (options.signal) {
return await Promise.race([
Promise.resolve(route.responder(href, options)),
abortPromise(options.signal),
])
}
return route.responder(href, options)
})
}
const abortPromise = (signal) => new Promise((resolve, reject) => {
if (signal.aborted) {
return reject(Object.assign(new Error('Aborted'), { name: 'AbortError' }))
}
signal.addEventListener('abort', () => {
reject(Object.assign(new Error('Aborted'), { name: 'AbortError' }))
}, { once: true })
})
FetchMock.restore = () => {
if (originalFetch) { global.fetch = originalFetch } else { delete global.fetch }
originalFetch = null
routes = []
callLog = []
}
//
// route('https://example.com/', responder) or route(/example/, responder).
// Responders are called with (url, options) and may be functions returning a
// Response, so a route can answer differently on each call.
//
FetchMock.route = (pattern, responder) => {
const match = pattern instanceof RegExp
? (href) => pattern.test(href)
: (href) => href === pattern
routes.push({ match, responder: typeof responder === 'function' ? responder : () => responder })
}
// Answer with a counter so "retried exactly N times" is assertable.
FetchMock.routeSequence = (pattern, responses) => {
let index = 0
FetchMock.route(pattern, () => {
const response = responses[Math.min(index, responses.length - 1)]
index++
return typeof response === 'function' ? response() : response
})
}
FetchMock.html = (body, { status = 200, headers = {}, contentType = 'text/html; charset=utf-8' } = {}) => {
return new Response(body, {
status,
headers: { 'content-type': contentType, ...headers },
})
}
// Raw bytes, for the charset tests where the encoding is the point.
FetchMock.buffer = (buffer, { status = 200, contentType = 'text/html', headers = {} } = {}) => {
return new Response(buffer, {
status,
headers: { 'content-type': contentType, ...headers },
})
}
FetchMock.status = (status, { headers = {}, body = '' } = {}) => {
return new Response(body || null, { status, headers })
}
FetchMock.redirect = (location, status = 302) => {
return new Response(null, { status, headers: { location } })
}
//
// A body that never ends. Proves the size cap aborts mid-stream rather than
// buffering until the process dies.
//
FetchMock.oversized = (contentType = 'text/html') => {
const chunk = Buffer.alloc(64 * 1024, 0x61)
const stream = new ReadableStream({
pull(controller) { controller.enqueue(new Uint8Array(chunk)) },
})
return new Response(stream, { status: 200, headers: { 'content-type': contentType } })
}
// Never settles - the AbortController is the only way out.
FetchMock.hang = () => new Promise(() => {})
FetchMock.networkError = (code = 'ECONNRESET') => () => {
throw Object.assign(new Error(`socket ${code}`), { code })
}
FetchMock.calls = () => callLog.slice()
FetchMock.callsTo = (pattern) => callLog.filter(entry =>
pattern instanceof RegExp ? pattern.test(entry.url) : entry.url === pattern)
FetchMock.callCount = () => callLog.length