1717
1818import static io .serverlessworkflow .fluent .func .dsl .FuncDSL .agent ;
1919import static io .serverlessworkflow .fluent .func .dsl .FuncDSL .withUniqueId ;
20- import static org .junit .jupiter .api .Assertions .assertEquals ;
21- import static org .junit .jupiter .api .Assertions .assertNotNull ;
22- import static org .junit .jupiter .api .Assertions .fail ;
20+ import static org .junit .jupiter .api .Assertions .*;
21+ import static org .mockito .Mockito .*;
2322
2423import io .serverlessworkflow .api .types .Task ;
25- import io .serverlessworkflow .api .types .TaskBase ;
2624import io .serverlessworkflow .api .types .TaskItem ;
2725import io .serverlessworkflow .api .types .Workflow ;
2826import io .serverlessworkflow .api .types .func .CallJava ;
2927import io .serverlessworkflow .api .types .func .JavaFilterFunction ;
3028import io .serverlessworkflow .fluent .func .dsl .UniqueIdBiFunction ;
3129import io .serverlessworkflow .impl .TaskContextData ;
3230import io .serverlessworkflow .impl .WorkflowContextData ;
33- import io .serverlessworkflow .impl .WorkflowDefinitionData ;
3431import io .serverlessworkflow .impl .WorkflowInstanceData ;
35- import io .serverlessworkflow .impl .WorkflowModel ;
3632import io .serverlessworkflow .impl .WorkflowPosition ;
37- import io .serverlessworkflow .impl .WorkflowStatus ;
38- import java .time .Instant ;
3933import java .util .List ;
4034import java .util .concurrent .atomic .AtomicReference ;
4135import org .junit .jupiter .api .DisplayName ;
4236import org .junit .jupiter .api .Test ;
4337
4438/**
4539 * Verifies that withUniqueId/agent wrap the user's function so that, at runtime, the first argument
46- * is a "unique id" composed as instanceId + "-" + taskName .
40+ * is a "unique id" composed as instanceId + "-" + jsonPointer (e.g., inst-123-/do/0/task) .
4741 */
4842class FuncDSLUniqueIdTest {
4943
@@ -57,12 +51,12 @@ private static JavaFilterFunction<Object, Object> extractJavaFilterFunction(Call
5751 }
5852
5953 @ Test
60- @ DisplayName ("withUniqueId(name, fn, in) composes uniqueId = instanceId-taskName and passes it" )
61- void withUniqueId_named_composes_and_passes_unique_id () throws Exception {
54+ @ DisplayName (
55+ "withUniqueId(name, fn, in) composes uniqueId = instanceId-jsonPointer and passes it" )
56+ void withUniqueId_uses_json_pointer_for_unique_id () throws Exception {
6257 AtomicReference <String > receivedUniqueId = new AtomicReference <>();
6358 AtomicReference <String > receivedPayload = new AtomicReference <>();
6459
65- // (uniqueId, payload) -> result; we capture inputs for assertion
6660 UniqueIdBiFunction <String , String > fn =
6761 (uniqueId , payload ) -> {
6862 receivedUniqueId .set (uniqueId );
@@ -72,39 +66,45 @@ void withUniqueId_named_composes_and_passes_unique_id() throws Exception {
7266
7367 Workflow wf =
7468 FuncWorkflowBuilder .workflow ("wf-unique-named" )
75- .tasks (
76- // important: NAME is provided → should appear in the uniqueId
77- withUniqueId ("notify" , fn , String .class ))
69+ .tasks (withUniqueId ("notify" , fn , String .class ))
7870 .build ();
7971
8072 List <TaskItem > items = wf .getDo ();
8173 assertEquals (1 , items .size (), "one task expected" );
8274 Task t = items .get (0 ).getTask ();
8375 assertNotNull (t .getCallTask (), "CallTask expected" );
8476
85- String taskName = items .get (0 ).getName ();
86- assertEquals ("notify" , taskName , "task name should be set on the step" );
87-
8877 CallJava cj = (CallJava ) t .getCallTask ().get ();
8978 var jff = extractJavaFilterFunction (cj );
9079 assertNotNull (jff , "JavaFilterFunction must be present for withUniqueId" );
9180
92- // Invoke the wrapped function "as runtime" with fake contexts
93- var wctx = new FakeWorkflowContextData ("inst-123" );
94- var tctx = new FakeTaskContextData (taskName );
81+ // Mockito stubs for runtime contexts
82+ WorkflowInstanceData inst = mock (WorkflowInstanceData .class );
83+ when (inst .id ()).thenReturn ("inst-123" );
84+
85+ WorkflowContextData wctx = mock (WorkflowContextData .class );
86+ when (wctx .instanceData ()).thenReturn (inst );
87+
88+ // Use JSON Pointer for the unique component instead of task name
89+ final String pointer = "/do/0/task" ;
90+ WorkflowPosition pos = mock (WorkflowPosition .class );
91+ when (pos .jsonPointer ()).thenReturn (pointer );
92+
93+ TaskContextData tctx = mock (TaskContextData .class );
94+ when (tctx .position ()).thenReturn (pos );
9595
96- // The JavaFilterFunction signature in your impl is (payload, wctx, tctx) -> result
9796 Object result = jff .apply ("hello" , wctx , tctx );
9897
99- assertEquals ("inst-123-notify" , receivedUniqueId .get (), "uniqueId must be instanceId-taskName" );
98+ assertEquals (
99+ "inst-123-" + pointer , receivedUniqueId .get (), "uniqueId must be instanceId-jsonPointer" );
100100 assertEquals (
101101 "hello" , receivedPayload .get (), "payload should be forwarded to the user function" );
102102 assertEquals ("HELLO" , result , "wrapped function result should be returned" );
103103 }
104104
105105 @ Test
106- @ DisplayName ("agent(fn, in) is sugar for withUniqueId(fn, in) and passes instanceId-taskName " )
107- void agent_unnamed_composes_and_passes_unique_id () throws Exception {
106+ @ DisplayName ("agent(fn, in) composes uniqueId = instanceId-jsonPointer and passes it " )
107+ void agent_uses_json_pointer_for_unique_id () throws Exception {
108108 AtomicReference <String > receivedUniqueId = new AtomicReference <>();
109109 AtomicReference <Integer > receivedPayload = new AtomicReference <>();
110110
@@ -115,146 +115,35 @@ void agent_unnamed_composes_and_passes_unique_id() throws Exception {
115115 return payload + 1 ;
116116 };
117117
118- Workflow wf =
119- FuncWorkflowBuilder .workflow ("wf-agent" )
120- .tasks (
121- // No explicit name here; builder should still set a task name,
122- // which participates in the uniqueId (instanceId-taskName)
123- agent (fn , Integer .class ))
124- .build ();
118+ Workflow wf = FuncWorkflowBuilder .workflow ("wf-agent" ).tasks (agent (fn , Integer .class )).build ();
125119
126120 List <TaskItem > items = wf .getDo ();
127121 assertEquals (1 , items .size (), "one task expected" );
128122 Task t = items .get (0 ).getTask ();
129123 assertNotNull (t .getCallTask (), "CallTask expected" );
130- String taskName = items .get (0 ).getName ();
131- assertNotNull (taskName , "task name should be assigned even if not explicitly provided" );
132124
133125 CallJava cj = (CallJava ) t .getCallTask ().get ();
134126 var jff = extractJavaFilterFunction (cj );
135127 assertNotNull (jff , "JavaFilterFunction must be present for agent/withUniqueId" );
136128
137- WorkflowContextData wctx = new FakeWorkflowContextData ("wf-999" );
138- TaskContextData tctx = new FakeTaskContextData (taskName );
129+ WorkflowInstanceData inst = mock (WorkflowInstanceData .class );
130+ when (inst .id ()).thenReturn ("wf-999" );
131+
132+ WorkflowContextData wctx = mock (WorkflowContextData .class );
133+ when (wctx .instanceData ()).thenReturn (inst );
134+
135+ final String pointer = "/do/0/task" ;
136+ WorkflowPosition pos = mock (WorkflowPosition .class );
137+ when (pos .jsonPointer ()).thenReturn (pointer );
138+
139+ TaskContextData tctx = mock (TaskContextData .class );
140+ when (tctx .position ()).thenReturn (pos );
139141
140142 Object result = jff .apply (41 , wctx , tctx );
141143
142144 assertEquals (
143- "wf-999-" + taskName ,
144- receivedUniqueId .get (),
145- "agent should compose uniqueId as instanceId-taskName" );
145+ "wf-999-" + pointer , receivedUniqueId .get (), "uniqueId must be instanceId-jsonPointer" );
146146 assertEquals (41 , receivedPayload .get (), "payload should be forwarded to the user function" );
147147 assertEquals (42 , result , "wrapped function result should be returned" );
148148 }
149-
150- /**
151- * Minimal test doubles to satisfy the JavaFilterFunction call path. We only implement the members
152- * used by the DSL composition: - wctx.instanceData().id() - tctx.taskName()
153- */
154- static final class FakeWorkflowContextData implements WorkflowContextData {
155- private final String id ;
156-
157- FakeWorkflowContextData (String id ) {
158- this .id = id ;
159- }
160-
161- @ Override
162- public WorkflowInstanceData instanceData () {
163- // Provide just the id() accessor
164- return new WorkflowInstanceData () {
165- @ Override
166- public String id () {
167- return id ;
168- }
169-
170- @ Override
171- public Instant startedAt () {
172- return null ;
173- }
174-
175- @ Override
176- public Instant completedAt () {
177- return null ;
178- }
179-
180- @ Override
181- public WorkflowModel input () {
182- return null ;
183- }
184-
185- @ Override
186- public WorkflowStatus status () {
187- return null ;
188- }
189-
190- @ Override
191- public WorkflowModel output () {
192- return null ;
193- }
194-
195- @ Override
196- public WorkflowModel context () {
197- return null ;
198- }
199-
200- @ Override
201- public <T > T outputAs (Class <T > clazz ) {
202- return null ;
203- }
204- };
205- }
206-
207- @ Override
208- public WorkflowModel context () {
209- return null ;
210- }
211-
212- @ Override
213- public WorkflowDefinitionData definition () {
214- return null ;
215- }
216- }
217-
218- record FakeTaskContextData (String taskName ) implements TaskContextData {
219-
220- @ Override
221- public WorkflowModel input () {
222- return null ;
223- }
224-
225- @ Override
226- public WorkflowModel rawInput () {
227- return null ;
228- }
229-
230- @ Override
231- public TaskBase task () {
232- return null ;
233- }
234-
235- @ Override
236- public WorkflowModel rawOutput () {
237- return null ;
238- }
239-
240- @ Override
241- public WorkflowModel output () {
242- return null ;
243- }
244-
245- @ Override
246- public WorkflowPosition position () {
247- return null ;
248- }
249-
250- @ Override
251- public Instant startedAt () {
252- return null ;
253- }
254-
255- @ Override
256- public Instant completedAt () {
257- return null ;
258- }
259- }
260149}
0 commit comments