Plan Checker (neues Studenten-Werkzeug, Seitenleiste + Admin → Plan-Checker- Merkmale): lädt einen Bauplan als PDF hoch und lässt ChatGPT oder Claude ihn gegen eine gepflegte Merkmalsliste prüfen. PDF-Rendering (pdf.js + @napi-rs/canvas) berücksichtigt jetzt Ebenen (Optional Content Groups) und WASM-Decoder korrekt, sendet eine Übersicht plus mehrere hochaufgelöste Kachel-Ausschnitte statt eines einzelnen, zu groben Bilds, und beide Anbieter bekommen dasselbe Bildmaterial. Jedes Prüfergebnis kann auf eines der gesendeten Bilder als Beleg verweisen (bildIndex) — als Screenshot im Ergebnis anklickbar, öffnet vergrößert in einem Modal statt eines von Chrome blockierten target=_blank-Links auf eine data:-URL. KI-Modellname ist jetzt pro Konto überschreibbar (Einstellungen → Mein Konto) für Drittanbieter mit abweichendem Modell-Katalog. Wochenbericht: dichte Kartenansicht bekommt einen Fokus-Modus — nur der Kommentar liegt offen, Leistungsphasen und Beteiligte stecken in aufklappbaren Abschnitten statt dauerhaft nebeneinander zu stehen. Projektbereich: Terminplan-Auslastung rechnet jetzt exakt wie der Auslastungs-Tab/die Wandtafel (statt einer unabhängigen, tageweisen Schätzung); die LPH-Fortschrittsliste im Dashboard zeigt nur noch die beauftragten Leistungsphasen. Wandtafel: Dashboard-Kopf mit Kennzahlen-Reihe und Panel-Reihe neu strukturiert, in den Blautönen des Firmenlogos; dieselbe Palette wurde anschließend auf den gesamten App-weiten Dark Mode übernommen. Dateibereinigung: nicht mehr benötigte Test-Uploads/Backups entfernt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
19 KiB
MockPool
Stability: 2 - Stable
A MockPool is a Pool that intercepts requests matching registered routes
and replies with mocked responses instead of contacting the network. It is
created by a MockAgent and exposes the same interception API as
MockClient.
A MockPool is not constructed directly in most cases; instead it is obtained
through [mockAgent.get(origin)][]. Once registered as the global dispatcher,
any request whose origin matches the pool is matched against the mocks it holds.
import { MockAgent } from 'undici'
const mockAgent = new MockAgent()
const mockPool = mockAgent.get('http://localhost:3000')
const { MockAgent } = require('undici')
const mockAgent = new MockAgent()
const mockPool = mockAgent.get('http://localhost:3000')
Class: MockPool
- Extends: {Pool}
Extends Pool and implements the Interceptable interface, allowing
requests made through it to be matched against registered mocks.
new MockPool(origin[, options])
origin{string} The origin to associate this mock pool with. It should only include the protocol, hostname, and port.options{MockPoolOptions} Extends thePooloptions.- Returns: {MockPool}
The agent option is required and must implement the Agent interface;
otherwise an InvalidArgumentError is thrown.
Parameter: MockPoolOptions
Extends: PoolOptions
agent{MockAgent} The agent to associate this mock pool with.ignoreTrailingSlash{boolean} Whether trailing slashes should be ignored when matching the path of intercepted requests. Default:false.
mockPool.intercept(options)
options{MockPoolInterceptOptions} The matching criteria for the requests to intercept.- Returns: {MockInterceptor} The interceptor used to define the mocked reply.
Registers a route on the mock pool and returns a MockInterceptor for any
matching request that uses the same origin as this mock pool. The interceptor is
then used to define the reply, for example with reply() or replyWithError().
Each intercept() call is consumed by a single matching request. To match more
than one request, call intercept() once per expected request, or use
persist() or times() on the returned MockScope. When no registered
mock matches a request, a real request is attempted unless network connections
have been disabled on the MockAgent, in which case a MockNotMatchedError
is thrown.
A request is intercepted only when every defined matcher passes. The accepted matcher forms behave as follows:
| Matcher type | Condition to pass |
|---|---|
string |
Exact match against the value |
RegExp |
The expression must match |
Function |
The function must return true |
Parameter: MockPoolInterceptOptions
path{string|RegExp|Function} A matcher for the HTTP request path. The function form has the signature(path: string) => boolean. When aRegExpor function is used, it matches against the request path including all query parameters in alphabetical order. When astringis used, query parameters can instead be supplied throughquery.method{string|RegExp|Function} A matcher for the HTTP request method. The function form has the signature(method: string) => boolean. Default:'GET'.body{string|RegExp|Function} A matcher for the HTTP request body. The function form has the signature(body: string) => boolean.headers{Object|Function} A matcher for the HTTP request headers, given either as a map of header name to astring,RegExp, or(value: string) => booleanmatcher, or as a single function that receives all headers and returns aboolean. When a map is used, the request must match every defined header; extra headers not listed here do not affect matching.query{Object} A matcher for the HTTP request query string parameters. Only applies when astringwas provided forpath.ignoreTrailingSlash{boolean} Whether a trailing slash onpathis ignored when matching. Inherited from the mock pool'signoreTrailingSlashoption when not set.
Return: MockInterceptor
The reply behaviour of a matching request is defined through the returned
MockInterceptor.
reply(statusCode[, data[, responseOptions]]){Function} Defines the reply for a matching request.statusCode{number} The status code of the mocked reply.data{string|Buffer|Object|Function} The body of the mocked reply. Objects are serialized to JSON; strings andBuffers are sent as-is. The function form has the signature(opts: MockResponseCallbackOptions) => string | Buffer | Objectand is invoked with the incoming request to compute the reply body.responseOptions{MockResponseOptions} Additional reply options. Default:{}.- Returns: {MockScope}
reply(callback){Function} Defines the reply for a matching request, computing all reply options dynamically rather than just the body.callback{Function} A(opts: MockResponseCallbackOptions) => { statusCode, data, responseOptions }function invoked with the incoming request. The callback may be asynchronous; a returned promise is awaited and must resolve to the same shape.- Returns: {MockScope}
replyWithError(error){Function} Defines an error for a matching request to throw.error{Error} The error thrown when a request matches.- Returns: {MockScope}
defaultReplyHeaders(headers){Function} Sets default headers included on every subsequent reply defined on this interceptor, in addition to any headers set on a specific reply.headers{Object} A map of header name to value.- Returns: {MockInterceptor}
defaultReplyTrailers(trailers){Function} Sets default trailers included on every subsequent reply defined on this interceptor, in addition to any trailers set on a specific reply.trailers{Object} A map of trailer name to value.- Returns: {MockInterceptor}
replyContentLength(){Function} Sets an automatically calculatedcontent-lengthheader on every subsequent reply defined on this interceptor.- Returns: {MockInterceptor}
By default, reply() and replyWithError() define the behaviour for the first
matching request only; subsequent requests are not affected unless persist()
or times() is used on the returned MockScope.
Parameter: MockResponseCallbackOptions
The argument passed to the reply() data and options callbacks.
path{string} The path of the intercepted request.method{string} The method of the intercepted request.headers{Object|Headers} The headers of the intercepted request.origin{string} The origin of the intercepted request.body{string|null} The body of the intercepted request.
Parameter: MockResponseOptions
headers{Object} Headers to include on the mocked reply.trailers{Object} Trailers to include on the mocked reply.
Return: MockScope
A MockScope is associated with a single MockInterceptor and configures
how many times the defined reply is used.
delay(waitInMs){Function} Delays the associated reply by a set amount of time.waitInMs{number} The delay in milliseconds.- Returns: {MockScope}
persist(){Function} Makes the associated reply match indefinitely, so every matching request receives the defined response.- Returns: {MockScope}
times(repeatTimes){Function} Makes the associated reply match a fixed number of times. This is overridden bypersist().repeatTimes{number} The number of matching requests the reply applies to.- Returns: {MockScope}
The following examples show common interception patterns.
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({ path: '/foo' }).reply(200, 'foo')
const { statusCode, body } = await request('http://localhost:3000/foo')
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // data foo
}
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/echo',
method: 'GET'
}).reply(200, ({ headers }) => ({ message: headers.message }))
const { statusCode, body } = await request('http://localhost:3000/echo', {
headers: { message: 'hello world!' }
})
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // {"message":"hello world!"}
}
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/echo',
method: 'GET'
}).reply(({ headers }) => ({
statusCode: 200,
data: { message: headers.message }
}))
const { statusCode, body } = await request('http://localhost:3000/echo', {
headers: { message: 'hello world!' }
})
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // {"message":"hello world!"}
}
import { readFile } from 'node:fs/promises'
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/fixture',
method: 'GET'
}).reply(async ({ path }) => ({
statusCode: 200,
data: await readFile(new URL('./fixture.json', import.meta.url))
}))
const { statusCode, body } = await request('http://localhost:3000/fixture')
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // contents of fixture.json
}
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({ path: '/foo', method: 'GET' }).reply(200, 'foo')
mockPool.intercept({ path: '/hello', method: 'GET' }).reply(200, 'hello')
const fooResult = await request('http://localhost:3000/foo')
for await (const data of fooResult.body) {
console.log('data', data.toString('utf8')) // data foo
}
const helloResult = await request('http://localhost:3000/hello')
for await (const data of helloResult.body) {
console.log('data', data.toString('utf8')) // data hello
}
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/foo?hello=there&see=ya',
method: 'POST',
body: 'form1=data1&form2=data2',
headers: {
'User-Agent': 'undici',
Host: 'example.com'
}
}).reply(200, { foo: 'bar' }, {
headers: { 'content-type': 'application/json' },
trailers: { 'Content-MD5': 'test' }
})
const { statusCode, headers, trailers, body } = await request(
'http://localhost:3000/foo?hello=there&see=ya',
{
method: 'POST',
body: 'form1=data1&form2=data2',
headers: {
foo: 'bar',
'User-Agent': 'undici',
Host: 'example.com'
}
}
)
console.log('response received', statusCode) // response received 200
console.log('headers', headers) // { 'content-type': 'application/json' }
for await (const data of body) {
console.log('data', data.toString('utf8')) // {"foo":"bar"}
}
console.log('trailers', trailers) // { 'content-md5': 'test' }
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/foo',
method: /^GET$/,
body: (value) => value === 'form=data',
headers: {
'User-Agent': 'undici',
Host: /^example\.com$/
}
}).reply(200, 'foo')
const { statusCode, body } = await request('http://localhost:3000/foo', {
method: 'GET',
body: 'form=data',
headers: {
foo: 'bar',
'User-Agent': 'undici',
Host: 'example.com'
}
})
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // data foo
}
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({
path: '/foo',
method: 'GET'
}).replyWithError(new Error('kaboom'))
try {
await request('http://localhost:3000/foo', { method: 'GET' })
} catch (error) {
console.error(error.message) // kaboom
}
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({ path: '/foo', method: 'GET' })
.defaultReplyHeaders({ foo: 'bar' })
.reply(200, 'foo')
const { headers } = await request('http://localhost:3000/foo')
console.log('headers', headers) // headers { foo: 'bar' }
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({ path: '/foo', method: 'GET' })
.defaultReplyTrailers({ foo: 'bar' })
.reply(200, 'foo')
const { trailers } = await request('http://localhost:3000/foo')
console.log('trailers', trailers) // trailers { foo: 'bar' }
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({ path: '/foo', method: 'GET' })
.replyContentLength()
.reply(200, 'foo')
const { headers } = await request('http://localhost:3000/foo')
console.log('headers', headers) // headers { 'content-length': '3' }
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({ path: '/foo', method: 'GET' }).reply(200, 'foo').persist()
await request('http://localhost:3000/foo') // Matches and returns the mock
await request('http://localhost:3000/foo') // Matches again, indefinitely
import { MockAgent, setGlobalDispatcher, request } from 'undici'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({ path: '/foo', method: 'GET' }).reply(200, 'foo').times(2)
await request('http://localhost:3000/foo') // Matches and returns the mock
await request('http://localhost:3000/foo') // Matches and returns the mock
await request('http://localhost:3000/foo') // No match; a real request is attempted
import { MockAgent, setGlobalDispatcher, request } from 'undici'
import querystring from 'node:querystring'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const mockPool = mockAgent.get('http://localhost:3000')
const matchPath = (requestPath) => {
const [pathname, search] = requestPath.split('?')
const requestQuery = querystring.parse(search)
if (!pathname.startsWith('/foo')) {
return false
}
return requestQuery.foo === 'bar'
}
mockPool.intercept({ path: matchPath, method: 'GET' }).reply(200, 'foo')
await request('http://localhost:3000/foo?foo=bar') // Matches and returns the mock
mockPool.close()
- Returns: {Promise} Fulfills with
undefinedonce the mock pool is closed.
Closes the mock pool, gracefully waiting for any enqueued requests to complete,
and removes it from the associated MockAgent.
import { MockAgent } from 'undici'
const mockAgent = new MockAgent()
const mockPool = mockAgent.get('http://localhost:3000')
await mockPool.close()
mockPool.dispatch(options, handlers)
options{DispatchOptions} The request options.handlers{DispatchHandler} The handlers invoked over the request lifecycle.- Returns: {boolean}
falseif the dispatcher is busy and the caller should wait before dispatching further requests, otherwisetrue.
Dispatches a request, matching it against the registered mocks. This override of
dispatcher.dispatch(options, handlers) is what drives the mocking
behaviour for every higher-level method such as request.
mockPool.request(options[, callback])
options{DispatchOptions}callback{Function} (optional) Invoked with the response when noPromiseis requested.- Returns: {Promise} Fulfills with the mocked response when no
callbackis provided.
Performs a request and resolves it against the registered mocks. Inherited from
Pool; see [dispatcher.request(options[, callback])][] for the full
parameter and return value documentation.
import { MockAgent } from 'undici'
const mockAgent = new MockAgent()
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({ path: '/foo', method: 'GET' }).reply(200, 'foo')
const { statusCode, body } = await mockPool.request({
origin: 'http://localhost:3000',
path: '/foo',
method: 'GET'
})
console.log('response received', statusCode) // response received 200
for await (const data of body) {
console.log('data', data.toString('utf8')) // data foo
}
mockPool.cleanMocks()
- Returns: {undefined}
Removes all registered interceptors from the mock pool. Pending mocks defined before this call no longer match incoming requests.
import { MockAgent } from 'undici'
const mockAgent = new MockAgent()
const mockPool = mockAgent.get('http://localhost:3000')
mockPool.intercept({ path: '/foo' }).reply(200, 'foo')
mockPool.cleanMocks()
[dispatcher.request(options[, callback])]: Dispatcher.md#dispatcherrequestoptions-callback
[mockAgent.get(origin)]: MockAgent.md#mockagentgetorigin