@@ -22,21 +22,31 @@ import graphql.GraphQLException
2222import graphql.schema.DataFetchingEnvironment
2323import io.mockk.every
2424import io.mockk.mockk
25+ import kotlinx.coroutines.CoroutineScope
26+ import kotlinx.coroutines.asCoroutineDispatcher
2527import kotlinx.coroutines.coroutineScope
28+ import kotlinx.coroutines.runBlocking
2629import org.junit.jupiter.api.Test
2730import java.util.concurrent.CompletableFuture
28- import kotlin.test.assertEquals
29- import kotlin.test.assertFalse
30- import kotlin.test.assertNotNull
31- import kotlin.test.assertNull
32- import kotlin.test.assertTrue
31+ import java.util.concurrent.Executors
32+ import java.util.concurrent.ThreadFactory
33+ import kotlin.test.*
3334
3435class FunctionDataFetcherTest {
3536
3637 interface MyInterface {
3738 fun print (string : String ): String
3839 }
3940
41+ val threadFactory = object : ThreadFactory {
42+ override fun newThread (r : Runnable ): Thread ? {
43+ val thread = Thread (r)
44+ thread.name = " custom-thread-1"
45+ return thread
46+ }
47+ }
48+ val customCoroutineDispatcher = Executors .newSingleThreadExecutor(threadFactory).asCoroutineDispatcher()
49+
4050 class MyClass : MyInterface {
4151 override fun print (string : String ) = string
4252
@@ -52,7 +62,9 @@ class FunctionDataFetcherTest {
5262 string
5363 }
5464
55- fun throwException () { throw GraphQLException (" Test Exception" ) }
65+ fun throwException () {
66+ throw GraphQLException (" Test Exception" )
67+ }
5668
5769 suspend fun suspendThrow (): String = coroutineScope<String > {
5870 throw GraphQLException (" Suspended Exception" )
@@ -75,14 +87,21 @@ class FunctionDataFetcherTest {
7587 is OptionalInput .Undefined -> " optional was UNDEFINED"
7688 is OptionalInput .Defined -> " optional was ${input.optional.value} "
7789 }
90+
91+ fun threadNameSync (): String {
92+ return Thread .currentThread().name
93+ }
94+
95+ fun threadNameAsync (): String {
96+ return Thread .currentThread().name
97+ }
7898 }
7999
80100 data class InputWrapper (val required : String , val optional : OptionalInput <String >)
81101
82102 @GraphQLName(" MyInputClassRenamed" )
83103 data class MyInputClass (
84- @GraphQLName(" jacksonField" )
85- val field1 : String
104+ @GraphQLName(" jacksonField" ) val field1 : String
86105 )
87106
88107 @Test
@@ -101,6 +120,7 @@ class FunctionDataFetcherTest {
101120 every { getSource<Any >() } returns MyClass ()
102121 every { arguments } returns mapOf (" string" to " hello" )
103122 every { containsArgument(" string" ) } returns true
123+ every { graphQlContext } returns GraphQLContext .newContext().build()
104124 }
105125 assertEquals(expected = " hello" , actual = dataFetcher.get(mockEnvironment))
106126 }
@@ -111,6 +131,7 @@ class FunctionDataFetcherTest {
111131 val mockEnvironment: DataFetchingEnvironment = mockk {
112132 every { arguments } returns mapOf (" string" to " hello" )
113133 every { containsArgument(" string" ) } returns true
134+ every { graphQlContext } returns GraphQLContext .newContext().build()
114135 }
115136 assertEquals(expected = " hello" , actual = dataFetcher.get(mockEnvironment))
116137 }
@@ -137,6 +158,7 @@ class FunctionDataFetcherTest {
137158 every { getSource<Any >() } returns MyClass ()
138159 every { arguments } returns mapOf (" string" to " hello" )
139160 every { containsArgument(" string" ) } returns true
161+ every { graphQlContext } returns GraphQLContext .newContext().build()
140162 }
141163 assertEquals(expected = " hello" , actual = dataFetcher.get(mockEnvironment))
142164 }
@@ -148,6 +170,7 @@ class FunctionDataFetcherTest {
148170 every { getSource<Any >() } returns MyClass ()
149171 every { arguments } returns emptyMap()
150172 every { containsArgument(any()) } returns false
173+ every { graphQlContext } returns GraphQLContext .newContext().build()
151174 }
152175 assertEquals(expected = " hello" , actual = dataFetcher.get(mockEnvironment))
153176 }
@@ -159,6 +182,7 @@ class FunctionDataFetcherTest {
159182 every { getSource<Any >() } returns MyClass ()
160183 every { arguments } returns mapOf (" string" to " foo" )
161184 every { containsArgument(" string" ) } returns true
185+ every { graphQlContext } returns GraphQLContext .newContext().build()
162186 }
163187 assertEquals(expected = " foo" , actual = dataFetcher.get(mockEnvironment))
164188 }
@@ -170,6 +194,7 @@ class FunctionDataFetcherTest {
170194 every { getSource<Any >() } returns MyClass ()
171195 every { arguments } returns mapOf (" string" to null )
172196 every { containsArgument(" string" ) } returns true
197+ every { graphQlContext } returns GraphQLContext .newContext().build()
173198 }
174199 assertNull(dataFetcher.get(mockEnvironment))
175200 }
@@ -180,6 +205,7 @@ class FunctionDataFetcherTest {
180205 val mockEnvironment: DataFetchingEnvironment = mockk {
181206 every { arguments } returns mapOf (" items" to listOf (" foo" , " bar" ))
182207 every { containsArgument(" items" ) } returns true
208+ every { graphQlContext } returns GraphQLContext .newContext().build()
183209 }
184210
185211 assertEquals(expected = " foo:bar" , actual = dataFetcher.get(mockEnvironment))
@@ -194,6 +220,7 @@ class FunctionDataFetcherTest {
194220 every { field } returns mockk {
195221 every { name } returns " fooBarBaz"
196222 }
223+ every { graphQlContext } returns GraphQLContext .newContext().build()
197224 }
198225 assertEquals(expected = " fooBarBaz" , actual = dataFetcher.get(mockEnvironment))
199226 }
@@ -219,6 +246,7 @@ class FunctionDataFetcherTest {
219246 val mockEnvironment: DataFetchingEnvironment = mockk {
220247 every { arguments } returns emptyMap()
221248 every { containsArgument(any()) } returns false
249+ every { graphQlContext } returns GraphQLContext .newContext().build()
222250 }
223251
224252 try {
@@ -256,6 +284,7 @@ class FunctionDataFetcherTest {
256284 val mockEnvironment: DataFetchingEnvironment = mockk {
257285 every { arguments } returns mapOf (" myCustomArgument" to mapOf (" jacksonField" to " foo" ))
258286 every { containsArgument(" myCustomArgument" ) } returns true
287+ every { graphQlContext } returns GraphQLContext .newContext().build()
259288 }
260289 assertEquals(expected = " You sent foo" , actual = dataFetcher.get(mockEnvironment))
261290 }
@@ -266,6 +295,7 @@ class FunctionDataFetcherTest {
266295 val mockEnvironment: DataFetchingEnvironment = mockk {
267296 every { arguments } returns mapOf (" input" to " hello" )
268297 every { containsArgument(" input" ) } returns true
298+ every { graphQlContext } returns GraphQLContext .newContext().build()
269299 }
270300 assertEquals(expected = " input was hello" , actual = dataFetcher.get(mockEnvironment))
271301 }
@@ -276,6 +306,7 @@ class FunctionDataFetcherTest {
276306 val mockEnvironment: DataFetchingEnvironment = mockk {
277307 every { arguments } returns mapOf (" input" to null )
278308 every { containsArgument(" input" ) } returns true
309+ every { graphQlContext } returns GraphQLContext .newContext().build()
279310 }
280311 assertEquals(expected = " input was null" , actual = dataFetcher.get(mockEnvironment))
281312 }
@@ -286,6 +317,7 @@ class FunctionDataFetcherTest {
286317 val mockEnvironment: DataFetchingEnvironment = mockk {
287318 every { arguments } returns emptyMap()
288319 every { containsArgument(any()) } returns false
320+ every { graphQlContext } returns GraphQLContext .newContext().build()
289321 }
290322 assertEquals(expected = " input was UNDEFINED" , actual = dataFetcher.get(mockEnvironment))
291323 }
@@ -296,6 +328,7 @@ class FunctionDataFetcherTest {
296328 val mockEnvironment: DataFetchingEnvironment = mockk {
297329 every { arguments } returns mapOf (" input" to listOf (linkedMapOf(" jacksonField" to " foo" )))
298330 every { containsArgument(" input" ) } returns true
331+ every { graphQlContext } returns GraphQLContext .newContext().build()
299332 }
300333 val result = dataFetcher.get(mockEnvironment)
301334 assertEquals(expected = " first input was foo" , actual = result)
@@ -307,6 +340,7 @@ class FunctionDataFetcherTest {
307340 val mockEnvironment: DataFetchingEnvironment = mockk {
308341 every { arguments } returns mapOf (" input" to mapOf (" required" to " hello" , " optional" to " hello" ))
309342 every { containsArgument(" input" ) } returns true
343+ every { graphQlContext } returns GraphQLContext .newContext().build()
310344 }
311345 assertEquals(expected = " optional was hello" , actual = dataFetcher.get(mockEnvironment))
312346 }
@@ -317,7 +351,59 @@ class FunctionDataFetcherTest {
317351 val mockEnvironment = mockk<DataFetchingEnvironment > {
318352 every { arguments } returns mapOf (" input" to mapOf (" required" to " hello" ))
319353 every { containsArgument(" input" ) } returns true
354+ every { graphQlContext } returns GraphQLContext .newContext().build()
320355 }
321356 assertEquals(expected = " optional was UNDEFINED" , actual = dataFetcher.get(mockEnvironment))
322357 }
358+
359+ @Test
360+ fun `use default scope for sync function` () {
361+ val dataFetcher = FunctionDataFetcher (target = MyClass (), fn = MyClass ::threadNameSync)
362+ val mockEnvironment = mockk<DataFetchingEnvironment > {
363+ every { arguments } returns mapOf (" input" to mapOf (" required" to " hello" ))
364+ every { containsArgument(" input" ) } returns true
365+ every { graphQlContext } returns GraphQLContext .newContext().build()
366+ }
367+ val result = dataFetcher.get(mockEnvironment) as String
368+ assertContains(charSequence = result, other = " Test worker" )
369+ }
370+
371+ @Test
372+ fun `use provided scope for sync function` () = runBlocking {
373+ val dataFetcher = FunctionDataFetcher (target = MyClass (), fn = MyClass ::threadNameSync)
374+ val scope = CoroutineScope (customCoroutineDispatcher)
375+ val mockEnvironment = mockk<DataFetchingEnvironment > {
376+ every { arguments } returns mapOf (" input" to mapOf (" required" to " hello" ))
377+ every { containsArgument(" input" ) } returns true
378+ every { graphQlContext } returns GraphQLContext .newContext().put(CoroutineScope ::class , scope).build()
379+ }
380+ val result = dataFetcher.get(mockEnvironment) as String
381+ assertContains(charSequence = result, other = " custom-thread-1" )
382+ }
383+
384+
385+ @Test
386+ fun `use default scope for async function` () {
387+ val dataFetcher = FunctionDataFetcher (target = MyClass (), fn = MyClass ::threadNameAsync)
388+ val mockEnvironment = mockk<DataFetchingEnvironment > {
389+ every { arguments } returns mapOf (" input" to mapOf (" required" to " hello" ))
390+ every { containsArgument(" input" ) } returns true
391+ every { graphQlContext } returns GraphQLContext .newContext().build()
392+ }
393+ val result = dataFetcher.get(mockEnvironment) as String
394+ assertContains(charSequence = result, other = " Test worker" )
395+ }
396+
397+ @Test
398+ fun `use provided scope for async function` () = runBlocking {
399+ val dataFetcher = FunctionDataFetcher (target = MyClass (), fn = MyClass ::threadNameAsync)
400+ val scope = CoroutineScope (customCoroutineDispatcher)
401+ val mockEnvironment = mockk<DataFetchingEnvironment > {
402+ every { arguments } returns mapOf (" input" to mapOf (" required" to " hello" ))
403+ every { containsArgument(" input" ) } returns true
404+ every { graphQlContext } returns GraphQLContext .newContext().put(CoroutineScope ::class , scope).build()
405+ }
406+ val result = dataFetcher.get(mockEnvironment) as String
407+ assertContains(charSequence = result, other = " custom-thread-1" )
408+ }
323409}
0 commit comments