Visitar URL original
fix(cloud_functions): lowercase Android error codes with Locale.ROOT … · firebase/flutterfire@8f0a5d0 · GitHub
Skip to content

Commit 8f0a5d0

Browse files
authored
fix(cloud_functions): lowercase Android error codes with Locale.ROOT (#18651)
* fix(cloud_functions): lowercase Android error codes with Locale.ROOT Turkish and Azerbaijani locales map 'I' to 'ı', so callable codes such as failed-precondition no longer match the documented ASCII strings. * style(cloud_functions): apply ktfmt to Android locale unit test
1 parent efed3e2 commit 8f0a5d0

4 files changed

Lines changed: 84 additions & 2 deletions

File tree

‎.github/workflows/android_unit_tests.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
# Tasks are listed per package rather than using the root testDebugUnitTest
4444
# task, which would also build the Dart sources of the aggregate test app.
4545
# Add a task here when a package gains an android/src/test directory.
46-
run: cd tests/android && ./gradlew :firebase_crashlytics:testDebugUnitTest
46+
run: cd tests/android && ./gradlew :firebase_crashlytics:testDebugUnitTest :cloud_functions:testDebugUnitTest
4747
- name: 'Upload test reports'
4848
if: failure()
4949
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a

‎packages/cloud_functions/cloud_functions/android/build.gradle‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ android {
7878
implementation 'com.google.firebase:firebase-functions'
7979
implementation 'androidx.annotation:annotation:1.7.0'
8080
implementation 'org.reactivestreams:reactive-streams:1.0.4'
81+
testImplementation 'junit:junit:4.13.2'
82+
}
83+
84+
testOptions {
85+
unitTests.returnDefaultValues = true
8186
}
8287

8388
}

‎packages/cloud_functions/cloud_functions/android/src/main/kotlin/io/flutter/plugins/firebase/functions/FlutterFirebaseFunctionsPlugin.kt‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ class FlutterFirebaseFunctionsPlugin : FlutterPlugin, FlutterFirebasePlugin, Clo
136136
}
137137
}
138138

139-
details["code"] = code.replace("_", "-").lowercase(Locale.getDefault())
139+
details["code"] = mapFunctionsErrorCode(code)
140140
details["message"] = message
141141

142142
if (additionalData != null) {
@@ -164,6 +164,11 @@ class FlutterFirebaseFunctionsPlugin : FlutterPlugin, FlutterFirebasePlugin, Clo
164164

165165
companion object {
166166
private const val METHOD_CHANNEL_NAME = "plugins.flutter.io/firebase_functions"
167+
168+
// Locale.ROOT: Turkish/Azerbaijani map 'I' → 'ı' under Locale.getDefault().
169+
internal fun mapFunctionsErrorCode(code: String): String {
170+
return code.replace("_", "-").lowercase(Locale.ROOT)
171+
}
167172
}
168173

169174
override fun call(arguments: Map<String, Any?>, callback: (Result<Any?>) -> Unit) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2026 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
package io.flutter.plugins.firebase.functions
5+
6+
import java.util.Locale
7+
import org.junit.After
8+
import org.junit.Assert.assertEquals
9+
import org.junit.Before
10+
import org.junit.Test
11+
import org.junit.runner.RunWith
12+
import org.junit.runners.JUnit4
13+
14+
@RunWith(JUnit4::class)
15+
class FlutterFirebaseFunctionsPluginTest {
16+
private lateinit var originalLocale: Locale
17+
18+
@Before
19+
fun setUp() {
20+
originalLocale = Locale.getDefault()
21+
}
22+
23+
@After
24+
fun tearDown() {
25+
Locale.setDefault(originalLocale)
26+
}
27+
28+
@Test
29+
fun mapFunctionsErrorCode_usesRootLocaleUnderTurkishDefault() {
30+
Locale.setDefault(Locale("tr", "TR"))
31+
32+
// Canary: the JVM's Turkish locale really does map 'I' to 'ı'.
33+
assertEquals(
34+
"faıled-precondıtıon",
35+
"FAILED_PRECONDITION".replace("_", "-").lowercase(Locale.getDefault()))
36+
37+
assertMappedCodes()
38+
}
39+
40+
@Test
41+
fun mapFunctionsErrorCode_usesRootLocaleUnderAzerbaijaniDefault() {
42+
Locale.setDefault(Locale("az", "AZ"))
43+
44+
assertEquals(
45+
"faıled-precondıtıon",
46+
"FAILED_PRECONDITION".replace("_", "-").lowercase(Locale.getDefault()))
47+
48+
assertMappedCodes()
49+
}
50+
51+
private fun assertMappedCodes() {
52+
val expected =
53+
mapOf(
54+
"FAILED_PRECONDITION" to "failed-precondition",
55+
"INVALID_ARGUMENT" to "invalid-argument",
56+
"PERMISSION_DENIED" to "permission-denied",
57+
"UNAUTHENTICATED" to "unauthenticated",
58+
"INTERNAL" to "internal",
59+
"UNAVAILABLE" to "unavailable",
60+
"DEADLINE_EXCEEDED" to "deadline-exceeded",
61+
"ALREADY_EXISTS" to "already-exists",
62+
"UNIMPLEMENTED" to "unimplemented",
63+
"UNKNOWN" to "unknown",
64+
"NOT_FOUND" to "not-found",
65+
"ABORTED" to "aborted",
66+
)
67+
68+
for ((enumName, canonical) in expected) {
69+
assertEquals(canonical, FlutterFirebaseFunctionsPlugin.mapFunctionsErrorCode(enumName))
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)