Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepare apollo-testing-support to new MockServer #5934

Merged
merged 2 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.apollographql.apollo3.annotations.ApolloExperimental
import com.apollographql.apollo3.api.internal.ResponseParser
import com.apollographql.apollo3.api.json.JsonReader
import com.apollographql.apollo3.api.json.JsonWriter
import com.apollographql.apollo3.api.json.buildJsonString
import com.apollographql.apollo3.api.json.writeObject
import com.apollographql.apollo3.exception.ApolloException
import com.apollographql.apollo3.exception.ApolloNetworkException
Expand Down Expand Up @@ -145,6 +146,19 @@ fun <D : Operation.Data> Operation<D>.composeJsonResponse(
}
}

@ApolloExperimental
fun <D : Operation.Data> Operation<D>.composeJsonResponse(
data: D,
customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty,
): String {
return buildJsonString {
writeObject {
name("data")
adapter().toJson(this, customScalarAdapters, data)
}
}
}

/**
* Parses the [JsonReader] into an [ApolloResponse]
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
package test.network

import app.cash.turbine.test
import com.apollographql.apollo3.ApolloClient
import com.apollographql.apollo3.api.ApolloRequest
import com.apollographql.apollo3.api.ApolloResponse
import com.apollographql.apollo3.api.Operation
import com.apollographql.apollo3.exception.ApolloNetworkException
import com.apollographql.apollo3.interceptor.ApolloInterceptor
import com.apollographql.apollo3.interceptor.ApolloInterceptorChain
import com.apollographql.apollo3.mockserver.MockServer
import com.apollographql.apollo3.mockserver.assertNoRequest
import com.apollographql.apollo3.mockserver.enqueueString
import com.apollographql.apollo3.network.NetworkMonitor
import com.apollographql.apollo3.testing.FooQuery
import com.apollographql.apollo3.testing.internal.ApolloTestResult
import com.apollographql.apollo3.testing.mockServerTest
import com.apollographql.apollo3.testing.internal.runTest
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
Expand All @@ -21,6 +24,7 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.takeWhile
import okio.use
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFails
Expand Down Expand Up @@ -107,3 +111,19 @@ class NetworkMonitorInterceptor(private val networkMonitor: NetworkMonitor): Apo
}
}

class MockServerTest(val mockServer: MockServer, val apolloClient: ApolloClient, val scope: CoroutineScope)

fun mockServerTest(
clientBuilder: ApolloClient.Builder.() -> Unit = {},
block: suspend MockServerTest.() -> Unit
) = runTest(true) {
MockServer().use { mockServer ->
ApolloClient.Builder()
.serverUrl(mockServer.url())
.apply(clientBuilder)
.build()
.use {apolloClient ->
MockServerTest(mockServer, apolloClient, this).block()
}
}
}
Comment on lines +114 to +129
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is duplicated a bunch of times. It's a bit sad but it's very trivial code so it shouldn't be too hard to maintain.

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import com.apollographql.apollo3.interceptor.addRetryOnErrorInterceptor
import com.apollographql.apollo3.mockserver.CloseFrame
import com.apollographql.apollo3.mockserver.MockServer
import com.apollographql.apollo3.mockserver.TextMessage
import com.apollographql.apollo3.mockserver.WebSocketBody
import com.apollographql.apollo3.mockserver.WebsocketMockRequest
import com.apollographql.apollo3.mockserver.awaitWebSocketRequest
import com.apollographql.apollo3.mockserver.enqueueWebSocket
import com.apollographql.apollo3.mpp.Platform
Expand All @@ -24,7 +26,7 @@ import com.apollographql.apollo3.testing.FooSubscription.Companion.nextMessage
import com.apollographql.apollo3.testing.awaitSubscribe
import com.apollographql.apollo3.testing.connectionAckMessage
import com.apollographql.apollo3.testing.internal.runTest
import com.apollographql.apollo3.testing.mockServerWebSocketTest
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.merge
import okio.use
Expand Down Expand Up @@ -351,4 +353,57 @@ class WebSocketNetworkTransportTest {
assertEquals("oh no!", message)
}
}
}
}

internal fun WebSocketBody.enqueueMessage(message: String) {
enqueueMessage(TextMessage(message))
}

class MockServerWebSocketTest(
val apolloClient: ApolloClient,
private val mockServer: MockServer,
val coroutineScope: CoroutineScope,
) {
/**
* Enqueue the response straight away
*/
val serverWriter: WebSocketBody = mockServer.enqueueWebSocket()
private var _serverReader: WebsocketMockRequest? = null

val serverReader: WebsocketMockRequest
get() {
check(_serverReader != null) {
"You need to call awaitConnectionInit or awaitWebSocketRequest first"
}
return _serverReader!!
}

suspend fun awaitWebSocketRequest() {
_serverReader = mockServer.awaitWebSocketRequest()
}

suspend fun awaitConnectionInit() {
awaitWebSocketRequest()

serverReader.awaitMessage()
serverWriter.enqueueMessage(TextMessage(connectionAckMessage()))
}
}

fun mockServerWebSocketTest(customizeTransport: WebSocketNetworkTransport.Builder.() -> Unit = {}, block: suspend MockServerWebSocketTest.() -> Unit) = runTest(false) {
MockServer().use { mockServer ->

ApolloClient.Builder()
.serverUrl(mockServer.url())
.subscriptionNetworkTransport(
WebSocketNetworkTransport.Builder()
.serverUrl(mockServer.url())
.apply(customizeTransport)
.build()
)
.build().use { apolloClient ->
@Suppress("DEPRECATION")
MockServerWebSocketTest(apolloClient, mockServer, this@runTest).block()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ import com.apollographql.apollo3.testing.FooSubscription.Companion.nextMessage
import com.apollographql.apollo3.testing.awaitSubscribe
import com.apollographql.apollo3.testing.connectionAckMessage
import com.apollographql.apollo3.testing.internal.runTest
import com.apollographql.apollo3.testing.mockServerTest
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeout
import okio.use
import test.network.enqueueMessage
import test.network.mockServerTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
Expand Down Expand Up @@ -231,7 +232,6 @@ class RetryWebSocketsTest {
clientBuilder = {
retryOnError { it.operation is Subscription }
},
skipDelays = false
) {
mockServer.enqueue(MockResponse.Builder().statusCode(500).build())

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ public final class com/apollographql/apollo3/testing/FooQuery$Companion {
}

public final class com/apollographql/apollo3/testing/FooSubscription$Companion {
public final fun completeMessage (Ljava/lang/String;)Lcom/apollographql/apollo3/mockserver/TextMessage;
public final fun errorMessage (Ljava/lang/String;Ljava/lang/String;)Lcom/apollographql/apollo3/mockserver/TextMessage;
public final fun nextMessage (Ljava/lang/String;I)Lcom/apollographql/apollo3/mockserver/TextMessage;
public final fun completeMessage (Ljava/lang/String;)Ljava/lang/String;
public final fun errorMessage (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
public final fun nextMessage (Ljava/lang/String;I)Ljava/lang/String;
}

public final class com/apollographql/apollo3/testing/MockserverKt {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package com.apollographql.apollo3.testing

import com.apollographql.apollo3.annotations.ApolloDeprecatedSince

@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v4_0_0)
@Deprecated("This is only used for internal Apollo tests and will be removed in a future version.")
actual fun shouldUpdateTestFixtures(): Boolean = false

@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v4_0_0)
@Deprecated("This is only used for internal Apollo tests and will be removed in a future version.")
actual val testsPath: String = "../"
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

package com.apollographql.apollo3.testing

import com.apollographql.apollo3.annotations.ApolloDeprecatedSince
import com.apollographql.apollo3.api.json.JsonReader
import com.apollographql.apollo3.api.json.jsonReader
import okio.IOException
import okio.Path
import okio.Path.Companion.toPath
import okio.buffer
import okio.use
Expand All @@ -17,10 +19,14 @@ import kotlin.jvm.JvmName
*
* @param path: the path to the file, from the "tests" directory
*/
@Deprecated("This is only used for internal Apollo tests and will be removed in a future version.")
@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v4_0_0)
fun checkFile(actualText: String, path: String) {
@Suppress("DEPRECATION")
val updateTestFixtures = shouldUpdateTestFixtures()
val expected = path.toTestsPath()
val expectedText = try {
@Suppress("DEPRECATION")
HostFileSystem.openReadOnly(expected).source().buffer().readUtf8()
} catch (e: IOException) {
if (updateTestFixtures) {
Expand All @@ -34,7 +40,9 @@ fun checkFile(actualText: String, path: String) {

if (actualText != expectedText) {
if (updateTestFixtures) {
@Suppress("DEPRECATION")
HostFileSystem.delete(expected)
@Suppress("DEPRECATION")
HostFileSystem.openReadWrite(
file = expected,
).use {
Expand All @@ -53,18 +61,27 @@ fun checkFile(actualText: String, path: String) {
}
}

private fun String.toTestsPath() = testsPath.toPath().resolve(this.toPath())
private fun String.toTestsPath(): Path {
@Suppress("DEPRECATION")
return testsPath.toPath().resolve(this.toPath())
}

/**
* @param path: the path to the file, from the "tests" directory
*/
@Deprecated("This function is not Apollo specific and will be removed in a future version. Copy/paste it in your codebase if you need it")
@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v4_0_0)
fun pathToUtf8(path: String): String {
@Suppress("DEPRECATION")
return HostFileSystem.openReadOnly(path.toTestsPath()).source().buffer().readUtf8()
}

/**
* @param path: the path to the file, from the "tests" directory
*/
@Deprecated("This function is not Apollo specific and will be removed in a future version. Copy/paste it in your codebase if you need it")
@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v4_0_0)
fun pathToJsonReader(path: String): JsonReader {
@Suppress("DEPRECATION")
return HostFileSystem.openReadOnly(path.toTestsPath()).source().buffer().jsonReader()
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.apollographql.apollo3.testing

import com.apollographql.apollo3.annotations.ApolloDeprecatedSince
import com.apollographql.apollo3.annotations.ApolloExperimental
import com.apollographql.apollo3.api.Adapter
import com.apollographql.apollo3.api.CompiledField
Expand All @@ -14,14 +15,16 @@ import com.apollographql.apollo3.api.json.buildJsonString
import com.apollographql.apollo3.api.json.writeArray
import com.apollographql.apollo3.api.json.writeObject
import com.apollographql.apollo3.api.missingField
import com.apollographql.apollo3.mockserver.TextMessage

/**
* [FooQuery] is a query for tests that doesn't require codegen.
*
* Use it to test parts of the runtime without having to use included builds.
*/
@ApolloExperimental
@Deprecated("This is only used for internal Apollo tests and will be removed in a future version.")
@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v4_0_0)
@Suppress("DEPRECATION")
class FooQuery: FooOperation("query"), Query<FooOperation.Data> {
companion object {
val successResponse = "{\"data\": {\"foo\": 42}}"
Expand All @@ -34,9 +37,12 @@ class FooQuery: FooOperation("query"), Query<FooOperation.Data> {
* Use it to test parts of the runtime without having to use included builds.
*/
@ApolloExperimental
@Deprecated("This is only used for internal Apollo tests and will be removed in a future version.")
@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v4_0_0)
@Suppress("DEPRECATION")
class FooSubscription: FooOperation("subscription"), Subscription<FooOperation.Data> {
companion object {
fun nextMessage(id: String, foo: Int): TextMessage {
fun nextMessage(id: String, foo: Int): String {
return buildJsonString {
writeObject {
name("id")
Expand All @@ -52,21 +58,21 @@ class FooSubscription: FooOperation("subscription"), Subscription<FooOperation.D
}
}
}
}.let { TextMessage(it) }
}
}

fun completeMessage(id: String): TextMessage {
fun completeMessage(id: String): String {
return buildJsonString {
writeObject {
name("id")
value(id)
name("type")
value("complete")
}
}.let { TextMessage(it) }
}
}

fun errorMessage(id: String, message: String): TextMessage {
fun errorMessage(id: String, message: String): String {
return buildJsonString {
writeObject {
name("id")
Expand All @@ -81,7 +87,7 @@ class FooSubscription: FooOperation("subscription"), Subscription<FooOperation.D
}
}
}
}.let { TextMessage(it) }
}
}
}
}
Expand All @@ -91,6 +97,9 @@ class FooSubscription: FooOperation("subscription"), Subscription<FooOperation.D
* Note we can't make [FooOperation] extend both [Query] and [Subscription] because that confuses [ApolloClient] when deciding whant NetworkTransport to use.
*/
@ApolloExperimental
@Deprecated("This is only used for internal Apollo tests and will be removed in a future version.")
@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v4_0_0)
@Suppress("DEPRECATION")
abstract class FooOperation(private val operationType: String): Operation<FooOperation.Data> {
class Data(val foo: Int): Query.Data, Subscription.Data {
override fun toString(): String {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package com.apollographql.apollo3.testing

import com.apollographql.apollo3.annotations.ApolloDeprecatedSince
import com.apollographql.apollo3.annotations.ApolloExperimental
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.withTimeout

@ApolloExperimental
@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v4_0_0)
@Deprecated("This function is not Apollo specific and will be removed in a future version. Copy/paste it in your codebase if you need it")
suspend fun <T> Channel<T>.awaitElement(timeoutMillis: Long = 30000) = withTimeout(timeoutMillis) {
receive()
}

@ApolloExperimental
@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v4_0_0)
@Deprecated("This function is not Apollo specific and will be removed in a future version. Copy/paste it in your codebase if you need it")
suspend fun <T> Channel<T>.assertNoElement(timeoutMillis: Long = 300): Unit {
try {
withTimeout(timeoutMillis) {
Expand Down
Loading
Loading