Skip to content

Commit e5ff3fe

Browse files
committed
Expose APIs with some documentation
1 parent 9bc2207 commit e5ff3fe

File tree

1 file changed

+292
-0
lines changed

1 file changed

+292
-0
lines changed

packages/vitest-pool-workers/types/cloudflare-test.d.ts

Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,298 @@ declare module "cloudflare:test" {
127127
migrationsTableName?: string
128128
): Promise<void>;
129129

130+
/**
131+
* Creates an **introspector** for a specific Workflow instance, used to
132+
* **modify** its behavior, **await** outcomes, and **clean up** its state during tests.
133+
* This is the primary entry point for testing individual Workflow instances.
134+
*
135+
* @param workflow - The Workflow binding, e.g., `env.MY_WORKFLOW`.
136+
* @param instanceId - The known ID of the Workflow instance to target.
137+
* @returns A `WorkflowInstanceIntrospector` to control the instance behavior.
138+
*
139+
* @example Full test of a Workflow instance with a known ID:
140+
* ```ts
141+
* it("should disable all sleeps and complete", async () => {
142+
* // 1. CONFIGURATION
143+
* const instance = await introspectWorkflowInstance(env.MY_WORKFLOW, "123456");
144+
* await instance.modify(async (m) => {
145+
* await m.disableSleeps();
146+
* });
147+
*
148+
* // 2. EXECUTION
149+
* await env.MY_WORKFLOW.create({ id: "123456" });
150+
*
151+
* // 3. ASSERTION
152+
* await instance.waitForStatus("complete");
153+
*
154+
* // 4. CLEANUP
155+
* await instance.cleanUp();
156+
* });
157+
* ```
158+
*/
159+
export function introspectWorkflowInstance(
160+
workflow: Workflow,
161+
instanceId: string
162+
): Promise<WorkflowInstanceIntrospector>;
163+
164+
/**
165+
* Provides methods to control a single Workflow instance.
166+
*/
167+
export interface WorkflowInstanceIntrospector {
168+
/**
169+
* Applies modifications to the Workflow instance's behavior.
170+
* Takes a callback function to apply modifications.
171+
*
172+
* @param fn - An async callback that receives a `WorkflowInstanceModifier` object.
173+
* @returns The `WorkflowInstanceIntrospector` instance for chaining.
174+
*/
175+
modify(
176+
fn: (m: WorkflowInstanceModifier) => Promise<void>
177+
): Promise<WorkflowInstanceIntrospector>;
178+
179+
/**
180+
* Waits for a specific step to complete and return a result.
181+
* If the step has already completed, this promise resolves immediately.
182+
*
183+
* @param step - An object specifying the step `name` and optional `index` (1-based).
184+
* If multiple steps share the same name, `index` targets a specific one.
185+
* Defaults to the first step found (`index: 1`).
186+
* @returns A promise that resolves with the step's result,
187+
* or rejects with an error if the step fails.
188+
*/
189+
waitForStepResult(step: { name: string; index?: number }): Promise<unknown>;
190+
191+
/**
192+
* Waits for the Workflow instance to reach a specific InstanceStatus status
193+
* (e.g., 'running', 'complete').
194+
* If the instance is already in the target status, this promise resolves immediately.
195+
* Throws an error if the Workflow instance reaches a finite state
196+
* (e.g., complete, errored) that is different from the target status.
197+
*
198+
* @param status - The target `InstanceStatus` to wait for.
199+
*/
200+
waitForStatus(status: InstanceStatus["status"]): Promise<void>;
201+
202+
/**
203+
* Cleans up the Workflow instance's state.
204+
* This is crucial for ensuring test isolation by preventing state from
205+
* leaking between tests. It's best practice to call this in an `afterEach`
206+
* hook or at the end of every test.
207+
*/
208+
cleanUp(): Promise<void>;
209+
}
210+
211+
/**
212+
* Provides methods to mock or alter the behavior of a Workflow instance's
213+
* steps, events, and sleeps.
214+
*/
215+
interface WorkflowInstanceModifier {
216+
/**
217+
* Disables sleeps, causing `step.sleep()` and `step.sleepUntil()` to
218+
* resolve immediately.
219+
*
220+
* @example Disable all sleeps:
221+
* ```ts
222+
* await instance.modify(m => {
223+
* m.disableSleeps();
224+
* });
225+
*
226+
* @example Disable a specific set of sleeps by their step names:
227+
* ```ts
228+
* await instance.modify(m => {
229+
* m.disableSleeps([{ name: "sleep1" }, { name: "sleep5" }, { name: "sleep7" }]);
230+
* });
231+
*
232+
* @param steps - Optional array of specific steps to disable sleeps for.
233+
* If omitted, **all sleeps** in the Workflow will be disabled.
234+
* A step is an object specifying the step `name` and optional `index` (1-based).
235+
* If multiple steps share the same name, `index` targets a specific one.
236+
* Defaults to the first step found (`index: 1`).
237+
*/
238+
disableSleeps(steps?: { name: string; index?: number }[]): Promise<void>;
239+
240+
/**
241+
* Mocks the result of a `step.do()`, causing it to return a specified
242+
* value instantly without executing the step's actual implementation.
243+
*
244+
* If called multiple times for the same step, an error will be thrown.
245+
*
246+
* @param step - An object specifying the step `name` and optional `index` (1-based).
247+
* If multiple steps share the same name, `index` targets a specific one.
248+
* Defaults to the first step found (`index: 1`).
249+
* @param stepResult - The mock value to be returned by the step.
250+
*
251+
* @example Mock the result of the third step named "fetch-data":
252+
* ```ts
253+
* await instance.modify(m => {
254+
* m.mockStepResult(
255+
* { name: "fetch-data", index: 3 },
256+
* { success: true, data: [1, 2, 3] }
257+
* );
258+
* });
259+
* ```
260+
*/
261+
mockStepResult(
262+
step: { name: string; index?: number },
263+
stepResult: unknown
264+
): Promise<void>;
265+
266+
/**
267+
* Forces a `step.do()` to throw an error, simulating a failure without
268+
* executing the step's actual implementation. Useful for testing retry logic
269+
* and error handling.
270+
*
271+
* @example Mock a step that errors 3 times before succeeding:
272+
* ```ts
273+
* // This example assumes the "fetch-data" step is configured with at least 3 retries.
274+
* await instance.modify(m => {
275+
* m.mockStepError(
276+
* { name: "fetch-data" },
277+
* new Error("Failed!"),
278+
* 3
279+
* );
280+
* m.mockStepResult(
281+
* { name: "fetch-data" },
282+
* { success: true, data: [1, 2, 3] }
283+
* );
284+
* });
285+
*
286+
* @param step - An object specifying the step `name` and optional `index` (1-based).
287+
* If multiple steps share the same name, `index` targets a specific one.
288+
* Defaults to the first step found (`index: 1`).
289+
* @param error - The `Error` object to be thrown.
290+
* @param times - Optional number of times to throw the error. If a step has
291+
* retries configured, it will fail this many times before potentially
292+
* succeeding on a subsequent attempt. If omitted, it will throw on **every attempt**.
293+
*/
294+
mockStepError(
295+
step: { name: string; index?: number },
296+
error: Error,
297+
times?: number
298+
): Promise<void>;
299+
300+
/**
301+
* Forces a `step.do()` to fail by timing out immediately, without executing
302+
* the step's actual implementation. Default step timeout is 10 minutes.
303+
*
304+
* @example Mock a step that times out 3 times before succeeding:
305+
* ```ts
306+
* // This example assumes the "fetch-data" step is configured with at least 3 retries.
307+
* await instance.modify(m => {
308+
* m.forceStepTimeout(
309+
* { name: "fetch-data" },
310+
* 3
311+
* );
312+
* m.mockStepResult(
313+
* { name: "fetch-data" },
314+
* { success: true, data: [1, 2, 3] }
315+
* );
316+
* });
317+
*
318+
* @param step - An object specifying the step `name` and optional `index` (1-based).
319+
* If multiple steps share the same name, `index` targets a specific one.
320+
* Defaults to the first step found (`index: 1`).
321+
* @param times - Optional number of times the step will time out. Useful for
322+
* testing retry logic. If omitted, it will time out on **every attempt**.
323+
*/
324+
forceStepTimeout(step: { name: string; index?: number }, times?: number);
325+
326+
/**
327+
* Sends a mock event to the Workflow instance. This causes a `step.waitForEvent()`
328+
* to resolve with the provided payload, as long as the step's timeout has not
329+
* yet expired. Default event timeout is 24 hours.
330+
*
331+
* @example Mock a step event:
332+
* ```ts
333+
* await instance.modify(m => {
334+
* m.mockEvent(
335+
* { type: "user-approval", payload: { approved: true } },
336+
* );
337+
*
338+
* @param event - The event to send, including its `type` and `payload`.
339+
*/
340+
mockEvent(event: { type: string; payload: unknown }): Promise<void>;
341+
342+
/**
343+
* Forces a `step.waitForEvent()` to time out instantly, causing the step to fail.
344+
* This simulates a scenario where an expected event never arrives.
345+
* Default event timeout is 24 hours.
346+
*
347+
* @example Mock a step to time out:
348+
* ```ts
349+
* await instance.modify(m => {
350+
* m.forceEventTimeout(
351+
* { name: "user-approval" },
352+
* );
353+
*
354+
* @param step - An object specifying the step `name` and optional `index` (1-based).
355+
* If multiple steps share the same name, `index` targets a specific one.
356+
* Defaults to the first step found (`index: 1`).
357+
*/
358+
forceEventTimeout(step: { name: string; index?: number }): Promise<void>;
359+
}
360+
361+
/**
362+
* Creates an **introspector** for a Workflow, where instance IDs are unknown
363+
* beforehand. This allows for defining modifications that will apply to
364+
* **all subsequently created instances**.
365+
*
366+
* This is the primary entry point for testing Workflow instances where the id is unknown before their creation.
367+
*
368+
* @param workflow - The Workflow binding, e.g., `env.MY_WORKFLOW`.
369+
* @returns A `WorkflowIntrospector` to control the instances behavior.
370+
*
371+
* @example Full test of a Workflow instance with a unknown ID:
372+
* ```ts
373+
* it("should disable all sleeps and complete", async () => {
374+
* // 1. CONFIGURATION
375+
* const introspector = await introspectWorkflow(env.MY_WORKFLOW);
376+
* await introspector.modifyAll(async (m) => {
377+
* await m.disableSleeps();
378+
* });
379+
*
380+
* // 2. EXECUTION
381+
* await env.MY_WORKFLOW.create();
382+
*
383+
* // 3. ASSERTION & CLEANUP
384+
* const instances = introspector.get();
385+
* for(const instance of instances) {
386+
* await instance.waitForStatus("complete");
387+
* await instance.cleanUp();
388+
* }
389+
* });
390+
* ```
391+
*/
392+
export function introspectWorkflow(
393+
workflow: Workflow
394+
): Promise<WorkflowIntrospector>;
395+
396+
/**
397+
* Provides methods to control all instances created by a Worflow.
398+
*/
399+
export interface WorkflowIntrospector {
400+
/**
401+
* Applies modifications to all Workflow instances created after calling
402+
* `introspectWorkflow`. Takes a callback function to apply modifications.
403+
*
404+
* @param fn - An async callback that receives a `WorkflowInstanceModifier` object.
405+
*/
406+
modifyAll(fn: (m: WorkflowInstanceModifier) => Promise<void>): void;
407+
/**
408+
* Returns all `WorkflowInstanceIntrospectors` from Workflow instances
409+
* created after calling `introspectWorkflow`.
410+
*/
411+
get(): WorkflowInstanceIntrospector[];
412+
413+
/**
414+
* Cleans up the Workflow introspector state.
415+
* This is crucial for ensuring that the introspection of a Workflow does
416+
* not get persisted across tests.
417+
* Call this in an `afterEach` hook or at the end of every test.
418+
*/
419+
cleanUp(): void;
420+
}
421+
130422
// Only require `params` and `data` to be specified if they're non-empty
131423
interface EventContextInitBase {
132424
request: Request<unknown, IncomingRequestCfProperties>;

0 commit comments

Comments
 (0)