Add tests for SmartlingClient.

This commit is contained in:
Greyson Parrelli
2025-12-31 14:24:25 -05:00
committed by jeffrey-signal
parent ffecdb3747
commit c31b2edeab
4 changed files with 229 additions and 7 deletions

View File

@@ -25,8 +25,9 @@ dependencies {
implementation(gradleApi()) implementation(gradleApi())
implementation(libs.dnsjava) implementation(libs.dnsjava)
implementation(libs.square.okhttp3) api(libs.square.okhttp3)
testImplementation(testLibs.junit.junit) testImplementation(testLibs.junit.junit)
testImplementation(testLibs.mockk) testImplementation(testLibs.mockk)
testImplementation(testLibs.square.mockwebserver)
} }

View File

@@ -17,10 +17,12 @@ import java.util.concurrent.TimeUnit
class SmartlingClient( class SmartlingClient(
private val userIdentifier: String, private val userIdentifier: String,
private val userSecret: String, private val userSecret: String,
private val projectId: String private val projectId: String,
private val baseUrl: String = "https://api.smartling.com",
client: OkHttpClient? = null
) { ) {
private val client = OkHttpClient.Builder() private val client = client ?: OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS) .connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS)
@@ -41,7 +43,7 @@ class SmartlingClient(
).toString() ).toString()
val request = Request.Builder() val request = Request.Builder()
.url("https://api.smartling.com/auth-api/v2/authenticate") .url("$baseUrl/auth-api/v2/authenticate")
.post(jsonBody.toRequestBody("application/json".toMediaType())) .post(jsonBody.toRequestBody("application/json".toMediaType()))
.build() .build()
@@ -73,7 +75,7 @@ class SmartlingClient(
.build() .build()
val request = Request.Builder() val request = Request.Builder()
.url("https://api.smartling.com/files-api/v2/projects/$projectId/file") .url("$baseUrl/files-api/v2/projects/$projectId/file")
.header("Authorization", "Bearer $authToken") .header("Authorization", "Bearer $authToken")
.post(requestBody) .post(requestBody)
.build() .build()
@@ -94,7 +96,7 @@ class SmartlingClient(
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun getLocales(authToken: String, fileUri: String): List<String> { fun getLocales(authToken: String, fileUri: String): List<String> {
val request = Request.Builder() val request = Request.Builder()
.url("https://api.smartling.com/files-api/v2/projects/$projectId/file/status?fileUri=$fileUri") .url("$baseUrl/files-api/v2/projects/$projectId/file/status?fileUri=$fileUri")
.header("Authorization", "Bearer $authToken") .header("Authorization", "Bearer $authToken")
.get() .get()
.build() .build()
@@ -120,7 +122,7 @@ class SmartlingClient(
*/ */
fun downloadFile(authToken: String, fileUri: String, locale: String): String { fun downloadFile(authToken: String, fileUri: String, locale: String): String {
val request = Request.Builder() val request = Request.Builder()
.url("https://api.smartling.com/files-api/v2/projects/$projectId/locales/$locale/file?fileUri=$fileUri") .url("$baseUrl/files-api/v2/projects/$projectId/locales/$locale/file?fileUri=$fileUri")
.header("Authorization", "Bearer $authToken") .header("Authorization", "Bearer $authToken")
.get() .get()
.build() .build()

View File

@@ -0,0 +1,218 @@
package org.signal.buildtools
import mockwebserver3.MockResponse
import mockwebserver3.MockWebServer
import okhttp3.ExperimentalOkHttpApi
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import java.io.File
@OptIn(ExperimentalOkHttpApi::class)
class SmartlingClientTest {
private lateinit var server: MockWebServer
private lateinit var client: SmartlingClient
@Before
fun setUp() {
server = MockWebServer()
server.start()
client = SmartlingClient(
userIdentifier = "test-user",
userSecret = "test-secret",
projectId = "test-project",
baseUrl = server.url("/").toString().removeSuffix("/")
)
}
@After
fun tearDown() {
server.shutdown()
}
@Test
fun `authenticate returns access token on success`() {
server.enqueue(
MockResponse.Builder()
.code(200)
.body(
"""
{
"response": {
"data": {
"accessToken": "test-token-123"
}
}
}
""".trimIndent()
)
.build()
)
val token = client.authenticate()
assertEquals("test-token-123", token)
val request = server.takeRequest()
assertEquals("POST", request.method)
assertEquals("/auth-api/v2/authenticate", request.path)
assertTrue(request.body.readUtf8().contains("test-user"))
}
@Test(expected = SmartlingClient.SmartlingException::class)
fun `authenticate throws on HTTP error`() {
server.enqueue(
MockResponse.Builder()
.code(401)
.body("Unauthorized")
.build()
)
client.authenticate()
}
@Test(expected = SmartlingClient.SmartlingException::class)
fun `authenticate throws on malformed response`() {
server.enqueue(
MockResponse.Builder()
.code(200)
.body("{}")
.build()
)
client.authenticate()
}
@Test
fun `uploadFile returns response body on success`() {
server.enqueue(
MockResponse.Builder()
.code(200)
.body("""{"response": {"data": {"uploaded": true}}}""")
.build()
)
val tempFile = File.createTempFile("test-strings", ".xml").apply {
writeText("<resources><string name=\"test\">Test</string></resources>")
deleteOnExit()
}
val response = client.uploadFile("auth-token", tempFile, "strings.xml")
assertTrue(response.contains("uploaded"))
val request = server.takeRequest()
assertEquals("POST", request.method)
assertEquals("/files-api/v2/projects/test-project/file", request.path)
assertEquals("Bearer auth-token", request.headers["Authorization"])
}
@Test(expected = SmartlingClient.SmartlingException::class)
fun `uploadFile throws on HTTP error`() {
server.enqueue(
MockResponse.Builder()
.code(500)
.body("Internal Server Error")
.build()
)
val tempFile = File.createTempFile("test-strings", ".xml").apply {
writeText("<resources/>")
deleteOnExit()
}
client.uploadFile("auth-token", tempFile, "strings.xml")
}
@Test
fun `getLocales returns list of locale IDs`() {
server.enqueue(
MockResponse.Builder()
.code(200)
.body(
"""
{
"response": {
"data": {
"items": [
{"localeId": "de"},
{"localeId": "fr"},
{"localeId": "es"}
]
}
}
}
""".trimIndent()
)
.build()
)
val locales = client.getLocales("auth-token", "strings.xml")
assertEquals(listOf("de", "fr", "es"), locales)
val request = server.takeRequest()
assertEquals("GET", request.method)
assertEquals("/files-api/v2/projects/test-project/file/status?fileUri=strings.xml", request.path)
assertEquals("Bearer auth-token", request.headers["Authorization"])
}
@Test(expected = SmartlingClient.SmartlingException::class)
fun `getLocales throws on HTTP error`() {
server.enqueue(
MockResponse.Builder()
.code(404)
.body("Not Found")
.build()
)
client.getLocales("auth-token", "strings.xml")
}
@Test(expected = SmartlingClient.SmartlingException::class)
fun `getLocales throws on malformed response`() {
server.enqueue(
MockResponse.Builder()
.code(200)
.body("""{"response": {"data": {}}}""")
.build()
)
client.getLocales("auth-token", "strings.xml")
}
@Test
fun `downloadFile returns file content`() {
val xmlContent = """<resources><string name="hello">Hallo</string></resources>"""
server.enqueue(
MockResponse.Builder()
.code(200)
.body(xmlContent)
.build()
)
val content = client.downloadFile("auth-token", "strings.xml", "de")
assertEquals(xmlContent, content)
val request = server.takeRequest()
assertEquals("GET", request.method)
assertEquals("/files-api/v2/projects/test-project/locales/de/file?fileUri=strings.xml", request.path)
assertEquals("Bearer auth-token", request.headers["Authorization"])
}
@Test(expected = SmartlingClient.SmartlingException::class)
fun `downloadFile throws on HTTP error`() {
server.enqueue(
MockResponse.Builder()
.code(500)
.body("Internal Server Error")
.build()
)
client.downloadFile("auth-token", "strings.xml", "de")
}
}

View File

@@ -24,5 +24,6 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1"
mockk = "io.mockk:mockk:1.13.17" mockk = "io.mockk:mockk:1.13.17"
mockk-android = "io.mockk:mockk-android:1.13.17" mockk-android = "io.mockk:mockk-android:1.13.17"
square-mockwebserver = "com.squareup.okhttp3:mockwebserver:5.0.0-alpha.16"
conscrypt-openjdk-uber = "org.conscrypt:conscrypt-openjdk-uber:2.5.2" conscrypt-openjdk-uber = "org.conscrypt:conscrypt-openjdk-uber:2.5.2"
diff-utils = "io.github.java-diff-utils:java-diff-utils:4.12" diff-utils = "io.github.java-diff-utils:java-diff-utils:4.12"