Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@ export class RuntimeOrchestrationContext extends OrchestrationContext {
async run(generator: Generator<Task<any>, any, any>) {
this._generator = generator;

// TODO: do something with this task
// start the generator
// Start the generator
const { value, done } = await this._generator.next();

// if the generator finished, complete the orchestration.
Expand All @@ -119,12 +118,15 @@ export class RuntimeOrchestrationContext extends OrchestrationContext {
return;
}

// TODO: check if the task is null?
if (!(value instanceof Task)) {
throw new Error("The orchestrator generator yielded a non-Task object");
}

this._previousTask = value;

// If the yielded task is already complete (e.g., whenAll with an empty array),
// resume immediately so the generator can continue.
if (this._previousTask instanceof Task && this._previousTask.isComplete) {
if (this._previousTask.isComplete) {
await this.resume();
}
}
Expand Down
63 changes: 63 additions & 0 deletions packages/durabletask-js/test/orchestration_executor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1549,6 +1549,69 @@
expect(completeAction?.getResult()?.getValue()).toEqual(JSON.stringify(expectedResult));
});

it.each([
{ description: "null", yieldedValue: null as any },
{ description: "undefined", yieldedValue: undefined as any },
])(
"should fail when orchestrator yields $description as its first value",
async ({ description, yieldedValue }) => {

Check failure on line 1557 in packages/durabletask-js/test/orchestration_executor.spec.ts

View workflow job for this annotation

GitHub Actions / lint-and-unit-tests

'description' is defined but never used. Allowed unused args must match /^_/u
// An orchestrator that yields a non-Task value as its first yield should fail with a clear error
const badOrchestrator: TOrchestrator = async function* (_ctx: OrchestrationContext): any {
yield yieldedValue;
};

const registry = new Registry();
const name = registry.addOrchestrator(badOrchestrator);
const newEvents = [
newOrchestratorStartedEvent(),
newExecutionStartedEvent(name, TEST_INSTANCE_ID, undefined),
];
const executor = new OrchestrationExecutor(registry, testLogger);
const result = await executor.execute(TEST_INSTANCE_ID, [], newEvents);
const completeAction = getAndValidateSingleCompleteOrchestrationAction(result);
expect(completeAction?.getOrchestrationstatus()).toEqual(pb.OrchestrationStatus.ORCHESTRATION_STATUS_FAILED);
expect(completeAction?.getFailuredetails()?.getErrormessage()).toContain("non-Task");
}
);

it("should fail when orchestrator yields a plain object as its first value", async () => {
// An orchestrator that yields a non-Task object should fail with a clear error
const badOrchestrator: TOrchestrator = async function* (_ctx: OrchestrationContext): any {
yield { someProperty: "not a task" };
};

const registry = new Registry();
const name = registry.addOrchestrator(badOrchestrator);
const newEvents = [
newOrchestratorStartedEvent(),
newExecutionStartedEvent(name, TEST_INSTANCE_ID, undefined),
];
const executor = new OrchestrationExecutor(registry, testLogger);
const result = await executor.execute(TEST_INSTANCE_ID, [], newEvents);
const completeAction = getAndValidateSingleCompleteOrchestrationAction(result);
expect(completeAction?.getOrchestrationstatus()).toEqual(pb.OrchestrationStatus.ORCHESTRATION_STATUS_FAILED);
expect(completeAction?.getFailuredetails()?.getErrormessage()).toContain("non-Task");
});

it("should fail when orchestrator yields a primitive as its first value", async () => {
// An orchestrator that yields a primitive (number) instead of a Task should fail
const badOrchestrator: TOrchestrator = async function* (_ctx: OrchestrationContext): any {
yield 42;
};

const registry = new Registry();
const name = registry.addOrchestrator(badOrchestrator);
const newEvents = [
newOrchestratorStartedEvent(),
newExecutionStartedEvent(name, TEST_INSTANCE_ID, undefined),
];
const executor = new OrchestrationExecutor(registry, testLogger);
const result = await executor.execute(TEST_INSTANCE_ID, [], newEvents);
const completeAction = getAndValidateSingleCompleteOrchestrationAction(result);
expect(completeAction?.getOrchestrationstatus()).toEqual(pb.OrchestrationStatus.ORCHESTRATION_STATUS_FAILED);
expect(completeAction?.getFailuredetails()?.getErrormessage()).toContain("non-Task");
});

it("should propagate inner whenAll failure to outer whenAny in nested composites", async () => {
const hello = (_: any, name: string) => {
return `Hello ${name}!`;
Expand Down
Loading