2
0

WebSocketTests.swift 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. //
  2. // WebSocketTests.swift
  3. // Alamofire
  4. //
  5. // Created by Jon Shier on 1/17/21.
  6. // Copyright © 2021 Alamofire. All rights reserved.
  7. //
  8. #if canImport(Darwin) && !canImport(FoundationNetworking) // Only Apple platforms support URLSessionWebSocketTask.
  9. @_spi(WebSocket) import Alamofire
  10. import Foundation
  11. import XCTest
  12. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  13. final class WebSocketTests: BaseTestCase {
  14. func testThatWebSocketsCanReceiveMessageEvents() {
  15. // Given
  16. let didConnect = expectation(description: "didConnect")
  17. let didReceiveMessage = expectation(description: "didReceiveMessage")
  18. let didDisconnect = expectation(description: "didDisconnect")
  19. let didComplete = expectation(description: "didComplete")
  20. let session = stored(Session())
  21. var connectedProtocol: String?
  22. var message: URLSessionWebSocketTask.Message?
  23. var closeCode: URLSessionWebSocketTask.CloseCode?
  24. var closeReason: Data?
  25. var receivedCompletion: WebSocketRequest.Completion?
  26. // When
  27. session.webSocketRequest(.websocket()).streamMessageEvents { event in
  28. switch event.kind {
  29. case let .connected(`protocol`):
  30. connectedProtocol = `protocol`
  31. didConnect.fulfill()
  32. case let .receivedMessage(receivedMessage):
  33. message = receivedMessage
  34. didReceiveMessage.fulfill()
  35. case let .disconnected(code, reason):
  36. closeCode = code
  37. closeReason = reason
  38. didDisconnect.fulfill()
  39. case let .completed(completion):
  40. receivedCompletion = completion
  41. didComplete.fulfill()
  42. }
  43. }
  44. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  45. timeout: timeout,
  46. enforceOrder: true)
  47. // Then
  48. XCTAssertNil(connectedProtocol)
  49. XCTAssertNotNil(message)
  50. XCTAssertEqual(closeCode, .normalClosure)
  51. XCTAssertNil(closeReason)
  52. XCTAssertNil(receivedCompletion?.error)
  53. }
  54. func testThatWebSocketsCanReceiveMessageEventsWithParameters() {
  55. // Given
  56. let didConnect = expectation(description: "didConnect")
  57. let didReceiveMessage = expectation(description: "didReceiveMessage")
  58. let didDisconnect = expectation(description: "didDisconnect")
  59. let didComplete = expectation(description: "didComplete")
  60. let session = stored(Session())
  61. var connectedProtocol: String?
  62. var message: URLSessionWebSocketTask.Message?
  63. var closeCode: URLSessionWebSocketTask.CloseCode?
  64. var closeReason: Data?
  65. var receivedCompletion: WebSocketRequest.Completion?
  66. // When
  67. session.webSocketRequest(.websocket()).streamMessageEvents { event in
  68. switch event.kind {
  69. case let .connected(`protocol`):
  70. connectedProtocol = `protocol`
  71. didConnect.fulfill()
  72. case let .receivedMessage(receivedMessage):
  73. message = receivedMessage
  74. didReceiveMessage.fulfill()
  75. case let .disconnected(code, reason):
  76. closeCode = code
  77. closeReason = reason
  78. didDisconnect.fulfill()
  79. case let .completed(completion):
  80. receivedCompletion = completion
  81. didComplete.fulfill()
  82. }
  83. }
  84. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  85. timeout: timeout,
  86. enforceOrder: true)
  87. // Then
  88. XCTAssertNil(connectedProtocol)
  89. XCTAssertNotNil(message)
  90. XCTAssertEqual(closeCode, .normalClosure)
  91. XCTAssertNil(closeReason)
  92. XCTAssertNil(receivedCompletion?.error)
  93. }
  94. func testThatWebSocketsCanReceiveAMessage() {
  95. // Given
  96. let didReceiveMessage = expectation(description: "didReceiveMessage")
  97. let didComplete = expectation(description: "didComplete")
  98. let session = stored(Session())
  99. var receivedMessage: URLSessionWebSocketTask.Message?
  100. // When
  101. session.webSocketRequest(.websocket()).streamMessages { message in
  102. receivedMessage = message
  103. didReceiveMessage.fulfill()
  104. }
  105. .onCompletion {
  106. didComplete.fulfill()
  107. }
  108. wait(for: [didReceiveMessage, didComplete], timeout: timeout, enforceOrder: true)
  109. // Then
  110. XCTAssertNotNil(receivedMessage)
  111. XCTAssertNotNil(receivedMessage?.data)
  112. }
  113. func testThatWebSocketsCanReceiveADecodableMessage() {
  114. // Given
  115. let didConnect = expectation(description: "didConnect")
  116. let didReceiveMessage = expectation(description: "didReceiveMessage")
  117. let didDisconnect = expectation(description: "didDisconnect")
  118. let didComplete = expectation(description: "didComplete")
  119. let session = stored(Session())
  120. var connectedProtocol: String?
  121. var message: TestResponse?
  122. var closeCode: URLSessionWebSocketTask.CloseCode?
  123. var closeReason: Data?
  124. var receivedCompletion: WebSocketRequest.Completion?
  125. // When
  126. session.webSocketRequest(.websocketCount(1)).streamDecodableEvents(TestResponse.self) { event in
  127. switch event.kind {
  128. case let .connected(`protocol`):
  129. connectedProtocol = `protocol`
  130. didConnect.fulfill()
  131. case let .receivedMessage(receivedMessage):
  132. message = receivedMessage
  133. didReceiveMessage.fulfill()
  134. case let .serializerFailed(error):
  135. XCTFail("websocket message serialization failed with error: \(error)")
  136. case let .disconnected(code, reason):
  137. closeCode = code
  138. closeReason = reason
  139. didDisconnect.fulfill()
  140. case let .completed(completion):
  141. receivedCompletion = completion
  142. didComplete.fulfill()
  143. }
  144. }
  145. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  146. timeout: timeout,
  147. enforceOrder: true)
  148. // Then
  149. XCTAssertNil(connectedProtocol)
  150. XCTAssertNotNil(message)
  151. XCTAssertEqual(closeCode, .normalClosure)
  152. XCTAssertNil(closeReason)
  153. XCTAssertNil(receivedCompletion?.error)
  154. }
  155. func testThatWebSocketsCanReceiveADecodableValue() {
  156. // Given
  157. let didReceiveValue = expectation(description: "didReceiveMessage")
  158. let didComplete = expectation(description: "didComplete")
  159. let session = stored(Session())
  160. var receivedValue: TestResponse?
  161. // When
  162. session.webSocketRequest(.websocket()).streamDecodable(TestResponse.self) { value in
  163. receivedValue = value
  164. didReceiveValue.fulfill()
  165. }
  166. .onCompletion {
  167. didComplete.fulfill()
  168. }
  169. wait(for: [didReceiveValue, didComplete], timeout: timeout, enforceOrder: true)
  170. // Then
  171. XCTAssertNotNil(receivedValue)
  172. }
  173. func testThatWebSocketsCanReceiveAMessageWithAProtocol() {
  174. // Given
  175. let didConnect = expectation(description: "didConnect")
  176. let didReceiveMessage = expectation(description: "didReceiveMessage")
  177. let didDisconnect = expectation(description: "didDisconnect")
  178. let didComplete = expectation(description: "didComplete")
  179. let session = stored(Session())
  180. let `protocol` = "protocol"
  181. var connectedProtocol: String?
  182. var message: URLSessionWebSocketTask.Message?
  183. var closeCode: URLSessionWebSocketTask.CloseCode?
  184. var closeReason: Data?
  185. var receivedCompletion: WebSocketRequest.Completion?
  186. // When
  187. session.webSocketRequest(.websocket(), configuration: .protocol(`protocol`)).streamMessageEvents { event in
  188. switch event.kind {
  189. case let .connected(`protocol`):
  190. connectedProtocol = `protocol`
  191. didConnect.fulfill()
  192. case let .receivedMessage(receivedMessage):
  193. message = receivedMessage
  194. didReceiveMessage.fulfill()
  195. case let .disconnected(code, reason):
  196. closeCode = code
  197. closeReason = reason
  198. didDisconnect.fulfill()
  199. case let .completed(completion):
  200. receivedCompletion = completion
  201. didComplete.fulfill()
  202. }
  203. }
  204. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  205. timeout: timeout,
  206. enforceOrder: true)
  207. // Then
  208. XCTAssertEqual(connectedProtocol, `protocol`)
  209. XCTAssertNotNil(message)
  210. XCTAssertEqual(closeCode, .normalClosure)
  211. XCTAssertNil(closeReason)
  212. XCTAssertNil(receivedCompletion?.error)
  213. }
  214. func testThatWebSocketsCanReceiveMultipleMessages() {
  215. // Given
  216. let count = 5
  217. let didConnect = expectation(description: "didConnect")
  218. let didReceiveMessage = expectation(description: "didReceiveMessage")
  219. didReceiveMessage.expectedFulfillmentCount = count
  220. let didDisconnect = expectation(description: "didDisconnect")
  221. let didComplete = expectation(description: "didComplete")
  222. let session = stored(Session())
  223. var connectedProtocol: String?
  224. var messages: [URLSessionWebSocketTask.Message] = []
  225. var closeCode: URLSessionWebSocketTask.CloseCode?
  226. var closeReason: Data?
  227. var receivedCompletion: WebSocketRequest.Completion?
  228. // When
  229. session.webSocketRequest(.websocketCount(count)).streamMessageEvents { event in
  230. switch event.kind {
  231. case let .connected(`protocol`):
  232. connectedProtocol = `protocol`
  233. didConnect.fulfill()
  234. case let .receivedMessage(receivedMessage):
  235. messages.append(receivedMessage)
  236. didReceiveMessage.fulfill()
  237. case let .disconnected(code, reason):
  238. closeCode = code
  239. closeReason = reason
  240. didDisconnect.fulfill()
  241. case let .completed(completion):
  242. receivedCompletion = completion
  243. didComplete.fulfill()
  244. }
  245. }
  246. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete], timeout: timeout, enforceOrder: true)
  247. // Then
  248. XCTAssertNil(connectedProtocol)
  249. XCTAssertEqual(messages.count, count)
  250. XCTAssertEqual(closeCode, .normalClosure)
  251. XCTAssertNil(closeReason)
  252. XCTAssertNil(receivedCompletion?.error)
  253. }
  254. func testThatWebSocketsCanSendAndReceiveMessages() {
  255. // Given
  256. let didConnect = expectation(description: "didConnect")
  257. let didSend = expectation(description: "didSend")
  258. let didReceiveMessage = expectation(description: "didReceiveMessage")
  259. let didDisconnect = expectation(description: "didDisconnect")
  260. let didComplete = expectation(description: "didComplete")
  261. let session = stored(Session())
  262. let sentMessage = URLSessionWebSocketTask.Message.string("Echo")
  263. var connectedProtocol: String?
  264. var message: URLSessionWebSocketTask.Message?
  265. var closeCode: URLSessionWebSocketTask.CloseCode?
  266. var closeReason: Data?
  267. var receivedCompletion: WebSocketRequest.Completion?
  268. // When
  269. let request = session.webSocketRequest(.websocketEcho)
  270. request.streamMessageEvents { event in
  271. switch event.kind {
  272. case let .connected(`protocol`):
  273. connectedProtocol = `protocol`
  274. didConnect.fulfill()
  275. request.send(sentMessage) { _ in didSend.fulfill() }
  276. case let .receivedMessage(receivedMessage):
  277. message = receivedMessage
  278. event.close(sending: .normalClosure)
  279. didReceiveMessage.fulfill()
  280. case let .disconnected(code, reason):
  281. closeCode = code
  282. closeReason = reason
  283. didDisconnect.fulfill()
  284. case let .completed(completion):
  285. receivedCompletion = completion
  286. didComplete.fulfill()
  287. }
  288. }
  289. wait(for: [didConnect, didSend, didReceiveMessage, didDisconnect, didComplete],
  290. timeout: timeout,
  291. enforceOrder: true)
  292. // Then
  293. XCTAssertNil(connectedProtocol)
  294. XCTAssertNotNil(message)
  295. XCTAssertEqual(sentMessage, message)
  296. XCTAssertEqual(closeCode, .normalClosure)
  297. XCTAssertNil(closeReason)
  298. XCTAssertNil(receivedCompletion?.error)
  299. }
  300. func testThatWebSocketsCanBeCancelled() {
  301. // Given
  302. let didConnect = expectation(description: "didConnect")
  303. let didComplete = expectation(description: "didComplete")
  304. let session = stored(Session())
  305. var connectedProtocol: String?
  306. var receivedCompletion: WebSocketRequest.Completion?
  307. // When
  308. let request = session.webSocketRequest(.websocketEcho)
  309. request.streamMessageEvents { event in
  310. switch event.kind {
  311. case let .connected(`protocol`):
  312. connectedProtocol = `protocol`
  313. didConnect.fulfill()
  314. request.cancel()
  315. case let .receivedMessage(receivedMessage):
  316. XCTFail("cancelled socket received message: \(receivedMessage)")
  317. case .disconnected:
  318. XCTFail("cancelled socket shouldn't receive disconnected event")
  319. case let .completed(completion):
  320. receivedCompletion = completion
  321. didComplete.fulfill()
  322. }
  323. }
  324. wait(for: [didConnect, didComplete], timeout: timeout, enforceOrder: true)
  325. // Then
  326. XCTAssertNil(connectedProtocol)
  327. XCTAssertTrue(receivedCompletion?.error?.isExplicitlyCancelledError == true)
  328. XCTAssertTrue(request.error?.isExplicitlyCancelledError == true)
  329. }
  330. func testOnePingOnly() {
  331. // Given
  332. let didConnect = expectation(description: "didConnect")
  333. let didSend = expectation(description: "didSend")
  334. let didReceiveMessage = expectation(description: "didReceiveMessage")
  335. let didReceivePong = expectation(description: "didReceivePong")
  336. didReceivePong.expectedFulfillmentCount = 100
  337. let didDisconnect = expectation(description: "didDisconnect")
  338. let didComplete = expectation(description: "didComplete")
  339. let session = stored(Session())
  340. let sentMessage = URLSessionWebSocketTask.Message.string("Echo")
  341. var connectedProtocol: String?
  342. var message: URLSessionWebSocketTask.Message?
  343. var receivedPong: WebSocketRequest.PingResponse.Pong?
  344. var closeCode: URLSessionWebSocketTask.CloseCode?
  345. var closeReason: Data?
  346. var receivedCompletion: WebSocketRequest.Completion?
  347. // When
  348. let request = session.webSocketRequest(.websocketEcho)
  349. request.streamMessageEvents { event in
  350. switch event.kind {
  351. case let .connected(`protocol`):
  352. connectedProtocol = `protocol`
  353. didConnect.fulfill()
  354. request.send(sentMessage) { _ in didSend.fulfill() }
  355. case let .receivedMessage(receivedMessage):
  356. message = receivedMessage
  357. didReceiveMessage.fulfill()
  358. for count in 0..<100 {
  359. request.sendPing { response in
  360. switch response {
  361. case let .pong(pong):
  362. receivedPong = pong
  363. default:
  364. break
  365. }
  366. didReceivePong.fulfill()
  367. if count == 99 {
  368. request.close(sending: .normalClosure)
  369. }
  370. }
  371. }
  372. case let .disconnected(code, reason):
  373. closeCode = code
  374. closeReason = reason
  375. didDisconnect.fulfill()
  376. case let .completed(completion):
  377. receivedCompletion = completion
  378. didComplete.fulfill()
  379. }
  380. }
  381. wait(for: [didConnect, didSend, didReceiveMessage, didReceivePong, didDisconnect, didComplete],
  382. timeout: timeout,
  383. enforceOrder: true)
  384. // Then
  385. XCTAssertNil(connectedProtocol)
  386. XCTAssertNotNil(message)
  387. XCTAssertEqual(sentMessage, message)
  388. XCTAssertEqual(closeCode, .normalClosure)
  389. XCTAssertNil(closeReason)
  390. XCTAssertNotNil(receivedCompletion)
  391. XCTAssertNil(receivedCompletion?.error)
  392. XCTAssertNotNil(receivedPong)
  393. }
  394. func testThatTimePingsOccur() {
  395. // Given
  396. let didConnect = expectation(description: "didConnect")
  397. let didDisconnect = expectation(description: "didDisconnect")
  398. let didComplete = expectation(description: "didComplete")
  399. let session = stored(Session())
  400. var connectedProtocol: String?
  401. var closeCode: URLSessionWebSocketTask.CloseCode?
  402. var closeReason: Data?
  403. var receivedCompletion: WebSocketRequest.Completion?
  404. // When
  405. let request = session.webSocketRequest(.websocketPings(), configuration: .pingInterval(0.01))
  406. request.streamMessageEvents { event in
  407. switch event.kind {
  408. case let .connected(`protocol`):
  409. connectedProtocol = `protocol`
  410. didConnect.fulfill()
  411. case .receivedMessage:
  412. break
  413. case let .disconnected(code, reason):
  414. closeCode = code
  415. closeReason = reason
  416. didDisconnect.fulfill()
  417. case let .completed(completion):
  418. receivedCompletion = completion
  419. didComplete.fulfill()
  420. }
  421. }
  422. wait(for: [didConnect, didDisconnect, didComplete], timeout: timeout, enforceOrder: true)
  423. // Then
  424. XCTAssertNil(connectedProtocol)
  425. XCTAssertEqual(closeCode, .goingAway) // Default Vapor close() code.
  426. XCTAssertNil(closeReason)
  427. XCTAssertNotNil(receivedCompletion)
  428. XCTAssertNil(receivedCompletion?.error)
  429. }
  430. func testThatWebSocketFailsWithTooSmallMaximumMessageSize() {
  431. // Given
  432. let didConnect = expectation(description: "didConnect")
  433. let didComplete = expectation(description: "didComplete")
  434. let session = stored(Session())
  435. var connectedProtocol: String?
  436. var receivedCompletion: WebSocketRequest.Completion?
  437. // When
  438. session.webSocketRequest(.websocket(), configuration: .maximumMessageSize(1)).streamMessageEvents { event in
  439. switch event.kind {
  440. case let .connected(`protocol`):
  441. connectedProtocol = `protocol`
  442. didConnect.fulfill()
  443. case .receivedMessage, .disconnected:
  444. break
  445. case let .completed(completion):
  446. receivedCompletion = completion
  447. didComplete.fulfill()
  448. }
  449. }
  450. wait(for: [didConnect, didComplete], timeout: timeout, enforceOrder: true)
  451. // Then
  452. XCTAssertNil(connectedProtocol)
  453. XCTAssertNotNil(receivedCompletion?.error)
  454. }
  455. func testThatWebSocketsFinishAfterNonNormalResponseCode() {
  456. // Given
  457. let didConnect = expectation(description: "didConnect")
  458. let didReceiveMessage = expectation(description: "didReceiveMessage")
  459. let didDisconnect = expectation(description: "didDisconnect")
  460. let didComplete = expectation(description: "didComplete")
  461. let session = stored(Session())
  462. var connectedProtocol: String?
  463. var message: URLSessionWebSocketTask.Message?
  464. var closeCode: URLSessionWebSocketTask.CloseCode?
  465. var closeReason: Data?
  466. var receivedCompletion: WebSocketRequest.Completion?
  467. // When
  468. session.webSocketRequest(.websocket(closeCode: .goingAway)).streamMessageEvents { event in
  469. switch event.kind {
  470. case let .connected(`protocol`):
  471. connectedProtocol = `protocol`
  472. didConnect.fulfill()
  473. case let .receivedMessage(receivedMessage):
  474. message = receivedMessage
  475. didReceiveMessage.fulfill()
  476. case let .disconnected(code, reason):
  477. closeCode = code
  478. closeReason = reason
  479. didDisconnect.fulfill()
  480. case let .completed(completion):
  481. receivedCompletion = completion
  482. didComplete.fulfill()
  483. }
  484. }
  485. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  486. timeout: timeout,
  487. enforceOrder: true)
  488. // Then
  489. XCTAssertNil(connectedProtocol)
  490. XCTAssertNotNil(message)
  491. XCTAssertEqual(closeCode, .goingAway)
  492. XCTAssertNil(closeReason)
  493. XCTAssertNil(receivedCompletion?.error)
  494. }
  495. func testThatWebSocketsCanHaveMultipleHandlers() {
  496. // Given
  497. let didConnect = expectation(description: "didConnect")
  498. didConnect.expectedFulfillmentCount = 2
  499. let didReceiveMessage = expectation(description: "didReceiveMessage")
  500. didReceiveMessage.expectedFulfillmentCount = 2
  501. let didDisconnect = expectation(description: "didDisconnect")
  502. didDisconnect.expectedFulfillmentCount = 2
  503. let didComplete = expectation(description: "didComplete")
  504. didComplete.expectedFulfillmentCount = 2
  505. let session = stored(Session())
  506. var firstConnectedProtocol: String?
  507. var firstMessage: URLSessionWebSocketTask.Message?
  508. var firstCloseCode: URLSessionWebSocketTask.CloseCode?
  509. var firstCloseReason: Data?
  510. var firstReceivedCompletion: WebSocketRequest.Completion?
  511. var secondConnectedProtocol: String?
  512. var secondMessage: URLSessionWebSocketTask.Message?
  513. var secondCloseCode: URLSessionWebSocketTask.CloseCode?
  514. var secondCloseReason: Data?
  515. var secondReceivedCompletion: WebSocketRequest.Completion?
  516. // When
  517. session.webSocketRequest(.websocket(closeCode: .goingAway)).streamMessageEvents { event in
  518. switch event.kind {
  519. case let .connected(`protocol`):
  520. firstConnectedProtocol = `protocol`
  521. didConnect.fulfill()
  522. case let .receivedMessage(receivedMessage):
  523. firstMessage = receivedMessage
  524. didReceiveMessage.fulfill()
  525. case let .disconnected(code, reason):
  526. firstCloseCode = code
  527. firstCloseReason = reason
  528. didDisconnect.fulfill()
  529. case let .completed(completion):
  530. firstReceivedCompletion = completion
  531. didComplete.fulfill()
  532. }
  533. }
  534. .streamMessageEvents { event in
  535. switch event.kind {
  536. case let .connected(`protocol`):
  537. secondConnectedProtocol = `protocol`
  538. didConnect.fulfill()
  539. case let .receivedMessage(receivedMessage):
  540. secondMessage = receivedMessage
  541. didReceiveMessage.fulfill()
  542. case let .disconnected(code, reason):
  543. secondCloseCode = code
  544. secondCloseReason = reason
  545. didDisconnect.fulfill()
  546. case let .completed(completion):
  547. secondReceivedCompletion = completion
  548. didComplete.fulfill()
  549. }
  550. }
  551. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  552. timeout: timeout,
  553. enforceOrder: true)
  554. // Then
  555. XCTAssertNil(firstConnectedProtocol)
  556. XCTAssertEqual(firstConnectedProtocol, secondConnectedProtocol)
  557. XCTAssertNotNil(firstMessage)
  558. XCTAssertEqual(firstMessage, secondMessage)
  559. XCTAssertEqual(firstCloseCode, .goingAway)
  560. XCTAssertEqual(firstCloseCode, secondCloseCode)
  561. XCTAssertNil(firstCloseReason)
  562. XCTAssertEqual(firstCloseReason, secondCloseReason)
  563. XCTAssertNil(firstReceivedCompletion?.error)
  564. XCTAssertNil(secondReceivedCompletion?.error)
  565. }
  566. }
  567. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  568. final class WebSocketIntegrationTests: BaseTestCase {
  569. func testThatWebSocketsCanReceiveMessageEventsAfterRetry() {
  570. // Given
  571. let didConnect = expectation(description: "didConnect")
  572. let didReceiveMessage = expectation(description: "didReceiveMessage")
  573. let didDisconnect = expectation(description: "didDisconnect")
  574. let didComplete = expectation(description: "didComplete")
  575. let session = stored(Session())
  576. var connectedProtocol: String?
  577. var message: URLSessionWebSocketTask.Message?
  578. var closeCode: URLSessionWebSocketTask.CloseCode?
  579. var closeReason: Data?
  580. var receivedCompletion: WebSocketRequest.Completion?
  581. // When
  582. session.webSocketRequest(performing: .endpoints(.status(500), .websocket()), interceptor: .retryPolicy)
  583. .streamMessageEvents { event in
  584. switch event.kind {
  585. case let .connected(`protocol`):
  586. connectedProtocol = `protocol`
  587. didConnect.fulfill()
  588. case let .receivedMessage(receivedMessage):
  589. message = receivedMessage
  590. didReceiveMessage.fulfill()
  591. case let .disconnected(code, reason):
  592. closeCode = code
  593. closeReason = reason
  594. didDisconnect.fulfill()
  595. case let .completed(completion):
  596. receivedCompletion = completion
  597. didComplete.fulfill()
  598. }
  599. }
  600. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  601. timeout: 100,
  602. enforceOrder: true)
  603. // Then
  604. XCTAssertNil(connectedProtocol)
  605. XCTAssertNotNil(message)
  606. XCTAssertEqual(closeCode, .normalClosure)
  607. XCTAssertNil(closeReason)
  608. XCTAssertNil(receivedCompletion?.error)
  609. }
  610. }
  611. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  612. extension WebSocketRequest {
  613. @discardableResult
  614. func onCompletion(queue: DispatchQueue = .main, handler: @escaping @Sendable () -> Void) -> Self {
  615. streamMessageEvents(on: queue) { event in
  616. guard case .completed = event.kind else { return }
  617. handler()
  618. }
  619. }
  620. }
  621. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  622. extension Foundation.URLSessionWebSocketTask.Message: Swift.Equatable {
  623. public static func ==(lhs: URLSessionWebSocketTask.Message, rhs: URLSessionWebSocketTask.Message) -> Bool {
  624. switch (lhs, rhs) {
  625. case let (.string(left), .string(right)):
  626. left == right
  627. case let (.data(left), .data(right)):
  628. left == right
  629. default:
  630. false
  631. }
  632. }
  633. var string: String? {
  634. guard case let .string(string) = self else { return nil }
  635. return string
  636. }
  637. var data: Data? {
  638. guard case let .data(data) = self else { return nil }
  639. return data
  640. }
  641. }
  642. extension Session {
  643. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  644. func webSocketRequest(_ endpoint: Endpoint,
  645. configuration: WebSocketRequest.Configuration = .default,
  646. interceptor: (any RequestInterceptor)? = nil) -> WebSocketRequest {
  647. webSocketRequest(performing: endpoint as (any URLRequestConvertible),
  648. configuration: configuration,
  649. interceptor: interceptor)
  650. }
  651. }
  652. #endif