FunctionsErrorTests.swift 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // Copyright 2024 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. @testable import FirebaseFunctions
  15. import XCTest
  16. final class FunctionsErrorTests: XCTestCase {
  17. func testInitWithCode() {
  18. let error = FunctionsError(.permissionDenied)
  19. let nsError = error as NSError
  20. XCTAssertEqual(nsError.domain, "com.firebase.functions")
  21. XCTAssertEqual(nsError.code, 7)
  22. XCTAssertEqual(nsError.localizedDescription, "PERMISSION DENIED")
  23. XCTAssertEqual(nsError.userInfo.count, 1)
  24. }
  25. func testInitWithCodeAndUserInfo() {
  26. let error = FunctionsError(.unimplemented, userInfo: ["TEST_Key": "TEST_Value"])
  27. let nsError = error as NSError
  28. XCTAssertEqual(nsError.domain, "com.firebase.functions")
  29. XCTAssertEqual(nsError.code, 12)
  30. XCTAssertEqual(
  31. nsError.localizedDescription,
  32. "The operation couldn’t be completed. (com.firebase.functions error 12.)"
  33. )
  34. XCTAssertEqual(nsError.userInfo.count, 1)
  35. XCTAssertEqual(nsError.userInfo["TEST_Key"] as? String, "TEST_Value")
  36. }
  37. func testInitWithOKStatusCodeAndNoErrorBody() {
  38. // The error should be `nil`.
  39. let error = FunctionsError(
  40. httpStatusCode: 200,
  41. body: nil,
  42. serializer: FunctionsSerializer()
  43. )
  44. XCTAssertNil(error)
  45. }
  46. func testInitWithErrorStatusCodeAndNoErrorBody() {
  47. // The error should be inferred from the HTTP status code.
  48. let error = FunctionsError(
  49. httpStatusCode: 429,
  50. body: nil,
  51. serializer: FunctionsSerializer()
  52. )
  53. guard let error else { return XCTFail("Unexpected `nil` value") }
  54. let nsError = error as NSError
  55. XCTAssertEqual(nsError.domain, "com.firebase.functions")
  56. XCTAssertEqual(nsError.code, 8)
  57. XCTAssertEqual(nsError.localizedDescription, "RESOURCE EXHAUSTED")
  58. XCTAssertEqual(nsError.userInfo.count, 1)
  59. }
  60. func testInitWithOKStatusCodeAndIncompleteErrorBody() {
  61. // The status code in the error body takes precedence over the HTTP status code.
  62. let responseData = #"{ "error": { "status": "OUT_OF_RANGE" } }"#.data(using: .utf8)!
  63. let error = FunctionsError(
  64. httpStatusCode: 200,
  65. body: responseData,
  66. serializer: FunctionsSerializer()
  67. )
  68. guard let error else { return XCTFail("Unexpected `nil` value") }
  69. let nsError = error as NSError
  70. XCTAssertEqual(nsError.domain, "com.firebase.functions")
  71. XCTAssertEqual(nsError.code, 11)
  72. XCTAssertEqual(nsError.localizedDescription, "OUT OF RANGE")
  73. XCTAssertEqual(nsError.userInfo.count, 1)
  74. }
  75. func testInitWithErrorStatusCodeAndErrorBody() {
  76. // The status code in the error body takes precedence over the HTTP status code.
  77. let responseData =
  78. #"{ "error": { "status": "OUT_OF_RANGE", "message": "TEST_ErrorMessage", "details": 123 } }"#
  79. .data(using: .utf8)!
  80. let error = FunctionsError(
  81. httpStatusCode: 499,
  82. body: responseData,
  83. serializer: FunctionsSerializer()
  84. )
  85. guard let error else { return XCTFail("Unexpected `nil` value") }
  86. let nsError = error as NSError
  87. XCTAssertEqual(nsError.domain, "com.firebase.functions")
  88. XCTAssertEqual(nsError.code, 11)
  89. XCTAssertEqual(nsError.localizedDescription, "TEST_ErrorMessage")
  90. XCTAssertEqual(nsError.userInfo.count, 2)
  91. XCTAssertEqual(nsError.userInfo["details"] as? Int, 123)
  92. }
  93. func testInitWithErrorStatusCodeAndOKErrorBody() {
  94. // When the status code in the error body is `OK`, error should be `nil` regardless of the HTTP
  95. // status code.
  96. let responseData =
  97. #"{ "error": { "status": "OK", "message": "TEST_ErrorMessage", "details": 123 } }"#
  98. .data(using: .utf8)!
  99. let error = FunctionsError(
  100. httpStatusCode: 401,
  101. body: responseData,
  102. serializer: FunctionsSerializer()
  103. )
  104. XCTAssertNil(error)
  105. }
  106. func testInitWithErrorStatusCodeAndIncompleteErrorBody() {
  107. // The error name is not in the body; it should be inferred from the HTTP status code.
  108. let responseData = #"{ "error": { "message": "TEST_ErrorMessage", "details": null } }"#
  109. .data(using: .utf8)!
  110. let error = FunctionsError(
  111. httpStatusCode: 403,
  112. body: responseData,
  113. serializer: FunctionsSerializer()
  114. )
  115. guard let error else { return XCTFail("Unexpected `nil` value") }
  116. let nsError = error as NSError
  117. XCTAssertEqual(nsError.domain, "com.firebase.functions")
  118. XCTAssertEqual(nsError.code, 7) // `permissionDenied`, inferred from the HTTP status code
  119. XCTAssertEqual(nsError.localizedDescription, "TEST_ErrorMessage")
  120. XCTAssertEqual(nsError.userInfo.count, 2)
  121. XCTAssertEqual(nsError.userInfo["details"] as? NSNull, NSNull())
  122. }
  123. func testInitWithErrorStatusCodeAndInvalidErrorBody() {
  124. // An unsupported status code in the error body should result in the rest of the body ignored.
  125. let responseData =
  126. #"{ "error": { "status": "TEST_UNKNOWN_ERROR", "message": "TEST_ErrorMessage", "details": 123 } }"#
  127. .data(using: .utf8)!
  128. let error = FunctionsError(
  129. httpStatusCode: 503,
  130. body: responseData,
  131. serializer: FunctionsSerializer()
  132. )
  133. guard let error else { return XCTFail("Unexpected `nil` value") }
  134. let nsError = error as NSError
  135. XCTAssertEqual(nsError.domain, "com.firebase.functions")
  136. // Currently, `internal` is used as the fallback error code. Is this correct?
  137. // Seems like we could get more information from the HTTP status code in such cases.
  138. XCTAssertEqual(nsError.code, 13)
  139. XCTAssertEqual(nsError.localizedDescription, "INTERNAL")
  140. XCTAssertEqual(nsError.userInfo.count, 1)
  141. }
  142. }