diff --git a/packages/inngest/src/next.ts b/packages/inngest/src/next.ts index 1e80e8df..e1b41738 100644 --- a/packages/inngest/src/next.ts +++ b/packages/inngest/src/next.ts @@ -21,7 +21,7 @@ */ import { type NextApiRequest, type NextApiResponse } from "next"; -import { type NextRequest } from "next/server"; +import { type NextRequest } from "next/server.js"; import { InngestCommHandler, type ServeHandlerOptions, diff --git a/packages/test/CHANGELOG.md b/packages/test/CHANGELOG.md index 9c280373..6f3a4af6 100644 --- a/packages/test/CHANGELOG.md +++ b/packages/test/CHANGELOG.md @@ -1,5 +1,13 @@ # @inngest/test +## 0.1.1 + +### Patch Changes + +- [#741](https://github.com/inngest/inngest-js/pull/741) [`6782497`](https://github.com/inngest/inngest-js/commit/67824978ddd3cab7b923555341a2fbfe4ae96280) Thanks [@jpwilliams](https://github.com/jpwilliams)! - Fix the first step in a run running twice + +- [#741](https://github.com/inngest/inngest-js/pull/741) [`6782497`](https://github.com/inngest/inngest-js/commit/67824978ddd3cab7b923555341a2fbfe4ae96280) Thanks [@jpwilliams](https://github.com/jpwilliams)! - Fix immediate function/step failures not returning `error` correctly + ## 0.1.0 ### Minor Changes diff --git a/packages/test/jsr.json b/packages/test/jsr.json index de2af8da..1fd2e193 100644 --- a/packages/test/jsr.json +++ b/packages/test/jsr.json @@ -2,7 +2,7 @@ "$schema": "https://jsr.io/schema/config-file.v1.json", "name": "@inngest/test", "description": "Tooling for testing Inngest functions.", - "version": "0.1.0", + "version": "0.1.1", "include": [ "./src/**/*.ts" ], diff --git a/packages/test/package.json b/packages/test/package.json index bc952502..d0b3130b 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,6 +1,6 @@ { "name": "@inngest/test", - "version": "0.1.0", + "version": "0.1.1", "description": "Tooling for testing Inngest functions.", "main": "./index.js", "types": "./index.d.ts", diff --git a/packages/test/src/InngestTestEngine.ts b/packages/test/src/InngestTestEngine.ts index c5b642ca..48220586 100644 --- a/packages/test/src/InngestTestEngine.ts +++ b/packages/test/src/InngestTestEngine.ts @@ -29,7 +29,7 @@ export namespace InngestTestEngine { * TODO Potentially later allow many functions such that we can invoke and * send events. */ - function: InngestFunction.Any; + function: InngestFunction; /** * The event payloads to send to the function. If none is given, an @@ -91,6 +91,7 @@ export namespace InngestTestEngine { export interface MockedStep { id: string; + idIsHashed?: boolean; handler: () => any; } @@ -209,33 +210,64 @@ export class InngestTestEngine { */ inlineOpts?: InngestTestEngine.ExecuteOptions ): Promise { - const { run } = await this.individualExecution(inlineOpts); + const output = await this.individualExecution(inlineOpts); - return run - .waitFor("function-resolved") - .then((output) => { + const resolutionHandler = ( + output: InngestTestEngine.ExecutionOutput<"function-resolved"> + ) => { + return { + ctx: output.ctx, + state: output.state, + result: output.result.data, + }; + }; + + const rejectionHandler = ( + output: InngestTestEngine.ExecutionOutput<"function-rejected">, + error: unknown = output.result.error + ) => { + if ( + typeof output === "object" && + output !== null && + "ctx" in output && + "state" in output + ) { return { ctx: output.ctx, state: output.state, - result: output.result.data, + error, }; - }) - .catch((rejectedOutput) => { - if ( - typeof rejectedOutput === "object" && - rejectedOutput !== null && - "ctx" in rejectedOutput && - "state" in rejectedOutput - ) { - return { - ctx: rejectedOutput.ctx, - state: rejectedOutput.state, - error: rejectedOutput.error, - }; - } + } - throw rejectedOutput; - }); + throw output; + }; + + if (output.result.type === "function-resolved") { + return resolutionHandler( + output as InngestTestEngine.ExecutionOutput<"function-resolved"> + ); + } else if (output.result.type === "function-rejected") { + return rejectionHandler( + output as InngestTestEngine.ExecutionOutput<"function-rejected"> + ); + } else if (output.result.type === "step-ran") { + // Any error halts execution until retries are modelled + if ( + (output as InngestTestEngine.ExecutionOutput<"step-ran">).result.step + .error + ) { + return rejectionHandler( + output as InngestTestEngine.ExecutionOutput<"function-rejected">, + (output as InngestTestEngine.ExecutionOutput<"step-ran">).result.step + .error + ); + } + } + + return output.run + .waitFor("function-resolved") + .then(resolutionHandler) + .catch(rejectionHandler); } /** @@ -371,7 +403,7 @@ export class InngestTestEngine { const steps = (options.steps || []).map((step) => { return { ...step, - id: _internals.hashId(step.id), + id: step.idIsHashed ? step.id : _internals.hashId(step.id), }; }); @@ -476,6 +508,8 @@ export class InngestTestEngine { Promise.resolve({}) as Promise ); + InngestTestRun["updateState"](options, result); + const run = new InngestTestRun({ testEngine: this.clone(options), }); diff --git a/packages/test/src/InngestTestRun.ts b/packages/test/src/InngestTestRun.ts index f7314982..2d4c55bc 100644 --- a/packages/test/src/InngestTestRun.ts +++ b/packages/test/src/InngestTestRun.ts @@ -145,21 +145,17 @@ export class InngestTestRun { return finish(exec); } + InngestTestRun.updateState(runningState, exec.result); + const resultHandlers: Record void> = { "function-resolved": () => finish(exec), "function-rejected": () => finish(exec), - "step-not-found": () => { - processChain(); - }, + "step-not-found": () => processChain(), "steps-found": () => { // run all const result = exec.result as InngestTestRun.Checkpoint<"steps-found">; - if (result.steps.length > 1) { - runningState.disableImmediateExecution = true; - } - result.steps.forEach((step) => { processChain(step.id); }); @@ -167,18 +163,10 @@ export class InngestTestRun { "step-ran": () => { const result = exec.result as InngestTestRun.Checkpoint<"step-ran">; - // add to our running state - runningState.steps ??= []; - runningState.steps.push({ - id: result.step.name as string, // TODO we need the non-hashed ID here, or a way to map it - handler: () => { - if (result.step.error) { - throw result.step.error; - } - - return result.step.data; - }, - }); + // if this is an error, we should stop. Later we model retries. + if (result.step.error) { + return finish(exec); + } processChain(); }, @@ -192,4 +180,36 @@ export class InngestTestRun { return promise; } + + /** + * Given existing state and an execution result, mutate the state. + */ + protected static updateState( + options: InngestTestEngine.InlineOptions, + checkpoint: InngestTestRun.Checkpoint + ): void { + if (checkpoint.type === "steps-found") { + const steps = (checkpoint as InngestTestRun.Checkpoint<"steps-found">) + .steps; + + if (steps.length > 1) { + options.disableImmediateExecution = true; + } + } else if (checkpoint.type === "step-ran") { + const step = (checkpoint as InngestTestRun.Checkpoint<"step-ran">).step; + + options.steps ??= []; + options.steps.push({ + id: step.id, + idIsHashed: true, + handler: () => { + if (step.error) { + throw step.error; + } + + return step.data; + }, + }); + } + } }