@@ -24,6 +24,7 @@ import me.snoty.backend.wiring.flow.execution.FlowExecutionEventService
2424import me.snoty.backend.wiring.node.NodeRegistryImpl
2525import me.snoty.integration.common.snotyJson
2626import me.snoty.integration.common.wiring.data.IntermediateData
27+ import me.snoty.integration.common.wiring.data.NodeInput
2728import me.snoty.integration.common.wiring.data.impl.SimpleIntermediateData
2829import me.snoty.integration.common.wiring.flow.Workflow
2930import me.snoty.integration.common.wiring.flow.WorkflowWithNodes
@@ -53,13 +54,17 @@ class FlowRunnerImplTest {
5354 }
5455 }
5556
56- fun nodeMetadata (name : String ) = nodeMetadata(namespace = namespace, name = name)
57+ fun nodeMetadata (name : String , receiveEmptyInput : Boolean ) = nodeMetadata(namespace = namespace, name = name, receiveEmptyInput = receiveEmptyInput )
5758
5859 private val mapHandler = GlobalMapHandler ()
60+ private val wantsEmptyProvidesNonEmptyHandler = WantsEmptyProvidesNonEmptyHandler ()
61+ private val wantsNonEmptyProvidesEmptyHandler = WantsNonEmptyProvidesEmptyHandler ()
5962 private val nodeRegistry = NodeRegistryImpl ().apply {
60- registerHandler(nodeMetadata(name = TYPE_MAP ), mapHandler)
61- registerHandler(nodeMetadata(name = TYPE_QUOTE ), QuoteHandler )
62- registerHandler(nodeMetadata(name = TYPE_EXCEPTION ), ExceptionHandler )
63+ registerHandler(nodeMetadata(name = TYPE_MAP , false ), mapHandler)
64+ registerHandler(nodeMetadata(name = TYPE_QUOTE , false ), QuoteHandler )
65+ registerHandler(nodeMetadata(name = TYPE_EXCEPTION , false ), ExceptionHandler )
66+ registerHandler(nodeMetadata(name = TYPE_WANTS_EMPTY_PROVIDES_NONEMPTY , true ), wantsEmptyProvidesNonEmptyHandler)
67+ registerHandler(nodeMetadata(name = TYPE_WANTS_NONEMPTY_PROVIDES_EMPTY , false ), wantsNonEmptyProvidesEmptyHandler)
6368 registerEmitHandler()
6469 }
6570 private val otel = createOpenTelemetry()
@@ -76,11 +81,11 @@ class FlowRunnerImplTest {
7681 appender.start()
7782 }
7883
79- private suspend fun FlowRunnerImpl.executeStartNode (jobId : String , flow : WorkflowWithNodes , input : IntermediateData ) {
84+ private suspend fun FlowRunnerImpl.executeStartNode (jobId : String , flow : WorkflowWithNodes , input : NodeInput ) {
8085 // set in the scheduler
8186 KMDC .put(JOB_ID , jobId)
8287 withContext(MDCContext ()) {
83- execute(jobId, FlowTriggerReason .Unknown , logger, Level .DEBUG , flow, listOf ( input) )
88+ execute(jobId, FlowTriggerReason .Unknown , logger, Level .DEBUG , flow, input)
8489 }
8590 }
8691
@@ -108,9 +113,9 @@ class FlowRunnerImplTest {
108113 val emit = emitNode(node)
109114 val flow = relationalFlow(emit, node)
110115 val jobId = " basic"
111- runner.executeStartNode(jobId, flow, intermediateData)
116+ runner.executeStartNode(jobId, flow, listOf ( intermediateData) )
112117 assertNoWarnings(flow)
113- assertEquals(intermediateDataRaw , mapHandler[node._id ])
118+ assertEquals(listOf (intermediateData) , mapHandler[node._id ])
114119 val spans = otel.spanExporter.finishedSpanItems
115120 .sortedBy { it.startEpochNanos }
116121
@@ -130,9 +135,9 @@ class FlowRunnerImplTest {
130135 val emit = emitNode(processor)
131136 val flow = relationalFlow(emit, processor, map)
132137
133- runner.executeStartNode(" basic withQuote" , flow, intermediateData)
138+ runner.executeStartNode(" basic withQuote" , flow, listOf ( intermediateData) )
134139 assertNoWarnings(flow)
135- assertEquals(" 'test'" , mapHandler[map._id ])
140+ assertEquals(" 'test'" , mapHandler[map._id ]?.single()?.value )
136141
137142 val spans = otel.spanExporter.finishedSpanItems
138143 assertEquals(4 , spans.size)
@@ -149,9 +154,9 @@ class FlowRunnerImplTest {
149154 val flow = relationalFlow(emit, node)
150155
151156 suspend fun verifyTrace (flow : WorkflowWithNodes , input : IntermediateData , withConfig : Boolean ) {
152- runner.executeStartNode(" traces config attribute" , flow, input)
157+ runner.executeStartNode(" traces config attribute" , flow, listOf ( input) )
153158 assertNoWarnings(flow)
154- assertEquals(input.value , mapHandler[node._id ])
159+ assertEquals(input, mapHandler[node._id ]?.single() )
155160 val spans = otel.spanExporter.finishedSpanItems
156161 .sortedBy { it.startEpochNanos }
157162
@@ -185,7 +190,7 @@ class FlowRunnerImplTest {
185190 val flow = relationalFlow(emit, mapNode, exNode)
186191
187192 assertThrows<FlowExecutionException > {
188- runner.executeStartNode(" traces exception attributes" , flow, intermediateData)
193+ runner.executeStartNode(" traces exception attributes" , flow, listOf ( intermediateData) )
189194 }
190195 assertNull(mapHandler[exNode._id ])
191196 val spans = otel.spanExporter.finishedSpanItems
@@ -220,4 +225,28 @@ class FlowRunnerImplTest {
220225 assertEquals(FlowExecutionException ::class .qualifiedName, rootExceptionEvent.attributes.get(ExceptionAttributes .EXCEPTION_TYPE ))
221226 assertNotNull(rootExceptionEvent.attributes.get(ExceptionAttributes .EXCEPTION_MESSAGE ))
222227 }
228+
229+ @Test
230+ fun `test receive empty input transitively - #225` () = runBlocking {
231+ val nodex = node(NodeDescriptor (namespace, TYPE_WANTS_EMPTY_PROVIDES_NONEMPTY ))
232+ val nodex1 = node(NodeDescriptor (namespace, TYPE_QUOTE ), next = listOf (nodex))
233+ val nodex2 = node(NodeDescriptor (namespace, TYPE_WANTS_EMPTY_PROVIDES_NONEMPTY ), next = listOf (nodex1))
234+ val nodex3 = node(NodeDescriptor (namespace, TYPE_WANTS_NONEMPTY_PROVIDES_EMPTY ), next = listOf (nodex2)) // also skipped
235+ val nodex4 = node(NodeDescriptor (namespace, TYPE_WANTS_NONEMPTY_PROVIDES_EMPTY ), next = listOf (nodex3)) // skipped
236+ val emit = emitNode(nodex4) // won't emit anything
237+
238+ val flow = relationalFlow(emit, nodex4, nodex3, nodex2, nodex1, nodex)
239+ println (flow.nodes.joinToString(" \n " ) { " ${it._id } : ${it.descriptor.name} " })
240+
241+ runner.executeStartNode(" receive empty input transitively" , flow, emptyList())
242+
243+ assertNoWarnings(flow)
244+ assertFalse(nodex4._id in wantsNonEmptyProvidesEmptyHandler)
245+ assertFalse(nodex3._id in wantsNonEmptyProvidesEmptyHandler)
246+ assertEquals(emptyList<SimpleIntermediateData >(), wantsEmptyProvidesNonEmptyHandler[nodex2._id ])
247+ assertEquals(
248+ listOf (SimpleIntermediateData (" '${WantsEmptyProvidesNonEmptyHandler .OUTPUT } '" )),
249+ wantsEmptyProvidesNonEmptyHandler[nodex._id ]
250+ )
251+ }
223252}
0 commit comments