RPCErrorTests.swift 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /*
  2. * Copyright 2023, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import GRPCCore
  17. import Testing
  18. @Suite("RPCError Tests")
  19. struct RPCErrorTests {
  20. @Test("Custom String Convertible")
  21. @available(gRPCSwift 2.0, *)
  22. func testCustomStringConvertible() {
  23. #expect(String(describing: RPCError(code: .dataLoss, message: "")) == #"dataLoss: """#)
  24. #expect(
  25. String(describing: RPCError(code: .unknown, message: "message")) == #"unknown: "message""#
  26. )
  27. #expect(
  28. String(describing: RPCError(code: .aborted, message: "message")) == #"aborted: "message""#
  29. )
  30. struct TestError: Error {}
  31. #expect(
  32. String(describing: RPCError(code: .aborted, message: "message", cause: TestError()))
  33. == #"aborted: "message" (cause: "TestError()")"#
  34. )
  35. }
  36. @Test("Error from Status")
  37. @available(gRPCSwift 2.0, *)
  38. func testErrorFromStatus() throws {
  39. var status = Status(code: .ok, message: "")
  40. // ok isn't an error
  41. #expect(RPCError(status: status) == nil)
  42. status.code = .invalidArgument
  43. var error = try #require(RPCError(status: status))
  44. #expect(error.code == .invalidArgument)
  45. #expect(error.message == "")
  46. #expect(error.metadata == [:])
  47. status.code = .cancelled
  48. status.message = "an error message"
  49. error = try #require(RPCError(status: status))
  50. #expect(error.code == .cancelled)
  51. #expect(error.message == "an error message")
  52. #expect(error.metadata == [:])
  53. }
  54. @Test(
  55. "Error Code from Status Code",
  56. arguments: [
  57. (Status.Code.ok, nil),
  58. (Status.Code.cancelled, RPCError.Code.cancelled),
  59. (Status.Code.unknown, RPCError.Code.unknown),
  60. (Status.Code.invalidArgument, RPCError.Code.invalidArgument),
  61. (Status.Code.deadlineExceeded, RPCError.Code.deadlineExceeded),
  62. (Status.Code.notFound, RPCError.Code.notFound),
  63. (Status.Code.alreadyExists, RPCError.Code.alreadyExists),
  64. (Status.Code.permissionDenied, RPCError.Code.permissionDenied),
  65. (Status.Code.resourceExhausted, RPCError.Code.resourceExhausted),
  66. (Status.Code.failedPrecondition, RPCError.Code.failedPrecondition),
  67. (Status.Code.aborted, RPCError.Code.aborted),
  68. (Status.Code.outOfRange, RPCError.Code.outOfRange),
  69. (Status.Code.unimplemented, RPCError.Code.unimplemented),
  70. (Status.Code.internalError, RPCError.Code.internalError),
  71. (Status.Code.unavailable, RPCError.Code.unavailable),
  72. (Status.Code.dataLoss, RPCError.Code.dataLoss),
  73. (Status.Code.unauthenticated, RPCError.Code.unauthenticated),
  74. ]
  75. )
  76. @available(gRPCSwift 2.0, *)
  77. func testErrorCodeFromStatusCode(statusCode: Status.Code, rpcErrorCode: RPCError.Code?) throws {
  78. #expect(RPCError.Code(statusCode) == rpcErrorCode)
  79. }
  80. @Test("Equatable Conformance")
  81. @available(gRPCSwift 2.0, *)
  82. func testEquatableConformance() {
  83. #expect(
  84. RPCError(code: .cancelled, message: "")
  85. == RPCError(code: .cancelled, message: "")
  86. )
  87. #expect(
  88. RPCError(code: .cancelled, message: "message")
  89. == RPCError(code: .cancelled, message: "message")
  90. )
  91. #expect(
  92. RPCError(code: .cancelled, message: "message", metadata: ["foo": "bar"])
  93. == RPCError(code: .cancelled, message: "message", metadata: ["foo": "bar"])
  94. )
  95. #expect(
  96. RPCError(code: .cancelled, message: "")
  97. != RPCError(code: .cancelled, message: "message")
  98. )
  99. #expect(
  100. RPCError(code: .cancelled, message: "message")
  101. != RPCError(code: .unknown, message: "message")
  102. )
  103. #expect(
  104. RPCError(code: .cancelled, message: "message", metadata: ["foo": "bar"])
  105. != RPCError(code: .cancelled, message: "message", metadata: ["foo": "baz"])
  106. )
  107. }
  108. @Test(
  109. "Status Code Raw Values",
  110. arguments: [
  111. (RPCError.Code.cancelled, 1),
  112. (.unknown, 2),
  113. (.invalidArgument, 3),
  114. (.deadlineExceeded, 4),
  115. (.notFound, 5),
  116. (.alreadyExists, 6),
  117. (.permissionDenied, 7),
  118. (.resourceExhausted, 8),
  119. (.failedPrecondition, 9),
  120. (.aborted, 10),
  121. (.outOfRange, 11),
  122. (.unimplemented, 12),
  123. (.internalError, 13),
  124. (.unavailable, 14),
  125. (.dataLoss, 15),
  126. (.unauthenticated, 16),
  127. ]
  128. )
  129. @available(gRPCSwift 2.0, *)
  130. func testStatusCodeRawValues(statusCode: RPCError.Code, rawValue: Int) {
  131. #expect(statusCode.rawValue == rawValue, "\(statusCode) had unexpected raw value")
  132. }
  133. @Test("Flatten causes with same status code")
  134. @available(gRPCSwift 2.0, *)
  135. func testFlattenCausesWithSameStatusCode() {
  136. let error1 = RPCError(code: .unknown, message: "Error 1.")
  137. let error2 = RPCError(code: .unknown, message: "Error 2.", cause: error1)
  138. let error3 = RPCError(code: .dataLoss, message: "Error 3.", cause: error2)
  139. let error4 = RPCError(code: .aborted, message: "Error 4.", cause: error3)
  140. let error5 = RPCError(
  141. code: .aborted,
  142. message: "Error 5.",
  143. cause: error4
  144. )
  145. let unknownMerged = RPCError(code: .unknown, message: "Error 2. Error 1.")
  146. let dataLossMerged = RPCError(code: .dataLoss, message: "Error 3.", cause: unknownMerged)
  147. let abortedMerged = RPCError(
  148. code: .aborted,
  149. message: "Error 5. Error 4.",
  150. cause: dataLossMerged
  151. )
  152. #expect(error5 == abortedMerged)
  153. }
  154. @Test("Causes of errors with different status codes aren't flattened")
  155. @available(gRPCSwift 2.0, *)
  156. func testDifferentStatusCodeAreNotFlattened() throws {
  157. let error1 = RPCError(code: .unknown, message: "Error 1.")
  158. let error2 = RPCError(code: .dataLoss, message: "Error 2.", cause: error1)
  159. let error3 = RPCError(code: .alreadyExists, message: "Error 3.", cause: error2)
  160. let error4 = RPCError(code: .aborted, message: "Error 4.", cause: error3)
  161. let error5 = RPCError(
  162. code: .deadlineExceeded,
  163. message: "Error 5.",
  164. cause: error4
  165. )
  166. #expect(error5.code == .deadlineExceeded)
  167. #expect(error5.message == "Error 5.")
  168. let wrappedError4 = try #require(error5.cause as? RPCError)
  169. #expect(wrappedError4.code == .aborted)
  170. #expect(wrappedError4.message == "Error 4.")
  171. let wrappedError3 = try #require(wrappedError4.cause as? RPCError)
  172. #expect(wrappedError3.code == .alreadyExists)
  173. #expect(wrappedError3.message == "Error 3.")
  174. let wrappedError2 = try #require(wrappedError3.cause as? RPCError)
  175. #expect(wrappedError2.code == .dataLoss)
  176. #expect(wrappedError2.message == "Error 2.")
  177. let wrappedError1 = try #require(wrappedError2.cause as? RPCError)
  178. #expect(wrappedError1.code == .unknown)
  179. #expect(wrappedError1.message == "Error 1.")
  180. #expect(wrappedError1.cause == nil)
  181. }
  182. @Test("Convert type to RPCError")
  183. @available(gRPCSwift 2.0, *)
  184. func convertTypeUsingRPCErrorConvertible() {
  185. struct Cause: Error {}
  186. struct ConvertibleError: RPCErrorConvertible {
  187. var rpcErrorCode: RPCError.Code { .unknown }
  188. var rpcErrorMessage: String { "uhoh" }
  189. var rpcErrorMetadata: Metadata { ["k": "v"] }
  190. var rpcErrorCause: (any Error)? { Cause() }
  191. }
  192. let error = RPCError(ConvertibleError())
  193. #expect(error.code == .unknown)
  194. #expect(error.message == "uhoh")
  195. #expect(error.metadata == ["k": "v"])
  196. #expect(error.cause is Cause)
  197. }
  198. @Test("Convert type to RPCError with defaults")
  199. @available(gRPCSwift 2.0, *)
  200. func convertTypeUsingRPCErrorConvertibleDefaults() {
  201. struct ConvertibleType: RPCErrorConvertible {
  202. var rpcErrorCode: RPCError.Code { .unknown }
  203. var rpcErrorMessage: String { "uhoh" }
  204. }
  205. let error = RPCError(ConvertibleType())
  206. #expect(error.code == .unknown)
  207. #expect(error.message == "uhoh")
  208. #expect(error.metadata == [:])
  209. #expect(error.cause == nil)
  210. }
  211. @Test("Convert error to RPCError with defaults")
  212. @available(gRPCSwift 2.0, *)
  213. func convertErrorUsingRPCErrorConvertibleDefaults() {
  214. struct ConvertibleType: RPCErrorConvertible, Error {
  215. var rpcErrorCode: RPCError.Code { .unknown }
  216. var rpcErrorMessage: String { "uhoh" }
  217. }
  218. let error = RPCError(ConvertibleType())
  219. #expect(error.code == .unknown)
  220. #expect(error.message == "uhoh")
  221. #expect(error.metadata == [:])
  222. #expect(error.cause is ConvertibleType)
  223. }
  224. }