Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial @inngest/test package #704

Merged
merged 21 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ebef13f
Reestablish build and release for `@inngest/test`
jpwilliams Sep 7, 2024
4e2625c
Add `DeepPartial` type for shoehorning subset matching
jpwilliams Sep 7, 2024
b25de92
Allow minimal matching when using `run.waitFor()`
jpwilliams Sep 7, 2024
4532ddb
Sanitize provided subsets for matching to abstract ID hashing
jpwilliams Sep 7, 2024
a0004d1
Fix incorrect checkpoints being returned during testing
jpwilliams Sep 7, 2024
8040933
Refactor `t.executeAndWaitFor()` to allow more friendly API
jpwilliams Sep 7, 2024
b2d3c9f
Remove `@jest/globals`; remove immediate mocking
jpwilliams Sep 12, 2024
a063768
Merge branch 'main' into feat/inngest-test
jpwilliams Sep 12, 2024
409b524
Make sure we have `prettier`
jpwilliams Sep 12, 2024
40d15f9
Log the result when encountering an unexpected checkpoint
jpwilliams Sep 12, 2024
b5c5580
Try to re-add automatic mocks for `ctx.step`
jpwilliams Sep 12, 2024
ca8616c
Update `inngest` peer dep to `^3.22.12`
jpwilliams Sep 12, 2024
376ba09
Allow `state` assertions using `.resolves`/`,rejects` instead of proxies
jpwilliams Sep 12, 2024
1a2c705
Merge branch 'main' into feat/inngest-test
jpwilliams Sep 12, 2024
170e94f
Refactor to use `.execute()` and `.executeStep()`
jpwilliams Sep 13, 2024
74077ef
Add README.md
jpwilliams Sep 13, 2024
0482dcc
Add links to README
jpwilliams Sep 13, 2024
3bb13a6
Update README.md
jpwilliams Sep 16, 2024
b56dd0d
Merge branch 'main' into feat/inngest-test
jpwilliams Oct 14, 2024
3b8e43c
Create violet-bikes-study.md
jpwilliams Oct 14, 2024
1452f4d
Merge branch 'main' into feat/inngest-test
jpwilliams Oct 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/test/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
dist
node_modules
inngest-test.tgz
Empty file added packages/test/CHANGELOG.md
Empty file.
11 changes: 9 additions & 2 deletions packages/test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@
},
"scripts": {
"test": "jest",
"build": "pnpm run build:clean && pnpm run build:tsc && pnpm run build:copy",
"build:clean": "rm -rf ./dist",
"build:tsc": "tsc --project tsconfig.build.json",
"build:copy": "cp package.json LICENSE.md README.md dist",
"pack": "pnpm run build && yarn pack --verbose --frozen-lockfile --filename inngest-test.tgz --cwd dist"
"build:copy": "cp package.json LICENSE.md README.md CHANGELOG.md dist",
"pack": "pnpm run build && yarn pack --verbose --frozen-lockfile --filename inngest-test.tgz --cwd dist",
"postversion": "pnpm run build && pnpm run build:copy",
"release": "DIST_DIR=dist node ../../scripts/release/publish.js && pnpm dlx jsr publish --allow-slow-types --allow-dirty",
"release:version": "node ../../scripts/release/jsrVersion.js"
},
"exports": {
".": {
Expand All @@ -39,5 +43,8 @@
},
"dependencies": {
"ulid": "^2.3.0"
},
"peerDependencies": {
"inngest": "^3.22.9"
}
}
34 changes: 21 additions & 13 deletions packages/test/src/InngestTestEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ServerTiming } from "inngest/helpers/ServerTiming";
import { Context, EventPayload } from "inngest/types";
import { ulid } from "ulid";
import { InngestTestRun } from "./InngestTestRun.js";
import { createMockEvent, mockCtx } from "./util.js";
import { createMockEvent, mockCtx, type DeepPartial } from "./util.js";

/**
* A test engine for running Inngest functions in a test environment, providing
Expand Down Expand Up @@ -110,6 +110,23 @@ export namespace InngestTestEngine {
*/
export type InlineOptions = Omit<Options, "function">;

/**
* Options that can be passed to an initial execution that then waits for a
* particular checkpoint to occur.
*/
export type ExecuteAndWaitForOptions<
T extends InngestTestRun.CheckpointKey = InngestTestRun.CheckpointKey,
> = InlineOptions & {
/**
* An optional subset of the checkpoint to match against. Any checkpoint of
* this type will be matched.
*
* When providing a `subset`, use `expect` tooling such as
* `expect.stringContaining` to match partial values.
*/
subset?: DeepPartial<InngestTestRun.Checkpoint<T>>;
};

/**
* A mocked state object that allows you to assert step usage, input, and
* output.
Expand Down Expand Up @@ -186,28 +203,19 @@ export class InngestTestEngine {
* Is a shortcut for and uses `run.waitFor()`.
*/
public async executeAndWaitFor<T extends InngestTestRun.CheckpointKey>(
/**
* Options and state to start the run with.
*/
inlineOpts: InngestTestEngine.InlineOptions,

/**
* The checkpoint to wait for.
*/
checkpoint: T,

/**
* An optional subset of the checkpoint to match against. Any checkpoint of
* this type will be matched.
*
* When providing a `subset`, use `expect` tooling such as
* `expect.stringContaining` to match partial values.
* Options and state to start the run with.
*/
subset?: Partial<InngestTestRun.Checkpoint<T>>
inlineOpts?: InngestTestEngine.ExecuteAndWaitForOptions<T>
): Promise<InngestTestEngine.ExecutionOutput<T>> {
const { run } = await this.execute(inlineOpts);

return run.waitFor(checkpoint, subset);
return run.waitFor(checkpoint, inlineOpts?.subset);
}

/**
Expand Down
35 changes: 31 additions & 4 deletions packages/test/src/InngestTestRun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import type {
ExecutionResult,
ExecutionResults,
} from "inngest/components/execution/InngestExecution";
import { _internals } from "inngest/components/execution/v1";
import { createDeferredPromise } from "inngest/helpers/promises";
import type { InngestTestEngine } from "./InngestTestEngine.js";
import type { DeepPartial } from "./util";

/**
* A test run that allows you to wait for specific checkpoints in a run that
Expand Down Expand Up @@ -68,19 +70,44 @@ export class InngestTestRun {
* When providing a `subset`, use `expect` tooling such as
* `expect.stringContaining` to match partial values.
*/
subset?: Partial<InngestTestRun.Checkpoint<T>>
subset?: DeepPartial<InngestTestRun.Checkpoint<T>>
): Promise<InngestTestEngine.ExecutionOutput<T>> {
let finished = false;
const runningState: InngestTestEngine.InlineOptions = {};

const { promise, resolve } =
const { promise, resolve, reject } =
createDeferredPromise<InngestTestEngine.ExecutionOutput<T>>();

const finish = (output: InngestTestEngine.ExecutionOutput) => {
finished = true;

if (output.result.type !== checkpoint) {
reject(
new Error(
`Expected checkpoint "${checkpoint}" but got "${output.result.type}"`
)
);
}

resolve(output as InngestTestEngine.ExecutionOutput<T>);
};

/**
* Make sure we sanitize any given ID to prehash it for the user. This is
* abstracted from the user entirely so they shouldn't be expected to be
* providing hashes.
*/
const sanitizedSubset: typeof subset = subset && {
...subset,
...("step" in subset &&
typeof subset.step === "object" &&
subset.step !== null &&
"id" in subset.step &&
typeof subset.step.id === "string" && {
step: { ...subset.step, id: _internals.hashId(subset.step.id) },
}),
};

const processChain = async (targetStepId?: string) => {
if (finished) {
return;
Expand All @@ -93,8 +120,8 @@ export class InngestTestRun {

if (exec.result.type === checkpoint) {
try {
if (subset) {
expect(exec.result).toMatchObject(subset);
if (sanitizedSubset) {
expect(exec.result).toMatchObject(sanitizedSubset);
}

return finish(exec);
Expand Down
7 changes: 7 additions & 0 deletions packages/test/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,10 @@ export const createMockEvent = () => {
ts: Date.now(),
} satisfies EventPayload;
};

/**
* A deep partial, where every key of every object is optional.
*/
export type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
67 changes: 67 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading