1010import io .temporal .activity .ActivityInfo ;
1111import io .temporal .activity .ActivityInterface ;
1212import io .temporal .activity .ActivityMethod ;
13+ import io .temporal .api .activity .v1 .ActivityExecutionInfo ;
1314import io .temporal .api .enums .v1 .ActivityExecutionStatus ;
1415import io .temporal .api .enums .v1 .ActivityIdConflictPolicy ;
1516import io .temporal .api .enums .v1 .ActivityIdReusePolicy ;
17+ import io .temporal .api .failure .v1 .Failure ;
1618import io .temporal .client .*;
19+ import io .temporal .common .RetryOptions ;
1720import io .temporal .common .interceptors .ActivityClientCallsInterceptor ;
1821import io .temporal .common .interceptors .ActivityClientCallsInterceptor .*;
1922import io .temporal .common .interceptors .ActivityClientCallsInterceptorBase ;
2023import io .temporal .common .interceptors .ActivityClientInterceptorBase ;
24+ import io .temporal .failure .ApplicationFailure ;
2125import io .temporal .failure .CanceledFailure ;
2226import io .temporal .testing .internal .SDKTestWorkflowRule ;
2327import java .time .Duration ;
@@ -86,6 +90,12 @@ public interface ConcatActivity {
8690 String concat (String a , String b );
8791 }
8892
93+ @ ActivityInterface
94+ public interface AlwaysFailActivity {
95+ @ ActivityMethod (name = "AlwaysFail" )
96+ void alwaysFail ();
97+ }
98+
8999 /** Snapshot of {@link ActivityInfo} fields captured inside an activity body. */
90100 public static class ActivityInfoSnapshot {
91101 public String activityId ;
@@ -183,6 +193,13 @@ public String concat(String a, String b) {
183193 }
184194 }
185195
196+ public static class AlwaysFailActivityImpl implements AlwaysFailActivity {
197+ @ Override
198+ public void alwaysFail () {
199+ throw ApplicationFailure .newFailure ("deliberate failure" , "test-type" );
200+ }
201+ }
202+
186203 // ---------------------------------------------------------------------------
187204 // Test rule
188205 // ---------------------------------------------------------------------------
@@ -197,7 +214,8 @@ public String concat(String a, String b) {
197214 new AsyncCompletionActivityImpl (),
198215 new InspectInfoActivityImpl (),
199216 new EchoVoidActivityImpl (),
200- new ConcatActivityImpl ())
217+ new ConcatActivityImpl (),
218+ new AlwaysFailActivityImpl ())
201219 .build ();
202220
203221 // ---------------------------------------------------------------------------
@@ -812,6 +830,65 @@ public void testCompletionClientStandaloneCompleteWithRunId() throws Interrupted
812830 }
813831 }
814832
833+ @ Test
834+ public void testDescribeRawInfoMatchesTypedAccessors () {
835+ assumeTrue (SDKTestWorkflowRule .useExternalService );
836+ ActivityClient client = newActivityClient ();
837+ String activityId = uniqueId ();
838+
839+ ActivityHandle <String > handle =
840+ client .start (SimpleActivity .class , SimpleActivity ::execute , simpleOpts (activityId ), "raw" );
841+ handle .getResult ();
842+
843+ ActivityExecutionDescription desc = handle .describe ();
844+ ActivityExecutionInfo rawInfo = desc .getRawInfo ();
845+
846+ assertEquals (desc .getActivityId (), rawInfo .getActivityId ());
847+ assertEquals (desc .getActivityType (), rawInfo .getActivityType ().getName ());
848+ assertEquals (desc .getTaskQueue (), rawInfo .getTaskQueue ());
849+ assertEquals (desc .getStatus (), rawInfo .getStatus ());
850+ assertEquals (desc .getAttempt (), rawInfo .getAttempt ());
851+ }
852+
853+ @ Test
854+ public void testDescribeLastFailureIsPopulatedDuringRetryBackoff () {
855+ assumeTrue (SDKTestWorkflowRule .useExternalService );
856+ ActivityClient client = newActivityClient ();
857+ StartActivityOptions opts =
858+ StartActivityOptions .newBuilder ()
859+ .setId (uniqueId ())
860+ .setTaskQueue (testWorkflowRule .getTaskQueue ())
861+ .setScheduleToCloseTimeout (Duration .ofMinutes (5 ))
862+ .setStartToCloseTimeout (Duration .ofSeconds (10 ))
863+ .setRetryOptions (
864+ RetryOptions .newBuilder ()
865+ .setMaximumAttempts (10 )
866+ .setInitialInterval (Duration .ofSeconds (15 ))
867+ .build ())
868+ .build ();
869+
870+ ActivityHandle <Void > handle =
871+ client .start (AlwaysFailActivity .class , AlwaysFailActivity ::alwaysFail , opts );
872+ try {
873+ assertEventually (
874+ Duration .ofSeconds (60 ),
875+ () -> {
876+ ActivityExecutionDescription desc = handle .describe ();
877+ Failure lastFailure = desc .getLastFailure ();
878+ assertNotNull ("last_failure should be set after a failed attempt" , lastFailure );
879+ assertEquals ("deliberate failure" , lastFailure .getMessage ());
880+ // raw info must agree with the typed accessor
881+ assertTrue (desc .getRawInfo ().hasLastFailure ());
882+ assertEquals (lastFailure , desc .getRawInfo ().getLastFailure ());
883+ });
884+ } finally {
885+ try {
886+ handle .terminate ("test cleanup" );
887+ } catch (Exception ignored ) {
888+ }
889+ }
890+ }
891+
815892 // ---------------------------------------------------------------------------
816893 // Interceptor helpers
817894 // ---------------------------------------------------------------------------
0 commit comments