@@ -5,6 +5,7 @@ import { OrchestrationExecutor } from "../src/worker/orchestration-executor";
55import { Registry } from "../src/worker/registry" ;
66import { OrchestrationContext } from "../src/task/context/orchestration-context" ;
77import { EntityInstanceId } from "../src/entities/entity-instance-id" ;
8+ import { EntityOperationFailedException } from "../src/entities/entity-operation-failed-exception" ;
89import * as pb from "../src/proto/orchestrator_service_pb" ;
910import * as ph from "../src/utils/pb-helper.util" ;
1011import { StringValue } from "google-protobuf/google/protobuf/wrappers_pb" ;
@@ -190,7 +191,7 @@ describe("OrchestrationExecutor Entity Operation Events", () => {
190191 } ) ;
191192
192193 describe ( "ENTITYOPERATIONFAILED" , ( ) => {
193- it ( "should fail entity call task with error details " , async ( ) => {
194+ it ( "should fail entity call task with EntityOperationFailedException " , async ( ) => {
194195 // Arrange
195196 let caughtError : Error | undefined ;
196197 const orchestrator = async function * ( ctx : OrchestrationContext ) : AsyncGenerator < Task < number > , string , number > {
@@ -225,10 +226,19 @@ describe("OrchestrationExecutor Entity Operation Events", () => {
225226
226227 await executor . execute ( "test-instance" , oldEvents2 , newEvents2 ) ;
227228
228- // Assert
229+ // Assert - error should be EntityOperationFailedException
229230 expect ( caughtError ) . toBeDefined ( ) ;
231+ expect ( caughtError ) . toBeInstanceOf ( EntityOperationFailedException ) ;
230232 expect ( caughtError ! . message ) . toContain ( "badOperation" ) ;
231233 expect ( caughtError ! . message ) . toContain ( "Operation not supported" ) ;
234+
235+ // Verify entity-specific context is preserved
236+ const entityError = caughtError as EntityOperationFailedException ;
237+ expect ( entityError . entityId . name ) . toBe ( "counter" ) ;
238+ expect ( entityError . entityId . key ) . toBe ( "my-counter" ) ;
239+ expect ( entityError . operationName ) . toBe ( "badOperation" ) ;
240+ expect ( entityError . failureDetails . errorType ) . toBe ( "InvalidOperationError" ) ;
241+ expect ( entityError . failureDetails . errorMessage ) . toBe ( "Operation not supported" ) ;
232242 } ) ;
233243
234244 it ( "should propagate failure to orchestration if not caught" , async ( ) => {
@@ -268,6 +278,49 @@ describe("OrchestrationExecutor Entity Operation Events", () => {
268278 const completeAction = completeActionWrapper ! . getCompleteorchestration ( ) ! ;
269279 expect ( completeAction . getOrchestrationstatus ( ) ) . toBe ( pb . OrchestrationStatus . ORCHESTRATION_STATUS_FAILED ) ;
270280 } ) ;
281+
282+ it ( "should throw EntityOperationFailedException details when uncaught" , async ( ) => {
283+ // Arrange — verify the uncaught path produces an EntityOperationFailedException in the
284+ // orchestration failure details message, matching the documented API contract
285+ const orchestrator = async function * ( ctx : OrchestrationContext ) : AsyncGenerator < Task < number > , string , number > {
286+ const entityId = new EntityInstanceId ( "counter" , "my-counter" ) ;
287+ yield ctx . entities . callEntity < number > ( entityId , "badOperation" ) ;
288+ return "should not reach here" ;
289+ } ;
290+
291+ registry . addNamedOrchestrator ( "TestOrchestrator" , orchestrator ) ;
292+
293+ const executor = new OrchestrationExecutor ( registry ) ;
294+
295+ const oldEvents : pb . HistoryEvent [ ] = [ ] ;
296+ const newEvents : pb . HistoryEvent [ ] = [
297+ ph . newOrchestratorStartedEvent ( new Date ( ) ) ,
298+ ph . newExecutionStartedEvent ( "TestOrchestrator" , "test-instance" , undefined ) ,
299+ ] ;
300+
301+ const result1 = await executor . execute ( "test-instance" , oldEvents , newEvents ) ;
302+ const requestId = result1 . actions [ 0 ] . getSendentitymessage ( ) ! . getEntityoperationcalled ( ) ! . getRequestid ( ) ;
303+
304+ // Fail the operation
305+ const oldEvents2 = [ ...newEvents ] ;
306+ const newEvents2 = [
307+ ph . newOrchestratorStartedEvent ( new Date ( ) ) ,
308+ newEntityOperationFailedEvent ( 100 , requestId , "ValidationError" , "Invalid input" ) ,
309+ ] ;
310+
311+ const result2 = await executor . execute ( "test-instance" , oldEvents2 , newEvents2 ) ;
312+
313+ // Assert - orchestration should fail with the EntityOperationFailedException message
314+ const completeActionWrapper = result2 . actions . find ( ( a ) => a . hasCompleteorchestration ( ) ) ;
315+ expect ( completeActionWrapper ) . toBeDefined ( ) ;
316+ const completeAction = completeActionWrapper ! . getCompleteorchestration ( ) ! ;
317+ expect ( completeAction . getOrchestrationstatus ( ) ) . toBe ( pb . OrchestrationStatus . ORCHESTRATION_STATUS_FAILED ) ;
318+ const failureDetails = completeAction . getFailuredetails ( ) ;
319+ expect ( failureDetails ) . toBeDefined ( ) ;
320+ expect ( failureDetails ! . getErrortype ( ) ) . toBe ( "EntityOperationFailedException" ) ;
321+ expect ( failureDetails ! . getErrormessage ( ) ) . toContain ( "badOperation" ) ;
322+ expect ( failureDetails ! . getErrormessage ( ) ) . toContain ( "Invalid input" ) ;
323+ } ) ;
271324 } ) ;
272325
273326 describe ( "Multiple entity calls" , ( ) => {
0 commit comments