From 5fde3e73a4fce597eff4916d4642a5566fda6bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ing=2E=20Jan=20Kal=C3=A1b?= Date: Fri, 2 Jul 2021 09:04:06 +0200 Subject: [PATCH 01/43] Initial JS support with TODOs --- apollo-mockserver/build.gradle.kts | 2 +- .../apollo3/mockserver/MockServer.kt | 19 +++++++++++++++++++ apollo-runtime/build.gradle.kts | 2 +- .../apollo3/internal/NonMainWorker.kt | 7 +++++++ .../apollo3/internal/dispatchers.kt | 15 +++++++++++++++ .../apollo3/network/http/OkHttpEngine.kt | 10 ++++++++++ .../network/ws/OkHttpWebSocketEngine.kt | 10 ++++++++++ apollo-testing-support/build.gradle.kts | 2 +- .../apollo3/testing/readFileJs.kt | 9 +++++++++ .../apollo3/testing/runWithMainLoopJs.kt | 16 ++++++++++++++++ build-logic/src/main/kotlin/Mpp.kt | 2 +- gradle/dependencies.gradle | 2 +- 12 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 apollo-mockserver/src/jsMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt create mode 100644 apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/NonMainWorker.kt create mode 100644 apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/dispatchers.kt create mode 100644 apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt create mode 100644 apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/ws/OkHttpWebSocketEngine.kt create mode 100644 apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt create mode 100644 apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJs.kt diff --git a/apollo-mockserver/build.gradle.kts b/apollo-mockserver/build.gradle.kts index 685157dcd6..60e199591b 100644 --- a/apollo-mockserver/build.gradle.kts +++ b/apollo-mockserver/build.gradle.kts @@ -2,7 +2,7 @@ plugins { kotlin("multiplatform") } -configureMppDefaults(withJs = false) +configureMppDefaults() kotlin { sourceSets { diff --git a/apollo-mockserver/src/jsMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt b/apollo-mockserver/src/jsMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt new file mode 100644 index 0000000000..7ac8c323b2 --- /dev/null +++ b/apollo-mockserver/src/jsMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt @@ -0,0 +1,19 @@ +package com.apollographql.apollo3.mockserver + +actual class MockServer { + actual fun url(): String { + TODO() + } + + actual fun enqueue(mockResponse: MockResponse) { + TODO() + } + + actual fun takeRequest(): MockRecordedRequest { + TODO() + } + + actual fun stop() { + TODO() + } +} \ No newline at end of file diff --git a/apollo-runtime/build.gradle.kts b/apollo-runtime/build.gradle.kts index 437e49d65d..801a08310c 100644 --- a/apollo-runtime/build.gradle.kts +++ b/apollo-runtime/build.gradle.kts @@ -2,7 +2,7 @@ plugins { kotlin("multiplatform") } -configureMppDefaults(withJs = false) +configureMppDefaults() kotlin { sourceSets { diff --git a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/NonMainWorker.kt b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/NonMainWorker.kt new file mode 100644 index 0000000000..03f93eb5c1 --- /dev/null +++ b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/NonMainWorker.kt @@ -0,0 +1,7 @@ +package com.apollographql.apollo3.internal + +actual class NonMainWorker { + actual suspend fun doWork(block: () -> R): R { + TODO() + } +} \ No newline at end of file diff --git a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/dispatchers.kt b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/dispatchers.kt new file mode 100644 index 0000000000..3df569bab2 --- /dev/null +++ b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/dispatchers.kt @@ -0,0 +1,15 @@ +package com.apollographql.apollo3.internal + +import kotlinx.coroutines.CoroutineDispatcher + +actual fun defaultDispatcher(requested: CoroutineDispatcher?): CoroutineDispatcher { + TODO() +} + +actual class WebSocketDispatcher { + actual val coroutineDispatcher: CoroutineDispatcher = TODO() + + actual fun dispose() { + TODO() + } +} \ No newline at end of file diff --git a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt new file mode 100644 index 0000000000..5c6981e493 --- /dev/null +++ b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt @@ -0,0 +1,10 @@ +package com.apollographql.apollo3.network.http + +import com.apollographql.apollo3.api.http.HttpRequest +import com.apollographql.apollo3.api.http.HttpResponse + +actual class DefaultHttpEngine actual constructor(connectTimeoutMillis: Long, readTimeoutMillis: Long) : HttpEngine { + override suspend fun execute(request: HttpRequest): HttpResponse { + TODO() + } +} \ No newline at end of file diff --git a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/ws/OkHttpWebSocketEngine.kt b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/ws/OkHttpWebSocketEngine.kt new file mode 100644 index 0000000000..b222e1e169 --- /dev/null +++ b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/ws/OkHttpWebSocketEngine.kt @@ -0,0 +1,10 @@ +package com.apollographql.apollo3.network.ws + +import kotlinx.coroutines.ExperimentalCoroutinesApi + +@OptIn(ExperimentalCoroutinesApi::class) +actual class DefaultWebSocketEngine : WebSocketEngine { + override suspend fun open(url: String, headers: Map): WebSocketConnection { + TODO() + } +} \ No newline at end of file diff --git a/apollo-testing-support/build.gradle.kts b/apollo-testing-support/build.gradle.kts index fa27574fae..dd37a5e66b 100644 --- a/apollo-testing-support/build.gradle.kts +++ b/apollo-testing-support/build.gradle.kts @@ -2,7 +2,7 @@ plugins { kotlin("multiplatform") } -configureMppDefaults(withJs = false) +configureMppDefaults() kotlin { sourceSets { diff --git a/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt new file mode 100644 index 0000000000..1695a2b4d7 --- /dev/null +++ b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt @@ -0,0 +1,9 @@ +package com.apollographql.apollo3.testing + +actual fun readFile(path: String): String { + TODO() +} + +actual fun checkFile(actualText: String, path: String) { + TODO() +} diff --git a/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJs.kt b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJs.kt new file mode 100644 index 0000000000..134350b9c4 --- /dev/null +++ b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJs.kt @@ -0,0 +1,16 @@ +package com.apollographql.apollo3.testing + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlin.coroutines.CoroutineContext + +actual fun runBlocking(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T { + TODO() +} + +actual fun runWithMainLoop(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T { + TODO() +} + +actual val MainLoopDispatcher: CoroutineDispatcher = Dispatchers.Default \ No newline at end of file diff --git a/build-logic/src/main/kotlin/Mpp.kt b/build-logic/src/main/kotlin/Mpp.kt index 152d863e6d..41146de235 100644 --- a/build-logic/src/main/kotlin/Mpp.kt +++ b/build-logic/src/main/kotlin/Mpp.kt @@ -13,7 +13,7 @@ fun Project.configureMppDefaults(withJs: Boolean = true) { jvm() if (withJs) { - js { + js(BOTH) { useCommonJs() browser() nodejs() diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index a83450d882..30efef41ed 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -18,7 +18,7 @@ def versions = [ moshix : '0.11.2', okHttp4 : '4.9.0', okHttp : '3.12.11', // Keep this to keep supporting older Android devices - okio : '2.10.0', + okio : '3.0.0-alpha.6', rxjava : '2.2.20', rxjava3 : '3.0.7', rxandroid : '2.0.1', From 9b49bbc6b51d6dbc88997cef07feff437e79fa5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ing=2E=20Jan=20Kal=C3=A1b?= Date: Fri, 9 Jul 2021 08:34:08 +0200 Subject: [PATCH 02/43] configureMppTestsDefaults --- apollo-cache-interceptor/build.gradle.kts | 2 +- build-logic/src/main/kotlin/Mpp.kt | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/apollo-cache-interceptor/build.gradle.kts b/apollo-cache-interceptor/build.gradle.kts index b28dacbae7..c1a66fefe3 100644 --- a/apollo-cache-interceptor/build.gradle.kts +++ b/apollo-cache-interceptor/build.gradle.kts @@ -2,7 +2,7 @@ plugins { kotlin("multiplatform") } -configureMppDefaults(withJs = false) +configureMppDefaults() kotlin { sourceSets { diff --git a/build-logic/src/main/kotlin/Mpp.kt b/build-logic/src/main/kotlin/Mpp.kt index 41146de235..2f7a21839b 100644 --- a/build-logic/src/main/kotlin/Mpp.kt +++ b/build-logic/src/main/kotlin/Mpp.kt @@ -71,6 +71,11 @@ fun Project.configureMppTestsDefaults() { * configure targets */ jvm() + js(IR) { + useCommonJs() + browser() + nodejs() + } macosX64("apple") addTestDependencies(false) From 2f2af42ac312ee7309a9d094887559d59420dc7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ing=2E=20Jan=20Kal=C3=A1b?= Date: Fri, 9 Jul 2021 10:30:08 +0200 Subject: [PATCH 03/43] Progress --- apollo-normalized-cache/build.gradle.kts | 2 +- .../apollo3/internal/dispatchers.kt | 19 +++++++++++++++---- .../apollo3/network/http/OkHttpEngine.kt | 4 ++++ build-logic/src/main/kotlin/Mpp.kt | 16 +++++++++------- .../src/commonTest/kotlin/test/BasicTest.kt | 2 +- .../commonTest/kotlin/test/ExceptionsTest.kt | 8 ++++---- .../commonTest/kotlin/test/HTTPHeadersTest.kt | 2 +- .../kotlin/test/HttpRequestComposerTest.kt | 4 ++-- .../kotlin/test/ParseResponseBodyTest.kt | 12 ++++++------ 9 files changed, 43 insertions(+), 26 deletions(-) diff --git a/apollo-normalized-cache/build.gradle.kts b/apollo-normalized-cache/build.gradle.kts index a4408c28c9..0b9c57d21b 100644 --- a/apollo-normalized-cache/build.gradle.kts +++ b/apollo-normalized-cache/build.gradle.kts @@ -3,7 +3,7 @@ plugins { kotlin("multiplatform") } -configureMppDefaults(withJs = false) +configureMppDefaults() kotlin { sourceSets { diff --git a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/dispatchers.kt b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/dispatchers.kt index 3df569bab2..ed97f30ae4 100644 --- a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/dispatchers.kt +++ b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/dispatchers.kt @@ -1,15 +1,26 @@ package com.apollographql.apollo3.internal import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers actual fun defaultDispatcher(requested: CoroutineDispatcher?): CoroutineDispatcher { - TODO() + return requested ?: Dispatchers.Default } -actual class WebSocketDispatcher { - actual val coroutineDispatcher: CoroutineDispatcher = TODO() +// We can't use threads in JS, so just fallback to defaultDispatcher() +actual class BackgroundDispatcher { + actual val coroutineDispatcher: CoroutineDispatcher = defaultDispatcher(null) actual fun dispose() { - TODO() + } +} + +actual class DefaultMutex: Mutex { + private val lock = Unit + + override fun lock(block: () -> T): T { + synchronized(lock) { + return block() + } } } \ No newline at end of file diff --git a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt index 5c6981e493..23fc4310e3 100644 --- a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt +++ b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt @@ -7,4 +7,8 @@ actual class DefaultHttpEngine actual constructor(connectTimeoutMillis: Long, re override suspend fun execute(request: HttpRequest): HttpResponse { TODO() } + + override fun dispose() { + TODO() + } } \ No newline at end of file diff --git a/build-logic/src/main/kotlin/Mpp.kt b/build-logic/src/main/kotlin/Mpp.kt index 2f7a21839b..9272de2ab3 100644 --- a/build-logic/src/main/kotlin/Mpp.kt +++ b/build-logic/src/main/kotlin/Mpp.kt @@ -59,9 +59,9 @@ fun Project.configureMppDefaults(withJs: Boolean = true) { } /** - * Same as [configureMppDefaults] but without iOS targets. Tests only run on the JVM and MacOS + * Same as [configureMppDefaults] but without iOS targets. Tests only run on the JVM, JS and MacOS */ -fun Project.configureMppTestsDefaults() { +fun Project.configureMppTestsDefaults(withJs: Boolean = true) { val kotlinExtension = extensions.findByName("kotlin") as? org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension check(kotlinExtension != null) { "No multiplatform extension found" @@ -71,14 +71,16 @@ fun Project.configureMppTestsDefaults() { * configure targets */ jvm() - js(IR) { - useCommonJs() - browser() - nodejs() + if (withJs) { + js(IR) { + useCommonJs() + browser() + nodejs() + } } macosX64("apple") - addTestDependencies(false) + addTestDependencies(withJs) } } diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BasicTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BasicTest.kt index ef825f714c..6368b5a87e 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BasicTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BasicTest.kt @@ -147,7 +147,7 @@ class BasicTest { @Test - fun `requesting the same field twice with an alias`() = basicTest( + fun requestingTheSameFieldTwiceWithAnAlias() = basicTest( "SameHeroTwiceResponse.json", SameHeroTwiceQuery() ) { diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/ExceptionsTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/ExceptionsTest.kt index bca957a4d6..82e0c4b641 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/ExceptionsTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/ExceptionsTest.kt @@ -28,7 +28,7 @@ class ExceptionsTest { } @Test - fun `when query and malformed network response, assert Exception`() = runWithMainLoop { + fun whenQueryAndMalformedNetworkResponseAssertException() = runWithMainLoop { mockServer.enqueue("malformed") val result = kotlin.runCatching { @@ -39,7 +39,7 @@ class ExceptionsTest { } @Test - fun `when http error, assert execute fails`() = runWithMainLoop { + fun whenHttpErrorAssertExecuteFails() = runWithMainLoop { mockServer.enqueue(MockResponse(statusCode = 404)) val result = kotlin.runCatching { @@ -52,7 +52,7 @@ class ExceptionsTest { } @Test - fun `when network error, assert ApolloNetworkException`() = runWithMainLoop { + fun whenNetworkErrorAssertApolloNetworkException() = runWithMainLoop { mockServer.stop() val result = kotlin.runCatching { @@ -64,7 +64,7 @@ class ExceptionsTest { } @Test - fun `when query and malformed network response, assert success after retry`() { + fun WhenQueryAndMalformedNetworkResponseAssertSuccessAfterRetry() { mockServer.enqueue("") val query = HeroNameQuery() val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HTTPHeadersTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HTTPHeadersTest.kt index b293bd407b..80fffcc01e 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HTTPHeadersTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HTTPHeadersTest.kt @@ -27,7 +27,7 @@ class HTTPHeadersTest { } @Test - fun `Make sure headers are set`() { + fun makeSureHeadersAreSet() { val query = HeroNameQuery() val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HttpRequestComposerTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HttpRequestComposerTest.kt index f0cd6c15af..51f455e032 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HttpRequestComposerTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HttpRequestComposerTest.kt @@ -17,7 +17,7 @@ import kotlin.test.assertEquals class HttpRequestComposerTest { @Test - fun `request POST body contains operation, query and variables by default`() { + fun requestPostBodyContainsOperationQueryAndVariablesByDefault() { val composer = DefaultHttpRequestComposer("/") val apolloRequest = ApolloRequest(AllPlanetsQuery()) val httpRequest = composer.compose(apolloRequest) @@ -28,7 +28,7 @@ class HttpRequestComposerTest { } @Test - fun `request headers are forwarded to the server`() { + fun requestHeadersAreForwardedToTheServer() { val mockServer = MockServer() val apolloClient = ApolloClient(mockServer.url()) diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/ParseResponseBodyTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/ParseResponseBodyTest.kt index a0d3dc07cc..0177f0fc8a 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/ParseResponseBodyTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/ParseResponseBodyTest.kt @@ -31,7 +31,7 @@ class ParseResponseBodyTest { @Test @Throws(Exception::class) - fun `errors are properly read`() { + fun errorsAreProperlyRead() { val response = AllPlanetsQuery().parseJsonResponse(readResource("ResponseError.json")) assertTrue(response.hasErrors()) val errors = response.errors @@ -43,7 +43,7 @@ class ParseResponseBodyTest { @Test @Throws(Exception::class) - fun `error with no message, no location and custom attributes`() { + fun errorWithNoMessageNoLocationAndCustomAttributes() { val response = AllPlanetsQuery().parseJsonResponse(readResource("ResponseErrorWithNullsAndCustomAttributes.json")) assertTrue(response.hasErrors()) assertEquals(response.errors?.size, 1) @@ -56,7 +56,7 @@ class ParseResponseBodyTest { @Test @Throws(Exception::class) - fun `error with message, location and custom attributes`() { + fun errorWithMessageLocationAndCustomAttributes() { val response = AllPlanetsQuery().parseJsonResponse(readResource("ResponseErrorWithCustomAttributes.json")) assertTrue(response.hasErrors()) assertEquals(response.errors!![0].customAttributes.size, 4) @@ -162,7 +162,7 @@ class ParseResponseBodyTest { @Test @Throws(Exception::class) - fun `extensions are read from response`() { + fun extensionsAreReadFromResponse() { val query = HeroNameQuery() val extensions = query.parseJsonResponse(readResource("HeroNameResponse.json")).extensions assertEquals( @@ -182,7 +182,7 @@ class ParseResponseBodyTest { } @Test - fun `not registering an adapter, neither at runtime or in the gradle plugin defaults to Any`() { + fun notRegisteringAnAdapterNeitherAtRuntimeOrInTheGradlePluginDefaultsToAny() { val data = GetJsonScalarQuery.Data( json = mapOf("1" to "2", "3" to listOf("a", "b")) ) @@ -193,7 +193,7 @@ class ParseResponseBodyTest { @Test - fun `forgetting to add a runtime adapter for a scalar registered in the plugin fails`() { + fun forgettingToAddARuntimeAdapterForAScalarRegisteredInThePluginFails() { val data = CharacterWithBirthDateQuery.Data( character = CharacterWithBirthDateQuery.Data.Character( birthDate = LocalDate(1970, 1, 1), From d2c1715f7e13fc98d10b0f5e4949503f845854d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ing=2E=20Jan=20Kal=C3=A1b?= Date: Fri, 16 Jul 2021 08:23:24 +0200 Subject: [PATCH 04/43] okio 3.0.0-alpha.8 --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 2d167af005..f02a7ad0c7 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -18,7 +18,7 @@ def versions = [ moshix : '0.11.2', okHttp4 : '4.9.0', okHttp : '3.12.11', // Keep this to keep supporting older Android devices - okio : '3.0.0-alpha.6', + okio : '3.0.0-alpha.8', rxjava : '2.2.20', rxjava3 : '3.0.7', rxandroid : '2.0.1', From 350cdb5a9773f4b24989d7bcf077d796de046b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ing=2E=20Jan=20Kal=C3=A1b?= Date: Mon, 19 Jul 2021 14:34:03 +0200 Subject: [PATCH 05/43] Updates --- .../com/apollographql/apollo3/api/Operation.kt | 2 ++ .../apollographql/apollo3/mockserver/MockServer.kt | 8 ++++---- apollo-testing-support/build.gradle.kts | 10 ++++++++-- .../apollo3/testing/runWithMainLoopApple.kt | 4 ++++ .../apollo3/testing/runWithMainLoop.kt | 4 ++-- .../apollographql/apollo3/testing/readFileJs.kt | 9 +++++++-- .../apollo3/testing/runWithMainLoopJs.kt | 14 +++++++++++--- .../apollo3/testing/runWithMainLoopJvm.kt | 4 ++++ build.gradle.kts | 1 + composite/build.gradle.kts | 1 + .../kotlin/test/AutoPersistedQueriesTest.kt | 6 +++--- gradle/dependencies.gradle | 3 ++- 12 files changed, 49 insertions(+), 17 deletions(-) diff --git a/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/Operation.kt b/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/Operation.kt index 98fea19974..d87dd4b755 100644 --- a/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/Operation.kt +++ b/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/Operation.kt @@ -10,6 +10,7 @@ import okio.Buffer import okio.BufferedSink import okio.BufferedSource import okio.ByteString +import kotlin.js.JsName /** * Represents a GraphQL operation (mutation, query or subscription). @@ -31,6 +32,7 @@ interface Operation : Executable { /** * An unique identifier for the operation. Used for Automatic Persisted Queries. You can customize it with a [OperationIdGenerator] */ + @JsName("operationId") fun id(): String override fun adapter(): Adapter diff --git a/apollo-mockserver/src/jsMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt b/apollo-mockserver/src/jsMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt index 7ac8c323b2..b1f17395b7 100644 --- a/apollo-mockserver/src/jsMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt +++ b/apollo-mockserver/src/jsMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt @@ -2,18 +2,18 @@ package com.apollographql.apollo3.mockserver actual class MockServer { actual fun url(): String { - TODO() + TODO("MockServer.url()") } actual fun enqueue(mockResponse: MockResponse) { - TODO() + TODO("MockServer.enqueue()") } actual fun takeRequest(): MockRecordedRequest { - TODO() + TODO("MockServer.takeRequest()") } actual fun stop() { - TODO() + TODO("MockServer.stop()") } } \ No newline at end of file diff --git a/apollo-testing-support/build.gradle.kts b/apollo-testing-support/build.gradle.kts index dd37a5e66b..fd9cd18ac9 100644 --- a/apollo-testing-support/build.gradle.kts +++ b/apollo-testing-support/build.gradle.kts @@ -21,6 +21,12 @@ kotlin { implementation(groovy.util.Eval.x(project, "x.dep.truth")) } } - } -} + val jsMain by getting { + dependencies { + implementation(groovy.util.Eval.x(project, "x.dep.kotlin.nodejs")) + implementation(kotlin("test-js")) + } + } + } +} \ No newline at end of file diff --git a/apollo-testing-support/src/appleMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopApple.kt b/apollo-testing-support/src/appleMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopApple.kt index d314c502bf..0e75fe23fa 100644 --- a/apollo-testing-support/src/appleMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopApple.kt +++ b/apollo-testing-support/src/appleMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopApple.kt @@ -20,6 +20,10 @@ import platform.darwin.dispatch_get_main_queue import platform.darwin.dispatch_time import kotlin.coroutines.CoroutineContext +actual fun runTest(block: suspend CoroutineScope.() -> Unit) { + kotlinx.coroutines.runBlocking { block() } +} + /** * A specialized version of `runBlocking` that keeps a CFRunLoop alive so that apple code can dispatch on the main * queue. There is more to the story and this might hopefully be merged with runBlocking below diff --git a/apollo-testing-support/src/commonMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoop.kt b/apollo-testing-support/src/commonMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoop.kt index 9182c699f0..f048840485 100644 --- a/apollo-testing-support/src/commonMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoop.kt +++ b/apollo-testing-support/src/commonMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoop.kt @@ -7,7 +7,7 @@ import kotlinx.coroutines.withTimeout import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +expect fun runTest(block: suspend CoroutineScope.() -> Unit) expect fun runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T expect fun runWithMainLoop(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T -expect val MainLoopDispatcher: CoroutineDispatcher - +expect val MainLoopDispatcher: CoroutineDispatcher \ No newline at end of file diff --git a/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt index 1695a2b4d7..4016bae4b6 100644 --- a/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt +++ b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt @@ -1,9 +1,14 @@ package com.apollographql.apollo3.testing +import __dirname +import fs.readFileSync +import kotlin.test.assertEquals + actual fun readFile(path: String): String { - TODO() + println(__dirname) + return readFileSync(path, null as String?) as String } actual fun checkFile(actualText: String, path: String) { - TODO() + assertEquals(actualText, readFile(path)) } diff --git a/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJs.kt b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJs.kt index 134350b9c4..6426d45e56 100644 --- a/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJs.kt +++ b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJs.kt @@ -2,15 +2,23 @@ package com.apollographql.apollo3.testing import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.promise import kotlin.coroutines.CoroutineContext +@OptIn(DelicateCoroutinesApi::class) +actual fun runTest(block: suspend CoroutineScope.() -> Unit): dynamic { + return GlobalScope.promise { block() } +} + actual fun runBlocking(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T { - TODO() + TODO("Can't be done in JS!") } actual fun runWithMainLoop(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T { - TODO() + return runBlocking(MainLoopDispatcher + context, block) } -actual val MainLoopDispatcher: CoroutineDispatcher = Dispatchers.Default \ No newline at end of file +actual val MainLoopDispatcher: CoroutineDispatcher = Dispatchers.Main \ No newline at end of file diff --git a/apollo-testing-support/src/jvmMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJvm.kt b/apollo-testing-support/src/jvmMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJvm.kt index f07f710b96..4088144ea3 100644 --- a/apollo-testing-support/src/jvmMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJvm.kt +++ b/apollo-testing-support/src/jvmMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJvm.kt @@ -5,6 +5,10 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlin.coroutines.CoroutineContext +actual fun runTest(block: suspend CoroutineScope.() -> Unit) { + kotlinx.coroutines.runBlocking { block() } +} + actual fun runBlocking(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T { return kotlinx.coroutines.runBlocking(context, block) } diff --git a/build.gradle.kts b/build.gradle.kts index 90c7c9992f..b9a164cf8f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -64,6 +64,7 @@ subprojects { repositories { google() mavenCentral() + jcenter() // https://github.com/Kotlin/kotlinx-nodejs/issues/16 } group = property("GROUP")!! diff --git a/composite/build.gradle.kts b/composite/build.gradle.kts index f14c707434..d11c17de0e 100644 --- a/composite/build.gradle.kts +++ b/composite/build.gradle.kts @@ -20,6 +20,7 @@ subprojects { repositories { google() mavenCentral() + jcenter() // https://github.com/Kotlin/kotlinx-nodejs/issues/16 } afterEvaluate { diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/AutoPersistedQueriesTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/AutoPersistedQueriesTest.kt index 898d5f10ff..af02f3cf37 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/AutoPersistedQueriesTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/AutoPersistedQueriesTest.kt @@ -4,7 +4,7 @@ import com.apollographql.apollo3.ApolloClient import com.apollographql.apollo3.integration.normalizer.HeroNameQuery import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import com.apollographql.apollo3.withAutoPersistedQueries import readResource import kotlin.test.BeforeTest @@ -22,7 +22,7 @@ class AutoPersistedQueriesTest { } @Test - fun withApqsDoesntSendDocument() = runWithMainLoop { + fun withApqsDoesntSendDocument() = runTest { mockServer.enqueue(readResource("HeroNameResponse.json")) val apolloClient = ApolloClient(mockServer.url()).withAutoPersistedQueries() @@ -35,7 +35,7 @@ class AutoPersistedQueriesTest { } @Test - fun withApqsRetriesAfterError() = runWithMainLoop { + fun withApqsRetriesAfterError() = runTest { mockServer.enqueue(""" { "errors": [ diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index f02a7ad0c7..923ee2025d 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -64,7 +64,8 @@ ext.dep = [ coroutinesReactive : "org.jetbrains.kotlinx:kotlinx-coroutines-reactive:$versions.kotlinCoroutines", coroutinesReactor : "org.jetbrains.kotlinx:kotlinx-coroutines-reactor:$versions.kotlinCoroutines", coroutinesAndroid : "org.jetbrains.kotlinx:kotlinx-coroutines-android:$versions.kotlinCoroutines", - reflect : "org.jetbrains.kotlin:kotlin-reflect:$versions.kotlin" + reflect : "org.jetbrains.kotlin:kotlin-reflect:$versions.kotlin", + nodejs : "org.jetbrains.kotlinx:kotlinx-nodejs:0.0.7" ], moshi : [ adapters : "com.squareup.moshi:moshi-adapters:$versions.moshi", From ba71e58a1e295aa3283f1a293adc6128100e010a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ing=2E=20Jan=20Kal=C3=A1b?= Date: Mon, 19 Jul 2021 17:34:43 +0200 Subject: [PATCH 06/43] readFile --- .../kotlin/com/apollographql/apollo3/testing/readFileJs.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt index 4016bae4b6..a7be99ac0c 100644 --- a/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt +++ b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt @@ -1,12 +1,13 @@ package com.apollographql.apollo3.testing -import __dirname import fs.readFileSync import kotlin.test.assertEquals actual fun readFile(path: String): String { - println(__dirname) - return readFileSync(path, null as String?) as String + val pathPrefix = "../../../../../composite/tests/integration-tests/" + return readFileSync("$pathPrefix$path", object: fs.`T$44` { + override var encoding: String? = "utf8" + }) as String } actual fun checkFile(actualText: String, path: String) { From 35136e308af3e382115d588caa9c94ba7d5122dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ing=2E=20Jan=20Kal=C3=A1b?= Date: Tue, 20 Jul 2021 13:30:20 +0200 Subject: [PATCH 07/43] MockServer and Http Client --- apollo-mockserver/build.gradle.kts | 6 +++ .../apollo3/mockserver/MockServer.kt | 25 +++++++++++-- apollo-runtime/build.gradle.kts | 6 +++ .../apollo3/internal/NonMainWorker.kt | 2 +- .../apollo3/network/http/OkHttpEngine.kt | 37 ++++++++++++++++++- .../network/ws/OkHttpWebSocketEngine.kt | 2 +- .../apollo3/testing/runWithMainLoopApple.kt | 2 +- .../apollo3/testing/runWithMainLoop.kt | 2 +- .../apollo3/testing/runWithMainLoopJs.kt | 2 +- .../apollo3/testing/runWithMainLoopJvm.kt | 2 +- .../kotlin/test/AutoPersistedQueriesTest.kt | 6 +++ .../src/commonTest/kotlin/test/BasicTest.kt | 10 ++++- .../kotlin/test/BearerTokenInterceptorTest.kt | 32 ++++++++-------- .../src/jsTest/kotlin/test/PromiseTest.kt | 32 ++++++++++++++++ gradle/dependencies.gradle | 6 ++- 15 files changed, 143 insertions(+), 29 deletions(-) create mode 100644 composite/tests/integration-tests-kotlin/src/jsTest/kotlin/test/PromiseTest.kt diff --git a/apollo-mockserver/build.gradle.kts b/apollo-mockserver/build.gradle.kts index 60e199591b..445e106bcb 100644 --- a/apollo-mockserver/build.gradle.kts +++ b/apollo-mockserver/build.gradle.kts @@ -21,6 +21,12 @@ kotlin { } } + val jsMain by getting { + dependencies { + implementation(groovy.util.Eval.x(project, "x.dep.kotlin.nodejs")) + } + } + val commonTest by getting { dependencies { implementation(project(":apollo-testing-support")) { diff --git a/apollo-mockserver/src/jsMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt b/apollo-mockserver/src/jsMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt index b1f17395b7..222dec4dae 100644 --- a/apollo-mockserver/src/jsMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt +++ b/apollo-mockserver/src/jsMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt @@ -1,12 +1,26 @@ package com.apollographql.apollo3.mockserver actual class MockServer { + + private val responseQueue = mutableListOf() + + private val server = http.createServer { req, res -> + println(req) + res.writeHead(200) + res.end("Hello, World!") + }.listen(PORT) + actual fun url(): String { - TODO("MockServer.url()") + //TODO Use `client.address()` but it might return null, before the server is listening. So this will have to be suspend fun + return "http://localhost:$PORT" + } + + init { + println("MockServer UP") } actual fun enqueue(mockResponse: MockResponse) { - TODO("MockServer.enqueue()") + responseQueue.add(mockResponse) } actual fun takeRequest(): MockRecordedRequest { @@ -14,6 +28,11 @@ actual class MockServer { } actual fun stop() { - TODO("MockServer.stop()") + server.close() + println("MockServer DOWN") + } + + private companion object { + const val PORT = 8080 } } \ No newline at end of file diff --git a/apollo-runtime/build.gradle.kts b/apollo-runtime/build.gradle.kts index 801a08310c..0fb0e45364 100644 --- a/apollo-runtime/build.gradle.kts +++ b/apollo-runtime/build.gradle.kts @@ -22,6 +22,12 @@ kotlin { } } + val jsMain by getting { + dependencies { + api(groovy.util.Eval.x(project, "x.dep.ktor.clientJs")) + } + } + val appleMain by getting { dependencies { } diff --git a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/NonMainWorker.kt b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/NonMainWorker.kt index 03f93eb5c1..942ef18788 100644 --- a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/NonMainWorker.kt +++ b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/NonMainWorker.kt @@ -2,6 +2,6 @@ package com.apollographql.apollo3.internal actual class NonMainWorker { actual suspend fun doWork(block: () -> R): R { - TODO() + TODO("NonMainWorker.doWork()") } } \ No newline at end of file diff --git a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt index 23fc4310e3..293bf47677 100644 --- a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt +++ b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt @@ -1,14 +1,47 @@ package com.apollographql.apollo3.network.http +import com.apollographql.apollo3.api.http.HttpMethod import com.apollographql.apollo3.api.http.HttpRequest import com.apollographql.apollo3.api.http.HttpResponse +import io.ktor.client.HttpClient +import io.ktor.client.call.receive +import io.ktor.client.engine.js.Js +import io.ktor.client.request.header +import io.ktor.client.request.request +import io.ktor.http.HttpHeaders +import io.ktor.util.toMap +import okio.Buffer +import okio.ByteString.Companion.toByteString actual class DefaultHttpEngine actual constructor(connectTimeoutMillis: Long, readTimeoutMillis: Long) : HttpEngine { + private val client = HttpClient(Js) + override suspend fun execute(request: HttpRequest): HttpResponse { - TODO() + val response = client.request(request.url) { + method = when (request.method) { + HttpMethod.Get -> io.ktor.http.HttpMethod.Get + HttpMethod.Post -> io.ktor.http.HttpMethod.Post + } + request.headers.forEach { + header(it.key, it.value) + } + request.body?.let { + header(HttpHeaders.ContentType, it.contentType) + val buffer = Buffer() + it.writeTo(buffer) + body = buffer.readUtf8() + } + } + val responseByteArray: ByteArray = response.receive() + return HttpResponse( + response.status.value, + response.headers.toMap().mapValues { it.value.first() }, + Buffer().write(responseByteArray), + responseByteArray.toByteString() + ) } override fun dispose() { - TODO() + client.close() } } \ No newline at end of file diff --git a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/ws/OkHttpWebSocketEngine.kt b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/ws/OkHttpWebSocketEngine.kt index b222e1e169..01c6109f96 100644 --- a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/ws/OkHttpWebSocketEngine.kt +++ b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/ws/OkHttpWebSocketEngine.kt @@ -5,6 +5,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi @OptIn(ExperimentalCoroutinesApi::class) actual class DefaultWebSocketEngine : WebSocketEngine { override suspend fun open(url: String, headers: Map): WebSocketConnection { - TODO() + TODO("DefaultWebSocketEngine.open()") } } \ No newline at end of file diff --git a/apollo-testing-support/src/appleMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopApple.kt b/apollo-testing-support/src/appleMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopApple.kt index 0e75fe23fa..705f765937 100644 --- a/apollo-testing-support/src/appleMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopApple.kt +++ b/apollo-testing-support/src/appleMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopApple.kt @@ -20,7 +20,7 @@ import platform.darwin.dispatch_get_main_queue import platform.darwin.dispatch_time import kotlin.coroutines.CoroutineContext -actual fun runTest(block: suspend CoroutineScope.() -> Unit) { +actual fun runTest(block: suspend () -> Unit) { kotlinx.coroutines.runBlocking { block() } } diff --git a/apollo-testing-support/src/commonMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoop.kt b/apollo-testing-support/src/commonMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoop.kt index f048840485..672139f59d 100644 --- a/apollo-testing-support/src/commonMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoop.kt +++ b/apollo-testing-support/src/commonMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoop.kt @@ -7,7 +7,7 @@ import kotlinx.coroutines.withTimeout import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -expect fun runTest(block: suspend CoroutineScope.() -> Unit) +expect fun runTest(block: suspend () -> Unit) expect fun runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T expect fun runWithMainLoop(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T expect val MainLoopDispatcher: CoroutineDispatcher \ No newline at end of file diff --git a/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJs.kt b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJs.kt index 6426d45e56..190f8b5666 100644 --- a/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJs.kt +++ b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJs.kt @@ -9,7 +9,7 @@ import kotlinx.coroutines.promise import kotlin.coroutines.CoroutineContext @OptIn(DelicateCoroutinesApi::class) -actual fun runTest(block: suspend CoroutineScope.() -> Unit): dynamic { +actual fun runTest(block: suspend () -> Unit): dynamic { return GlobalScope.promise { block() } } diff --git a/apollo-testing-support/src/jvmMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJvm.kt b/apollo-testing-support/src/jvmMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJvm.kt index 4088144ea3..78f7d97c78 100644 --- a/apollo-testing-support/src/jvmMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJvm.kt +++ b/apollo-testing-support/src/jvmMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJvm.kt @@ -5,7 +5,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlin.coroutines.CoroutineContext -actual fun runTest(block: suspend CoroutineScope.() -> Unit) { +actual fun runTest(block: suspend () -> Unit) { kotlinx.coroutines.runBlocking { block() } } diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/AutoPersistedQueriesTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/AutoPersistedQueriesTest.kt index af02f3cf37..2cd7826cda 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/AutoPersistedQueriesTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/AutoPersistedQueriesTest.kt @@ -7,10 +7,12 @@ import com.apollographql.apollo3.mockserver.enqueue import com.apollographql.apollo3.testing.runTest import com.apollographql.apollo3.withAutoPersistedQueries import readResource +import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertFalse import kotlin.test.assertTrue +import kotlinx.coroutines.delay class AutoPersistedQueriesTest { private lateinit var mockServer: MockServer @@ -18,7 +20,11 @@ class AutoPersistedQueriesTest { @BeforeTest fun setUp() { mockServer = MockServer() + } + @AfterTest + fun tearDown() { + mockServer.stop() } @Test diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BasicTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BasicTest.kt index eb658d992f..a798f35390 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BasicTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BasicTest.kt @@ -21,8 +21,9 @@ import com.apollographql.apollo3.cache.normalized.withFetchPolicy import com.apollographql.apollo3.cache.normalized.withStore import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import readResource +import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertFalse @@ -51,7 +52,12 @@ class BasicTest { apolloClient = ApolloClient(mockServer.url()).withStore(store) } - private fun basicTest(resourceName: String, query: Query, block: ApolloResponse.() -> Unit) = runWithMainLoop { + @AfterTest + fun tearDown() { + mockServer.stop() + } + + private fun basicTest(resourceName: String, query: Query, block: ApolloResponse.() -> Unit) = runTest { mockServer.enqueue(readResource(resourceName)) var response = apolloClient.query(ApolloRequest(query).withFetchPolicy(FetchPolicy.NetworkOnly)) response.block() diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BearerTokenInterceptorTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BearerTokenInterceptorTest.kt index b85dfaa4e5..4c905e6551 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BearerTokenInterceptorTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BearerTokenInterceptorTest.kt @@ -9,12 +9,13 @@ import com.apollographql.apollo3.mockserver.enqueue import com.apollographql.apollo3.network.http.BearerTokenInterceptor import com.apollographql.apollo3.network.http.HttpNetworkTransport import com.apollographql.apollo3.testing.TestTokenProvider -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import readResource import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals @@ -35,8 +36,13 @@ class BearerTokenInterceptorTest { mockServer.enqueue(readResource("HeroNameResponse.json")) } + @AfterTest + fun tearDown() { + mockServer.stop() + } + @Test - fun succeedsWithInterceptor() { + fun succeedsWithInterceptor() = runTest { apolloClient = ApolloClient( networkTransport = HttpNetworkTransport( serverUrl = mockServer.url(), @@ -44,29 +50,25 @@ class BearerTokenInterceptorTest { ) ) - runWithMainLoop { - val response = apolloClient.query(HeroNameQuery()) - assertEquals("R2-D2", response.data?.hero?.name) + val response = apolloClient.query(HeroNameQuery()) + assertEquals("R2-D2", response.data?.hero?.name) - assertEquals("Bearer $token1", mockServer.takeRequest().headers["Authorization"]) - assertEquals("Bearer $token2", mockServer.takeRequest().headers["Authorization"]) - } + assertEquals("Bearer $token1", mockServer.takeRequest().headers["Authorization"]) + assertEquals("Bearer $token2", mockServer.takeRequest().headers["Authorization"]) } @Test - fun failsWithoutInterceptor() { + fun failsWithoutInterceptor() = runTest { apolloClient = ApolloClient( networkTransport = HttpNetworkTransport( serverUrl = mockServer.url(), ) ) - runWithMainLoop { - try { - apolloClient.query(HeroNameQuery()) - } catch (e: ApolloHttpException) { - assertEquals(401, e.statusCode) - } + try { + apolloClient.query(HeroNameQuery()) + } catch (e: ApolloHttpException) { + assertEquals(401, e.statusCode) } } } \ No newline at end of file diff --git a/composite/tests/integration-tests-kotlin/src/jsTest/kotlin/test/PromiseTest.kt b/composite/tests/integration-tests-kotlin/src/jsTest/kotlin/test/PromiseTest.kt new file mode 100644 index 0000000000..45e93b2308 --- /dev/null +++ b/composite/tests/integration-tests-kotlin/src/jsTest/kotlin/test/PromiseTest.kt @@ -0,0 +1,32 @@ +package test + +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertTrue +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.promise + +class PromiseTest { + var inTest = false + + @BeforeTest + fun before() { + inTest = true + } + + @AfterTest + fun after() { + inTest = false + } + + @Test + fun withoutPromise() { + assertTrue(inTest) + } + + @Test + fun withPromise() = GlobalScope.promise { + assertTrue(inTest) // FAIL?! + } +} diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 7802ef6bd1..8622baa4e7 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -24,6 +24,7 @@ def versions = [ rxandroid : '2.0.1', sqldelight : '1.5.0', truth : '0.30', + ktor : '1.6.1', ] ext.dep = [ @@ -106,7 +107,10 @@ ext.dep = [ kspGradlePlugin: "com.google.devtools.ksp:symbol-processing-gradle-plugin:1.5.20-1.0.0-beta04", kotlinxdatetime: "org.jetbrains.kotlinx:kotlinx-datetime:0.1.1", kotlinxserializationjson: "org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0", - atomicfu: "org.jetbrains.kotlinx:atomicfu:0.15.1" + atomicfu: "org.jetbrains.kotlinx:atomicfu:0.15.1", + ktor : [ + clientJs: "io.ktor:ktor-client-js:$versions.ktor" + ] ] ext.androidConfig = [ compileSdkVersion: 30, From 111cbe2e459d6348ce9922612fc8af99e03ae1cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ing=2E=20Jan=20Kal=C3=A1b?= Date: Wed, 21 Jul 2021 08:43:57 +0200 Subject: [PATCH 08/43] Apply suggestions from code review Co-authored-by: Martin Bonnin --- build.gradle.kts | 7 ++++++- composite/build.gradle.kts | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index b9a164cf8f..9aed1a8765 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -64,7 +64,12 @@ subprojects { repositories { google() mavenCentral() - jcenter() // https://github.com/Kotlin/kotlinx-nodejs/issues/16 + jcenter { + content { + // https://github.com/Kotlin/kotlinx-nodejs/issues/16 + includeModule("org.jetbrains.kotlinx", "kotlinx-nodejs") + } + } } group = property("GROUP")!! diff --git a/composite/build.gradle.kts b/composite/build.gradle.kts index d11c17de0e..0245e3a6c0 100644 --- a/composite/build.gradle.kts +++ b/composite/build.gradle.kts @@ -20,7 +20,12 @@ subprojects { repositories { google() mavenCentral() - jcenter() // https://github.com/Kotlin/kotlinx-nodejs/issues/16 + jcenter { + content { + // https://github.com/Kotlin/kotlinx-nodejs/issues/16 + includeModule("org.jetbrains.kotlinx", "kotlinx-nodejs") + } + } } afterEvaluate { From 0b2c169ce71a2d6f95022796f399a38662ed2728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ing=2E=20Jan=20Kal=C3=A1b?= Date: Thu, 22 Jul 2021 16:26:55 +0200 Subject: [PATCH 09/43] Tests are almost green --- .../apollo3/mockserver/MockServer.kt | 4 +- .../apollo3/mockserver/MockServer.kt | 4 +- .../apollo3/mockserver/MockServerCommon.kt | 2 +- .../apollo3/mockserver/MockServer.kt | 64 +++-- .../apollo3/mockserver/MockServer.kt | 4 +- .../apollo3/internal/NonMainWorker.kt | 2 +- .../apollo3/network/http/OkHttpEngine.kt | 50 ++-- .../apollo3/testing/runWithMainLoopApple.kt | 29 +- .../apollo3/testing/runWithMainLoop.kt | 13 +- .../apollo3/testing/runWithMainLoopJs.kt | 28 +- .../apollo3/testing/runWithMainLoopJvm.kt | 29 +- .../kotlin/test/AutoPersistedQueriesTest.kt | 12 +- .../src/commonTest/kotlin/test/BasicTest.kt | 14 +- .../kotlin/test/BearerTokenInterceptorTest.kt | 12 +- .../commonTest/kotlin/test/CacheFlagsTest.kt | 108 ++++---- .../kotlin/test/CacheResolverTest.kt | 10 +- .../src/commonTest/kotlin/test/CancelTest.kt | 26 +- .../commonTest/kotlin/test/ExceptionsTest.kt | 29 +- .../commonTest/kotlin/test/FetchPolicyTest.kt | 256 +++++++++--------- .../commonTest/kotlin/test/FileUploadTest.kt | 18 +- .../commonTest/kotlin/test/HTTPHeadersTest.kt | 38 +-- .../kotlin/test/HttpInterceptorTest.kt | 19 +- .../kotlin/test/HttpRequestComposerTest.kt | 32 ++- .../commonTest/kotlin/test/JsonScalarTest.kt | 12 +- .../kotlin/test/OptimisticCacheTest.kt | 17 +- .../commonTest/kotlin/test/OtherCacheTest.kt | 20 +- .../src/commonTest/kotlin/test/StoreTest.kt | 18 +- .../src/commonTest/kotlin/test/WatcherTest.kt | 34 +-- .../kotlin/test/batching/QueryBatchingTest.kt | 137 +++++----- .../declarativecache/DeclarativeCacheTest.kt | 10 +- .../src/jsTest/kotlin/test/PromiseTest.kt | 32 --- .../test/WriteToCacheAsynchronouslyTest.kt | 15 +- 32 files changed, 546 insertions(+), 552 deletions(-) delete mode 100644 composite/tests/integration-tests-kotlin/src/jsTest/kotlin/test/PromiseTest.kt diff --git a/apollo-mockserver/src/appleMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt b/apollo-mockserver/src/appleMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt index cc89953f3a..74b8b0201d 100644 --- a/apollo-mockserver/src/appleMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt +++ b/apollo-mockserver/src/appleMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt @@ -79,7 +79,7 @@ actual class MockServer { }, stableRef.asCPointer()) } - actual fun url(): String { + actual suspend fun url(): String { return "http://localhost:$port" } @@ -92,7 +92,7 @@ actual class MockServer { * If stop() is called while we're reading a request, this might wait forever * Revisit once okio has native Timeout */ - actual fun stop() { + actual suspend fun stop() { socket.stop() pthread_join(pthreadT.value, null) diff --git a/apollo-mockserver/src/commonMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt b/apollo-mockserver/src/commonMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt index f6a306eafe..f290a9e865 100644 --- a/apollo-mockserver/src/commonMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt +++ b/apollo-mockserver/src/commonMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt @@ -3,14 +3,14 @@ package com.apollographql.apollo3.mockserver import okio.ByteString.Companion.encodeUtf8 expect class MockServer() { - fun url(): String + suspend fun url(): String fun enqueue(mockResponse: MockResponse) /** * Returns a request from the recorded requests or throws if no request has been received */ fun takeRequest(): MockRecordedRequest - fun stop() + suspend fun stop() } fun MockServer.enqueue(string: String, delayMs: Long = 0) { diff --git a/apollo-mockserver/src/commonMain/kotlin/com/apollographql/apollo3/mockserver/MockServerCommon.kt b/apollo-mockserver/src/commonMain/kotlin/com/apollographql/apollo3/mockserver/MockServerCommon.kt index 6298ccf04d..404b971736 100644 --- a/apollo-mockserver/src/commonMain/kotlin/com/apollographql/apollo3/mockserver/MockServerCommon.kt +++ b/apollo-mockserver/src/commonMain/kotlin/com/apollographql/apollo3/mockserver/MockServerCommon.kt @@ -15,7 +15,7 @@ fun parseHeader(line: String): Pair { return line.substring(0, index).trim() to line.substring(index + 1, line.length).trim() } -class MockRecordedRequest( +data class MockRecordedRequest( val method: String, val path: String, val version: String, diff --git a/apollo-mockserver/src/jsMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt b/apollo-mockserver/src/jsMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt index 222dec4dae..f0651a883e 100644 --- a/apollo-mockserver/src/jsMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt +++ b/apollo-mockserver/src/jsMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt @@ -1,22 +1,49 @@ package com.apollographql.apollo3.mockserver +import Buffer +import net.AddressInfo +import okio.ByteString.Companion.toByteString +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine + actual class MockServer { private val responseQueue = mutableListOf() + private val requests = mutableListOf() private val server = http.createServer { req, res -> - println(req) - res.writeHead(200) - res.end("Hello, World!") - }.listen(PORT) - - actual fun url(): String { - //TODO Use `client.address()` but it might return null, before the server is listening. So this will have to be suspend fun - return "http://localhost:$PORT" - } - - init { - println("MockServer UP") + val requestBody = StringBuilder() + req.on("data") { chunk -> + when (chunk) { + is String -> requestBody.append(chunk) + is Buffer -> requestBody.append(chunk.toString("utf8")) + else -> println("WTF") + } + } + req.on("end") { _ -> + requests.add( + MockRecordedRequest( + req.method, + req.url, + req.httpVersion, + req.rawHeaders.toList().zipWithNext().toMap(), + requestBody.toString().encodeToByteArray().toByteString() + ) + ) + } + + val mockResponse = responseQueue.removeFirst() + res.statusCode = mockResponse.statusCode + mockResponse.headers.forEach { + res.setHeader(it.key, it.value) + } + res.end(mockResponse.body.utf8()) + }.listen() + + actual suspend fun url() = suspendCoroutine { cont -> + server.on("listening") { _ -> + cont.resume("http://localhost:${server.address().unsafeCast().port}") + } } actual fun enqueue(mockResponse: MockResponse) { @@ -24,15 +51,12 @@ actual class MockServer { } actual fun takeRequest(): MockRecordedRequest { - TODO("MockServer.takeRequest()") - } - - actual fun stop() { - server.close() - println("MockServer DOWN") + return requests.removeFirst() } - private companion object { - const val PORT = 8080 + actual suspend fun stop() = suspendCoroutine { cont -> + server.close { + cont.resume(Unit) + } } } \ No newline at end of file diff --git a/apollo-mockserver/src/jvmMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt b/apollo-mockserver/src/jvmMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt index 28a50f8821..426b39f59a 100644 --- a/apollo-mockserver/src/jvmMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt +++ b/apollo-mockserver/src/jvmMain/kotlin/com/apollographql/apollo3/mockserver/MockServer.kt @@ -37,11 +37,11 @@ actual class MockServer { name(it) to get(name(it))!! }.toMap() - actual fun url(): String { + actual suspend fun url(): String { return mockWebServer.url("/").toString() } - actual fun stop() { + actual suspend fun stop() { mockWebServer.shutdown() } } \ No newline at end of file diff --git a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/NonMainWorker.kt b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/NonMainWorker.kt index 942ef18788..902429b4bb 100644 --- a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/NonMainWorker.kt +++ b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/internal/NonMainWorker.kt @@ -2,6 +2,6 @@ package com.apollographql.apollo3.internal actual class NonMainWorker { actual suspend fun doWork(block: () -> R): R { - TODO("NonMainWorker.doWork()") + return block() } } \ No newline at end of file diff --git a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt index 293bf47677..30fe7bf177 100644 --- a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt +++ b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt @@ -3,6 +3,7 @@ package com.apollographql.apollo3.network.http import com.apollographql.apollo3.api.http.HttpMethod import com.apollographql.apollo3.api.http.HttpRequest import com.apollographql.apollo3.api.http.HttpResponse +import com.apollographql.apollo3.exception.ApolloNetworkException import io.ktor.client.HttpClient import io.ktor.client.call.receive import io.ktor.client.engine.js.Js @@ -14,31 +15,38 @@ import okio.Buffer import okio.ByteString.Companion.toByteString actual class DefaultHttpEngine actual constructor(connectTimeoutMillis: Long, readTimeoutMillis: Long) : HttpEngine { - private val client = HttpClient(Js) + private val client = HttpClient(Js) { + expectSuccess = false + } override suspend fun execute(request: HttpRequest): HttpResponse { - val response = client.request(request.url) { - method = when (request.method) { - HttpMethod.Get -> io.ktor.http.HttpMethod.Get - HttpMethod.Post -> io.ktor.http.HttpMethod.Post - } - request.headers.forEach { - header(it.key, it.value) - } - request.body?.let { - header(HttpHeaders.ContentType, it.contentType) - val buffer = Buffer() - it.writeTo(buffer) - body = buffer.readUtf8() + try { + val response = client.request(request.url) { + method = when (request.method) { + HttpMethod.Get -> io.ktor.http.HttpMethod.Get + HttpMethod.Post -> io.ktor.http.HttpMethod.Post + } + request.headers.forEach { + header(it.key, it.value) + } + request.body?.let { + header(HttpHeaders.ContentType, it.contentType) + val buffer = Buffer() + it.writeTo(buffer) + body = buffer.readUtf8() + } } + println(response.headers) + val responseByteArray: ByteArray = response.receive() + return HttpResponse( + response.status.value, + response.headers.toMap().mapValues { it.value.first() }, + Buffer().write(responseByteArray), + responseByteArray.toByteString() + ) + } catch (t: Throwable) { + throw ApolloNetworkException(t.message, t) } - val responseByteArray: ByteArray = response.receive() - return HttpResponse( - response.status.value, - response.headers.toMap().mapValues { it.value.first() }, - Buffer().write(responseByteArray), - responseByteArray.toByteString() - ) } override fun dispose() { diff --git a/apollo-testing-support/src/appleMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopApple.kt b/apollo-testing-support/src/appleMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopApple.kt index 705f765937..6aed6df840 100644 --- a/apollo-testing-support/src/appleMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopApple.kt +++ b/apollo-testing-support/src/appleMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopApple.kt @@ -8,7 +8,6 @@ import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.Runnable -import kotlinx.coroutines.async import kotlinx.coroutines.launch import platform.CoreFoundation.CFRunLoopGetCurrent import platform.CoreFoundation.CFRunLoopRun @@ -20,32 +19,31 @@ import platform.darwin.dispatch_get_main_queue import platform.darwin.dispatch_time import kotlin.coroutines.CoroutineContext -actual fun runTest(block: suspend () -> Unit) { - kotlinx.coroutines.runBlocking { block() } -} - /** * A specialized version of `runBlocking` that keeps a CFRunLoop alive so that apple code can dispatch on the main * queue. There is more to the story and this might hopefully be merged with runBlocking below * but for now that allows us to run integration tests against a mocked server */ -actual fun runWithMainLoop(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T { - var result: Result? = null - GlobalScope.launch (MainLoopDispatcher) { - result = kotlin.runCatching { +actual fun runTest( + context: CoroutineContext, + before: suspend CoroutineScope.() -> Unit, + after: suspend CoroutineScope.() -> Unit, + block: suspend CoroutineScope.() -> Unit, +) { + GlobalScope.launch(context + MainLoopDispatcher) { + before() + try { block() + } finally { + after() } CFRunLoopStop(CFRunLoopGetCurrent()) } CFRunLoopRun() - - return result!!.getOrThrow() } -actual fun runBlocking(context: CoroutineContext, block: suspend CoroutineScope.() -> T) = kotlinx.coroutines.runBlocking { block() } - @OptIn(InternalCoroutinesApi::class) -actual val MainLoopDispatcher: CoroutineDispatcher = object : CoroutineDispatcher(), Delay { +val MainLoopDispatcher: CoroutineDispatcher = object : CoroutineDispatcher(), Delay { override fun dispatch(context: CoroutineContext, block: Runnable) { dispatch_async(dispatch_get_main_queue()) { @@ -80,5 +78,4 @@ actual val MainLoopDispatcher: CoroutineDispatcher = object : CoroutineDispatche return handle } -} - +} \ No newline at end of file diff --git a/apollo-testing-support/src/commonMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoop.kt b/apollo-testing-support/src/commonMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoop.kt index 672139f59d..204a2b7f17 100644 --- a/apollo-testing-support/src/commonMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoop.kt +++ b/apollo-testing-support/src/commonMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoop.kt @@ -1,13 +1,12 @@ package com.apollographql.apollo3.testing -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.withTimeout import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -expect fun runTest(block: suspend () -> Unit) -expect fun runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T -expect fun runWithMainLoop(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T -expect val MainLoopDispatcher: CoroutineDispatcher \ No newline at end of file +expect fun runTest( + context: CoroutineContext = EmptyCoroutineContext, + before: suspend CoroutineScope.() -> Unit = {}, + after: suspend CoroutineScope.() -> Unit = {}, + block: suspend CoroutineScope.() -> Unit, +) \ No newline at end of file diff --git a/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJs.kt b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJs.kt index 190f8b5666..c16d7a50c8 100644 --- a/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJs.kt +++ b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJs.kt @@ -1,24 +1,22 @@ package com.apollographql.apollo3.testing -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.promise import kotlin.coroutines.CoroutineContext @OptIn(DelicateCoroutinesApi::class) -actual fun runTest(block: suspend () -> Unit): dynamic { - return GlobalScope.promise { block() } -} - -actual fun runBlocking(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T { - TODO("Can't be done in JS!") -} - -actual fun runWithMainLoop(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T { - return runBlocking(MainLoopDispatcher + context, block) -} - -actual val MainLoopDispatcher: CoroutineDispatcher = Dispatchers.Main \ No newline at end of file +actual fun runTest( + context: CoroutineContext, + before: suspend CoroutineScope.() -> Unit, + after: suspend CoroutineScope.() -> Unit, + block: suspend CoroutineScope.() -> Unit, +): dynamic = GlobalScope.promise(context) { + before() + try { + block() + } finally { + after() + } +} \ No newline at end of file diff --git a/apollo-testing-support/src/jvmMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJvm.kt b/apollo-testing-support/src/jvmMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJvm.kt index 78f7d97c78..885e5b1cf4 100644 --- a/apollo-testing-support/src/jvmMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJvm.kt +++ b/apollo-testing-support/src/jvmMain/kotlin/com/apollographql/apollo3/testing/runWithMainLoopJvm.kt @@ -1,20 +1,19 @@ package com.apollographql.apollo3.testing -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking import kotlin.coroutines.CoroutineContext -actual fun runTest(block: suspend () -> Unit) { - kotlinx.coroutines.runBlocking { block() } -} - -actual fun runBlocking(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T { - return kotlinx.coroutines.runBlocking(context, block) -} - -actual fun runWithMainLoop(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T { - return kotlinx.coroutines.runBlocking(context, block) -} - -actual val MainLoopDispatcher: CoroutineDispatcher = Dispatchers.Default \ No newline at end of file +actual fun runTest( + context: CoroutineContext, + before: suspend CoroutineScope.() -> Unit, + after: suspend CoroutineScope.() -> Unit, + block: suspend CoroutineScope.() -> Unit, +) = runBlocking(context) { + before() + try { + block() + } finally { + after() + } +} \ No newline at end of file diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/AutoPersistedQueriesTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/AutoPersistedQueriesTest.kt index 2cd7826cda..1436a7bf25 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/AutoPersistedQueriesTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/AutoPersistedQueriesTest.kt @@ -7,8 +7,6 @@ import com.apollographql.apollo3.mockserver.enqueue import com.apollographql.apollo3.testing.runTest import com.apollographql.apollo3.withAutoPersistedQueries import readResource -import kotlin.test.AfterTest -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -17,18 +15,16 @@ import kotlinx.coroutines.delay class AutoPersistedQueriesTest { private lateinit var mockServer: MockServer - @BeforeTest - fun setUp() { + private suspend fun setUp() { mockServer = MockServer() } - @AfterTest - fun tearDown() { + private suspend fun tearDown() { mockServer.stop() } @Test - fun withApqsDoesntSendDocument() = runTest { + fun withApqsDoesntSendDocument() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(readResource("HeroNameResponse.json")) val apolloClient = ApolloClient(mockServer.url()).withAutoPersistedQueries() @@ -41,7 +37,7 @@ class AutoPersistedQueriesTest { } @Test - fun withApqsRetriesAfterError() = runTest { + fun withApqsRetriesAfterError() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(""" { "errors": [ diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BasicTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BasicTest.kt index a798f35390..99b7fd6236 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BasicTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BasicTest.kt @@ -23,8 +23,6 @@ import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue import com.apollographql.apollo3.testing.runTest import readResource -import kotlin.test.AfterTest -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertFalse import kotlin.test.assertNull @@ -42,8 +40,7 @@ class BasicTest { private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore - @BeforeTest - fun setUp() { + private suspend fun setUp() { store = ApolloStore( normalizedCacheFactory = MemoryCacheFactory(), objectIdGenerator = IdObjectIdGenerator @@ -52,12 +49,15 @@ class BasicTest { apolloClient = ApolloClient(mockServer.url()).withStore(store) } - @AfterTest - fun tearDown() { + private suspend fun tearDown() { mockServer.stop() } - private fun basicTest(resourceName: String, query: Query, block: ApolloResponse.() -> Unit) = runTest { + private fun basicTest( + resourceName: String, + query: Query, + block: ApolloResponse.() -> Unit, + ) = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(readResource(resourceName)) var response = apolloClient.query(ApolloRequest(query).withFetchPolicy(FetchPolicy.NetworkOnly)) response.block() diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BearerTokenInterceptorTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BearerTokenInterceptorTest.kt index 4c905e6551..63da1d489a 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BearerTokenInterceptorTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/BearerTokenInterceptorTest.kt @@ -15,8 +15,6 @@ import kotlinx.coroutines.launch import readResource import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -import kotlin.test.AfterTest -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals @@ -28,21 +26,19 @@ class BearerTokenInterceptorTest { private var token1 = "token1" private var token2 = "token2" - @BeforeTest - fun setUp() { + private suspend fun setUp() { tokenProvider = TestTokenProvider(token1, token2) mockServer = MockServer() mockServer.enqueue(MockResponse(statusCode = 401)) mockServer.enqueue(readResource("HeroNameResponse.json")) } - @AfterTest - fun tearDown() { + private suspend fun tearDown() { mockServer.stop() } @Test - fun succeedsWithInterceptor() = runTest { + fun succeedsWithInterceptor() = runTest(before = { setUp() }, after = { tearDown() }) { apolloClient = ApolloClient( networkTransport = HttpNetworkTransport( serverUrl = mockServer.url(), @@ -58,7 +54,7 @@ class BearerTokenInterceptorTest { } @Test - fun failsWithoutInterceptor() = runTest { + fun failsWithoutInterceptor() = runTest(before = { setUp() }, after = { tearDown() }) { apolloClient = ApolloClient( networkTransport = HttpNetworkTransport( serverUrl = mockServer.url(), diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/CacheFlagsTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/CacheFlagsTest.kt index c88626d1c0..3455724ad6 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/CacheFlagsTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/CacheFlagsTest.kt @@ -20,8 +20,7 @@ import com.apollographql.apollo3.cache.normalized.withStore import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue import com.apollographql.apollo3.testing.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop -import kotlin.test.BeforeTest +import com.apollographql.apollo3.testing.runTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -32,53 +31,52 @@ class CacheFlagsTest { private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore - @BeforeTest - fun setUp() { + private suspend fun setUp() { store = ApolloStore(MemoryCacheFactory()) mockServer = MockServer() apolloClient = ApolloClient(mockServer.url()).withStore(store) } + private suspend fun tearDown() { + mockServer.stop() + } + @Test - fun doNotStore() { - runWithMainLoop { - val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) - mockServer.enqueue(query, data) - - apolloClient.query(ApolloRequest(query).withCacheFlags(CACHE_FLAG_DO_NOT_STORE)) - - // Since the previous request was not stored, this should fail - assertFailsWith(CacheMissException::class) { - apolloClient.query(ApolloRequest(query).withFetchPolicy(FetchPolicy.CacheOnly)) - } + fun doNotStore() = runTest(before = { setUp() }, after = { tearDown() }) { + val query = HeroNameQuery() + val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) + mockServer.enqueue(query, data) + + apolloClient.query(ApolloRequest(query).withCacheFlags(CACHE_FLAG_DO_NOT_STORE)) + + // Since the previous request was not stored, this should fail + assertFailsWith(CacheMissException::class) { + apolloClient.query(ApolloRequest(query).withFetchPolicy(FetchPolicy.CacheOnly)) } } @Test - fun testEvictAfterRead() { - runWithMainLoop { - val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) - mockServer.enqueue(query, data) - - // Store the data - apolloClient.query(ApolloRequest(query).withFetchPolicy(FetchPolicy.NetworkOnly)) - - // This should work and evict the entries - val response = apolloClient.query( - ApolloRequest(query) - .withFetchPolicy(FetchPolicy.CacheOnly) - .withCacheHeaders( - CacheHeaders.builder().addHeader(ApolloCacheHeaders.EVICT_AFTER_READ, "true").build() - ) - ) - assertEquals("R2-D2", response.data?.hero?.name) - - // Second time should fail - assertFailsWith(CacheMissException::class) { - apolloClient.query(ApolloRequest(query).withFetchPolicy(FetchPolicy.CacheOnly)) - } + fun testEvictAfterRead() = runTest(before = { setUp() }, after = { tearDown() }) { + val query = HeroNameQuery() + val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) + mockServer.enqueue(query, data) + + // Store the data + apolloClient.query(ApolloRequest(query).withFetchPolicy(FetchPolicy.NetworkOnly)) + + // This should work and evict the entries + val response = apolloClient.query( + ApolloRequest(query) + .withFetchPolicy(FetchPolicy.CacheOnly) + .withCacheHeaders( + CacheHeaders.builder().addHeader(ApolloCacheHeaders.EVICT_AFTER_READ, "true").build() + ) + ) + assertEquals("R2-D2", response.data?.hero?.name) + + // Second time should fail + assertFailsWith(CacheMissException::class) { + apolloClient.query(ApolloRequest(query).withFetchPolicy(FetchPolicy.CacheOnly)) } } @@ -100,31 +98,27 @@ class CacheFlagsTest { ) @Test - fun partialResponsesAreNotStored() { - runWithMainLoop { - val query = HeroNameQuery() - mockServer.enqueue(AnyAdapter.toJson(partialResponse)) + fun partialResponsesAreNotStored() = runTest(before = { setUp() }, after = { tearDown() }) { + val query = HeroNameQuery() + mockServer.enqueue(AnyAdapter.toJson(partialResponse)) - // this should not store the response - apolloClient.query(ApolloRequest(query)) + // this should not store the response + apolloClient.query(ApolloRequest(query)) - assertFailsWith(CacheMissException::class) { - apolloClient.query(ApolloRequest(query).withFetchPolicy(FetchPolicy.CacheOnly)) - } + assertFailsWith(CacheMissException::class) { + apolloClient.query(ApolloRequest(query).withFetchPolicy(FetchPolicy.CacheOnly)) } } @Test - fun storePartialResponse() { - runWithMainLoop { - val query = HeroNameQuery() - mockServer.enqueue(AnyAdapter.toJson(partialResponse)) + fun storePartialResponse() = runTest(before = { setUp() }, after = { tearDown() }) { + val query = HeroNameQuery() + mockServer.enqueue(AnyAdapter.toJson(partialResponse)) - // this should not store the response - apolloClient.query(ApolloRequest(query).withCacheFlags(CACHE_FLAG_STORE_PARTIAL_RESPONSE)) + // this should not store the response + apolloClient.query(ApolloRequest(query).withCacheFlags(CACHE_FLAG_STORE_PARTIAL_RESPONSE)) - val response = apolloClient.query(ApolloRequest(query).withFetchPolicy(FetchPolicy.CacheOnly)) - assertNotNull(response.data) - } + val response = apolloClient.query(ApolloRequest(query).withFetchPolicy(FetchPolicy.CacheOnly)) + assertNotNull(response.data) } } \ No newline at end of file diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/CacheResolverTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/CacheResolverTest.kt index 69835708fd..247ca3af71 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/CacheResolverTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/CacheResolverTest.kt @@ -9,13 +9,13 @@ import com.apollographql.apollo3.cache.normalized.MapCacheResolver import com.apollographql.apollo3.cache.normalized.MemoryCacheFactory import com.apollographql.apollo3.integration.normalizer.HeroNameQuery import com.apollographql.apollo3.cache.normalized.withStore -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import kotlin.test.Test import kotlin.test.assertEquals class CacheResolverTest { @Test - fun cacheResolverCanResolveQuery() { + fun cacheResolverCanResolveQuery() = runTest { val resolver = object : CacheResolver { override fun resolveField(field: CompiledField, variables: Executable.Variables, parent: Map, parentId: String): Any? { return when (field.name) { @@ -31,10 +31,8 @@ class CacheResolverTest { ) ) - runWithMainLoop { - val response = apolloClient.query(HeroNameQuery()) + val response = apolloClient.query(HeroNameQuery()) - assertEquals("Luke", response.data?.hero?.name) - } + assertEquals("Luke", response.data?.hero?.name) } } \ No newline at end of file diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/CancelTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/CancelTest.kt index 392b931052..4af983cbc4 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/CancelTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/CancelTest.kt @@ -5,37 +5,37 @@ import com.apollographql.apollo3.integration.normalizer.EpisodeHeroNameQuery import com.apollographql.apollo3.integration.normalizer.type.Episode import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import kotlinx.coroutines.delay import kotlinx.coroutines.launch import readTestFixture -import kotlin.test.BeforeTest import kotlin.test.Test class CancelTest { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient - @BeforeTest - fun setUp() { + private suspend fun setUp() { mockServer = MockServer() apolloClient = ApolloClient(mockServer.url()) } + private suspend fun tearDown() { + mockServer.stop() + } + @Test @Throws(Exception::class) - fun cancelFlow() { + fun cancelFlow() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(readTestFixture("resources/EpisodeHeroNameResponse.json")) - runWithMainLoop { - val job = launch { - delay(100) - apolloClient.query(EpisodeHeroNameQuery(Episode.EMPIRE)) - error("The Flow should have been canceled before reaching that state") - } - job.cancel() - job.join() + val job = launch { + delay(100) + apolloClient.query(EpisodeHeroNameQuery(Episode.EMPIRE)) + error("The Flow should have been canceled before reaching that state") } + job.cancel() + job.join() } // @Test diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/ExceptionsTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/ExceptionsTest.kt index 82e0c4b641..802c5e8391 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/ExceptionsTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/ExceptionsTest.kt @@ -8,11 +8,11 @@ import com.apollographql.apollo3.integration.normalizer.HeroNameQuery import com.apollographql.apollo3.mockserver.MockResponse import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import com.apollographql.apollo3.testing.enqueue +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.retryWhen import kotlinx.coroutines.flow.single -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -21,14 +21,17 @@ class ExceptionsTest { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient - @BeforeTest - fun setUp() { + private suspend fun setUp() { mockServer = MockServer() apolloClient = ApolloClient(mockServer.url()) } + private suspend fun tearDown() { + mockServer.stop() + } + @Test - fun whenQueryAndMalformedNetworkResponseAssertException() = runWithMainLoop { + fun whenQueryAndMalformedNetworkResponseAssertException() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue("malformed") val result = kotlin.runCatching { @@ -39,7 +42,7 @@ class ExceptionsTest { } @Test - fun whenHttpErrorAssertExecuteFails() = runWithMainLoop { + fun whenHttpErrorAssertExecuteFails() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(MockResponse(statusCode = 404)) val result = kotlin.runCatching { @@ -52,7 +55,7 @@ class ExceptionsTest { } @Test - fun whenNetworkErrorAssertApolloNetworkException() = runWithMainLoop { + fun whenNetworkErrorAssertApolloNetworkException() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.stop() val result = kotlin.runCatching { @@ -64,18 +67,16 @@ class ExceptionsTest { } @Test - fun WhenQueryAndMalformedNetworkResponseAssertSuccessAfterRetry() { + fun WhenQueryAndMalformedNetworkResponseAssertSuccessAfterRetry() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue("") val query = HeroNameQuery() val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) mockServer.enqueue(query, data) - val response = runWithMainLoop { - apolloClient - .queryAsFlow(ApolloRequest(query)) - .retryWhen { _, attempt -> attempt == 0L } - .single() - } + val response = apolloClient + .queryAsFlow(ApolloRequest(query)) + .retryWhen { _, attempt -> attempt == 0L } + .single() assertEquals(data, response.data) } diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/FetchPolicyTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/FetchPolicyTest.kt index 467c350972..6f4e19842c 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/FetchPolicyTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/FetchPolicyTest.kt @@ -15,9 +15,8 @@ import com.apollographql.apollo3.mockserver.MockResponse import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue import com.apollographql.apollo3.testing.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import kotlinx.coroutines.flow.toList -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -31,181 +30,174 @@ class FetchPolicyTest { private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore - @BeforeTest - fun setUp() { + private suspend fun setUp() { store = ApolloStore(MemoryCacheFactory()) mockServer = MockServer() apolloClient = ApolloClient(mockServer.url()).withStore(store) } + private suspend fun tearDown() { + mockServer.stop() + } + @Test - fun cacheFirst() { + fun cacheFirst() = runTest(before = { setUp() }, after = { tearDown() }) { val query = HeroNameQuery() val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) mockServer.enqueue(query, data) // Cache first is also the default, no need to set the fetchPolicy - runWithMainLoop { - // First query should hit the network and save in cache - val request = ApolloRequest(query).withFetchPolicy(FetchPolicy.NetworkFirst) - var response = apolloClient.query(request) - - assertNotNull(response.data) - assertFalse(response.isFromCache) - - // Second query should only hit the cache - response = apolloClient.query(query) - - assertNotNull(response.data) - assertTrue(response.isFromCache) - - // Clear the store and offer a malformed response, we should get a composite error - store.clearAll() - mockServer.enqueue("malformed") - try { - apolloClient.query(query) - fail("we expected the query to fail") - } catch (e: Exception) { - assertTrue(e is ApolloCompositeException) - } + // First query should hit the network and save in cache + val request = ApolloRequest(query).withFetchPolicy(FetchPolicy.NetworkFirst) + var response = apolloClient.query(request) + + assertNotNull(response.data) + assertFalse(response.isFromCache) + + // Second query should only hit the cache + response = apolloClient.query(query) + + assertNotNull(response.data) + assertTrue(response.isFromCache) + + // Clear the store and offer a malformed response, we should get a composite error + store.clearAll() + mockServer.enqueue("malformed") + try { + apolloClient.query(query) + fail("we expected the query to fail") + } catch (e: Exception) { + assertTrue(e is ApolloCompositeException) } } @Test - fun networkFirst() { - runWithMainLoop { - val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) + fun networkFirst() = runTest(before = { setUp() }, after = { tearDown() }) { + val query = HeroNameQuery() + val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) - val request = ApolloRequest(query).withFetchPolicy(FetchPolicy.NetworkFirst) + val request = ApolloRequest(query).withFetchPolicy(FetchPolicy.NetworkFirst) - // First query should hit the network and save in cache - mockServer.enqueue(query, data) - var response = apolloClient.query(request) + // First query should hit the network and save in cache + mockServer.enqueue(query, data) + var response = apolloClient.query(request) - assertNotNull(response.data) - assertFalse(response.isFromCache) + assertNotNull(response.data) + assertFalse(response.isFromCache) - // Now data is cached but it shouldn't be used since network will go through - mockServer.enqueue(query, data) - response = apolloClient.query(request) + // Now data is cached but it shouldn't be used since network will go through + mockServer.enqueue(query, data) + response = apolloClient.query(request) - assertNotNull(response.data) - assertFalse(response.isFromCache) + assertNotNull(response.data) + assertFalse(response.isFromCache) - // Network error -> we should hit now the cache - mockServer.enqueue("malformed") - response = apolloClient.query(request) + // Network error -> we should hit now the cache + mockServer.enqueue("malformed") + response = apolloClient.query(request) - assertNotNull(response.data) - assertTrue(response.isFromCache) + assertNotNull(response.data) + assertTrue(response.isFromCache) - // Network error and no cache -> we should get an error - mockServer.enqueue("malformed") - store.clearAll() - try { - apolloClient.query(request) - fail("NETWORK_FIRST should throw the network exception if nothing is in the cache") - } catch (e: Exception) { + // Network error and no cache -> we should get an error + mockServer.enqueue("malformed") + store.clearAll() + try { + apolloClient.query(request) + fail("NETWORK_FIRST should throw the network exception if nothing is in the cache") + } catch (e: Exception) { - } } } @Test - fun cacheOnly() { - runWithMainLoop { - val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) + fun cacheOnly() = runTest(before = { setUp() }, after = { tearDown() }) { + val query = HeroNameQuery() + val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) - var request = ApolloRequest(query) + var request = ApolloRequest(query) - // First query should hit the network and save in cache - mockServer.enqueue(query, data) - var response = apolloClient.query(request) + // First query should hit the network and save in cache + mockServer.enqueue(query, data) + var response = apolloClient.query(request) - assertNotNull(response.data) - assertFalse(response.isFromCache) + assertNotNull(response.data) + assertFalse(response.isFromCache) - request = request.withFetchPolicy(FetchPolicy.CacheOnly) + request = request.withFetchPolicy(FetchPolicy.CacheOnly) - // Second query should only hit the cache - response = apolloClient.query(request) + // Second query should only hit the cache + response = apolloClient.query(request) - // And make sure we don't read the network - assertNotNull(response.data) - assertTrue(response.isFromCache) - } + // And make sure we don't read the network + assertNotNull(response.data) + assertTrue(response.isFromCache) } @Test - fun networkOnly() { - runWithMainLoop { - val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) + fun networkOnly() = runTest(before = { setUp() }, after = { tearDown() }) { + val query = HeroNameQuery() + val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) - val request = ApolloRequest(query).withFetchPolicy(FetchPolicy.NetworkOnly) + val request = ApolloRequest(query).withFetchPolicy(FetchPolicy.NetworkOnly) - // First query should hit the network and save in cache - mockServer.enqueue(query, data) - val response = apolloClient.query(request) + // First query should hit the network and save in cache + mockServer.enqueue(query, data) + val response = apolloClient.query(request) - assertNotNull(response.data) - assertFalse(response.isFromCache) + assertNotNull(response.data) + assertFalse(response.isFromCache) - // Offer a malformed response, it should fail - mockServer.enqueue("malformed") - try { - apolloClient.query(request) - fail("we expected a failure") - } catch (e: Exception) { + // Offer a malformed response, it should fail + mockServer.enqueue("malformed") + try { + apolloClient.query(request) + fail("we expected a failure") + } catch (e: Exception) { - } } } @Test - fun queryCacheAndNetwork() { - runWithMainLoop { - val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) - - // Initial state: everything fails - // Cache Error + Network Error => Error - mockServer.enqueue(MockResponse(statusCode = 500)) - assertFailsWith(ApolloCompositeException::class) { - apolloClient.queryCacheAndNetwork(query).toList() - } - - // Make the network return something - // Cache Error + Nework Success => 1 response - mockServer.enqueue(query, data) - var responses = apolloClient.queryCacheAndNetwork(query).toList() - - assertEquals(1, responses.size) - assertNotNull(responses[0].data) - assertFalse(responses[0].isFromCache) - assertEquals("R2-D2", responses[0].data?.hero?.name) - - // Now cache is populated but make the network fail again - // Cache Success + Network Error => 1 response - mockServer.enqueue(MockResponse(statusCode = 500)) - responses = apolloClient.queryCacheAndNetwork(query).toList() - - assertEquals(1, responses.size) - assertNotNull(responses[0].data) - assertTrue(responses[0].isFromCache) - assertEquals("R2-D2", responses[0].data?.hero?.name) - - // Cache Success + Network Success => 1 response - mockServer.enqueue(query, data) - responses = apolloClient.queryCacheAndNetwork(query).toList() - - assertEquals(2, responses.size) - assertNotNull(responses[0].data) - assertTrue(responses[0].isFromCache) - assertNotNull(responses[1].data) - assertFalse(responses[1].isFromCache) + fun queryCacheAndNetwork() = runTest(before = { setUp() }, after = { tearDown() }) { + val query = HeroNameQuery() + val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) + + // Initial state: everything fails + // Cache Error + Network Error => Error + mockServer.enqueue(MockResponse(statusCode = 500)) + assertFailsWith(ApolloCompositeException::class) { + apolloClient.queryCacheAndNetwork(query).toList() } + + // Make the network return something + // Cache Error + Nework Success => 1 response + mockServer.enqueue(query, data) + var responses = apolloClient.queryCacheAndNetwork(query).toList() + + assertEquals(1, responses.size) + assertNotNull(responses[0].data) + assertFalse(responses[0].isFromCache) + assertEquals("R2-D2", responses[0].data?.hero?.name) + + // Now cache is populated but make the network fail again + // Cache Success + Network Error => 1 response + mockServer.enqueue(MockResponse(statusCode = 500)) + responses = apolloClient.queryCacheAndNetwork(query).toList() + + assertEquals(1, responses.size) + assertNotNull(responses[0].data) + assertTrue(responses[0].isFromCache) + assertEquals("R2-D2", responses[0].data?.hero?.name) + + // Cache Success + Network Success => 1 response + mockServer.enqueue(query, data) + responses = apolloClient.queryCacheAndNetwork(query).toList() + + assertEquals(2, responses.size) + assertNotNull(responses[0].data) + assertTrue(responses[0].isFromCache) + assertNotNull(responses[1].data) + assertFalse(responses[1].isFromCache) } -} +} \ No newline at end of file diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/FileUploadTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/FileUploadTest.kt index 56914c47c4..23e46cf863 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/FileUploadTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/FileUploadTest.kt @@ -14,9 +14,8 @@ import com.apollographql.apollo3.integration.upload.type.NestedObject import com.apollographql.apollo3.mockserver.MockRecordedRequest import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import okio.Buffer -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -52,8 +51,7 @@ class FileUploadTest { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient - @BeforeTest - fun setUp() { + private suspend fun setUp() { mockServer = MockServer() // We only test the data that is sent to the server, we don't really mind the response @@ -66,9 +64,13 @@ class FileUploadTest { apolloClient = ApolloClient(mockServer.url()) } + private suspend fun tearDown() { + mockServer.stop() + } + @Test @Throws(Exception::class) - fun single() = runWithMainLoop { + fun single() = runTest(before = { setUp() }, after = { tearDown() }) { apolloClient.mutate(mutationSingle) val request = mockServer.takeRequest() @@ -83,7 +85,7 @@ class FileUploadTest { @Test @Throws(Exception::class) - fun twice() = runWithMainLoop { + fun twice() = runTest(before = { setUp() }, after = { tearDown() }) { apolloClient.mutate(mutationTwice) val request = mockServer.takeRequest() @@ -98,7 +100,7 @@ class FileUploadTest { @Test @Throws(Exception::class) - fun multiple() = runWithMainLoop { + fun multiple() = runTest(before = { setUp() }, after = { tearDown() }) { apolloClient.mutate(mutationMultiple) val request = mockServer.takeRequest() @@ -113,7 +115,7 @@ class FileUploadTest { @Test @Throws(Exception::class) - fun nested() = runWithMainLoop { + fun nested() = runTest(before = { setUp() }, after = { tearDown() }) { apolloClient.mutate(mutationNested) val request = mockServer.takeRequest() diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HTTPHeadersTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HTTPHeadersTest.kt index 80fffcc01e..b3f8b0baad 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HTTPHeadersTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HTTPHeadersTest.kt @@ -9,8 +9,7 @@ import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue import com.apollographql.apollo3.network.http.HttpResponseInfo import com.apollographql.apollo3.testing.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop -import kotlin.test.BeforeTest +import com.apollographql.apollo3.testing.runTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotEquals @@ -20,33 +19,34 @@ class HTTPHeadersTest { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient - @BeforeTest - fun setUp() { + private suspend fun setUp() { mockServer = MockServer() apolloClient = ApolloClient(mockServer.url()) } + private suspend fun tearDown() { + mockServer.stop() + } + @Test - fun makeSureHeadersAreSet() { + fun makeSureHeadersAreSet() = runTest(before = { setUp() }, after = { tearDown() }) { val query = HeroNameQuery() val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) mockServer.enqueue(query, data) - runWithMainLoop { - val response = apolloClient.query(query) + val response = apolloClient.query(query) - assertNotNull(response.data) + assertNotNull(response.data) - val recordedRequest = mockServer.takeRequest() - assertEquals("POST", recordedRequest.method) - assertNotEquals(null, recordedRequest.headers["Content-Length"]) - assertNotEquals("0", recordedRequest.headers["Content-Length"]) - } + val recordedRequest = mockServer.takeRequest() + assertEquals("POST", recordedRequest.method) + assertNotEquals(null, recordedRequest.headers["Content-Length"]) + assertNotEquals("0", recordedRequest.headers["Content-Length"]) } @Test - fun headersCanBeReadInResponseExecutionContext() { + fun headersCanBeReadInResponseExecutionContext() = runTest(before = { setUp() }, after = { tearDown() }) { val query = HeroNameQuery() val data = HeroNameQuery.Data(HeroNameQuery.Data.Hero("R2-D2")) @@ -63,11 +63,11 @@ class HTTPHeadersTest { ) ) - runWithMainLoop { - val response = apolloClient.query(query) + val response = apolloClient.query(query) + + fun Map.getCaseInsetive(key: String) = get(keys.find { it.equals(key, true)}) - assertEquals(response.executionContext[HttpResponseInfo]?.headers?.get("Header1"), "Value1") - assertEquals(response.executionContext[HttpResponseInfo]?.headers?.get("Header2"), "Value2") - } + assertEquals("Value1", response.executionContext[HttpResponseInfo]?.headers?.getCaseInsetive("Header1")) + assertEquals("Value2", response.executionContext[HttpResponseInfo]?.headers?.getCaseInsetive("Header2")) } } \ No newline at end of file diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HttpInterceptorTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HttpInterceptorTest.kt index 1b672911ba..8b64c56029 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HttpInterceptorTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HttpInterceptorTest.kt @@ -6,14 +6,23 @@ import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue import com.apollographql.apollo3.network.http.HttpNetworkTransport import com.apollographql.apollo3.network.http.LoggingInterceptor -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import readResource import kotlin.test.Test class HttpInterceptorTest { + private lateinit var mockServer: MockServer + + private suspend fun setUp() { + mockServer = MockServer() + } + + private suspend fun tearDown() { + mockServer.stop() + } + @Test - fun testLoggingInterceptor() { - val mockServer = MockServer() + fun testLoggingInterceptor() = runTest(before = { setUp() }, after = { tearDown() }) { val client = ApolloClient( networkTransport = HttpNetworkTransport( serverUrl = mockServer.url(), @@ -23,8 +32,6 @@ class HttpInterceptorTest { mockServer.enqueue(readResource("HeroNameResponse.json")) - runWithMainLoop { - client.query(HeroNameQuery()) - } + client.query(HeroNameQuery()) } } \ No newline at end of file diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HttpRequestComposerTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HttpRequestComposerTest.kt index 51f455e032..4a78d5e71d 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HttpRequestComposerTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/HttpRequestComposerTest.kt @@ -9,12 +9,21 @@ import com.apollographql.apollo3.integration.httpcache.AllPlanetsQuery import com.apollographql.apollo3.integration.normalizer.HeroNameQuery import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import okio.Buffer import kotlin.test.Test import kotlin.test.assertEquals class HttpRequestComposerTest { + private lateinit var mockServer: MockServer + + private suspend fun setUp() { + mockServer = MockServer() + } + + private suspend fun tearDown() { + mockServer.stop() + } @Test fun requestPostBodyContainsOperationQueryAndVariablesByDefault() { @@ -24,23 +33,20 @@ class HttpRequestComposerTest { val bodyText = Buffer().also { httpRequest.body?.writeTo(it) }.readUtf8() - checkTestFixture(bodyText , "IntegrationTest/allPlanets.json") + checkTestFixture(bodyText, "IntegrationTest/allPlanets.json") } @Test - fun requestHeadersAreForwardedToTheServer() { - val mockServer = MockServer() + fun requestHeadersAreForwardedToTheServer() = runTest(before = { setUp() }, after = { tearDown() }) { val apolloClient = ApolloClient(mockServer.url()) - runWithMainLoop { - kotlin.runCatching { - // No need to enqueue a successful response, we just want to make sure our headers reached the server - mockServer.enqueue("error") - apolloClient.query(ApolloRequest(AllPlanetsQuery()).withHttpHeader("test", "is passing")) - } - - val response = mockServer.takeRequest() - assertEquals("is passing", response.headers["test"]) + kotlin.runCatching { + // No need to enqueue a successful response, we just want to make sure our headers reached the server + mockServer.enqueue("error") + apolloClient.query(ApolloRequest(AllPlanetsQuery()).withHttpHeader("test", "is passing")) } + + val response = mockServer.takeRequest() + assertEquals("is passing", response.headers["test"]) } } \ No newline at end of file diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/JsonScalarTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/JsonScalarTest.kt index 6ce1376dd0..dc65d8d17c 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/JsonScalarTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/JsonScalarTest.kt @@ -12,9 +12,8 @@ import com.apollographql.apollo3.cache.normalized.withFetchPolicy import com.apollographql.apollo3.cache.normalized.withStore import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import readResource -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -24,8 +23,7 @@ class JsonScalarTest { private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore - @BeforeTest - fun setUp() { + private suspend fun setUp() { store = ApolloStore(MemoryCacheFactory()) mockServer = MockServer() apolloClient = ApolloClient(mockServer.url()) @@ -33,9 +31,13 @@ class JsonScalarTest { .withCustomScalarAdapter(Types.Json, AnyAdapter) } + private suspend fun tearDown() { + mockServer.stop() + } + // see https://github.com/apollographql/apollo-android/issues/2854 @Test - fun jsonScalar() = runWithMainLoop { + fun jsonScalar() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(readResource("JsonScalar.json")) var response = apolloClient.query(GetJsonScalarQuery()) diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/OptimisticCacheTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/OptimisticCacheTest.kt index 9ca9516f67..9dc598fa29 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/OptimisticCacheTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/OptimisticCacheTest.kt @@ -23,13 +23,12 @@ import com.apollographql.apollo3.cache.normalized.withStore import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue import com.apollographql.apollo3.testing.receiveOrTimeout -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import com.benasher44.uuid.uuid4 import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import readResource -import kotlin.test.BeforeTest import kotlin.test.Test import assertEquals2 as assertEquals @@ -38,20 +37,22 @@ class OptimisticCacheTest { private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore - @BeforeTest - fun setUp() { + private suspend fun setUp() { store = ApolloStore(MemoryCacheFactory(), objectIdGenerator = IdObjectIdGenerator) mockServer = MockServer() apolloClient = ApolloClient(mockServer.url()).withStore(store) } + private suspend fun tearDown() { + mockServer.stop() + } /** * Write the updates programmatically, make sure they are seen, * roll them back, make sure we're back to the initial state */ @Test - fun programmaticOptimiticUpdates() = runWithMainLoop { + fun programmaticOptimiticUpdates() = runTest(before = { setUp() }, after = { tearDown() }) { val query = HeroAndFriendsNamesQuery(Episode.JEDI) mockServer.enqueue(readResource("HeroAndFriendsNameResponse.json")) @@ -96,7 +97,7 @@ class OptimisticCacheTest { * A more complex scenario where we stack optimistic updates */ @Test - fun two_optimistic_two_rollback() = runWithMainLoop { + fun two_optimistic_two_rollback() = runTest(before = { setUp() }, after = { tearDown() }) { val query1 = HeroAndFriendsNamesWithIDsQuery(Episode.JEDI) val mutationId1 = uuid4() @@ -200,7 +201,7 @@ class OptimisticCacheTest { } @Test - fun mutation_and_query_watcher() = runWithMainLoop { + fun mutation_and_query_watcher() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(readResource("ReviewsEmpireEpisodeResponse.json")) val channel = Channel() val job = launch { @@ -287,7 +288,7 @@ class OptimisticCacheTest { @Test @Throws(Exception::class) - fun two_optimistic_reverse_rollback_order() = runWithMainLoop { + fun two_optimistic_reverse_rollback_order() = runTest(before = { setUp() }, after = { tearDown() }) { val query1 = HeroAndFriendsNamesWithIDsQuery(Episode.JEDI) val mutationId1 = uuid4() val query2 = HeroNameWithIdQuery() diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/OtherCacheTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/OtherCacheTest.kt index 1d59be0a12..400024565f 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/OtherCacheTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/OtherCacheTest.kt @@ -19,9 +19,8 @@ import com.apollographql.apollo3.cache.normalized.withFetchPolicy import com.apollographql.apollo3.cache.normalized.withStore import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import readResource -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNull @@ -36,15 +35,18 @@ class OtherCacheTest { private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore - @BeforeTest - fun setUp() { + private suspend fun setUp() { store = ApolloStore(MemoryCacheFactory(), objectIdGenerator = IdObjectIdGenerator, cacheResolver = IdCacheResolver) mockServer = MockServer() apolloClient = ApolloClient(mockServer.url()).withStore(store) } + private suspend fun tearDown() { + mockServer.stop() + } + @Test - fun masterDetailSuccess() = runWithMainLoop { + fun masterDetailSuccess() = runTest(before = { setUp() }, after = { tearDown() }) { // Store a query that contains all data mockServer.enqueue(readResource("HeroAndFriendsNameWithIdsResponse.json")) apolloClient.query( @@ -61,7 +63,7 @@ class OtherCacheTest { @Test @Throws(Exception::class) - fun masterDetailFailIncomplete() = runWithMainLoop { + fun masterDetailFailIncomplete() = runTest(before = { setUp() }, after = { tearDown() }) { // Store a query that contains all data mockServer.enqueue(readResource("HeroAndFriendsNameWithIdsResponse.json")) apolloClient.query( @@ -81,7 +83,7 @@ class OtherCacheTest { @Test - fun cacheMissThrows() = runWithMainLoop { + fun cacheMissThrows() = runTest(before = { setUp() }, after = { tearDown() }) { try { apolloClient.query( ApolloRequest(EpisodeHeroNameQuery(Episode.EMPIRE)).withFetchPolicy(FetchPolicy.CacheOnly) @@ -94,7 +96,7 @@ class OtherCacheTest { @Test @Throws(Exception::class) - fun skipIncludeDirective() = runWithMainLoop { + fun skipIncludeDirective() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(readResource("HeroAndFriendsNameResponse.json")) apolloClient.query( ApolloRequest(HeroAndFriendsDirectivesQuery(episode = Episode.JEDI, includeName = true, skipFriends = false)) @@ -131,7 +133,7 @@ class OtherCacheTest { @Test - fun skipIncludeDirectiveUnsatisfiedCache() = runWithMainLoop { + fun skipIncludeDirectiveUnsatisfiedCache() = runTest(before = { setUp() }, after = { tearDown() }) { // Store a response that doesn't contain friends mockServer.enqueue(readResource("HeroNameResponse.json")) apolloClient.query( diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/StoreTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/StoreTest.kt index c45e49aeac..f396505608 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/StoreTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/StoreTest.kt @@ -18,9 +18,8 @@ import com.apollographql.apollo3.cache.normalized.withFetchPolicy import com.apollographql.apollo3.cache.normalized.withStore import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import readResource -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.fail @@ -35,15 +34,18 @@ class StoreTest { private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore - @BeforeTest - fun setUp() { + private suspend fun setUp() { store = ApolloStore(MemoryCacheFactory(), objectIdGenerator = IdObjectIdGenerator, cacheResolver = IdCacheResolver) mockServer = MockServer() apolloClient = ApolloClient(mockServer.url()).withStore(store) } + private suspend fun tearDown() { + mockServer.stop() + } + @Test - fun removeFromStore() = runWithMainLoop { + fun removeFromStore() = runTest(before = { setUp() }, after = { tearDown() }) { storeAllFriends() assertFriendIsCached("1002", "Han Solo") @@ -74,7 +76,7 @@ class StoreTest { @Test @Throws(Exception::class) - fun removeMultipleFromStore() = runWithMainLoop { + fun removeMultipleFromStore() = runTest(before = { setUp() }, after = { tearDown() }) { storeAllFriends() assertFriendIsCached("1000", "Luke Skywalker") assertFriendIsCached("1002", "Han Solo") @@ -93,7 +95,7 @@ class StoreTest { @Test @Throws(Exception::class) - fun cascadeRemove() = runWithMainLoop { + fun cascadeRemove() = runTest(before = { setUp() }, after = { tearDown() }) { // put everything in the cache storeAllFriends() @@ -114,7 +116,7 @@ class StoreTest { @Test @Throws(Exception::class) - fun directAccess() = runWithMainLoop { + fun directAccess() = runTest(before = { setUp() }, after = { tearDown() }) { // put everything in the cache storeAllFriends() diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/WatcherTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/WatcherTest.kt index 33a0974138..465f7d1887 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/WatcherTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/WatcherTest.kt @@ -20,14 +20,13 @@ import com.apollographql.apollo3.cache.normalized.withStore import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue import com.apollographql.apollo3.testing.receiveOrTimeout -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import readResource -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.fail @@ -37,18 +36,21 @@ class WatcherTest { private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore - @BeforeTest - fun setUp() { + private suspend fun setUp() { store = ApolloStore(MemoryCacheFactory(), objectIdGenerator = IdObjectIdGenerator) mockServer = MockServer() apolloClient = ApolloClient(mockServer.url()).withStore(store) } + private suspend fun tearDown() { + mockServer.stop() + } + /** * Executing the same query out of band should update the watcher */ @Test - fun sameQueryTriggersWatcher() = runWithMainLoop { + fun sameQueryTriggersWatcher() = runTest(before = { setUp() }, after = { tearDown() }) { val query = EpisodeHeroNameQuery(Episode.EMPIRE) val channel = Channel() @@ -76,7 +78,7 @@ class WatcherTest { * Writing to the store out of band should update the watcher */ @Test - fun storeWriteTriggersWatcher() = runWithMainLoop { + fun storeWriteTriggersWatcher() = runTest(before = { setUp() }, after = { tearDown() }) { val channel = Channel() val operation = EpisodeHeroNameWithIdQuery(Episode.EMPIRE) mockServer.enqueue(readResource("EpisodeHeroNameResponseWithId.json")) @@ -107,7 +109,7 @@ class WatcherTest { * A new query updates the store with data that is the same as the one originally seen by the watcher */ @Test - fun noChangeSameQuery() = runWithMainLoop { + fun noChangeSameQuery() = runTest(before = { setUp() }, after = { tearDown() }) { val query = EpisodeHeroNameQuery(Episode.EMPIRE) val channel = Channel() @@ -134,7 +136,7 @@ class WatcherTest { * A new query that contains overlapping fields with the watched query should trigger the watcher */ @Test - fun differentQueryTriggersWatcher() = runWithMainLoop { + fun differentQueryTriggersWatcher() = runTest(before = { setUp() }, after = { tearDown() }) { val channel = Channel() // The first query should get a "R2-D2" name @@ -164,7 +166,7 @@ class WatcherTest { * Same as noChangeSameQuery with different queries */ @Test - fun noChangeDifferentQuery() = runWithMainLoop { + fun noChangeDifferentQuery() = runTest(before = { setUp() }, after = { tearDown() }) { val channel = Channel() // The first query should get a "R2-D2" name @@ -195,7 +197,7 @@ class WatcherTest { * from the network */ @Test - fun networkRefetchPolicy() = runWithMainLoop { + fun networkRefetchPolicy() = runTest(before = { setUp() }, after = { tearDown() }) { val channel = Channel() // The first query should get a "R2-D2" name @@ -231,7 +233,7 @@ class WatcherTest { @Test - fun nothingReceivedWhenCancelled() = runWithMainLoop { + fun nothingReceivedWhenCancelled() = runTest(before = { setUp() }, after = { tearDown() }) { val channel = Channel() // The first query should get a "R2-D2" name @@ -265,7 +267,7 @@ class WatcherTest { * Doing the initial query as cache only will detect when the query becomes available */ @Test - fun cacheOnlyFetchPolicy() = runWithMainLoop { + fun cacheOnlyFetchPolicy() = runTest(before = { setUp() }, after = { tearDown() }) { val query = EpisodeHeroNameQuery(Episode.EMPIRE) val channel = Channel() @@ -287,7 +289,7 @@ class WatcherTest { } @Test - fun queryWatcherWithCacheOnlyNeverGoesToTheNetwork() = runWithMainLoop { + fun queryWatcherWithCacheOnlyNeverGoesToTheNetwork() = runTest(before = { setUp() }, after = { tearDown() }) { val channel = Channel(capacity = Channel.UNLIMITED) val job = launch { val request = ApolloRequest(EpisodeHeroNameQuery(Episode.EMPIRE)) @@ -295,10 +297,10 @@ class WatcherTest { .withRefetchPolicy(FetchPolicy.CacheOnly) apolloClient.watch(request).collect { - channel.send(it.data) - } + channel.send(it.data) + } } - + mockServer.enqueue(readResource("StarshipByIdResponse.json")) mockServer.enqueue(readResource("EpisodeHeroNameResponseWithId.json")) diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/batching/QueryBatchingTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/batching/QueryBatchingTest.kt index da3aff55ee..2d77eb2a69 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/batching/QueryBatchingTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/batching/QueryBatchingTest.kt @@ -12,7 +12,7 @@ import com.apollographql.apollo3.mockserver.enqueue import com.apollographql.apollo3.network.http.BatchingHttpEngine import com.apollographql.apollo3.network.http.HttpNetworkTransport import com.apollographql.apollo3.network.http.canBeBatched -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import kotlinx.coroutines.async import kotlinx.coroutines.delay import okio.Buffer @@ -22,9 +22,19 @@ import kotlin.test.assertEquals import kotlin.test.assertFails class QueryBatchingTest { + private lateinit var mockServer: MockServer + + private suspend fun setUp() { + mockServer = MockServer() + } + + private suspend fun tearDown() { + mockServer.stop() + } + @Test @Ignore // because it uses a real-life server that might be down - fun testAgainstARealServer() { + fun testAgainstARealServer() = runTest(before = { setUp() }, after = { tearDown() }) { val apolloClient = ApolloClient( networkTransport = HttpNetworkTransport( httpRequestComposer = DefaultHttpRequestComposer("https://apollo-fullstack-tutorial.herokuapp.com/graphql"), @@ -32,25 +42,22 @@ class QueryBatchingTest { ) ) - runWithMainLoop { - val result1 = async { - apolloClient.query(GetLaunchQuery()) - } - val result2 = async { - apolloClient.query(GetLaunch2Query()) - } - assertEquals("83", result1.await().data?.launch?.id) - assertEquals("84", result2.await().data?.launch?.id) + val result1 = async { + apolloClient.query(GetLaunchQuery()) + } + val result2 = async { + apolloClient.query(GetLaunch2Query()) } + assertEquals("83", result1.await().data?.launch?.id) + assertEquals("84", result2.await().data?.launch?.id) } @Test - fun queriesAreBatchedByDefault() { + fun queriesAreBatchedByDefault() = runTest(before = { setUp() }, after = { tearDown() }) { val response = """ [{"data":{"launch":{"id":"83"}}},{"data":{"launch":{"id":"84"}}}] """.trimIndent() - val mockServer = MockServer() mockServer.enqueue(response) val apolloClient = ApolloClient( networkTransport = HttpNetworkTransport( @@ -61,36 +68,33 @@ class QueryBatchingTest { ) ) - runWithMainLoop { - val result1 = async { - apolloClient.query(GetLaunchQuery()) - } - val result2 = async { - // Make sure GetLaunch2Query gets executed after GetLaunchQuery as there is no guarantee otherwise - // 300ms batchIntervalMillis and 50ms delay here should be enough. Increase values if some tests become flaky - delay(50) - apolloClient.query(GetLaunch2Query()) - } - - assertEquals("83", result1.await().data?.launch?.id) - assertEquals("84", result2.await().data?.launch?.id) - - val request = mockServer.takeRequest() - val requests = AnyAdapter.fromJson(BufferedSourceJsonReader(Buffer().write(request.body))) as List> - assertEquals(2, requests.size) - assertEquals("GetLaunch", requests[0]["operationName"]) - assertEquals("GetLaunch2", requests[1]["operationName"]) - - // Only one request must have been sent - assertFails { - mockServer.takeRequest() - } + val result1 = async { + apolloClient.query(GetLaunchQuery()) + } + val result2 = async { + // Make sure GetLaunch2Query gets executed after GetLaunchQuery as there is no guarantee otherwise + // 300ms batchIntervalMillis and 50ms delay here should be enough. Increase values if some tests become flaky + delay(50) + apolloClient.query(GetLaunch2Query()) + } + + assertEquals("83", result1.await().data?.launch?.id) + assertEquals("84", result2.await().data?.launch?.id) + + val request = mockServer.takeRequest() + val requests = AnyAdapter.fromJson(BufferedSourceJsonReader(Buffer().write(request.body))) as List> + assertEquals(2, requests.size) + assertEquals("GetLaunch", requests[0]["operationName"]) + assertEquals("GetLaunch2", requests[1]["operationName"]) + + // Only one request must have been sent + assertFails { + mockServer.takeRequest() } } @Test - fun queriesAreNotBatchedIfSubmitedFarAppart() { - val mockServer = MockServer() + fun queriesAreNotBatchedIfSubmitedFarAppart() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue("""[{"data":{"launch":{"id":"83"}}}]""") mockServer.enqueue("""[{"data":{"launch":{"id":"84"}}}]""") val apolloClient = ApolloClient( @@ -102,27 +106,24 @@ class QueryBatchingTest { ) ) - runWithMainLoop { - val result1 = async { - apolloClient.query(GetLaunchQuery()) - } - val result2 = async { - // Wait for the first query to be executed - delay(50) - apolloClient.query(GetLaunch2Query()) - } + val result1 = async { + apolloClient.query(GetLaunchQuery()) + } + val result2 = async { + // Wait for the first query to be executed + delay(50) + apolloClient.query(GetLaunch2Query()) + } - assertEquals("83", result1.await().data?.launch?.id) - assertEquals("84", result2.await().data?.launch?.id) + assertEquals("83", result1.await().data?.launch?.id) + assertEquals("84", result2.await().data?.launch?.id) - mockServer.takeRequest() - mockServer.takeRequest() - } + mockServer.takeRequest() + mockServer.takeRequest() } @Test - fun queriesCanBeOptOutOfBatching() { - val mockServer = MockServer() + fun queriesCanBeOptOutOfBatching() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue("""{"data":{"launch":{"id":"83"}}}""") mockServer.enqueue("""[{"data":{"launch":{"id":"84"}}}]""") val apolloClient = ApolloClient( @@ -134,21 +135,19 @@ class QueryBatchingTest { ) ) - runWithMainLoop { - val result1 = async { - apolloClient.query(ApolloRequest(GetLaunchQuery()).canBeBatched(false)) - } - val result2 = async { - // Make sure GetLaunch2Query gets executed after GetLaunchQuery as there is no guarantee otherwise - delay(50) - apolloClient.query(GetLaunch2Query()) - } + val result1 = async { + apolloClient.query(ApolloRequest(GetLaunchQuery()).canBeBatched(false)) + } + val result2 = async { + // Make sure GetLaunch2Query gets executed after GetLaunchQuery as there is no guarantee otherwise + delay(50) + apolloClient.query(GetLaunch2Query()) + } - assertEquals("83", result1.await().data?.launch?.id) - assertEquals("84", result2.await().data?.launch?.id) + assertEquals("83", result1.await().data?.launch?.id) + assertEquals("84", result2.await().data?.launch?.id) - mockServer.takeRequest() - mockServer.takeRequest() - } + mockServer.takeRequest() + mockServer.takeRequest() } } diff --git a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/declarativecache/DeclarativeCacheTest.kt b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/declarativecache/DeclarativeCacheTest.kt index fcab5360a1..dab2b19e0f 100644 --- a/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/declarativecache/DeclarativeCacheTest.kt +++ b/composite/tests/integration-tests-kotlin/src/commonTest/kotlin/test/declarativecache/DeclarativeCacheTest.kt @@ -9,7 +9,7 @@ import com.apollographql.apollo3.cache.normalized.CacheKey import com.apollographql.apollo3.cache.normalized.CacheResolver import com.apollographql.apollo3.cache.normalized.FieldPolicyCacheResolver import com.apollographql.apollo3.cache.normalized.MemoryCacheFactory -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import declarativecache.GetBookQuery import declarativecache.GetBooksQuery import declarativecache.GetOtherBookQuery @@ -22,7 +22,7 @@ import kotlin.test.assertEquals class DeclarativeCacheTest { @Test - fun typePolicyIsWorking() = runWithMainLoop { + fun typePolicyIsWorking() = runTest { val store = ApolloStore(MemoryCacheFactory()) // Write a book at the "promo" path @@ -42,7 +42,7 @@ class DeclarativeCacheTest { } @Test - fun fallbackIdIsWorking() = runWithMainLoop { + fun fallbackIdIsWorking() = runTest { val store = ApolloStore(MemoryCacheFactory()) // Write a library at the "promo" path @@ -62,7 +62,7 @@ class DeclarativeCacheTest { } @Test - fun fieldPolicyIsWorking() = runWithMainLoop { + fun fieldPolicyIsWorking() = runTest { val store = ApolloStore(MemoryCacheFactory()) val promoOperation = GetPromoBookQuery() @@ -76,7 +76,7 @@ class DeclarativeCacheTest { } @Test - fun canResolveListProgrammatically() = runWithMainLoop { + fun canResolveListProgrammatically() = runTest { val cacheResolver = object : CacheResolver { override fun resolveField(field: CompiledField, variables: Executable.Variables, parent: Map, parentId: String): Any? { if (field.name == "books") { diff --git a/composite/tests/integration-tests-kotlin/src/jsTest/kotlin/test/PromiseTest.kt b/composite/tests/integration-tests-kotlin/src/jsTest/kotlin/test/PromiseTest.kt deleted file mode 100644 index 45e93b2308..0000000000 --- a/composite/tests/integration-tests-kotlin/src/jsTest/kotlin/test/PromiseTest.kt +++ /dev/null @@ -1,32 +0,0 @@ -package test - -import kotlin.test.AfterTest -import kotlin.test.BeforeTest -import kotlin.test.Test -import kotlin.test.assertTrue -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.promise - -class PromiseTest { - var inTest = false - - @BeforeTest - fun before() { - inTest = true - } - - @AfterTest - fun after() { - inTest = false - } - - @Test - fun withoutPromise() { - assertTrue(inTest) - } - - @Test - fun withPromise() = GlobalScope.promise { - assertTrue(inTest) // FAIL?! - } -} diff --git a/composite/tests/integration-tests-kotlin/src/jvmTest/kotlin/test/WriteToCacheAsynchronouslyTest.kt b/composite/tests/integration-tests-kotlin/src/jvmTest/kotlin/test/WriteToCacheAsynchronouslyTest.kt index c5d54a2925..771f47b14b 100644 --- a/composite/tests/integration-tests-kotlin/src/jvmTest/kotlin/test/WriteToCacheAsynchronouslyTest.kt +++ b/composite/tests/integration-tests-kotlin/src/jvmTest/kotlin/test/WriteToCacheAsynchronouslyTest.kt @@ -13,14 +13,12 @@ import com.apollographql.apollo3.integration.normalizer.HeroAndFriendsNamesQuery import com.apollographql.apollo3.integration.normalizer.type.Episode import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue -import com.apollographql.apollo3.testing.runBlocking -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.asCoroutineDispatcher import java.util.concurrent.Executors import readResource -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertNotNull import kotlin.test.assertNull @@ -31,8 +29,7 @@ class WriteToCacheAsynchronouslyTest { private lateinit var store: ApolloStore private lateinit var dispatcher: CoroutineDispatcher - @BeforeTest - fun setUp() { + private suspend fun setUp() { dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() store = ApolloStore(MemoryCacheFactory()) mockServer = MockServer() @@ -42,11 +39,15 @@ class WriteToCacheAsynchronouslyTest { ).withStore(store) } + private suspend fun tearDown() { + mockServer.stop() + } + /** * Write to cache asynchronously, make sure records are not in cache when we receive the response */ @Test - fun writeToCacheAsynchronously() = runWithMainLoop(dispatcher) { + fun writeToCacheAsynchronously() = runTest(dispatcher, { setUp() }, { tearDown() }) { val query = HeroAndFriendsNamesQuery(Episode.JEDI) mockServer.enqueue(readResource("HeroAndFriendsNameResponse.json")) @@ -63,7 +64,7 @@ class WriteToCacheAsynchronouslyTest { * Write to cache synchronously, make sure records are in cache when we receive the response */ @Test - fun writeToCacheSynchronously(): Unit = runBlocking(context = dispatcher) { + fun writeToCacheSynchronously() = runTest(dispatcher, { setUp() }, { tearDown() }) { val query = HeroAndFriendsNamesQuery(Episode.JEDI) mockServer.enqueue(readResource("HeroAndFriendsNameResponse.json")) From 079d1281b0f7beafb26ec3139f778abf8c9e714b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ing=2E=20Jan=20Kal=C3=A1b?= Date: Thu, 22 Jul 2021 16:36:13 +0200 Subject: [PATCH 10/43] Try another path --- .../com/apollographql/apollo3/testing/readFileJs.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt index a7be99ac0c..ec65be05f7 100644 --- a/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt +++ b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt @@ -5,9 +5,16 @@ import kotlin.test.assertEquals actual fun readFile(path: String): String { val pathPrefix = "../../../../../composite/tests/integration-tests/" - return readFileSync("$pathPrefix$path", object: fs.`T$44` { + val pathPrefixKotlin = "../../../../../composite/tests/integration-tests-kotlin/" + val options = object: fs.`T$44` { override var encoding: String? = "utf8" - }) as String + } + + return try { + readFileSync("$pathPrefix$path", options) + } catch (t: Throwable) { + readFileSync("$pathPrefixKotlin$path", options) + } as String } actual fun checkFile(actualText: String, path: String) { From e82fa457086372ab8f0e8498502c1f39d4482bf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ing=2E=20Jan=20Kal=C3=A1b?= Date: Fri, 6 Aug 2021 09:56:36 +0200 Subject: [PATCH 11/43] Fixes for new headers --- .../com/apollographql/apollo3/network/http/OkHttpEngine.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt index 30fe7bf177..0a5de2bdf6 100644 --- a/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt +++ b/apollo-runtime/src/jsMain/kotlin/com/apollographql/apollo3/network/http/OkHttpEngine.kt @@ -1,5 +1,6 @@ package com.apollographql.apollo3.network.http +import com.apollographql.apollo3.api.http.HttpHeader import com.apollographql.apollo3.api.http.HttpMethod import com.apollographql.apollo3.api.http.HttpRequest import com.apollographql.apollo3.api.http.HttpResponse @@ -10,7 +11,7 @@ import io.ktor.client.engine.js.Js import io.ktor.client.request.header import io.ktor.client.request.request import io.ktor.http.HttpHeaders -import io.ktor.util.toMap +import io.ktor.util.flattenEntries import okio.Buffer import okio.ByteString.Companion.toByteString @@ -27,7 +28,7 @@ actual class DefaultHttpEngine actual constructor(connectTimeoutMillis: Long, re HttpMethod.Post -> io.ktor.http.HttpMethod.Post } request.headers.forEach { - header(it.key, it.value) + header(it.name, it.value) } request.body?.let { header(HttpHeaders.ContentType, it.contentType) @@ -40,7 +41,7 @@ actual class DefaultHttpEngine actual constructor(connectTimeoutMillis: Long, re val responseByteArray: ByteArray = response.receive() return HttpResponse( response.status.value, - response.headers.toMap().mapValues { it.value.first() }, + response.headers.flattenEntries().map { HttpHeader(it.first, it.second) }, Buffer().write(responseByteArray), responseByteArray.toByteString() ) From f6b9fcd70d1b9c1c461bfc67ca71d81fb84fe5bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ing=2E=20Jan=20Kal=C3=A1b?= Date: Fri, 6 Aug 2021 14:01:06 +0200 Subject: [PATCH 12/43] Fix tests --- .../apollo3/testing/readFileJs.kt | 4 +-- .../CircularCacheReadTest.kt | 12 +++---- .../src/jvmTest/kotlin/test/CookiesTest.kt | 34 +++++++++---------- .../test/WriteToCacheAsynchronouslyTest.kt | 4 +-- .../src/commonTest/kotlin/test/BasicTest.kt | 16 +++++---- .../src/commonTest/kotlin/test/StoreTest.kt | 14 ++++---- .../src/commonTest/kotlin/test/BasicTest.kt | 16 +++++---- .../src/commonTest/kotlin/test/StoreTest.kt | 14 ++++---- .../src/commonTest/kotlin/test/BasicTest.kt | 16 +++++---- .../src/commonTest/kotlin/test/StoreTest.kt | 14 ++++---- .../src/commonTest/kotlin/FullStackTest.kt | 14 ++++---- .../src/commonTest/kotlin/GraphQLWsTest.kt | 17 ++++------ 12 files changed, 89 insertions(+), 86 deletions(-) diff --git a/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt index ec65be05f7..3f7c9254a0 100644 --- a/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt +++ b/apollo-testing-support/src/jsMain/kotlin/com/apollographql/apollo3/testing/readFileJs.kt @@ -4,8 +4,8 @@ import fs.readFileSync import kotlin.test.assertEquals actual fun readFile(path: String): String { - val pathPrefix = "../../../../../composite/tests/integration-tests/" - val pathPrefixKotlin = "../../../../../composite/tests/integration-tests-kotlin/" + val pathPrefix = "../../../../../tests/integration-tests/" + val pathPrefixKotlin = "../../../../../tests/integration-tests-kotlin/" val options = object: fs.`T$44` { override var encoding: String? = "utf8" } diff --git a/tests/integration-tests/src/commonTest/kotlin/test/circular_cache_read/CircularCacheReadTest.kt b/tests/integration-tests/src/commonTest/kotlin/test/circular_cache_read/CircularCacheReadTest.kt index 3c10e9cd90..504a2114c4 100644 --- a/tests/integration-tests/src/commonTest/kotlin/test/circular_cache_read/CircularCacheReadTest.kt +++ b/tests/integration-tests/src/commonTest/kotlin/test/circular_cache_read/CircularCacheReadTest.kt @@ -4,13 +4,13 @@ import circular_cache_read.GetUserQuery import com.apollographql.apollo3.api.CustomScalarAdapters import com.apollographql.apollo3.cache.normalized.ApolloStore import com.apollographql.apollo3.cache.normalized.MemoryCacheFactory -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import kotlin.test.Test import kotlin.test.assertEquals class CircularCacheReadTest { @Test - fun circularReferenceDoesNotStackOverflow() { + fun circularReferenceDoesNotStackOverflow() = runTest { val store = ApolloStore(MemoryCacheFactory()) val operation = GetUserQuery() @@ -29,10 +29,8 @@ class CircularCacheReadTest { ) ) - runWithMainLoop { - store.writeOperation(operation, data) - val result = store.readOperation(operation, customScalarAdapters = CustomScalarAdapters.Empty) - assertEquals("42", result!!.user.friend.id) - } + store.writeOperation(operation, data) + val result = store.readOperation(operation, customScalarAdapters = CustomScalarAdapters.Empty) + assertEquals("42", result!!.user.friend.id) } } \ No newline at end of file diff --git a/tests/integration-tests/src/jvmTest/kotlin/test/CookiesTest.kt b/tests/integration-tests/src/jvmTest/kotlin/test/CookiesTest.kt index 5bd5831627..d2168ad316 100644 --- a/tests/integration-tests/src/jvmTest/kotlin/test/CookiesTest.kt +++ b/tests/integration-tests/src/jvmTest/kotlin/test/CookiesTest.kt @@ -7,7 +7,7 @@ import com.apollographql.apollo3.mockserver.MockResponse import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue import com.apollographql.apollo3.network.http.HttpNetworkTransport -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import okhttp3.Cookie import okhttp3.CookieJar import okhttp3.HttpUrl @@ -35,7 +35,7 @@ class CookiesTest { } @Test - fun cookiesArePersisted() { + fun cookiesArePersisted() = runTest { val mockServer = MockServer() val cookieJar = ObservableCookieJar() val okHttpClient = OkHttpClient.Builder() @@ -49,25 +49,23 @@ class CookiesTest { ) ) - runWithMainLoop { - val json = HeroNameQuery().composeJsonData(HeroNameQuery.Data(hero = HeroNameQuery.Data.Hero(name = "Luke"))) + val json = HeroNameQuery().composeJsonData(HeroNameQuery.Data(hero = HeroNameQuery.Data.Hero(name = "Luke"))) - mockServer.enqueue(MockResponse( - body = json, - headers = mapOf("Set-Cookie" to "yummy_cookie=choco") - )) + mockServer.enqueue(MockResponse( + body = json, + headers = mapOf("Set-Cookie" to "yummy_cookie=choco") + )) - // first query should set the cookie - apolloClient.query(HeroNameQuery()) - // consume the first request - mockServer.takeRequest() + // first query should set the cookie + apolloClient.query(HeroNameQuery()) + // consume the first request + mockServer.takeRequest() - mockServer.enqueue(json) - // first query should send the cookie - apolloClient.query(HeroNameQuery()) + mockServer.enqueue(json) + // first query should send the cookie + apolloClient.query(HeroNameQuery()) - val cookie = mockServer.takeRequest().headers["Cookie"] - assertEquals("yummy_cookie=choco", cookie) - } + val cookie = mockServer.takeRequest().headers["Cookie"] + assertEquals("yummy_cookie=choco", cookie) } } \ No newline at end of file diff --git a/tests/integration-tests/src/jvmTest/kotlin/test/WriteToCacheAsynchronouslyTest.kt b/tests/integration-tests/src/jvmTest/kotlin/test/WriteToCacheAsynchronouslyTest.kt index a93918b5e4..49f09ace36 100644 --- a/tests/integration-tests/src/jvmTest/kotlin/test/WriteToCacheAsynchronouslyTest.kt +++ b/tests/integration-tests/src/jvmTest/kotlin/test/WriteToCacheAsynchronouslyTest.kt @@ -28,10 +28,9 @@ class WriteToCacheAsynchronouslyTest { private lateinit var mockServer: MockServer private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore - private lateinit var dispatcher: CoroutineDispatcher + private var dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() private suspend fun setUp() { - dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() store = ApolloStore(MemoryCacheFactory()) mockServer = MockServer() apolloClient = ApolloClient( @@ -42,6 +41,7 @@ class WriteToCacheAsynchronouslyTest { private suspend fun tearDown() { mockServer.stop() + dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() } /** diff --git a/tests/models-compat/src/commonTest/kotlin/test/BasicTest.kt b/tests/models-compat/src/commonTest/kotlin/test/BasicTest.kt index 0cdbe252c7..85f9ef57b1 100644 --- a/tests/models-compat/src/commonTest/kotlin/test/BasicTest.kt +++ b/tests/models-compat/src/commonTest/kotlin/test/BasicTest.kt @@ -15,9 +15,8 @@ import com.apollographql.apollo3.cache.normalized.withFetchPolicy import com.apollographql.apollo3.cache.normalized.withStore import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import readJson -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -28,8 +27,7 @@ class BasicTest { private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore - @BeforeTest - fun setUp() { + private suspend fun setUp() { store = ApolloStore( normalizedCacheFactory = MemoryCacheFactory(), objectIdGenerator = IdObjectIdGenerator @@ -38,7 +36,11 @@ class BasicTest { apolloClient = ApolloClient(mockServer.url()).withStore(store) } - private fun basicTest(resourceName: String, query: Query, block: ApolloResponse.() -> Unit) = runWithMainLoop { + private suspend fun tearDown() { + mockServer.stop() + } + + private fun basicTest(resourceName: String, query: Query, block: ApolloResponse.() -> Unit) = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(readJson(resourceName)) var response = apolloClient.query(ApolloRequest(query).withFetchPolicy(FetchPolicy.NetworkOnly)) response.block() @@ -66,7 +68,7 @@ class BasicTest { @Test - fun `polymorphic Droid fields get parsed to Droid`() = basicTest( + fun polymorphicDroidFieldsGetParsedToDroid() = basicTest( "MergedFieldWithSameShape_Droid.json", MergedFieldWithSameShapeQuery(Episode.NEWHOPE) ) { @@ -77,7 +79,7 @@ class BasicTest { } @Test - fun `polymorphic Human fields get parsed to Human`() = basicTest( + fun polymorphicHumanFieldsGetParsedToHuman() = basicTest( "MergedFieldWithSameShape_Human.json", MergedFieldWithSameShapeQuery(Episode.NEWHOPE) ) { diff --git a/tests/models-compat/src/commonTest/kotlin/test/StoreTest.kt b/tests/models-compat/src/commonTest/kotlin/test/StoreTest.kt index dc30abfee7..5d2d274385 100644 --- a/tests/models-compat/src/commonTest/kotlin/test/StoreTest.kt +++ b/tests/models-compat/src/commonTest/kotlin/test/StoreTest.kt @@ -14,9 +14,8 @@ import IdObjectIdGenerator import com.apollographql.apollo3.cache.normalized.withStore import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import readJson -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals @@ -25,8 +24,7 @@ class StoreTest { private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore - @BeforeTest - fun setUp() { + private suspend fun setUp() { store = ApolloStore( normalizedCacheFactory = MemoryCacheFactory(), objectIdGenerator = IdObjectIdGenerator @@ -35,8 +33,12 @@ class StoreTest { apolloClient = ApolloClient(mockServer.url()).withStore(store) } + private suspend fun tearDown() { + mockServer.stop() + } + @Test - fun readFragmentFromStore() = runWithMainLoop { + fun readFragmentFromStore() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(readJson("HeroAndFriendsWithTypename.json")) apolloClient.query(HeroAndFriendsWithTypenameQuery()) @@ -81,7 +83,7 @@ class StoreTest { * Modify the store by writing fragments */ @Test - fun fragments() = runWithMainLoop { + fun fragments() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(readJson("HeroAndFriendsNamesWithIDs.json")) val query = HeroAndFriendsWithFragmentsQuery() var response = apolloClient.query(query) diff --git a/tests/models-operation-based/src/commonTest/kotlin/test/BasicTest.kt b/tests/models-operation-based/src/commonTest/kotlin/test/BasicTest.kt index 2910210a49..d04be9baa8 100644 --- a/tests/models-operation-based/src/commonTest/kotlin/test/BasicTest.kt +++ b/tests/models-operation-based/src/commonTest/kotlin/test/BasicTest.kt @@ -15,9 +15,8 @@ import com.apollographql.apollo3.cache.normalized.withFetchPolicy import com.apollographql.apollo3.cache.normalized.withStore import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import readJson -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -28,8 +27,7 @@ class BasicTest { private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore - @BeforeTest - fun setUp() { + private suspend fun setUp() { store = ApolloStore( normalizedCacheFactory = MemoryCacheFactory(), objectIdGenerator = IdObjectIdGenerator @@ -38,7 +36,11 @@ class BasicTest { apolloClient = ApolloClient(mockServer.url()).withStore(store) } - private fun basicTest(resourceName: String, query: Query, block: ApolloResponse.() -> Unit) = runWithMainLoop { + private suspend fun tearDown() { + mockServer.stop() + } + + private fun basicTest(resourceName: String, query: Query, block: ApolloResponse.() -> Unit) = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(readJson(resourceName)) var response = apolloClient.query(ApolloRequest(query).withFetchPolicy(FetchPolicy.NetworkOnly)) response.block() @@ -65,7 +67,7 @@ class BasicTest { @Test - fun `polymorphic Droid fields get parsed to Droid`() = basicTest( + fun polymorphicDroidFieldsGetParsedToDroid() = basicTest( "MergedFieldWithSameShape_Droid.json", MergedFieldWithSameShapeQuery(Episode.NEWHOPE) ) { @@ -75,7 +77,7 @@ class BasicTest { } @Test - fun `polymorphic Human fields get parsed to Human`() = basicTest( + fun polymorphicHumanFieldsGetParsedToHuman() = basicTest( "MergedFieldWithSameShape_Human.json", MergedFieldWithSameShapeQuery(Episode.NEWHOPE) ) { diff --git a/tests/models-operation-based/src/commonTest/kotlin/test/StoreTest.kt b/tests/models-operation-based/src/commonTest/kotlin/test/StoreTest.kt index 83b5e6fa43..3723e75c46 100644 --- a/tests/models-operation-based/src/commonTest/kotlin/test/StoreTest.kt +++ b/tests/models-operation-based/src/commonTest/kotlin/test/StoreTest.kt @@ -14,9 +14,8 @@ import IdObjectIdGenerator import com.apollographql.apollo3.cache.normalized.withStore import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import readJson -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals @@ -25,8 +24,7 @@ class StoreTest { private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore - @BeforeTest - fun setUp() { + private suspend fun setUp() { store = ApolloStore( normalizedCacheFactory = MemoryCacheFactory(), objectIdGenerator = IdObjectIdGenerator @@ -35,8 +33,12 @@ class StoreTest { apolloClient = ApolloClient(mockServer.url()).withStore(store) } + private suspend fun tearDown() { + mockServer.stop() + } + @Test - fun readFragmentFromStore() = runWithMainLoop { + fun readFragmentFromStore() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(readJson("HeroAndFriendsWithTypename.json")) apolloClient.query(HeroAndFriendsWithTypenameQuery()) @@ -81,7 +83,7 @@ class StoreTest { * Modify the store by writing fragments */ @Test - fun fragments() = runWithMainLoop { + fun fragments() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(readJson("HeroAndFriendsNamesWithIDs.json")) val query = HeroAndFriendsWithFragmentsQuery() var response = apolloClient.query(query) diff --git a/tests/models-response-based/src/commonTest/kotlin/test/BasicTest.kt b/tests/models-response-based/src/commonTest/kotlin/test/BasicTest.kt index 268137eea1..79193db73d 100644 --- a/tests/models-response-based/src/commonTest/kotlin/test/BasicTest.kt +++ b/tests/models-response-based/src/commonTest/kotlin/test/BasicTest.kt @@ -19,9 +19,8 @@ import com.apollographql.apollo3.cache.normalized.withFetchPolicy import com.apollographql.apollo3.cache.normalized.withStore import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import readJson -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -32,8 +31,7 @@ class BasicTest { private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore - @BeforeTest - fun setUp() { + private suspend fun setUp() { store = ApolloStore( normalizedCacheFactory = MemoryCacheFactory(), objectIdGenerator = IdObjectIdGenerator @@ -42,7 +40,11 @@ class BasicTest { apolloClient = ApolloClient(mockServer.url()).withStore(store) } - private fun basicTest(resourceName: String, query: Query, block: ApolloResponse.() -> Unit) = runWithMainLoop { + private suspend fun tearDown() { + mockServer.stop() + } + + private fun basicTest(resourceName: String, query: Query, block: ApolloResponse.() -> Unit) = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(readJson(resourceName)) var response = apolloClient.query(ApolloRequest(query).withFetchPolicy(FetchPolicy.NetworkOnly)) response.block() @@ -69,7 +71,7 @@ class BasicTest { @Test - fun `polymorphic Droid fields get parsed to Droid`() = basicTest( + fun polymorphicDroidFieldsGetParsedToDroid() = basicTest( "MergedFieldWithSameShape_Droid.json", MergedFieldWithSameShapeQuery(Episode.NEWHOPE) ) { @@ -80,7 +82,7 @@ class BasicTest { } @Test - fun `polymorphic Human fields get parsed to Human`() = basicTest( + fun polymorphicHumanFieldsGetParsedToHuman() = basicTest( "MergedFieldWithSameShape_Human.json", MergedFieldWithSameShapeQuery(Episode.NEWHOPE) ) { diff --git a/tests/models-response-based/src/commonTest/kotlin/test/StoreTest.kt b/tests/models-response-based/src/commonTest/kotlin/test/StoreTest.kt index 66e21e5aac..15df458040 100644 --- a/tests/models-response-based/src/commonTest/kotlin/test/StoreTest.kt +++ b/tests/models-response-based/src/commonTest/kotlin/test/StoreTest.kt @@ -15,9 +15,8 @@ import IdObjectIdGenerator import com.apollographql.apollo3.cache.normalized.withStore import com.apollographql.apollo3.mockserver.MockServer import com.apollographql.apollo3.mockserver.enqueue -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import readJson -import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals @@ -26,8 +25,7 @@ class StoreTest { private lateinit var apolloClient: ApolloClient private lateinit var store: ApolloStore - @BeforeTest - fun setUp() { + private suspend fun setUp() { store = ApolloStore( normalizedCacheFactory = MemoryCacheFactory(), objectIdGenerator = IdObjectIdGenerator @@ -36,8 +34,12 @@ class StoreTest { apolloClient = ApolloClient(mockServer.url()).withStore(store) } + private suspend fun tearDown() { + mockServer.stop() + } + @Test - fun readFragmentFromStore() = runWithMainLoop { + fun readFragmentFromStore() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(readJson("HeroAndFriendsWithTypename.json")) apolloClient.query(HeroAndFriendsWithTypenameQuery()) @@ -82,7 +84,7 @@ class StoreTest { * Modify the store by writing fragments */ @Test - fun fragments() = runWithMainLoop { + fun fragments() = runTest(before = { setUp() }, after = { tearDown() }) { mockServer.enqueue(readJson("HeroAndFriendsNamesWithIDs.json")) val query = HeroAndFriendsWithFragmentsQuery() var response = apolloClient.query(query) diff --git a/tests/websockets/src/commonTest/kotlin/FullStackTest.kt b/tests/websockets/src/commonTest/kotlin/FullStackTest.kt index b9796ba169..cf7c21eb29 100644 --- a/tests/websockets/src/commonTest/kotlin/FullStackTest.kt +++ b/tests/websockets/src/commonTest/kotlin/FullStackTest.kt @@ -1,6 +1,6 @@ import com.apollographql.apollo3.ApolloClient import com.apollographql.apollo3.network.ws.WebSocketNetworkTransport -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import fullstack.tutorial.TripsBookedSubscription import kotlinx.coroutines.flow.collect import kotlin.test.Ignore @@ -9,18 +9,16 @@ import kotlin.test.Test class FullStackTest { @Test @Ignore - fun simple() { + fun simple() = runTest { val apolloClient = ApolloClient( networkTransport = WebSocketNetworkTransport( serverUrl = "https://apollo-fullstack-tutorial.herokuapp.com/graphql" ) ) - runWithMainLoop { - apolloClient.subscribe(TripsBookedSubscription()) - .collect { - println("trips booked: ${it.data?.tripsBooked}") - } - } + apolloClient.subscribe(TripsBookedSubscription()) + .collect { + println("trips booked: ${it.data?.tripsBooked}") + } } } \ No newline at end of file diff --git a/tests/websockets/src/commonTest/kotlin/GraphQLWsTest.kt b/tests/websockets/src/commonTest/kotlin/GraphQLWsTest.kt index 9f18656b36..5584c88a10 100644 --- a/tests/websockets/src/commonTest/kotlin/GraphQLWsTest.kt +++ b/tests/websockets/src/commonTest/kotlin/GraphQLWsTest.kt @@ -1,7 +1,7 @@ import com.apollographql.apollo3.ApolloClient import com.apollographql.apollo3.network.ws.GraphQLWsProtocol import com.apollographql.apollo3.network.ws.WebSocketNetworkTransport -import com.apollographql.apollo3.testing.runWithMainLoop +import com.apollographql.apollo3.testing.runTest import graphql.ws.GreetingsSubscription import graphql.ws.HelloQuery import kotlinx.coroutines.flow.toList @@ -14,7 +14,7 @@ import kotlin.test.assertEquals @Ignore class GraphQLWsTest { @Test - fun queryOverWebSocket() { + fun queryOverWebSocket() = runTest { val apolloClient = ApolloClient( networkTransport = WebSocketNetworkTransport( serverUrl = "http://localhost:9090/graphql", @@ -22,13 +22,11 @@ class GraphQLWsTest { ) ) - runWithMainLoop { - assertEquals("Hello World!", apolloClient.query(HelloQuery()).data?.hello) - } + assertEquals("Hello World!", apolloClient.query(HelloQuery()).data?.hello) } @Test - fun subscriptionOverWebSocket() { + fun subscriptionOverWebSocket() = runTest { val apolloClient = ApolloClient( networkTransport = WebSocketNetworkTransport( serverUrl = "http://localhost:9090/graphql", @@ -36,9 +34,8 @@ class GraphQLWsTest { ) ) - runWithMainLoop { - val list = apolloClient.subscribe(GreetingsSubscription()).toList() - assertEquals(listOf("Hi", "Bonjour", "Hola", "Ciao", "Zdravo"), list.map { it.data?.greetings }) - } + + val list = apolloClient.subscribe(GreetingsSubscription()).toList() + assertEquals(listOf("Hi", "Bonjour", "Hola", "Ciao", "Zdravo"), list.map { it.data?.greetings }) } } \ No newline at end of file From b82129fd07569196ebf286706e590755b5d5c382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ing=2E=20Jan=20Kal=C3=A1b?= Date: Fri, 6 Aug 2021 14:39:52 +0200 Subject: [PATCH 13/43] Revert data class --- .idea/codeStyles/Project.xml | 3 --- .../com/apollographql/apollo3/mockserver/MockServerCommon.kt | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 253cb96c78..89ee79e685 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -20,9 +20,6 @@