diff --git a/packages/durabletask-js/src/testing/in-memory-backend.ts b/packages/durabletask-js/src/testing/in-memory-backend.ts index 66cb406..a0ca836 100644 --- a/packages/durabletask-js/src/testing/in-memory-backend.ts +++ b/packages/durabletask-js/src/testing/in-memory-backend.ts @@ -4,6 +4,7 @@ import * as pb from "../proto/orchestrator_service_pb"; import * as pbh from "../utils/pb-helper.util"; import { OrchestrationStatus as ClientOrchestrationStatus } from "../orchestration/enum/orchestration-status.enum"; +import { ParentOrchestrationInstance } from "../types/parent-orchestration-instance.type"; /** * Internal orchestration instance state stored by the in-memory backend. @@ -82,6 +83,7 @@ export class InMemoryOrchestrationBackend { name: string, input?: string, scheduledStartTime?: Date, + parentInstance?: ParentOrchestrationInstance, ): string { if (this.instances.has(instanceId)) { throw new Error(`Orchestration instance '${instanceId}' already exists`); @@ -104,7 +106,7 @@ export class InMemoryOrchestrationBackend { // Add initial events to start the orchestration const orchestratorStarted = pbh.newOrchestratorStartedEvent(startTime); - const executionStarted = pbh.newExecutionStartedEvent(name, instanceId, input); + const executionStarted = pbh.newExecutionStartedEvent(name, instanceId, input, parentInstance); instance.pendingEvents.push(orchestratorStarted); instance.pendingEvents.push(executionStarted); @@ -580,9 +582,13 @@ export class InMemoryOrchestrationBackend { instance.status = pb.OrchestrationStatus.ORCHESTRATION_STATUS_RUNNING; } - // Create the sub-orchestration + // Create the sub-orchestration with parent instance info try { - this.createInstance(subInstanceId, name, input); + this.createInstance(subInstanceId, name, input, undefined, { + name: instance.name, + instanceId: instance.instanceId, + taskScheduledId: taskId, + }); // Watch for sub-orchestration completion this.watchSubOrchestration(instance.instanceId, subInstanceId, taskId); diff --git a/packages/durabletask-js/test/in-memory-backend.spec.ts b/packages/durabletask-js/test/in-memory-backend.spec.ts index 81a82a8..0374170 100644 --- a/packages/durabletask-js/test/in-memory-backend.spec.ts +++ b/packages/durabletask-js/test/in-memory-backend.spec.ts @@ -196,6 +196,57 @@ describe("In-Memory Backend", () => { expect(state?.serializedOutput).toContain("caught:"); }); + it("should set parent instance info on sub-orchestrations", async () => { + let capturedParent: import("../src").ParentOrchestrationInstance | undefined; + + const childOrchestrator: TOrchestrator = async (ctx: OrchestrationContext) => { + capturedParent = ctx.parent; + return "child-done"; + }; + + const parentOrchestrator: TOrchestrator = async function* (ctx: OrchestrationContext): any { + const result = yield ctx.callSubOrchestrator(childOrchestrator); + return result; + }; + + worker.addOrchestrator(childOrchestrator); + worker.addOrchestrator(parentOrchestrator); + await worker.start(); + + const id = await client.scheduleNewOrchestration(parentOrchestrator); + const state = await client.waitForOrchestrationCompletion(id, true, 10); + + expect(state).toBeDefined(); + expect(state?.runtimeStatus).toEqual(OrchestrationStatus.COMPLETED); + + // Verify parent instance info was populated on the sub-orchestration context + expect(capturedParent).toBeDefined(); + expect(capturedParent?.name).toEqual(getName(parentOrchestrator)); + expect(capturedParent?.instanceId).toEqual(id); + expect(typeof capturedParent?.taskScheduledId).toEqual("number"); + }); + + it("should not set parent instance info on top-level orchestrations", async () => { + let capturedParent: import("../src").ParentOrchestrationInstance | undefined; + + const topLevelOrchestrator: TOrchestrator = async (ctx: OrchestrationContext) => { + capturedParent = ctx.parent; + return "done"; + }; + + worker.addOrchestrator(topLevelOrchestrator); + await worker.start(); + + const id = await client.scheduleNewOrchestration(topLevelOrchestrator); + const state = await client.waitForOrchestrationCompletion(id, true, 10); + + expect(state).toBeDefined(); + expect(state?.runtimeStatus).toEqual(OrchestrationStatus.COMPLETED); + + // Top-level orchestrations should have no parent + expect(capturedParent).toBeUndefined(); + }); + it("should handle external events", async () => { const orchestrator: TOrchestrator = async function* (ctx: OrchestrationContext): any { const value = yield ctx.waitForExternalEvent("my_event");