-
Notifications
You must be signed in to change notification settings - Fork 1
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
[ECO-5014] feat: basic sandbox setup #47
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,61 @@ | ||||||||||||||||||||||||||||||||||||||
package com.ably.chat | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
import com.google.gson.JsonElement | ||||||||||||||||||||||||||||||||||||||
import com.google.gson.JsonParser | ||||||||||||||||||||||||||||||||||||||
import io.ably.lib.realtime.AblyRealtime | ||||||||||||||||||||||||||||||||||||||
import io.ktor.client.HttpClient | ||||||||||||||||||||||||||||||||||||||
import io.ktor.client.engine.cio.CIO | ||||||||||||||||||||||||||||||||||||||
import io.ktor.client.plugins.HttpRequestRetry | ||||||||||||||||||||||||||||||||||||||
import io.ktor.client.request.get | ||||||||||||||||||||||||||||||||||||||
import io.ktor.client.request.post | ||||||||||||||||||||||||||||||||||||||
import io.ktor.client.request.setBody | ||||||||||||||||||||||||||||||||||||||
import io.ktor.client.statement.HttpResponse | ||||||||||||||||||||||||||||||||||||||
import io.ktor.client.statement.bodyAsText | ||||||||||||||||||||||||||||||||||||||
import io.ktor.http.ContentType | ||||||||||||||||||||||||||||||||||||||
import io.ktor.http.contentType | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
val client = HttpClient(CIO) { | ||||||||||||||||||||||||||||||||||||||
install(HttpRequestRetry) { | ||||||||||||||||||||||||||||||||||||||
retryOnServerErrors(maxRetries = 4) | ||||||||||||||||||||||||||||||||||||||
exponentialDelay() | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
class Sandbox private constructor(val appId: String, val apiKey: String) { | ||||||||||||||||||||||||||||||||||||||
companion object { | ||||||||||||||||||||||||||||||||||||||
suspend fun createInstance(): Sandbox { | ||||||||||||||||||||||||||||||||||||||
val response: HttpResponse = client.post("https://sandbox-rest.ably.io/apps") { | ||||||||||||||||||||||||||||||||||||||
contentType(ContentType.Application.Json) | ||||||||||||||||||||||||||||||||||||||
setBody(loadAppCreationRequestBody().toString()) | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
val body = JsonParser.parseString(response.bodyAsText()) | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
return Sandbox( | ||||||||||||||||||||||||||||||||||||||
appId = body.asJsonObject["appId"].asString, | ||||||||||||||||||||||||||||||||||||||
// From JS chat repo at 7985ab7 — "The key we need to use is the one at index 5, which gives enough permissions to interact with Chat and Channels" | ||||||||||||||||||||||||||||||||||||||
apiKey = body.asJsonObject["keys"].asJsonArray[5].asJsonObject["keyStr"].asString, | ||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
internal fun Sandbox.createSandboxChatClient(): DefaultChatClient { | ||||||||||||||||||||||||||||||||||||||
val realtime = createSandboxRealtime(apiKey) | ||||||||||||||||||||||||||||||||||||||
return DefaultChatClient(realtime, ClientOptions()) | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
internal fun Sandbox.createSandboxRealtime(chatClientId: String = "sandbox-client"): AblyRealtime = | ||||||||||||||||||||||||||||||||||||||
AblyRealtime( | ||||||||||||||||||||||||||||||||||||||
io.ably.lib.types.ClientOptions().apply { | ||||||||||||||||||||||||||||||||||||||
key = apiKey | ||||||||||||||||||||||||||||||||||||||
environment = "sandbox" | ||||||||||||||||||||||||||||||||||||||
clientId = chatClientId | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||
Comment on lines
+47
to
+54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Generate unique client IDs for parallel test safety Using a fixed default client ID could cause issues in parallel test execution. -internal fun Sandbox.createSandboxRealtime(chatClientId: String = "sandbox-client"): AblyRealtime =
+internal fun Sandbox.createSandboxRealtime(
+ chatClientId: String = "sandbox-client-${System.currentTimeMillis()}-${Random.nextInt(1000)}"
+): AblyRealtime = 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
private suspend fun loadAppCreationRequestBody(): JsonElement = | ||||||||||||||||||||||||||||||||||||||
JsonParser.parseString( | ||||||||||||||||||||||||||||||||||||||
client.get("https://raw.githubusercontent.com/ably/ably-common/refs/heads/main/test-resources/test-app-setup.json") { | ||||||||||||||||||||||||||||||||||||||
contentType(ContentType.Application.Json) | ||||||||||||||||||||||||||||||||||||||
}.bodyAsText(), | ||||||||||||||||||||||||||||||||||||||
).asJsonObject.get("post_apps") | ||||||||||||||||||||||||||||||||||||||
Comment on lines
+56
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add timeout for configuration loading Configuration loading from external URL should have a reasonable timeout to prevent tests from hanging. +private const val CONFIG_TIMEOUT_MS = 5000L
private suspend fun loadAppCreationRequestBody(): JsonElement =
JsonParser.parseString(
client.get("https://raw.githubusercontent.com/ably/ably-common/refs/heads/main/test-resources/test-app-setup.json") {
+ timeout {
+ requestTimeoutMillis = CONFIG_TIMEOUT_MS
+ }
contentType(ContentType.Application.Json)
}.bodyAsText(),
).asJsonObject.get("post_apps") 📝 Committable suggestion
Suggested change
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.ably.chat | ||
|
||
import io.ably.lib.realtime.ChannelState | ||
import java.util.UUID | ||
import kotlinx.coroutines.test.runTest | ||
import org.junit.Assert.assertEquals | ||
import org.junit.Before | ||
import org.junit.Test | ||
|
||
class SandboxTest { | ||
|
||
private lateinit var sandbox: Sandbox | ||
|
||
@Before | ||
fun setUp() = runTest { | ||
sandbox = Sandbox.createInstance() | ||
} | ||
Comment on lines
+14
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add error handling and cleanup to test lifecycle methods The current setup lacks error handling and proper cleanup. Consider adding:
@Before
fun setUp() = runTest {
- sandbox = Sandbox.createInstance()
+ try {
+ withTimeout(5000) {
+ sandbox = Sandbox.createInstance()
+ }
+ } catch (e: Exception) {
+ throw IllegalStateException("Failed to set up sandbox environment: ${e.message}", e)
+ }
}
+
+@After
+fun tearDown() = runTest {
+ try {
+ sandbox.close()
+ } catch (e: Exception) {
+ println("Warning: Failed to tear down sandbox environment: ${e.message}")
+ throw e
+ }
+}
|
||
|
||
@Test | ||
fun basicIntegrationTest() = runTest { | ||
val chatClient = sandbox.createSandboxChatClient() | ||
val room = chatClient.rooms.get(UUID.randomUUID().toString()) | ||
room.attach() | ||
assertEquals(ChannelState.attached, room.messages.channel.state) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Make sandbox URL configurable
The sandbox URL should be configurable to support different environments (e.g., local development as suggested in past reviews).
📝 Committable suggestion