ReflectionServiceIntegrationTests.swift 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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 Foundation
  17. import GRPC
  18. import GRPCReflectionService
  19. import NIOPosix
  20. import SwiftProtobuf
  21. import XCTest
  22. @testable import GRPCReflectionService
  23. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  24. final class ReflectionServiceIntegrationTests: GRPCTestCase {
  25. private var server: Server?
  26. private var channel: GRPCChannel?
  27. private let protos: [Google_Protobuf_FileDescriptorProto] = makeProtosWithDependencies()
  28. private let independentProto: Google_Protobuf_FileDescriptorProto = generateFileDescriptorProto(
  29. fileName: "independentBar",
  30. suffix: "5"
  31. )
  32. private let versions: [ReflectionService.Version] = [.v1, .v1Alpha]
  33. private func setUpServerAndChannel(version: ReflectionService.Version) throws {
  34. let reflectionServiceProvider = try ReflectionService(
  35. fileDescriptorProtos: self.protos + [self.independentProto],
  36. version: version
  37. )
  38. let server = try Server.insecure(group: MultiThreadedEventLoopGroup.singleton)
  39. .withServiceProviders([reflectionServiceProvider])
  40. .withLogger(self.serverLogger)
  41. .bind(host: "127.0.0.1", port: 0)
  42. .wait()
  43. self.server = server
  44. let channel = try GRPCChannelPool.with(
  45. target: .hostAndPort("127.0.0.1", server.channel.localAddress!.port!),
  46. transportSecurity: .plaintext,
  47. eventLoopGroup: MultiThreadedEventLoopGroup.singleton
  48. ) {
  49. $0.backgroundActivityLogger = self.clientLogger
  50. }
  51. self.channel = channel
  52. }
  53. override func tearDown() {
  54. if let channel = self.channel {
  55. XCTAssertNoThrow(try channel.close().wait())
  56. }
  57. if let server = self.server {
  58. XCTAssertNoThrow(try server.close().wait())
  59. }
  60. super.tearDown()
  61. }
  62. private func getServerReflectionResponse(
  63. for request: Grpc_Reflection_V1_ServerReflectionRequest,
  64. version: ReflectionService.Version
  65. ) async throws -> Grpc_Reflection_V1_ServerReflectionResponse? {
  66. let response: Grpc_Reflection_V1_ServerReflectionResponse?
  67. switch version {
  68. case .v1:
  69. let client = Grpc_Reflection_V1_ServerReflectionAsyncClient(channel: self.channel!)
  70. let serviceReflectionInfo = client.makeServerReflectionInfoCall()
  71. try await serviceReflectionInfo.requestStream.send(request)
  72. serviceReflectionInfo.requestStream.finish()
  73. var iterator = serviceReflectionInfo.responseStream.makeAsyncIterator()
  74. response = try await iterator.next()
  75. case .v1Alpha:
  76. let client = Grpc_Reflection_V1alpha_ServerReflectionAsyncClient(channel: self.channel!)
  77. let serviceReflectionInfo = client.makeServerReflectionInfoCall()
  78. try await serviceReflectionInfo.requestStream.send(
  79. Grpc_Reflection_V1alpha_ServerReflectionRequest(request)
  80. )
  81. serviceReflectionInfo.requestStream.finish()
  82. var iterator = serviceReflectionInfo.responseStream.makeAsyncIterator()
  83. response = try await iterator.next().map {
  84. Grpc_Reflection_V1_ServerReflectionResponse($0)
  85. }
  86. default:
  87. return nil
  88. }
  89. return response
  90. }
  91. private func forEachVersion(
  92. _ body: (GRPCChannel?, ReflectionService.Version) async throws -> Void
  93. ) async throws {
  94. for version in self.versions {
  95. try setUpServerAndChannel(version: version)
  96. let result: Result<Void, Error>
  97. do {
  98. try await body(self.channel, version)
  99. result = .success(())
  100. } catch {
  101. result = .failure(error)
  102. }
  103. try result.get()
  104. try await self.tearDown()
  105. }
  106. }
  107. func testFileByFileName() async throws {
  108. try await self.forEachVersion { channel, version in
  109. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  110. $0.host = "127.0.0.1"
  111. $0.fileByFilename = "bar1.proto"
  112. }
  113. let response = try await self.getServerReflectionResponse(for: request, version: version)
  114. let message = try XCTUnwrap(response, "Could not get a response message.")
  115. // response can't be nil as we just checked it.
  116. let receivedFileDescriptorProto =
  117. try Google_Protobuf_FileDescriptorProto(
  118. serializedBytes: message.fileDescriptorResponse.fileDescriptorProto[0]
  119. )
  120. XCTAssertEqual(receivedFileDescriptorProto.name, "bar1.proto")
  121. XCTAssertEqual(receivedFileDescriptorProto.service.count, 1)
  122. let service = try XCTUnwrap(
  123. receivedFileDescriptorProto.service.first,
  124. "The received file descriptor proto doesn't have any services."
  125. )
  126. let method = try XCTUnwrap(
  127. service.method.first,
  128. "The service of the received file descriptor proto doesn't have any methods."
  129. )
  130. XCTAssertEqual(method.name, "testMethod1")
  131. XCTAssertEqual(message.fileDescriptorResponse.fileDescriptorProto.count, 4)
  132. }
  133. }
  134. func testListServices() async throws {
  135. try await self.forEachVersion { channel, version in
  136. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  137. $0.host = "127.0.0.1"
  138. $0.listServices = "services"
  139. }
  140. let response = try await self.getServerReflectionResponse(for: request, version: version)
  141. let message = try XCTUnwrap(response, "Could not get a response message.")
  142. let receivedServices = message.listServicesResponse.service.map { $0.name }.sorted()
  143. let servicesNames = (self.protos + [self.independentProto]).flatMap {
  144. $0.qualifiedServiceNames
  145. }
  146. .sorted()
  147. XCTAssertEqual(receivedServices, servicesNames)
  148. }
  149. }
  150. func testFileBySymbol() async throws {
  151. try await self.forEachVersion { channel, version in
  152. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  153. $0.host = "127.0.0.1"
  154. $0.fileContainingSymbol = "packagebar1.enumType1"
  155. }
  156. let response = try await self.getServerReflectionResponse(for: request, version: version)
  157. let message = try XCTUnwrap(response, "Could not get a response message.")
  158. let receivedData: [Google_Protobuf_FileDescriptorProto]
  159. do {
  160. receivedData = try message.fileDescriptorResponse.fileDescriptorProto.map {
  161. try Google_Protobuf_FileDescriptorProto(serializedBytes: $0)
  162. }
  163. } catch {
  164. return XCTFail("Could not serialize data received as a message.")
  165. }
  166. let fileToFind = self.protos[0]
  167. let dependentProtos = self.protos[1...]
  168. for fileDescriptorProto in receivedData {
  169. if fileDescriptorProto == fileToFind {
  170. XCTAssert(
  171. fileDescriptorProto.enumType.names.contains("enumType1"),
  172. """
  173. The response doesn't contain the serialized file descriptor proto \
  174. containing the \"packagebar1.enumType1\" symbol.
  175. """
  176. )
  177. } else {
  178. XCTAssert(
  179. dependentProtos.contains(fileDescriptorProto),
  180. """
  181. The \(fileDescriptorProto.name) is not a dependency of the \
  182. proto file containing the \"packagebar1.enumType1\" symbol.
  183. """
  184. )
  185. }
  186. }
  187. }
  188. }
  189. func testFileByExtension() async throws {
  190. try await self.forEachVersion { channel, version in
  191. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  192. $0.host = "127.0.0.1"
  193. $0.fileContainingExtension = .with {
  194. $0.containingType = "packagebar1.inputMessage1"
  195. $0.extensionNumber = 2
  196. }
  197. }
  198. let response = try await self.getServerReflectionResponse(for: request, version: version)
  199. let message = try XCTUnwrap(response, "Could not get a response message.")
  200. let receivedData: [Google_Protobuf_FileDescriptorProto]
  201. do {
  202. receivedData = try message.fileDescriptorResponse.fileDescriptorProto.map {
  203. try Google_Protobuf_FileDescriptorProto(serializedBytes: $0)
  204. }
  205. } catch {
  206. return XCTFail("Could not serialize data received as a message.")
  207. }
  208. let fileToFind = self.protos[0]
  209. let dependentProtos = self.protos[1...]
  210. var receivedProtoContainingExtension = 0
  211. var dependenciesCount = 0
  212. for fileDescriptorProto in receivedData {
  213. if fileDescriptorProto == fileToFind {
  214. receivedProtoContainingExtension += 1
  215. XCTAssert(
  216. fileDescriptorProto.extension.map { $0.name }.contains(
  217. "extension.packagebar1.inputMessage1-2"
  218. ),
  219. """
  220. The response doesn't contain the serialized file descriptor proto \
  221. containing the \"extensioninputMessage1-2\" extension.
  222. """
  223. )
  224. } else {
  225. dependenciesCount += 1
  226. XCTAssert(
  227. dependentProtos.contains(fileDescriptorProto),
  228. """
  229. The \(fileDescriptorProto.name) is not a dependency of the \
  230. proto file containing the \"extensioninputMessage1-2\" extension.
  231. """
  232. )
  233. }
  234. }
  235. XCTAssertEqual(
  236. receivedProtoContainingExtension,
  237. 1,
  238. "The file descriptor proto of the proto containing the extension was not received."
  239. )
  240. XCTAssertEqual(dependenciesCount, 3)
  241. }
  242. }
  243. func testAllExtensionNumbersOfType() async throws {
  244. try await self.forEachVersion { channel, version in
  245. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  246. $0.host = "127.0.0.1"
  247. $0.allExtensionNumbersOfType = "packagebar2.inputMessage2"
  248. }
  249. let response = try await self.getServerReflectionResponse(for: request, version: version)
  250. let message = try XCTUnwrap(response, "Could not get a response message.")
  251. XCTAssertEqual(message.allExtensionNumbersResponse.baseTypeName, "packagebar2.inputMessage2")
  252. XCTAssertEqual(message.allExtensionNumbersResponse.extensionNumber, [1, 2, 3, 4, 5])
  253. }
  254. }
  255. func testErrorResponseFileByFileNameRequest() async throws {
  256. try await self.forEachVersion { channel, version in
  257. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  258. $0.host = "127.0.0.1"
  259. $0.fileByFilename = "invalidFileName.proto"
  260. }
  261. let response = try await self.getServerReflectionResponse(for: request, version: version)
  262. let message = try XCTUnwrap(response, "Could not get a response message.")
  263. XCTAssertEqual(message.errorResponse.errorCode, Int32(GRPCStatus.Code.notFound.rawValue))
  264. XCTAssertEqual(
  265. message.errorResponse.errorMessage,
  266. "No reflection data for 'invalidFileName.proto'."
  267. )
  268. }
  269. }
  270. func testErrorResponseFileBySymbolRequest() async throws {
  271. try await self.forEachVersion { channel, version in
  272. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  273. $0.host = "127.0.0.1"
  274. $0.fileContainingSymbol = "packagebar1.invalidEnumType1"
  275. }
  276. let response = try await self.getServerReflectionResponse(for: request, version: version)
  277. let message = try XCTUnwrap(response, "Could not get a response message.")
  278. XCTAssertEqual(message.errorResponse.errorCode, Int32(GRPCStatus.Code.notFound.rawValue))
  279. XCTAssertEqual(message.errorResponse.errorMessage, "The provided symbol could not be found.")
  280. }
  281. }
  282. func testErrorResponseFileByExtensionRequest() async throws {
  283. try await self.forEachVersion { channel, version in
  284. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  285. $0.host = "127.0.0.1"
  286. $0.fileContainingExtension = .with {
  287. $0.containingType = "packagebar1.invalidInputMessage1"
  288. $0.extensionNumber = 2
  289. }
  290. }
  291. let response = try await self.getServerReflectionResponse(for: request, version: version)
  292. let message = try XCTUnwrap(response, "Could not get a response message.")
  293. XCTAssertEqual(message.errorResponse.errorCode, Int32(GRPCStatus.Code.notFound.rawValue))
  294. XCTAssertEqual(
  295. message.errorResponse.errorMessage,
  296. "The provided extension could not be found."
  297. )
  298. }
  299. }
  300. func testErrorResponseAllExtensionNumbersOfTypeRequest() async throws {
  301. try await self.forEachVersion { channel, version in
  302. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  303. $0.host = "127.0.0.1"
  304. $0.allExtensionNumbersOfType = "packagebar2.invalidInputMessage2"
  305. }
  306. let response = try await self.getServerReflectionResponse(for: request, version: version)
  307. let message = try XCTUnwrap(response, "Could not get a response message.")
  308. XCTAssertEqual(
  309. message.errorResponse.errorCode,
  310. Int32(GRPCStatus.Code.invalidArgument.rawValue)
  311. )
  312. XCTAssertEqual(message.errorResponse.errorMessage, "The provided type is invalid.")
  313. }
  314. }
  315. }