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. final class ReflectionServiceIntegrationTests: GRPCTestCase {
  24. private var server: Server?
  25. private var channel: GRPCChannel?
  26. private let protos: [Google_Protobuf_FileDescriptorProto] = makeProtosWithDependencies()
  27. private let independentProto: Google_Protobuf_FileDescriptorProto = generateFileDescriptorProto(
  28. fileName: "independentBar",
  29. suffix: "5"
  30. )
  31. private let versions: [ReflectionService.Version] = [.v1, .v1Alpha]
  32. private func setUpServerAndChannel(version: ReflectionService.Version) throws {
  33. let reflectionServiceProvider = try ReflectionService(
  34. fileDescriptorProtos: self.protos + [self.independentProto],
  35. version: version
  36. )
  37. let server = try Server.insecure(group: MultiThreadedEventLoopGroup.singleton)
  38. .withServiceProviders([reflectionServiceProvider])
  39. .withLogger(self.serverLogger)
  40. .bind(host: "127.0.0.1", port: 0)
  41. .wait()
  42. self.server = server
  43. let channel = try GRPCChannelPool.with(
  44. target: .hostAndPort("127.0.0.1", server.channel.localAddress!.port!),
  45. transportSecurity: .plaintext,
  46. eventLoopGroup: MultiThreadedEventLoopGroup.singleton
  47. ) {
  48. $0.backgroundActivityLogger = self.clientLogger
  49. }
  50. self.channel = channel
  51. }
  52. override func tearDown() {
  53. if let channel = self.channel {
  54. XCTAssertNoThrow(try channel.close().wait())
  55. }
  56. if let server = self.server {
  57. XCTAssertNoThrow(try server.close().wait())
  58. }
  59. super.tearDown()
  60. }
  61. private func getServerReflectionResponse(
  62. for request: Grpc_Reflection_V1_ServerReflectionRequest,
  63. version: ReflectionService.Version
  64. ) async throws -> Grpc_Reflection_V1_ServerReflectionResponse? {
  65. let response: Grpc_Reflection_V1_ServerReflectionResponse?
  66. switch version {
  67. case .v1:
  68. let client = Grpc_Reflection_V1_ServerReflectionAsyncClient(channel: self.channel!)
  69. let serviceReflectionInfo = client.makeServerReflectionInfoCall()
  70. try await serviceReflectionInfo.requestStream.send(request)
  71. serviceReflectionInfo.requestStream.finish()
  72. var iterator = serviceReflectionInfo.responseStream.makeAsyncIterator()
  73. response = try await iterator.next()
  74. case .v1Alpha:
  75. let client = Grpc_Reflection_V1alpha_ServerReflectionAsyncClient(channel: self.channel!)
  76. let serviceReflectionInfo = client.makeServerReflectionInfoCall()
  77. try await serviceReflectionInfo.requestStream.send(
  78. Grpc_Reflection_V1alpha_ServerReflectionRequest(request)
  79. )
  80. serviceReflectionInfo.requestStream.finish()
  81. var iterator = serviceReflectionInfo.responseStream.makeAsyncIterator()
  82. response = try await iterator.next().map {
  83. Grpc_Reflection_V1_ServerReflectionResponse($0)
  84. }
  85. default:
  86. return nil
  87. }
  88. return response
  89. }
  90. private func forEachVersion(
  91. _ body: (GRPCChannel?, ReflectionService.Version) async throws -> Void
  92. ) async throws {
  93. for version in self.versions {
  94. try setUpServerAndChannel(version: version)
  95. let result: Result<Void, Error>
  96. do {
  97. try await body(self.channel, version)
  98. result = .success(())
  99. } catch {
  100. result = .failure(error)
  101. }
  102. try result.get()
  103. try await self.tearDown()
  104. }
  105. }
  106. func testFileByFileName() async throws {
  107. try await self.forEachVersion { channel, version in
  108. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  109. $0.host = "127.0.0.1"
  110. $0.fileByFilename = "bar1.proto"
  111. }
  112. let response = try await self.getServerReflectionResponse(for: request, version: version)
  113. let message = try XCTUnwrap(response, "Could not get a response message.")
  114. // response can't be nil as we just checked it.
  115. let receivedFileDescriptorProto =
  116. try Google_Protobuf_FileDescriptorProto(
  117. serializedData: (message.fileDescriptorResponse
  118. .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(serializedData: $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(serializedData: $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. "The provided file or a dependency of the provided file could not be found."
  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. }