Skip to content

Commit bf62322

Browse files
authored
test(core): cover state typing in dowhile/dountil loop conditions (#18689)
## Description Adds type-level regression tests for workflow `state` inference in `dowhile`/`dountil` condition callbacks. `createWorkflow` infers the workflow state type from `stateSchema`, so `state` inside loop conditions is typed from the schema (the same typing already available inside `createStep`). The existing `workflow-schema-types.test-d.ts` covers `inputData` in `dowhile`/`dountil` but never `state`, so this behavior could regress silently. This PR closes that gap with three `expectTypeOf` checks: - `state` is inferred from `stateSchema` in a `dowhile` condition (`attempts: number`, `lastError: string | undefined`) - same for a `dountil` condition - with no `stateSchema`, `state` stays `unknown` (backward-compat baseline) ```ts const workflow = createWorkflow({ id: 'my-workflow', inputSchema: z.object({ value: z.number() }), outputSchema: z.object({ value: z.number() }), stateSchema: z.object({ attempts: z.number() }), }).dowhile(step, async ({ state }) => { // state.attempts is typed as `number` return state.attempts < 5; }); ``` ## Related issue(s) Closes #14627. The original report (state typed `unknown` in `dowhile`/`dountil` conditions) no longer reproduces on `main` — it was resolved by the `createWorkflow` schema-generic refactor in #18608. This PR does not change runtime or types; it only adds the missing regression coverage so the fix cannot silently regress, which is why it uses `Refs` rather than `Closes`. ## Type of change - [x] Test update ## Checklist - [x] I have linked the related issue(s) in the description above - [ ] I have made corresponding changes to the documentation (if applicable) - [x] I have added tests that prove my fix is effective or that my feature works - [x] I have addressed all Coderabbit comments on this PR
1 parent 06ff9e0 commit bf62322

1 file changed

Lines changed: 68 additions & 0 deletions

File tree

packages/core/src/workflows/workflow-schema-types.test-d.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,4 +550,72 @@ describe('Workflow schema type inference', () => {
550550
workflow.parallel([step]);
551551
});
552552
});
553+
554+
// Regression: https://github.com/mastra-ai/mastra/issues/14627
555+
// createWorkflow must infer the workflow state type from stateSchema so that
556+
// `state` in dowhile/dountil condition callbacks is typed from the schema,
557+
// not `unknown`. (state was already correct inside createStep; the factory
558+
// is what lost the inference.) These tests guard against regressing it.
559+
describe('stateSchema inference (issue #14627)', () => {
560+
const stateSchema = z.object({
561+
attempts: z.number(),
562+
lastError: z.string().optional(),
563+
});
564+
const ioSchema = z.object({ value: z.number() });
565+
566+
const loopStep = createStep({
567+
id: 'loop-step',
568+
inputSchema: ioSchema,
569+
outputSchema: ioSchema,
570+
stateSchema,
571+
execute: async ({ inputData }) => ({ value: inputData.value + 1 }),
572+
});
573+
574+
it('types `state` in the dowhile condition from the workflow stateSchema', () => {
575+
const workflow = createWorkflow({
576+
id: 'dowhile-state',
577+
inputSchema: ioSchema,
578+
outputSchema: ioSchema,
579+
stateSchema,
580+
});
581+
582+
workflow.dowhile(loopStep, async ({ state }) => {
583+
expectTypeOf(state).not.toBeUnknown();
584+
expectTypeOf(state).not.toBeAny();
585+
expectTypeOf(state.attempts).toBeNumber();
586+
expectTypeOf(state.lastError).toEqualTypeOf<string | undefined>();
587+
return state.attempts < 5;
588+
});
589+
});
590+
591+
it('types `state` in the dountil condition from the workflow stateSchema', () => {
592+
const workflow = createWorkflow({
593+
id: 'dountil-state',
594+
inputSchema: ioSchema,
595+
outputSchema: ioSchema,
596+
stateSchema,
597+
});
598+
599+
workflow.dountil(loopStep, async ({ state }) => {
600+
expectTypeOf(state).not.toBeUnknown();
601+
expectTypeOf(state).not.toBeAny();
602+
expectTypeOf(state.attempts).toBeNumber();
603+
expectTypeOf(state.lastError).toEqualTypeOf<string | undefined>();
604+
return state.attempts >= 5;
605+
});
606+
});
607+
608+
it('leaves `state` as unknown when no stateSchema is declared', () => {
609+
const workflow = createWorkflow({
610+
id: 'no-state',
611+
inputSchema: ioSchema,
612+
outputSchema: ioSchema,
613+
});
614+
615+
workflow.dowhile(loopStep, async ({ state }) => {
616+
expectTypeOf(state).toBeUnknown();
617+
return true;
618+
});
619+
});
620+
});
553621
});

0 commit comments

Comments
 (0)