InteroperabilityTestCases.swift 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. /*
  2. * Copyright 2019, 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 GRPCInteroperabilityTestModels
  19. import NIOHTTP1
  20. /// This test verifies that implementations support zero-size messages. Ideally, client
  21. /// implementations would verify that the request and response were zero bytes serialized, but
  22. /// this is generally prohibitive to perform, so is not required.
  23. ///
  24. /// Server features:
  25. /// - EmptyCall
  26. ///
  27. /// Procedure:
  28. /// 1. Client calls EmptyCall with the default Empty message
  29. ///
  30. /// Client asserts:
  31. /// - call was successful
  32. /// - response is non-null
  33. class EmptyUnary: InteroperabilityTest {
  34. func run(using connection: ClientConnection) throws {
  35. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  36. let call = client.emptyCall(Grpc_Testing_Empty())
  37. try waitAndAssertEqual(call.response, Grpc_Testing_Empty())
  38. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  39. }
  40. }
  41. /// This test verifies that gRPC requests marked as cacheable use GET verb instead of POST, and
  42. /// that server sets appropriate cache control headers for the response to be cached by a proxy.
  43. /// This test requires that the server is behind a caching proxy. Use of current timestamp in the
  44. /// request prevents accidental cache matches left over from previous tests.
  45. ///
  46. /// Server features:
  47. /// - CacheableUnaryCall
  48. ///
  49. /// Procedure:
  50. /// 1. Client calls CacheableUnaryCall with SimpleRequest request with payload set to current
  51. /// timestamp. Timestamp format is irrelevant, and resolution is in nanoseconds. Client adds a
  52. /// x-user-ip header with value 1.2.3.4 to the request. This is done since some proxys such as
  53. /// GFE will not cache requests from localhost. Client marks the request as cacheable by
  54. /// setting the cacheable flag in the request context. Longer term this should be driven by
  55. /// the method option specified in the proto file itself.
  56. /// 2. Client calls CacheableUnaryCall again immediately with the same request and configuration
  57. /// as the previous call.
  58. ///
  59. /// Client asserts:
  60. /// - Both calls were successful
  61. /// - The payload body of both responses is the same.
  62. class CacheableUnary: InteroperabilityTest {
  63. func run(using connection: ClientConnection) throws {
  64. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  65. var timestamp = DispatchTime.now().rawValue
  66. let request = Grpc_Testing_SimpleRequest.withPayload(of: .bytes(of: &timestamp))
  67. var headers = HTTPHeaders()
  68. headers.add(name: "x-user-ip", value: "1.2.3.4")
  69. let callOptions = CallOptions(customMetadata: headers, cacheable: true)
  70. let call1 = client.cacheableUnaryCall(request, callOptions: callOptions)
  71. let call2 = client.cacheableUnaryCall(request, callOptions: callOptions)
  72. // The server ignores the request payload so we must not validate against it.
  73. try waitAndAssertEqual(call1.response.map { $0.payload }, call2.response.map { $0.payload })
  74. try waitAndAssertEqual(call1.status.map { $0.code }, .ok)
  75. try waitAndAssertEqual(call2.status.map { $0.code }, .ok)
  76. }
  77. }
  78. /// This test verifies unary calls succeed in sending messages, and touches on flow control (even
  79. /// if compression is enabled on the channel).
  80. ///
  81. /// Server features:
  82. /// - UnaryCall
  83. ///
  84. /// Procedure:
  85. /// 1. Client calls UnaryCall with:
  86. /// ```
  87. /// {
  88. /// response_size: 314159
  89. /// payload:{
  90. /// body: 271828 bytes of zeros
  91. /// }
  92. /// }
  93. /// ```
  94. ///
  95. /// Client asserts:
  96. /// - call was successful
  97. /// - response payload body is 314159 bytes in size
  98. /// - clients are free to assert that the response payload body contents are zero and comparing
  99. /// the entire response message against a golden response
  100. class LargeUnary: InteroperabilityTest {
  101. func run(using connection: ClientConnection) throws {
  102. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  103. let request = Grpc_Testing_SimpleRequest.with { request in
  104. request.responseSize = 314_159
  105. request.payload = .zeros(count: 271_828)
  106. }
  107. let call = client.unaryCall(request)
  108. try waitAndAssertEqual(call.response.map { $0.payload }, .zeros(count: 314_159))
  109. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  110. }
  111. }
  112. /// This test verifies that client-only streaming succeeds.
  113. ///
  114. /// Server features:
  115. /// - StreamingInputCall
  116. ///
  117. /// Procedure:
  118. /// 1. Client calls StreamingInputCall
  119. /// 2. Client sends:
  120. /// ```
  121. /// {
  122. /// payload:{
  123. /// body: 27182 bytes of zeros
  124. /// }
  125. /// }
  126. /// ```
  127. /// 3. Client then sends:
  128. /// ```
  129. /// {
  130. /// payload:{
  131. /// body: 8 bytes of zeros
  132. /// }
  133. /// }
  134. /// ```
  135. /// 4. Client then sends:
  136. /// ```
  137. /// {
  138. /// payload:{
  139. /// body: 1828 bytes of zeros
  140. /// }
  141. /// }
  142. /// ```
  143. /// 5. Client then sends:
  144. /// ```
  145. /// {
  146. /// payload:{
  147. /// body: 45904 bytes of zeros
  148. /// }
  149. /// }
  150. /// ```
  151. /// 6. Client half-closes
  152. ///
  153. /// Client asserts:
  154. /// - call was successful
  155. /// - response aggregated_payload_size is 74922
  156. class ClientStreaming: InteroperabilityTest {
  157. func run(using connection: ClientConnection) throws {
  158. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  159. let call = client.streamingInputCall()
  160. let messagesSent = call.newMessageQueue().flatMap {
  161. call.sendMessage(.withPayload(of: .zeros(count: 27_182)))
  162. }.flatMap {
  163. call.sendMessage(.withPayload(of: .zeros(count: 8)))
  164. }.flatMap {
  165. call.sendMessage(.withPayload(of: .zeros(count: 1_828)))
  166. }.flatMap {
  167. call.sendMessage(.withPayload(of: .zeros(count: 45_904)))
  168. }.flatMap {
  169. call.sendEnd()
  170. }
  171. try messagesSent.wait()
  172. try waitAndAssertEqual(call.response.map { $0.aggregatedPayloadSize }, 74_922)
  173. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  174. }
  175. }
  176. /// This test verifies that server-only streaming succeeds.
  177. ///
  178. /// Server features:
  179. /// - StreamingOutputCall
  180. ///
  181. /// Procedure:
  182. /// 1. Client calls StreamingOutputCall with StreamingOutputCallRequest:
  183. /// ```
  184. /// {
  185. /// response_parameters:{
  186. /// size: 31415
  187. /// }
  188. /// response_parameters:{
  189. /// size: 9
  190. /// }
  191. /// response_parameters:{
  192. /// size: 2653
  193. /// }
  194. /// response_parameters:{
  195. /// size: 58979
  196. /// }
  197. /// }
  198. /// ```
  199. ///
  200. /// Client asserts:
  201. /// - call was successful
  202. /// - exactly four responses
  203. /// - response payload bodies are sized (in order): 31415, 9, 2653, 58979
  204. /// - clients are free to assert that the response payload body contents are zero and
  205. /// comparing the entire response messages against golden responses
  206. class ServerStreaming: InteroperabilityTest {
  207. func run(using connection: ClientConnection) throws {
  208. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  209. let responseSizes = [31_415, 9, 2_653, 58_979]
  210. let request = Grpc_Testing_StreamingOutputCallRequest.with { request in
  211. request.responseParameters = responseSizes.map { .size($0) }
  212. }
  213. var payloads: [Grpc_Testing_Payload] = []
  214. let call = client.streamingOutputCall(request) { response in
  215. payloads.append(response.payload)
  216. }
  217. // Wait for the status first to ensure we've finished collecting responses.
  218. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  219. try assertEqual(payloads, responseSizes.map { .zeros(count: $0) })
  220. }
  221. }
  222. /// This test verifies that full duplex bidi is supported.
  223. ///
  224. /// Server features:
  225. /// - FullDuplexCall
  226. ///
  227. /// Procedure:
  228. /// 1. Client calls FullDuplexCall with:
  229. /// ```
  230. /// {
  231. /// response_parameters:{
  232. /// size: 31415
  233. /// }
  234. /// payload:{
  235. /// body: 27182 bytes of zeros
  236. /// }
  237. /// }
  238. /// ```
  239. /// 2. After getting a reply, it sends:
  240. /// ```
  241. /// {
  242. /// response_parameters:{
  243. /// size: 9
  244. /// }
  245. /// payload:{
  246. /// body: 8 bytes of zeros
  247. /// }
  248. /// }
  249. /// ```
  250. /// 3. After getting a reply, it sends:
  251. /// ```
  252. /// {
  253. /// response_parameters:{
  254. /// size: 2653
  255. /// }
  256. /// payload:{
  257. /// body: 1828 bytes of zeros
  258. /// }
  259. /// }
  260. /// ```
  261. /// 4. After getting a reply, it sends:
  262. /// ```
  263. /// {
  264. /// response_parameters:{
  265. /// size: 58979
  266. /// }
  267. /// payload:{
  268. /// body: 45904 bytes of zeros
  269. /// }
  270. /// }
  271. /// ```
  272. /// 5. After getting a reply, client half-closes
  273. ///
  274. /// Client asserts:
  275. /// - call was successful
  276. /// - exactly four responses
  277. /// - response payload bodies are sized (in order): 31415, 9, 2653, 58979
  278. /// - clients are free to assert that the response payload body contents are zero and
  279. /// comparing the entire response messages against golden responses
  280. class PingPong: InteroperabilityTest {
  281. func run(using connection: ClientConnection) throws {
  282. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  283. let requestSizes = [27_182, 8, 1_828, 45_904]
  284. let responseSizes = [31_415, 9, 2_653, 58_979]
  285. let responseReceived = DispatchSemaphore(value: 0)
  286. var payloads: [Grpc_Testing_Payload] = []
  287. let call = client.fullDuplexCall { response in
  288. payloads.append(response.payload)
  289. responseReceived.signal()
  290. }
  291. try zip(requestSizes, responseSizes).map { requestSize, responseSize in
  292. Grpc_Testing_StreamingOutputCallRequest.with { request in
  293. request.payload = .zeros(count: requestSize)
  294. request.responseParameters = [.size(responseSize)]
  295. }
  296. }.forEach { request in
  297. call.sendMessage(request, promise: nil)
  298. try assertEqual(responseReceived.wait(timeout: .now() + .seconds(1)), .success)
  299. }
  300. call.sendEnd(promise: nil)
  301. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  302. try assertEqual(payloads, responseSizes.map { .zeros(count: $0) })
  303. }
  304. }
  305. /// This test verifies that streams support having zero-messages in both directions.
  306. ///
  307. /// Server features:
  308. /// - FullDuplexCall
  309. ///
  310. /// Procedure:
  311. /// 1. Client calls FullDuplexCall and then half-closes
  312. ///
  313. /// Client asserts:
  314. /// - call was successful
  315. /// - exactly zero responses
  316. class EmptyStream: InteroperabilityTest {
  317. func run(using connection: ClientConnection) throws {
  318. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  319. var responses: [Grpc_Testing_StreamingOutputCallResponse] = []
  320. let call = client.fullDuplexCall { response in
  321. responses.append(response)
  322. }
  323. try call.sendEnd().wait()
  324. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  325. try assertEqual(responses, [])
  326. }
  327. }
  328. /// This test verifies that custom metadata in either binary or ascii format can be sent as
  329. /// initial-metadata by the client and as both initial- and trailing-metadata by the server.
  330. ///
  331. /// Server features:
  332. /// - UnaryCall
  333. /// - FullDuplexCall
  334. /// - Echo Metadata
  335. ///
  336. /// Procedure:
  337. /// 1. The client attaches custom metadata with the following keys and values
  338. /// to a UnaryCall with request:
  339. /// - key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value"
  340. /// - key: "x-grpc-test-echo-trailing-bin", value: 0xababab
  341. /// ```
  342. /// {
  343. /// response_size: 314159
  344. /// payload:{
  345. /// body: 271828 bytes of zeros
  346. /// }
  347. /// }
  348. /// ```
  349. /// 2. The client attaches custom metadata with the following keys and values
  350. /// to a FullDuplexCall with request:
  351. /// - key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value"
  352. /// - key: "x-grpc-test-echo-trailing-bin", value: 0xababab
  353. /// ```
  354. /// {
  355. /// response_parameters:{
  356. /// size: 314159
  357. /// }
  358. /// payload:{
  359. /// body: 271828 bytes of zeros
  360. /// }
  361. /// }
  362. /// ```
  363. /// and then half-closes
  364. ///
  365. /// Client asserts:
  366. /// - call was successful
  367. /// - metadata with key "x-grpc-test-echo-initial" and value "test_initial_metadata_value" is
  368. /// received in the initial metadata for calls in Procedure steps 1 and 2.
  369. /// - metadata with key "x-grpc-test-echo-trailing-bin" and value 0xababab is received in the
  370. /// trailing metadata for calls in Procedure steps 1 and 2.
  371. class CustomMetadata: InteroperabilityTest {
  372. let initialMetadataName = "x-grpc-test-echo-initial"
  373. let initialMetadataValue = "test_initial_metadata_value"
  374. let trailingMetadataName = "x-grpc-test-echo-trailing-bin"
  375. let trailingMetadataValue = Data([0xab, 0xab, 0xab]).base64EncodedString()
  376. func checkMetadata<SpecificClientCall>(call: SpecificClientCall) throws where SpecificClientCall: ClientCall {
  377. let initialName = call.initialMetadata.map { $0[self.initialMetadataName] }
  378. try waitAndAssertEqual(initialName, [self.initialMetadataValue])
  379. let trailingName = call.trailingMetadata.map { $0[self.trailingMetadataName] }
  380. try waitAndAssertEqual(trailingName, [self.trailingMetadataValue])
  381. try waitAndAssertEqual(call.status.map { $0.code }, .ok)
  382. }
  383. func run(using connection: ClientConnection) throws {
  384. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  385. let unaryRequest = Grpc_Testing_SimpleRequest.with { request in
  386. request.responseSize = 314_159
  387. request.payload = .zeros(count: 217_828)
  388. }
  389. var customMetadata = HTTPHeaders()
  390. customMetadata.add(name: self.initialMetadataName, value: self.initialMetadataValue)
  391. customMetadata.add(name: self.trailingMetadataName, value: self.trailingMetadataValue)
  392. let callOptions = CallOptions(customMetadata: customMetadata)
  393. let unaryCall = client.unaryCall(unaryRequest, callOptions: callOptions)
  394. try self.checkMetadata(call: unaryCall)
  395. let duplexCall = client.fullDuplexCall(callOptions: callOptions) { _ in }
  396. let duplexRequest = Grpc_Testing_StreamingOutputCallRequest.with { request in
  397. request.responseParameters = [.size(314_159)]
  398. request.payload = .zeros(count: 271_828)
  399. }
  400. let messagesSent = duplexCall.newMessageQueue().flatMap {
  401. duplexCall.sendMessage(duplexRequest)
  402. }.flatMap {
  403. duplexCall.sendEnd()
  404. }
  405. try messagesSent.wait()
  406. try self.checkMetadata(call: duplexCall)
  407. }
  408. }
  409. /// This test verifies unary calls succeed in sending messages, and propagate back status code and
  410. /// message sent along with the messages.
  411. ///
  412. /// Server features:
  413. /// - UnaryCall
  414. /// - FullDuplexCall
  415. /// - Echo Status
  416. ///
  417. /// Procedure:
  418. /// 1. Client calls UnaryCall with:
  419. /// ```
  420. /// {
  421. /// response_status:{
  422. /// code: 2
  423. /// message: "test status message"
  424. /// }
  425. /// }
  426. /// ```
  427. /// 2. Client calls FullDuplexCall with:
  428. /// ```
  429. /// {
  430. /// response_status:{
  431. /// code: 2
  432. /// message: "test status message"
  433. /// }
  434. /// }
  435. /// ```
  436. /// 3. and then half-closes
  437. ///
  438. /// Client asserts:
  439. /// - received status code is the same as the sent code for both Procedure steps 1 and 2
  440. /// - received status message is the same as the sent message for both Procedure steps 1 and 2
  441. class StatusCodeAndMessage: InteroperabilityTest {
  442. let expectedCode = 2
  443. let expectedMessage = "test status message"
  444. func checkStatus<SpecificClientCall>(call: SpecificClientCall) throws where SpecificClientCall: ClientCall {
  445. try waitAndAssertEqual(call.status.map { $0.code.rawValue }, self.expectedCode)
  446. try waitAndAssertEqual(call.status.map { $0.message }, self.expectedMessage)
  447. }
  448. func run(using connection: ClientConnection) throws {
  449. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  450. let echoStatus = Grpc_Testing_EchoStatus(code: Int32(self.expectedCode), message: self.expectedMessage)
  451. let unaryCall = client.unaryCall(.withStatus(of: echoStatus))
  452. try self.checkStatus(call: unaryCall)
  453. var responses: [Grpc_Testing_StreamingOutputCallResponse] = []
  454. let duplexCall = client.fullDuplexCall { response in
  455. responses.append(response)
  456. }
  457. try duplexCall.newMessageQueue().flatMap {
  458. duplexCall.sendMessage(.withStatus(of: echoStatus))
  459. }.wait()
  460. try self.checkStatus(call: duplexCall)
  461. try assertEqual(responses, [])
  462. }
  463. }
  464. /// This test verifies Unicode and whitespace is correctly processed in status message. "\t" is
  465. /// horizontal tab. "\r" is carriage return. "\n" is line feed.
  466. ///
  467. /// Server features:
  468. /// - UnaryCall
  469. /// - Echo Status
  470. ///
  471. /// Procedure:
  472. /// 1. Client calls UnaryCall with:
  473. /// ```
  474. /// {
  475. /// response_status:{
  476. /// code: 2
  477. /// message: "\t\ntest with whitespace\r\nand Unicode BMP ☺ and non-BMP 😈\t\n"
  478. /// }
  479. /// }
  480. /// ```
  481. ///
  482. /// Client asserts:
  483. /// - received status code is the same as the sent code for Procedure step 1
  484. /// - received status message is the same as the sent message for Procedure step 1, including all
  485. /// whitespace characters
  486. class SpecialStatusMessage: InteroperabilityTest {
  487. func run(using connection: ClientConnection) throws {
  488. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  489. let code = 2
  490. let message = "\t\ntest with whitespace\r\nand Unicode BMP ☺ and non-BMP 😈\t\n"
  491. let call = client.unaryCall(.withStatus(of: .init(code: Int32(code), message: message)))
  492. try waitAndAssertEqual(call.status.map { $0.code.rawValue }, code)
  493. try waitAndAssertEqual(call.status.map { $0.message }, message)
  494. }
  495. }
  496. /// This test verifies that calling an unimplemented RPC method returns the UNIMPLEMENTED status
  497. /// code.
  498. ///
  499. /// Server features: N/A
  500. ///
  501. /// Procedure:
  502. /// 1. Client calls grpc.testing.TestService/UnimplementedCall with an empty request (defined as
  503. /// grpc.testing.Empty):
  504. /// ```
  505. /// {
  506. /// }
  507. /// ```
  508. ///
  509. /// Client asserts:
  510. /// - received status code is 12 (UNIMPLEMENTED)
  511. class UnimplementedMethod: InteroperabilityTest {
  512. func run(using connection: ClientConnection) throws {
  513. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  514. let call = client.unimplementedCall(Grpc_Testing_Empty())
  515. try waitAndAssertEqual(call.status.map { $0.code }, .unimplemented)
  516. }
  517. }
  518. /// This test verifies calling an unimplemented server returns the UNIMPLEMENTED status code.
  519. ///
  520. /// Server features: N/A
  521. ///
  522. /// Procedure:
  523. /// 1. Client calls grpc.testing.UnimplementedService/UnimplementedCall with an empty request
  524. /// (defined as grpc.testing.Empty):
  525. /// ```
  526. /// {
  527. /// }
  528. /// ```
  529. ///
  530. /// Client asserts:
  531. /// - received status code is 12 (UNIMPLEMENTED)
  532. class UnimplementedService: InteroperabilityTest {
  533. func run(using connection: ClientConnection) throws {
  534. let client = Grpc_Testing_UnimplementedServiceServiceClient(connection: connection)
  535. let call = client.unimplementedCall(Grpc_Testing_Empty())
  536. try waitAndAssertEqual(call.status.map { $0.code }, .unimplemented)
  537. }
  538. }
  539. /// This test verifies that a request can be cancelled after metadata has been sent but before
  540. /// payloads are sent.
  541. ///
  542. /// Server features:
  543. /// - StreamingInputCall
  544. ///
  545. /// Procedure:
  546. /// 1. Client starts StreamingInputCall
  547. /// 2. Client immediately cancels request
  548. ///
  549. /// Client asserts:
  550. /// - Call completed with status CANCELLED
  551. class CancelAfterBegin: InteroperabilityTest {
  552. func run(using connection: ClientConnection) throws {
  553. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  554. let call = client.streamingInputCall()
  555. call.cancel()
  556. try waitAndAssertEqual(call.status.map { $0.code }, .cancelled)
  557. }
  558. }
  559. /// This test verifies that a request can be cancelled after receiving a message from the server.
  560. ///
  561. /// Server features:
  562. /// - FullDuplexCall
  563. ///
  564. /// Procedure:
  565. /// 1. Client starts FullDuplexCall with
  566. /// ```
  567. /// {
  568. /// response_parameters:{
  569. /// size: 31415
  570. /// }
  571. /// payload:{
  572. /// body: 27182 bytes of zeros
  573. /// }
  574. /// }
  575. /// ```
  576. /// 2. After receiving a response, client cancels request
  577. ///
  578. /// Client asserts:
  579. /// - Call completed with status CANCELLED
  580. class CancelAfterFirstResponse: InteroperabilityTest {
  581. func run(using connection: ClientConnection) throws {
  582. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  583. let promise = client.connection.eventLoop.makePromise(of: Void.self)
  584. let call = client.fullDuplexCall { _ in
  585. promise.succeed(())
  586. }
  587. promise.futureResult.whenSuccess {
  588. call.cancel()
  589. }
  590. let request = Grpc_Testing_StreamingOutputCallRequest.with { request in
  591. request.responseParameters = [.size(31_415)]
  592. request.payload = .zeros(count: 27_182)
  593. }
  594. call.sendMessage(request, promise: nil)
  595. try waitAndAssertEqual(call.status.map { $0.code }, .cancelled)
  596. }
  597. }
  598. /// This test verifies that an RPC request whose lifetime exceeds its configured timeout value
  599. /// will end with the DeadlineExceeded status.
  600. ///
  601. /// Server features:
  602. /// - FullDuplexCall
  603. ///
  604. /// Procedure:
  605. /// 1. Client calls FullDuplexCall with the following request and sets its timeout to 1ms
  606. /// ```
  607. /// {
  608. /// payload:{
  609. /// body: 27182 bytes of zeros
  610. /// }
  611. /// }
  612. /// ```
  613. /// 2. Client waits
  614. ///
  615. /// Client asserts:
  616. /// - Call completed with status DEADLINE_EXCEEDED.
  617. class TimeoutOnSleepingServer: InteroperabilityTest {
  618. func run(using connection: ClientConnection) throws {
  619. let client = Grpc_Testing_TestServiceServiceClient(connection: connection)
  620. let callOptions = CallOptions(timeout: try .milliseconds(1))
  621. let call = client.fullDuplexCall(callOptions: callOptions) { _ in }
  622. try waitAndAssertEqual(call.status.map { $0.code }, .deadlineExceeded)
  623. }
  624. }