@@ -15,7 +15,6 @@ import org.junit.Test
1515import org.mockito.Mockito
1616import org.neo4j.driver.Driver
1717import org.neo4j.driver.Session
18- import org.neo4j.driver.types.Node
1918import org.neo4j.function.ThrowingSupplier
2019import streams.Assert
2120import streams.Neo4jContainerExtension
@@ -66,20 +65,20 @@ class Neo4jSourceTaskTest {
6665 val sourceTaskContextMock = Mockito .mock(SourceTaskContext ::class .java)
6766 val offsetStorageReader = Mockito .mock(OffsetStorageReader ::class .java)
6867 Mockito .`when `(sourceTaskContextMock.offsetStorageReader())
69- .thenReturn(offsetStorageReader)
68+ .thenReturn(offsetStorageReader)
7069 Mockito .`when `(offsetStorageReader.offset(Mockito .anyMap<String , Any >()))
71- .thenReturn(emptyMap())
70+ .thenReturn(emptyMap())
7271 task.initialize(sourceTaskContextMock)
7372 }
7473
7574 private fun structToMap (struct : Struct ): Map <String , Any ?> = struct.schema().fields()
76- .map {
77- it.name() to when (val value = struct[it.name()]) {
78- is Struct -> structToMap(value)
79- else -> value
80- }
75+ .map {
76+ it.name() to when (val value = struct[it.name()]) {
77+ is Struct -> structToMap(value)
78+ else -> value
8179 }
82- .toMap()
80+ }
81+ .toMap()
8382
8483 fun Struct.toMap () = structToMap(this )
8584
@@ -88,7 +87,7 @@ class Neo4jSourceTaskTest {
8887 val props = mutableMapOf<String , String >()
8988 props[Neo4jConnectorConfig .SERVER_URI ] = neo4j.boltUrl
9089 props[Neo4jSourceConnectorConfig .TOPIC ] = UUID .randomUUID().toString()
91- props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 10 "
90+ props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 1000 "
9291 props[Neo4jSourceConnectorConfig .STREAMING_PROPERTY ] = " timestamp"
9392 props[Neo4jSourceConnectorConfig .SOURCE_TYPE_QUERY ] = getSourceQuery()
9493 props[Neo4jConnectorConfig .AUTHENTICATION_TYPE ] = AuthenticationType .NONE .toString()
@@ -110,7 +109,7 @@ class Neo4jSourceTaskTest {
110109 val props = mutableMapOf<String , String >()
111110 props[Neo4jConnectorConfig .SERVER_URI ] = neo4j.boltUrl
112111 props[Neo4jSourceConnectorConfig .TOPIC ] = UUID .randomUUID().toString()
113- props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 10 "
112+ props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 1000 "
114113 props[Neo4jSourceConnectorConfig .ENFORCE_SCHEMA ] = " true"
115114 props[Neo4jSourceConnectorConfig .STREAMING_PROPERTY ] = " timestamp"
116115 props[Neo4jSourceConnectorConfig .SOURCE_TYPE_QUERY ] = getSourceQuery()
@@ -134,7 +133,7 @@ class Neo4jSourceTaskTest {
134133 props[Neo4jConnectorConfig .SERVER_URI ] = neo4j.boltUrl
135134 props[Neo4jSourceConnectorConfig .TOPIC ] = UUID .randomUUID().toString()
136135 props[Neo4jSourceConnectorConfig .STREAMING_FROM ] = " ALL"
137- props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 10 "
136+ props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 1000 "
138137 props[Neo4jSourceConnectorConfig .STREAMING_PROPERTY ] = " timestamp"
139138 props[Neo4jSourceConnectorConfig .SOURCE_TYPE_QUERY ] = getSourceQuery()
140139 props[Neo4jConnectorConfig .AUTHENTICATION_TYPE ] = AuthenticationType .NONE .toString()
@@ -157,7 +156,7 @@ class Neo4jSourceTaskTest {
157156 props[Neo4jConnectorConfig .SERVER_URI ] = neo4j.boltUrl
158157 props[Neo4jSourceConnectorConfig .TOPIC ] = UUID .randomUUID().toString()
159158 props[Neo4jSourceConnectorConfig .STREAMING_FROM ] = " ALL"
160- props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 10 "
159+ props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 1000 "
161160 props[Neo4jSourceConnectorConfig .ENFORCE_SCHEMA ] = " true"
162161 props[Neo4jSourceConnectorConfig .STREAMING_PROPERTY ] = " timestamp"
163162 props[Neo4jSourceConnectorConfig .SOURCE_TYPE_QUERY ] = getSourceQuery()
@@ -177,7 +176,8 @@ class Neo4jSourceTaskTest {
177176
178177 private fun insertRecords (totalRecords : Int , longToInt : Boolean = false) = session.beginTransaction().use { tx ->
179178 val elements = (1 .. totalRecords).map {
180- val result = tx.run ("""
179+ val result = tx.run (
180+ """
181181 |CREATE (n:Test{
182182 | name: 'Name ' + $it ,
183183 | timestamp: timestamp(),
@@ -196,11 +196,12 @@ class Neo4jSourceTaskTest {
196196 | key2: "value2"
197197 | } AS map,
198198 | n AS node
199- """ .trimMargin())
199+ """ .trimMargin()
200+ )
200201 val next = result.next()
201202 val map = next.asMap().toMutableMap()
202203 map[" array" ] = next[" array" ].asList()
203- .map { if (longToInt) (it as Long ).toInt() else it }
204+ .map { if (longToInt) (it as Long ).toInt() else it }
204205 map[" point" ] = JSONUtils .readValue<Map <String , Any >>(map[" point" ]!! )
205206 map[" datetime" ] = next[" datetime" ].asLocalDateTime().toString()
206207 val node = next[" node" ].asNode()
@@ -223,7 +224,7 @@ class Neo4jSourceTaskTest {
223224 val props = mutableMapOf<String , String >()
224225 props[Neo4jConnectorConfig .SERVER_URI ] = neo4j.boltUrl
225226 props[Neo4jSourceConnectorConfig .TOPIC ] = UUID .randomUUID().toString()
226- props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 10 "
227+ props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 1000 "
227228 props[Neo4jSourceConnectorConfig .SOURCE_TYPE_QUERY ] = getSourceQuery()
228229 props[Neo4jConnectorConfig .AUTHENTICATION_TYPE ] = AuthenticationType .NONE .toString()
229230
@@ -244,7 +245,7 @@ class Neo4jSourceTaskTest {
244245 val props = mutableMapOf<String , String >()
245246 props[Neo4jConnectorConfig .SERVER_URI ] = neo4j.boltUrl
246247 props[Neo4jSourceConnectorConfig .TOPIC ] = UUID .randomUUID().toString()
247- props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 10 "
248+ props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 1000 "
248249 props[Neo4jSourceConnectorConfig .ENFORCE_SCHEMA ] = " true"
249250 props[Neo4jSourceConnectorConfig .SOURCE_TYPE_QUERY ] = getSourceQuery()
250251 props[Neo4jConnectorConfig .AUTHENTICATION_TYPE ] = AuthenticationType .NONE .toString()
@@ -282,7 +283,7 @@ class Neo4jSourceTaskTest {
282283 val props = mutableMapOf<String , String >()
283284 props[Neo4jConnectorConfig .SERVER_URI ] = neo4j.boltUrl
284285 props[Neo4jSourceConnectorConfig .TOPIC ] = UUID .randomUUID().toString()
285- props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 10 "
286+ props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 1000 "
286287 props[Neo4jSourceConnectorConfig .SOURCE_TYPE_QUERY ] = " WRONG QUERY" .trimMargin()
287288 props[Neo4jConnectorConfig .AUTHENTICATION_TYPE ] = AuthenticationType .NONE .toString()
288289
@@ -308,7 +309,7 @@ class Neo4jSourceTaskTest {
308309 val props = mutableMapOf<String , String >()
309310 props[Neo4jConnectorConfig .SERVER_URI ] = neo4j.boltUrl
310311 props[Neo4jSourceConnectorConfig .TOPIC ] = UUID .randomUUID().toString()
311- props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 10 "
312+ props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 1000 "
312313 props[Neo4jSourceConnectorConfig .ENFORCE_SCHEMA ] = " true"
313314 props[Neo4jSourceConnectorConfig .SOURCE_TYPE_QUERY ] = """
314315 |WITH
@@ -328,8 +329,6 @@ class Neo4jSourceTaskTest {
328329 val totalRecords = 10
329330 insertRecords(totalRecords)
330331
331- val list = mutableListOf<SourceRecord >()
332-
333332 val expected = mapOf (
334333 " id" to " ROOT_ID" ,
335334 " data" to mapOf (
@@ -343,9 +342,49 @@ class Neo4jSourceTaskTest {
343342 )
344343
345344 Assert .assertEventually(ThrowingSupplier {
346- task.poll()?.let { list.addAll(it) }
347- val actualList = list.map { (it.value() as Struct ).toMap() }
348- actualList.first() == expected
349- }, Matchers .equalTo(true ), 30 , TimeUnit .SECONDS )
345+ task.poll()?.map { (it.value() as Struct ).toMap() }?.first()
346+ }, Matchers .equalTo(expected), 30 , TimeUnit .SECONDS )
347+ }
348+
349+ @Test
350+ fun `should support null values returned from query` () {
351+ val props = mutableMapOf<String , String >()
352+ props[Neo4jConnectorConfig .SERVER_URI ] = neo4j.boltUrl
353+ props[Neo4jSourceConnectorConfig .TOPIC ] = UUID .randomUUID().toString()
354+ props[Neo4jSourceConnectorConfig .STREAMING_POLL_INTERVAL ] = " 1000"
355+ props[Neo4jSourceConnectorConfig .ENFORCE_SCHEMA ] = " true"
356+ props[Neo4jSourceConnectorConfig .SOURCE_TYPE_QUERY ] = """
357+ |RETURN {
358+ | prop1: 1,
359+ | prop2: "string",
360+ | prop3: true,
361+ | prop4: null,
362+ | prop5: {
363+ | prop: null
364+ | },
365+ | prop6: [1],
366+ | prop7: [null]
367+ |} AS data, 1717773205 AS timestamp
368+ """ .trimMargin()
369+ props[Neo4jConnectorConfig .AUTHENTICATION_TYPE ] = AuthenticationType .NONE .toString()
370+
371+ task.start(props)
372+
373+
374+ val expected = mapOf (
375+ " data" to mapOf (
376+ " prop1" to 1L ,
377+ " prop2" to " string" ,
378+ " prop3" to true ,
379+ " prop4" to null ,
380+ " prop5" to mapOf (" prop" to null ),
381+ " prop6" to listOf (1L ),
382+ " prop7" to listOf<Any ?>(null )
383+ ), " timestamp" to 1717773205L
384+ )
385+
386+ Assert .assertEventually(ThrowingSupplier {
387+ task.poll()?.map { (it.value() as Struct ).toMap() }?.first()
388+ }, Matchers .equalTo(expected), 30 , TimeUnit .SECONDS )
350389 }
351390}
0 commit comments