StructuredSwift+ServerTests.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. /*
  2. * Copyright 2024, 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 Testing
  17. @testable import GRPCCodeGen
  18. extension StructuredSwiftTests {
  19. @Suite("Server")
  20. struct Server {
  21. @Test(
  22. "func <Method>(request:context:) async throws -> ...",
  23. arguments: AccessModifier.allCases,
  24. RPCKind.allCases
  25. )
  26. func serverMethodSignature(access: AccessModifier, kind: RPCKind) {
  27. let decl: FunctionSignatureDescription = .serverMethod(
  28. accessLevel: access,
  29. name: "foo",
  30. input: "Input",
  31. output: "Output",
  32. streamingInput: kind.streamsInput,
  33. streamingOutput: kind.streamsOutput
  34. )
  35. let expected: String
  36. switch kind {
  37. case .unary:
  38. expected = """
  39. \(access) func foo(
  40. request: GRPCCore.ServerRequest<Input>,
  41. context: GRPCCore.ServerContext
  42. ) async throws -> GRPCCore.ServerResponse<Output>
  43. """
  44. case .clientStreaming:
  45. expected = """
  46. \(access) func foo(
  47. request: GRPCCore.StreamingServerRequest<Input>,
  48. context: GRPCCore.ServerContext
  49. ) async throws -> GRPCCore.ServerResponse<Output>
  50. """
  51. case .serverStreaming:
  52. expected = """
  53. \(access) func foo(
  54. request: GRPCCore.ServerRequest<Input>,
  55. context: GRPCCore.ServerContext
  56. ) async throws -> GRPCCore.StreamingServerResponse<Output>
  57. """
  58. case .bidirectionalStreaming:
  59. expected = """
  60. \(access) func foo(
  61. request: GRPCCore.StreamingServerRequest<Input>,
  62. context: GRPCCore.ServerContext
  63. ) async throws -> GRPCCore.StreamingServerResponse<Output>
  64. """
  65. }
  66. #expect(render(.function(signature: decl)) == expected)
  67. }
  68. @Test("protocol StreamingServiceProtocol { ... }", arguments: AccessModifier.allCases)
  69. func serverStreamingServiceProtocol(access: AccessModifier) {
  70. let decl: ProtocolDescription = .streamingService(
  71. accessLevel: access,
  72. name: "FooService",
  73. methods: [
  74. .init(
  75. documentation: "/// Some docs",
  76. name: .init(base: "Foo", generatedUpperCase: "Foo", generatedLowerCase: "foo"),
  77. isInputStreaming: false,
  78. isOutputStreaming: false,
  79. inputType: "FooInput",
  80. outputType: "FooOutput"
  81. )
  82. ]
  83. )
  84. let expected = """
  85. \(access) protocol FooService: GRPCCore.RegistrableRPCService {
  86. /// Handle the "Foo" method.
  87. ///
  88. /// > Source IDL Documentation:
  89. /// >
  90. /// > Some docs
  91. ///
  92. /// - Parameters:
  93. /// - request: A streaming request of `FooInput` messages.
  94. /// - context: Context providing information about the RPC.
  95. /// - Throws: Any error which occurred during the processing of the request. Thrown errors
  96. /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted
  97. /// to an internal error.
  98. /// - Returns: A streaming response of `FooOutput` messages.
  99. func foo(
  100. request: GRPCCore.StreamingServerRequest<FooInput>,
  101. context: GRPCCore.ServerContext
  102. ) async throws -> GRPCCore.StreamingServerResponse<FooOutput>
  103. }
  104. """
  105. #expect(render(.protocol(decl)) == expected)
  106. }
  107. @Test("protocol ServiceProtocol { ... }", arguments: AccessModifier.allCases)
  108. func serverServiceProtocol(access: AccessModifier) {
  109. let decl: ProtocolDescription = .service(
  110. accessLevel: access,
  111. name: "FooService",
  112. streamingProtocol: "FooService_StreamingServiceProtocol",
  113. methods: [
  114. .init(
  115. documentation: "/// Some docs",
  116. name: .init(base: "Foo", generatedUpperCase: "Foo", generatedLowerCase: "foo"),
  117. isInputStreaming: false,
  118. isOutputStreaming: false,
  119. inputType: "FooInput",
  120. outputType: "FooOutput"
  121. )
  122. ]
  123. )
  124. let expected = """
  125. \(access) protocol FooService: FooService_StreamingServiceProtocol {
  126. /// Handle the "Foo" method.
  127. ///
  128. /// > Source IDL Documentation:
  129. /// >
  130. /// > Some docs
  131. ///
  132. /// - Parameters:
  133. /// - request: A request containing a single `FooInput` message.
  134. /// - context: Context providing information about the RPC.
  135. /// - Throws: Any error which occurred during the processing of the request. Thrown errors
  136. /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted
  137. /// to an internal error.
  138. /// - Returns: A response containing a single `FooOutput` message.
  139. func foo(
  140. request: GRPCCore.ServerRequest<FooInput>,
  141. context: GRPCCore.ServerContext
  142. ) async throws -> GRPCCore.ServerResponse<FooOutput>
  143. }
  144. """
  145. #expect(render(.protocol(decl)) == expected)
  146. }
  147. @Test("{ router, context in try await self.<Method>(...) }")
  148. func routerHandlerInvokingRPC() {
  149. let expression: ClosureInvocationDescription = .routerHandlerInvokingRPC(method: "foo")
  150. let expected = """
  151. { request, context in
  152. try await self.foo(
  153. request: request,
  154. context: context
  155. )
  156. }
  157. """
  158. #expect(render(.closureInvocation(expression)) == expected)
  159. }
  160. @Test("router.registerHandler(...) { ... }")
  161. func registerMethodsWithRouter() {
  162. let expression: FunctionCallDescription = .registerWithRouter(
  163. serviceNamespace: "FooService",
  164. methodNamespace: "Bar",
  165. methodName: "bar",
  166. inputDeserializer: "Deserialize<BarInput>()",
  167. outputSerializer: "Serialize<BarOutput>()"
  168. )
  169. let expected = """
  170. router.registerHandler(
  171. forMethod: FooService.Method.Bar.descriptor,
  172. deserializer: Deserialize<BarInput>(),
  173. serializer: Serialize<BarOutput>(),
  174. handler: { request, context in
  175. try await self.bar(
  176. request: request,
  177. context: context
  178. )
  179. }
  180. )
  181. """
  182. #expect(render(.functionCall(expression)) == expected)
  183. }
  184. @Test("func registerMethods(router:)", arguments: AccessModifier.allCases)
  185. func registerMethods(access: AccessModifier) {
  186. let expression: FunctionDescription = .registerMethods(
  187. accessLevel: access,
  188. serviceNamespace: "FooService",
  189. methods: [
  190. .init(
  191. documentation: "",
  192. name: .init(base: "Bar", generatedUpperCase: "Bar", generatedLowerCase: "bar"),
  193. isInputStreaming: false,
  194. isOutputStreaming: false,
  195. inputType: "BarInput",
  196. outputType: "BarOutput"
  197. )
  198. ]
  199. ) { type in
  200. "Serialize<\(type)>()"
  201. } deserializer: { type in
  202. "Deserialize<\(type)>()"
  203. }
  204. let expected = """
  205. \(access) func registerMethods<Transport>(with router: inout GRPCCore.RPCRouter<Transport>) where Transport: GRPCCore.ServerTransport {
  206. router.registerHandler(
  207. forMethod: FooService.Method.Bar.descriptor,
  208. deserializer: Deserialize<BarInput>(),
  209. serializer: Serialize<BarOutput>(),
  210. handler: { request, context in
  211. try await self.bar(
  212. request: request,
  213. context: context
  214. )
  215. }
  216. )
  217. }
  218. """
  219. #expect(render(.function(expression)) == expected)
  220. }
  221. @Test(
  222. "func <Method>(request:context:) async throw { ... (convert to/from single) ... }",
  223. arguments: AccessModifier.allCases,
  224. RPCKind.allCases
  225. )
  226. func serverStreamingMethodsCallingMethod(access: AccessModifier, kind: RPCKind) {
  227. let expression: FunctionDescription = .serverStreamingMethodsCallingMethod(
  228. accessLevel: access,
  229. name: "foo",
  230. input: "Input",
  231. output: "Output",
  232. streamingInput: kind.streamsInput,
  233. streamingOutput: kind.streamsOutput
  234. )
  235. let expected: String
  236. switch kind {
  237. case .unary:
  238. expected = """
  239. \(access) func foo(
  240. request: GRPCCore.StreamingServerRequest<Input>,
  241. context: GRPCCore.ServerContext
  242. ) async throws -> GRPCCore.StreamingServerResponse<Output> {
  243. let response = try await self.foo(
  244. request: GRPCCore.ServerRequest(stream: request),
  245. context: context
  246. )
  247. return GRPCCore.StreamingServerResponse(single: response)
  248. }
  249. """
  250. case .serverStreaming:
  251. expected = """
  252. \(access) func foo(
  253. request: GRPCCore.StreamingServerRequest<Input>,
  254. context: GRPCCore.ServerContext
  255. ) async throws -> GRPCCore.StreamingServerResponse<Output> {
  256. let response = try await self.foo(
  257. request: GRPCCore.ServerRequest(stream: request),
  258. context: context
  259. )
  260. return response
  261. }
  262. """
  263. case .clientStreaming:
  264. expected = """
  265. \(access) func foo(
  266. request: GRPCCore.StreamingServerRequest<Input>,
  267. context: GRPCCore.ServerContext
  268. ) async throws -> GRPCCore.StreamingServerResponse<Output> {
  269. let response = try await self.foo(
  270. request: request,
  271. context: context
  272. )
  273. return GRPCCore.StreamingServerResponse(single: response)
  274. }
  275. """
  276. case .bidirectionalStreaming:
  277. expected = """
  278. \(access) func foo(
  279. request: GRPCCore.StreamingServerRequest<Input>,
  280. context: GRPCCore.ServerContext
  281. ) async throws -> GRPCCore.StreamingServerResponse<Output> {
  282. let response = try await self.foo(
  283. request: request,
  284. context: context
  285. )
  286. return response
  287. }
  288. """
  289. }
  290. #expect(render(.function(expression)) == expected)
  291. }
  292. @Test("extension FooService_ServiceProtocol { ... }", arguments: AccessModifier.allCases)
  293. func streamingServiceProtocolDefaultImplementation(access: AccessModifier) {
  294. let decl: ExtensionDescription = .streamingServiceProtocolDefaultImplementation(
  295. accessModifier: access,
  296. on: "Foo_ServiceProtocol",
  297. methods: [
  298. .init(
  299. documentation: "",
  300. name: .init(base: "Foo", generatedUpperCase: "Foo", generatedLowerCase: "foo"),
  301. isInputStreaming: false,
  302. isOutputStreaming: false,
  303. inputType: "FooInput",
  304. outputType: "FooOutput"
  305. ),
  306. // Will be ignored as a bidirectional streaming method.
  307. .init(
  308. documentation: "",
  309. name: .init(base: "Bar", generatedUpperCase: "Bar", generatedLowerCase: "bar"),
  310. isInputStreaming: true,
  311. isOutputStreaming: true,
  312. inputType: "BarInput",
  313. outputType: "BarOutput"
  314. ),
  315. ]
  316. )
  317. let expected = """
  318. extension Foo_ServiceProtocol {
  319. \(access) func foo(
  320. request: GRPCCore.StreamingServerRequest<FooInput>,
  321. context: GRPCCore.ServerContext
  322. ) async throws -> GRPCCore.StreamingServerResponse<FooOutput> {
  323. let response = try await self.foo(
  324. request: GRPCCore.ServerRequest(stream: request),
  325. context: context
  326. )
  327. return GRPCCore.StreamingServerResponse(single: response)
  328. }
  329. }
  330. """
  331. #expect(render(.extension(decl)) == expected)
  332. }
  333. @Test(
  334. "func <Method>(request:response:context:) (simple)",
  335. arguments: AccessModifier.allCases,
  336. RPCKind.allCases
  337. )
  338. func simpleServerMethod(access: AccessModifier, kind: RPCKind) {
  339. let decl: FunctionSignatureDescription = .simpleServerMethod(
  340. accessLevel: access,
  341. name: "foo",
  342. input: "FooInput",
  343. output: "FooOutput",
  344. streamingInput: kind.streamsInput,
  345. streamingOutput: kind.streamsOutput
  346. )
  347. let expected: String
  348. switch kind {
  349. case .unary:
  350. expected = """
  351. \(access) func foo(
  352. request: FooInput,
  353. context: GRPCCore.ServerContext
  354. ) async throws -> FooOutput
  355. """
  356. case .clientStreaming:
  357. expected = """
  358. \(access) func foo(
  359. request: GRPCCore.RPCAsyncSequence<FooInput, any Swift.Error>,
  360. context: GRPCCore.ServerContext
  361. ) async throws -> FooOutput
  362. """
  363. case .serverStreaming:
  364. expected = """
  365. \(access) func foo(
  366. request: FooInput,
  367. response: GRPCCore.RPCWriter<FooOutput>,
  368. context: GRPCCore.ServerContext
  369. ) async throws
  370. """
  371. case .bidirectionalStreaming:
  372. expected = """
  373. \(access) func foo(
  374. request: GRPCCore.RPCAsyncSequence<FooInput, any Swift.Error>,
  375. response: GRPCCore.RPCWriter<FooOutput>,
  376. context: GRPCCore.ServerContext
  377. ) async throws
  378. """
  379. }
  380. #expect(render(.function(signature: decl)) == expected)
  381. }
  382. @Test("protocol SimpleServiceProtocol { ... }", arguments: AccessModifier.allCases)
  383. func simpleServiceProtocol(access: AccessModifier) {
  384. let decl: ProtocolDescription = .simpleServiceProtocol(
  385. accessModifier: access,
  386. name: "SimpleServiceProtocol",
  387. serviceProtocol: "ServiceProtocol",
  388. methods: [
  389. .init(
  390. documentation: "",
  391. name: .init(base: "Foo", generatedUpperCase: "Foo", generatedLowerCase: "foo"),
  392. isInputStreaming: false,
  393. isOutputStreaming: false,
  394. inputType: "Input",
  395. outputType: "Output"
  396. )
  397. ]
  398. )
  399. let expected = """
  400. \(access) protocol SimpleServiceProtocol: ServiceProtocol {
  401. /// Handle the "Foo" method.
  402. ///
  403. /// - Parameters:
  404. /// - request: A `Input` message.
  405. /// - context: Context providing information about the RPC.
  406. /// - Throws: Any error which occurred during the processing of the request. Thrown errors
  407. /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted
  408. /// to an internal error.
  409. /// - Returns: A `Output` to respond with.
  410. func foo(
  411. request: Input,
  412. context: GRPCCore.ServerContext
  413. ) async throws -> Output
  414. }
  415. """
  416. #expect(render(.protocol(decl)) == expected)
  417. }
  418. }
  419. }