2
0

ReflectionServiceIntegrationTests.swift 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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. serializedData: (message.fileDescriptorResponse
  119. .fileDescriptorProto[0])
  120. )
  121. XCTAssertEqual(receivedFileDescriptorProto.name, "bar1.proto")
  122. XCTAssertEqual(receivedFileDescriptorProto.service.count, 1)
  123. let service = try XCTUnwrap(
  124. receivedFileDescriptorProto.service.first,
  125. "The received file descriptor proto doesn't have any services."
  126. )
  127. let method = try XCTUnwrap(
  128. service.method.first,
  129. "The service of the received file descriptor proto doesn't have any methods."
  130. )
  131. XCTAssertEqual(method.name, "testMethod1")
  132. XCTAssertEqual(message.fileDescriptorResponse.fileDescriptorProto.count, 4)
  133. }
  134. }
  135. func testListServices() async throws {
  136. try await self.forEachVersion { channel, version in
  137. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  138. $0.host = "127.0.0.1"
  139. $0.listServices = "services"
  140. }
  141. let response = try await self.getServerReflectionResponse(for: request, version: version)
  142. let message = try XCTUnwrap(response, "Could not get a response message.")
  143. let receivedServices = message.listServicesResponse.service.map { $0.name }.sorted()
  144. let servicesNames = (self.protos + [self.independentProto]).flatMap {
  145. $0.qualifiedServiceNames
  146. }
  147. .sorted()
  148. XCTAssertEqual(receivedServices, servicesNames)
  149. }
  150. }
  151. func testFileBySymbol() async throws {
  152. try await self.forEachVersion { channel, version in
  153. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  154. $0.host = "127.0.0.1"
  155. $0.fileContainingSymbol = "packagebar1.enumType1"
  156. }
  157. let response = try await self.getServerReflectionResponse(for: request, version: version)
  158. let message = try XCTUnwrap(response, "Could not get a response message.")
  159. let receivedData: [Google_Protobuf_FileDescriptorProto]
  160. do {
  161. receivedData = try message.fileDescriptorResponse.fileDescriptorProto.map {
  162. try Google_Protobuf_FileDescriptorProto(serializedData: $0)
  163. }
  164. } catch {
  165. return XCTFail("Could not serialize data received as a message.")
  166. }
  167. let fileToFind = self.protos[0]
  168. let dependentProtos = self.protos[1...]
  169. for fileDescriptorProto in receivedData {
  170. if fileDescriptorProto == fileToFind {
  171. XCTAssert(
  172. fileDescriptorProto.enumType.names.contains("enumType1"),
  173. """
  174. The response doesn't contain the serialized file descriptor proto \
  175. containing the \"packagebar1.enumType1\" symbol.
  176. """
  177. )
  178. } else {
  179. XCTAssert(
  180. dependentProtos.contains(fileDescriptorProto),
  181. """
  182. The \(fileDescriptorProto.name) is not a dependency of the \
  183. proto file containing the \"packagebar1.enumType1\" symbol.
  184. """
  185. )
  186. }
  187. }
  188. }
  189. }
  190. func testFileByExtension() async throws {
  191. try await self.forEachVersion { channel, version in
  192. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  193. $0.host = "127.0.0.1"
  194. $0.fileContainingExtension = .with {
  195. $0.containingType = "packagebar1.inputMessage1"
  196. $0.extensionNumber = 2
  197. }
  198. }
  199. let response = try await self.getServerReflectionResponse(for: request, version: version)
  200. let message = try XCTUnwrap(response, "Could not get a response message.")
  201. let receivedData: [Google_Protobuf_FileDescriptorProto]
  202. do {
  203. receivedData = try message.fileDescriptorResponse.fileDescriptorProto.map {
  204. try Google_Protobuf_FileDescriptorProto(serializedData: $0)
  205. }
  206. } catch {
  207. return XCTFail("Could not serialize data received as a message.")
  208. }
  209. let fileToFind = self.protos[0]
  210. let dependentProtos = self.protos[1...]
  211. var receivedProtoContainingExtension = 0
  212. var dependenciesCount = 0
  213. for fileDescriptorProto in receivedData {
  214. if fileDescriptorProto == fileToFind {
  215. receivedProtoContainingExtension += 1
  216. XCTAssert(
  217. fileDescriptorProto.extension.map { $0.name }.contains(
  218. "extension.packagebar1.inputMessage1-2"
  219. ),
  220. """
  221. The response doesn't contain the serialized file descriptor proto \
  222. containing the \"extensioninputMessage1-2\" extension.
  223. """
  224. )
  225. } else {
  226. dependenciesCount += 1
  227. XCTAssert(
  228. dependentProtos.contains(fileDescriptorProto),
  229. """
  230. The \(fileDescriptorProto.name) is not a dependency of the \
  231. proto file containing the \"extensioninputMessage1-2\" extension.
  232. """
  233. )
  234. }
  235. }
  236. XCTAssertEqual(
  237. receivedProtoContainingExtension,
  238. 1,
  239. "The file descriptor proto of the proto containing the extension was not received."
  240. )
  241. XCTAssertEqual(dependenciesCount, 3)
  242. }
  243. }
  244. func testAllExtensionNumbersOfType() async throws {
  245. try await self.forEachVersion { channel, version in
  246. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  247. $0.host = "127.0.0.1"
  248. $0.allExtensionNumbersOfType = "packagebar2.inputMessage2"
  249. }
  250. let response = try await self.getServerReflectionResponse(for: request, version: version)
  251. let message = try XCTUnwrap(response, "Could not get a response message.")
  252. XCTAssertEqual(message.allExtensionNumbersResponse.baseTypeName, "packagebar2.inputMessage2")
  253. XCTAssertEqual(message.allExtensionNumbersResponse.extensionNumber, [1, 2, 3, 4, 5])
  254. }
  255. }
  256. func testErrorResponseFileByFileNameRequest() async throws {
  257. try await self.forEachVersion { channel, version in
  258. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  259. $0.host = "127.0.0.1"
  260. $0.fileByFilename = "invalidFileName.proto"
  261. }
  262. let response = try await self.getServerReflectionResponse(for: request, version: version)
  263. let message = try XCTUnwrap(response, "Could not get a response message.")
  264. XCTAssertEqual(message.errorResponse.errorCode, Int32(GRPCStatus.Code.notFound.rawValue))
  265. XCTAssertEqual(
  266. message.errorResponse.errorMessage,
  267. "The provided file or a dependency of the provided file could not be found."
  268. )
  269. }
  270. }
  271. func testErrorResponseFileBySymbolRequest() async throws {
  272. try await self.forEachVersion { channel, version in
  273. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  274. $0.host = "127.0.0.1"
  275. $0.fileContainingSymbol = "packagebar1.invalidEnumType1"
  276. }
  277. let response = try await self.getServerReflectionResponse(for: request, version: version)
  278. let message = try XCTUnwrap(response, "Could not get a response message.")
  279. XCTAssertEqual(message.errorResponse.errorCode, Int32(GRPCStatus.Code.notFound.rawValue))
  280. XCTAssertEqual(message.errorResponse.errorMessage, "The provided symbol could not be found.")
  281. }
  282. }
  283. func testErrorResponseFileByExtensionRequest() async throws {
  284. try await self.forEachVersion { channel, version in
  285. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  286. $0.host = "127.0.0.1"
  287. $0.fileContainingExtension = .with {
  288. $0.containingType = "packagebar1.invalidInputMessage1"
  289. $0.extensionNumber = 2
  290. }
  291. }
  292. let response = try await self.getServerReflectionResponse(for: request, version: version)
  293. let message = try XCTUnwrap(response, "Could not get a response message.")
  294. XCTAssertEqual(message.errorResponse.errorCode, Int32(GRPCStatus.Code.notFound.rawValue))
  295. XCTAssertEqual(
  296. message.errorResponse.errorMessage,
  297. "The provided extension could not be found."
  298. )
  299. }
  300. }
  301. func testErrorResponseAllExtensionNumbersOfTypeRequest() async throws {
  302. try await self.forEachVersion { channel, version in
  303. let request = Grpc_Reflection_V1_ServerReflectionRequest.with {
  304. $0.host = "127.0.0.1"
  305. $0.allExtensionNumbersOfType = "packagebar2.invalidInputMessage2"
  306. }
  307. let response = try await self.getServerReflectionResponse(for: request, version: version)
  308. let message = try XCTUnwrap(response, "Could not get a response message.")
  309. XCTAssertEqual(
  310. message.errorResponse.errorCode,
  311. Int32(GRPCStatus.Code.invalidArgument.rawValue)
  312. )
  313. XCTAssertEqual(message.errorResponse.errorMessage, "The provided type is invalid.")
  314. }
  315. }
  316. }